Merge branch 'master' into logging
13
BUILDING_ON_LINUX.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Linux
|
||||
|
||||
## Ubuntu 18.04
|
||||
*most likely works the same for other Debian-like distros*
|
||||
1. Install dependencies (and the C++ IDE Qt Creator) `sudo apt install qtcreator qtmultimedia5-dev libqt5svg5-dev libboost-dev libssl-dev libboost-system-dev`
|
||||
2. Open `chatterino.pro` with QT Creator and build
|
||||
|
||||
## Arch Linux
|
||||
install [chatterino2-git](https://aur.archlinux.org/packages/chatterino2-git/) from the aur or build manually as follows:
|
||||
1. `sudo pacman -S qt5-base qt5-multimedia qt5-svg gst-plugins-ugly gst-plugins-good boost rapidjson`
|
||||
2. go into project directory
|
||||
3. create build folder `mkdir build && cd build`
|
||||
4. `qmake .. && make`
|
22
BUILDING_ON_MAC.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Building on Mac OSX
|
||||
1. install Xcode and Xcode Command Line Utilites
|
||||
2. start Xcode, settings -> Locations, activate your Command Line Tools
|
||||
3. install Qt Creator
|
||||
4. install brew https://brew.sh/
|
||||
5. `brew install boost openssl rapidjson`
|
||||
6. build the project using Qt Creator
|
||||
|
||||
If the Project does not build at this point, you might need to add additional Paths/Libs, because brew does not install openssl and boost in the common path. You can get their path using
|
||||
|
||||
`brew info openssl`
|
||||
`brew info boost`
|
||||
|
||||
The lines which you need to add to your project file should look similar to this
|
||||
|
||||
```
|
||||
INCLUDEPATH += /usr/local/opt/openssl/include
|
||||
LIBS += -L/usr/local/opt/openssl/lib
|
||||
|
||||
INCLUDEPATH += "/usr/local/Cellar/boost/1.67.0_1/include"
|
||||
LIBS += -L"/usr/local/Cellar/boost/1.67.0_1/lib"
|
||||
```
|
47
BUILDING_ON_WINDOWS.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
# Building on Windows (Recommended)
|
||||
## Using Qt Creator
|
||||
### Visual Studio 2017
|
||||
1. Install Visual Studio 2017 and select "Desktop development with C++" and "Universal Windows Platform development.
|
||||
|
||||
### Boost
|
||||
1. Visual Studio 2017 64-bit: https://dl.bintray.com/boostorg/release/1.66.0/binaries/boost_1_66_0-msvc-14.1-64.exe
|
||||
2. When prompted, install boost to C:\local\boost
|
||||
3. When the installation is finished, go to C:\local\boost and rename the "lib64-msvc-14.1" folder to "lib"
|
||||
|
||||
### OpenSSL
|
||||
#### For our websocket library, we need OpenSSL 1.1
|
||||
1. Download OpenSSL development library: https://slproweb.com/download/Win64OpenSSL-1_1_0h.exe
|
||||
2. When prompted, install openssl to C:\local\openssl
|
||||
3. When prompted, copy the OpenSSL DLLs to "The OpenSSL binaries (/bin) directory"
|
||||
#### For Qt SSL, we need OpenSSL 1.0
|
||||
1. Download OpenSSL light: https://slproweb.com/download/Win64OpenSSL_Light-1_0_2o.exe
|
||||
2. When prompted, install it anywhere
|
||||
3. When prompted, copy the OpenSSL DLLS to "The OpenSSL binaries (/bin) directory"
|
||||
4. Copy the OpenSSL 1.0 files from its /bin folder to C:/local/bin (You will need to create the folder)
|
||||
5. Then copy the OpenSSL 1.1 files from its /bin folder to C:/local/bin (Overwrite any duplicate files)
|
||||
6. Add C:/local/bin to your path folder (Follow guide here if you don't know how to do it: https://www.computerhope.com/issues/ch000549.htm#windows8 )
|
||||
|
||||
### Qt
|
||||
1. Download Qt: https://www.qt.io/download
|
||||
2. Select "Open source" at the bottom of this page
|
||||
3. Then select "Download"
|
||||
#### When prompted which components to install:
|
||||
1. Under the latest Qt version:
|
||||
- Select MSVC 2017 64-bit (or MSVC 2015 64-bit if you still use Visual Studio 2015)
|
||||
- Optionally, enable Qt WebEngine
|
||||
2. Under Tools:
|
||||
- Select Qt Creator, and Qt Creator CDB Debugger Support
|
||||
|
||||
# Windows (Using MSYS2, not recommended)
|
||||
Note: This guide is currently out of date and will not work as is.
|
||||
Note: This build will have some features missing from the build.
|
||||
|
||||
Building using MSYS2 can be quite easier process. Check out MSYS2 at [msys2.org](http://www.msys2.org/).
|
||||
|
||||
Be sure to add `-j <number of threads>` as a make argument so it will use all your cpu cores to build. [example setup](https://i.imgur.com/qlESlS1.png)
|
||||
|
||||
You can also add `-o2` to optimize the final binary size but increase compilation time, and add `-pipe` to use more ram in compilation but increase compilation speed
|
||||
1. open appropriate MSYS2 terminal and do `pacman -S mingw-w64-<arch>-boost mingw-w64-<arch>-qt5 mingw-w64-<arch>-rapidjson` where `<arch>` is `x86_64` or `i686`
|
||||
2. go into the project directory
|
||||
3. create build folder `mkdir build && cd build`
|
||||
4. `qmake .. && mingw32-make`
|
110
README.md
|
@ -4,107 +4,27 @@ Chatterino 2
|
|||
|
||||
Chatterino 2 is the second installment of the Twitch chat client series "Chatterino". For now you can check out Chatterino 1 at [https://chatterino.com](https://chatterino.com).
|
||||
|
||||
## Code style
|
||||
The code is normally formated using clang format in Qt Creator. [.clang-format](https://github.com/fourtf/chatterino2/blob/master/.clang-format) contains the style file for clang format.
|
||||
|
||||
To setup automatic code formating with QT Creator, see [this guide](https://gist.github.com/pajlada/0296454198eb8f8789fd6fe7ea660c5b).
|
||||
|
||||
## Building
|
||||
Before building run `git submodule update --init --recursive` to get required submodules.
|
||||
|
||||
### Windows
|
||||
#### Using Qt Creator
|
||||
##### Visual Studio 2017
|
||||
1. Install Visual Studio 2017 and select "Desktop development with C++" and "Universal Windows Platform development.
|
||||
[Building on Windows](../master/BUILDING_ON_WINDOWS.md)
|
||||
|
||||
##### Boost
|
||||
1. Visual Studio 2017 64-bit: https://dl.bintray.com/boostorg/release/1.66.0/binaries/boost_1_66_0-msvc-14.1-64.exe
|
||||
2. When prompted, install boost to C:\local\boost
|
||||
3. When the installation is finished, go to C:\local\boost and rename the "lib64-msvc-14.1" folder to "lib"
|
||||
[Building on Linux](../master/BUILDING_ON_LINUX.md)
|
||||
|
||||
##### OpenSSL
|
||||
###### For our websocket library, we need OpenSSL 1.1
|
||||
1. Download OpenSSL development library: https://slproweb.com/download/Win64OpenSSL-1_1_0h.exe
|
||||
2. When prompted, install openssl to C:\local\openssl
|
||||
3. When prompted, copy the OpenSSL DLLs to "The OpenSSL binaries (/bin) directory"
|
||||
###### For Qt SSL, we need OpenSSL 1.0
|
||||
1. Download OpenSSL light: https://slproweb.com/download/Win64OpenSSL_Light-1_0_2o.exe
|
||||
2. When prompted, install it anywhere
|
||||
3. When prompted, copy the OpenSSL DLLS to "The OpenSSL binaries (/bin) directory"
|
||||
4. Copy the OpenSSL 1.0 files from its /bin folder to C:/local/bin (You will need to create the folder)
|
||||
5. Then copy the OpenSSL 1.1 files from its /bin folder to C:/local/bin (Overwrite any duplicate files)
|
||||
6. Add C:/local/bin to your path folder (Follow guide here if you don't know how to do it: https://www.computerhope.com/issues/ch000549.htm#windows8 )
|
||||
[Building on Mac](../master/BUILDING_ON_MAC.md)
|
||||
|
||||
##### Qt
|
||||
1. Download Qt: https://www.qt.io/download
|
||||
2. Select "Open source" at the bottom of this page
|
||||
3. Then select "Download"
|
||||
###### When prompted which components to install:
|
||||
1. Under the latest Qt version:
|
||||
- Select MSVC 2017 64-bit (or MSVC 2015 64-bit if you still use Visual Studio 2015)
|
||||
- Optionally, enable Qt WebEngine
|
||||
2. Under Tools:
|
||||
- Select Qt Creator, and Qt Creator CDB Debugger Support
|
||||
## Code style
|
||||
The code is formated using clang format in Qt Creator. [.clang-format](https://github.com/fourtf/chatterino2/blob/master/.clang-format) contains the style file for clang format.
|
||||
|
||||
To setup automatic code formating with QT Creator, see [this guide](https://gist.github.com/pajlada/0296454198eb8f8789fd6fe7ea660c5b).
|
||||
|
||||
### Windows (Using MSYS2)
|
||||
Note: This guide is currently out of date and will not work as is.
|
||||
Note: This build will have some features missing from the build.
|
||||
### Get it automated with QT Creator + Beautifier + Clang Format
|
||||
1. Download LLVM: http://releases.llvm.org/5.0.1/LLVM-5.0.1-win64.exe
|
||||
2. During the installation, make sure to add it to your path
|
||||
3. In QT Creator, select `Help` > `About Plugins` > `C++` > `Beautifier` to enable the plugin
|
||||
4. Restart QT Creator
|
||||
5. Select `Tools` > `Options` > `Beautifier`
|
||||
6. Under `General` select `Tool: ClangFormat` and enable `Automatic Formatting on File Save`
|
||||
7. Under `Clang Format` select `Use predefined style: File` and `Fallback style: None`
|
||||
|
||||
Building using MSYS2 can be quite easier process. Check out MSYS2 at [msys2.org](http://www.msys2.org/).
|
||||
|
||||
Be sure to add `-j <number of threads>` as a make argument so it will use all your cpu cores to build. [example setup](https://i.imgur.com/qlESlS1.png)
|
||||
|
||||
You can also add `-o2` to optimize the final binary size but increase compilation time, and add `-pipe` to use more ram in compilation but increase compilation speed
|
||||
1. open appropriate MSYS2 terminal and do `pacman -S mingw-w64-<arch>-boost mingw-w64-<arch>-qt5 mingw-w64-<arch>-rapidjson` where `<arch>` is `x86_64` or `i686`
|
||||
2. go into the project directory
|
||||
3. create build folder `mkdir build && cd build`
|
||||
4. `qmake .. && mingw32-make`
|
||||
|
||||
###
|
||||
|
||||
### Linux
|
||||
#### Ubuntu 16.04.2 LTS
|
||||
*most likely works the same for other Debian-like distros*
|
||||
1. install QT Creator `sudo apt-get install qtcreator qtmultimedia5-dev`
|
||||
2. install boost-dev `sudo apt-get install libboost-dev`
|
||||
3. open `chatterino.pro` with QT Creator and build
|
||||
|
||||
#### Ubuntu 18.04
|
||||
*most likely works the same for other Debian-like distros*
|
||||
1. Install dependencies (and the C++ IDE Qt Creator) `sudo apt install qtcreator qtmultimedia5-dev libqt5svg5-dev libboost-dev`
|
||||
2. Install rapidjson to `/usr/local/` like this: From the Chatterino2 root folder: `sudo cp -r lib/rapidjson/include/rapidjson /usr/local/include`. If you want to install it to another place, you have to make sure it's in the chatterino.pro include path
|
||||
3. open `chatterino.pro` with QT Creator and build
|
||||
|
||||
#### Arch Linux
|
||||
install [chatterino2-git](https://aur.archlinux.org/packages/chatterino2-git/) from the aur or build manually as follows:
|
||||
1. `sudo pacman -S qt5-base qt5-multimedia qt5-svg gst-plugins-ugly gst-plugins-good boost rapidjson`
|
||||
2. go into project directory
|
||||
3. create build folder `mkdir build && cd build`
|
||||
4. `qmake .. && make`
|
||||
|
||||
### Mac OSX
|
||||
1. install Xcode and Xcode Command Line Utilites
|
||||
2. start Xcode, settings -> Locations, activate your Command Line Tools
|
||||
3. install Qt Creator
|
||||
4. install brew https://brew.sh/
|
||||
5. `brew install boost openssl rapidjson`
|
||||
6. build the project using Qt Creator
|
||||
|
||||
If the Project does not build at this point, you might need to add additional Paths/Libs, because brew does not install openssl and boost in the common path. You can get their path using
|
||||
|
||||
`brew info openssl`
|
||||
`brew info boost`
|
||||
|
||||
The lines which you need to add to your project file should look similar to this
|
||||
|
||||
```
|
||||
INCLUDEPATH += /usr/local/opt/openssl/include
|
||||
LIBS += -L/usr/local/opt/openssl/lib
|
||||
|
||||
INCLUDEPATH += "/usr/local/Cellar/boost/1.67.0_1/include"
|
||||
LIBS += -L"/usr/local/Cellar/boost/1.67.0_1/lib"
|
||||
```
|
||||
|
||||
|
||||
Test 1
|
||||
Qt creator should now format the documents when saving it.
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
|
||||
# Change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# We recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
|
@ -1,102 +0,0 @@
|
|||
const ignoredPages = {
|
||||
"settings": true,
|
||||
"payments": true,
|
||||
"inventory": true,
|
||||
"messages": true,
|
||||
"subscriptions": true,
|
||||
"friends": true,
|
||||
"directory": true,
|
||||
};
|
||||
|
||||
const appName = "com.chatterino.chatterino";
|
||||
let port = null;
|
||||
|
||||
|
||||
/// Connect to port
|
||||
function connectPort() {
|
||||
port = chrome.runtime.connectNative("com.chatterino.chatterino");
|
||||
console.log("port connected");
|
||||
|
||||
port.onMessage.addListener(function (msg) {
|
||||
console.log(msg);
|
||||
});
|
||||
port.onDisconnect.addListener(function () {
|
||||
console.log("port disconnected");
|
||||
|
||||
port = null;
|
||||
});
|
||||
}
|
||||
|
||||
function getPort() {
|
||||
if (port) {
|
||||
return port;
|
||||
} else {
|
||||
// TODO: add cooldown
|
||||
connectPort();
|
||||
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Tab listeners
|
||||
chrome.tabs.onActivated.addListener((activeInfo) => {
|
||||
chrome.tabs.get(activeInfo.tabId, (tab) => {
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
if (!tab.url)
|
||||
return;
|
||||
|
||||
matchUrl(tab.url, tab);
|
||||
});
|
||||
});
|
||||
|
||||
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
||||
if (!tab.highlighted)
|
||||
return;
|
||||
|
||||
matchUrl(changeInfo.url, tab);
|
||||
});
|
||||
|
||||
|
||||
/// Misc
|
||||
function matchUrl(url, tab) {
|
||||
if (!url)
|
||||
return;
|
||||
|
||||
const match = url.match(/^https?:\/\/(www\.)?twitch.tv\/([a-zA-Z0-9]+)\/?$/);
|
||||
|
||||
let channelName;
|
||||
|
||||
console.log(tab);
|
||||
|
||||
if (match && (channelName = match[2], !ignoredPages[channelName])) {
|
||||
console.log("channelName " + channelName);
|
||||
console.log("winId " + tab.windowId);
|
||||
|
||||
chrome.windows.get(tab.windowId, {}, (window) => {
|
||||
let yOffset = window.height - tab.height;
|
||||
|
||||
let port = getPort();
|
||||
if (port) {
|
||||
port.postMessage({
|
||||
action: "select",
|
||||
attach: true,
|
||||
type: "twitch",
|
||||
name: channelName,
|
||||
winId: "" + tab.windowId,
|
||||
yOffset: yOffset
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let port = getPort();
|
||||
if (port) {
|
||||
port.postMessage({
|
||||
action: "detach",
|
||||
winId: "" + tab.windowId
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 20 KiB |
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"name": "Chatterino",
|
||||
"version": "1.0",
|
||||
"description": "xd",
|
||||
"permissions": [
|
||||
"tabs", "nativeMessaging"
|
||||
],
|
||||
"icons": {
|
||||
"256": "icon.png"
|
||||
},
|
||||
"manifest_version": 2,
|
||||
"background": {
|
||||
"scripts": [
|
||||
"background.js"
|
||||
],
|
||||
"persistent": false
|
||||
},
|
||||
"browser_action": {
|
||||
"default_popup": "popup.html"
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
xd
|
||||
</body>
|
||||
</html>
|
|
@ -26,6 +26,11 @@ equals(QMAKE_CXX, "clang++")|equals(QMAKE_CXX, "g++") {
|
|||
macx:ICON = resources/images/chatterino2.icns
|
||||
win32:RC_FILE = resources/windows.rc
|
||||
|
||||
|
||||
macx {
|
||||
LIBS += -L/usr/local/lib
|
||||
}
|
||||
|
||||
# Submodules
|
||||
include(dependencies/rapidjson.pri)
|
||||
include(dependencies/settings.pri)
|
||||
|
@ -39,10 +44,14 @@ include(dependencies/openssl.pri)
|
|||
include(dependencies/boost.pri)
|
||||
|
||||
# Optional feature: QtWebEngine
|
||||
exists ($(QTDIR)/include/QtWebEngine/QtWebEngine) {
|
||||
message(Using QWebEngine)
|
||||
QT += webenginewidgets
|
||||
DEFINES += "USEWEBENGINE"
|
||||
#exists ($(QTDIR)/include/QtWebEngine/QtWebEngine) {
|
||||
# message(Using QWebEngine)
|
||||
# QT += webenginewidgets
|
||||
# DEFINES += "USEWEBENGINE"
|
||||
#}
|
||||
|
||||
linux {
|
||||
LIBS += -lrt
|
||||
}
|
||||
|
||||
win32 {
|
||||
|
@ -105,7 +114,6 @@ SOURCES += \
|
|||
src/providers/twitch/twitchmessagebuilder.cpp \
|
||||
src/providers/twitch/twitchserver.cpp \
|
||||
src/providers/twitch/pubsub.cpp \
|
||||
src/singletons/accountmanager.cpp \
|
||||
src/singletons/commandmanager.cpp \
|
||||
src/singletons/emotemanager.cpp \
|
||||
src/singletons/fontmanager.cpp \
|
||||
|
@ -185,7 +193,6 @@ SOURCES += \
|
|||
src/widgets/attachedwindow.cpp \
|
||||
src/widgets/settingspages/externaltoolspage.cpp \
|
||||
src/widgets/helper/comboboxitemdelegate.cpp \
|
||||
src/util/signalvectormodel.cpp \
|
||||
src/controllers/commands/command.cpp \
|
||||
src/controllers/commands/commandmodel.cpp \
|
||||
src/controllers/commands/commandcontroller.cpp \
|
||||
|
@ -197,7 +204,15 @@ SOURCES += \
|
|||
src/controllers/accounts/accountcontroller.cpp \
|
||||
src/controllers/accounts/accountmodel.cpp \
|
||||
src/controllers/accounts/account.cpp \
|
||||
src/widgets/helper/splitoverlay.cpp
|
||||
src/widgets/helper/splitoverlay.cpp \
|
||||
src/widgets/helper/dropoverlay.cpp \
|
||||
src/widgets/helper/splitnode.cpp \
|
||||
src/widgets/notificationpopup.cpp \
|
||||
src/controllers/taggedusers/taggeduserscontroller.cpp \
|
||||
src/controllers/taggedusers/taggeduser.cpp \
|
||||
src/controllers/taggedusers/taggedusersmodel.cpp \
|
||||
src/util/emotemap.cpp \
|
||||
src/providers/irc/ircconnection2.cpp
|
||||
|
||||
HEADERS += \
|
||||
src/precompiled_header.hpp \
|
||||
|
@ -226,7 +241,6 @@ HEADERS += \
|
|||
src/providers/twitch/twitchmessagebuilder.hpp \
|
||||
src/providers/twitch/twitchserver.hpp \
|
||||
src/providers/twitch/pubsub.hpp \
|
||||
src/singletons/accountmanager.hpp \
|
||||
src/singletons/commandmanager.hpp \
|
||||
src/singletons/emotemanager.hpp \
|
||||
src/singletons/fontmanager.hpp \
|
||||
|
@ -343,7 +357,17 @@ HEADERS += \
|
|||
src/controllers/accounts/accountmodel.hpp \
|
||||
src/controllers/accounts/account.hpp \
|
||||
src/util/sharedptrelementless.hpp \
|
||||
src/widgets/helper/splitoverlay.hpp
|
||||
src/widgets/helper/splitoverlay.hpp \
|
||||
src/widgets/helper/dropoverlay.hpp \
|
||||
src/widgets/helper/splitnode.hpp \
|
||||
src/widgets/notificationpopup.hpp \
|
||||
src/controllers/taggedusers/taggeduserscontroller.hpp \
|
||||
src/controllers/taggedusers/taggeduser.hpp \
|
||||
src/providerid.hpp \
|
||||
src/controllers/taggedusers/taggedusersmodel.hpp \
|
||||
src/util/qstringhash.hpp \
|
||||
src/util/mutexvalue.hpp \
|
||||
src/providers/irc/ircconnection2.hpp
|
||||
|
||||
RESOURCES += \
|
||||
resources/resources.qrc
|
||||
|
|
Before Width: | Height: | Size: 793 B After Width: | Height: | Size: 361 B |
Before Width: | Height: | Size: 812 B After Width: | Height: | Size: 437 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 772 B |
Before Width: | Height: | Size: 811 B After Width: | Height: | Size: 494 B |
Before Width: | Height: | Size: 793 B After Width: | Height: | Size: 361 B |
23
resources/licenses/boost_boost.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
23
resources/licenses/fmt_bsd2.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
resources/licenses/libcommuni_BSD3.txt
Normal file
|
@ -0,0 +1,26 @@
|
|||
Copyright (C) 2008-2016 The Communi Project
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the {organization} nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
125
resources/licenses/openssl.txt
Normal file
|
@ -0,0 +1,125 @@
|
|||
|
||||
LICENSE ISSUES
|
||||
==============
|
||||
|
||||
The OpenSSL toolkit stays under a double license, i.e. both the conditions of
|
||||
the OpenSSL License and the original SSLeay license apply to the toolkit.
|
||||
See below for the actual license texts.
|
||||
|
||||
OpenSSL License
|
||||
---------------
|
||||
|
||||
/* ====================================================================
|
||||
* Copyright (c) 1998-2018 The OpenSSL Project. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
|
||||
*
|
||||
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please contact
|
||||
* openssl-core@openssl.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "OpenSSL"
|
||||
* nor may "OpenSSL" appear in their names without prior written
|
||||
* permission of the OpenSSL Project.
|
||||
*
|
||||
* 6. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
|
||||
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This product includes cryptographic software written by Eric Young
|
||||
* (eay@cryptsoft.com). This product includes software written by Tim
|
||||
* Hudson (tjh@cryptsoft.com).
|
||||
*
|
||||
*/
|
||||
|
||||
Original SSLeay License
|
||||
-----------------------
|
||||
|
||||
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* This package is an SSL implementation written
|
||||
* by Eric Young (eay@cryptsoft.com).
|
||||
* The implementation was written so as to conform with Netscapes SSL.
|
||||
*
|
||||
* This library is free for commercial and non-commercial use as long as
|
||||
* the following conditions are aheared to. The following conditions
|
||||
* apply to all code found in this distribution, be it the RC4, RSA,
|
||||
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
|
||||
* included with this distribution is covered by the same copyright terms
|
||||
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
|
||||
*
|
||||
* Copyright remains Eric Young's, and as such any Copyright notices in
|
||||
* the code are not to be removed.
|
||||
* If this package is used in a product, Eric Young should be given attribution
|
||||
* as the author of the parts of the library used.
|
||||
* This can be in the form of a textual message at program startup or
|
||||
* in documentation (online or textual) provided with the package.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* "This product includes cryptographic software written by
|
||||
* Eric Young (eay@cryptsoft.com)"
|
||||
* The word 'cryptographic' can be left out if the rouines from the library
|
||||
* being used are not cryptographic related :-).
|
||||
* 4. If you include any Windows specific code (or a derivative thereof) from
|
||||
* the apps directory (application code) you must include an acknowledgement:
|
||||
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* The licence and distribution terms for any publically available version or
|
||||
* derivative of this code cannot be changed. i.e. this code cannot simply be
|
||||
* copied and put under another distribution licence
|
||||
* [including the GNU Public Licence.]
|
||||
*/
|
||||
|
21
resources/licenses/pajlada_settings.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 pajlada
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
21
resources/licenses/pajlada_signals.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 pajlada
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
165
resources/licenses/qt_lgpl-3.0.txt
Normal file
|
@ -0,0 +1,165 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
57
resources/licenses/rapidjson.txt
Normal file
|
@ -0,0 +1,57 @@
|
|||
Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
|
||||
Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
|
||||
If you have downloaded a copy of the RapidJSON binary from Tencent, please note that the RapidJSON binary is licensed under the MIT License.
|
||||
If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON. To avoid the problematic JSON license in your own projects, it's sufficient to exclude the bin/jsonchecker/ directory, as it's the only code under the JSON license.
|
||||
A copy of the MIT License is included in this file.
|
||||
|
||||
Other dependencies and licenses:
|
||||
|
||||
Open Source Software Licensed Under the BSD License:
|
||||
--------------------------------------------------------------------
|
||||
|
||||
The msinttypes r29
|
||||
Copyright (c) 2006-2013 Alexander Chemeris
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Open Source Software Licensed Under the JSON License:
|
||||
--------------------------------------------------------------------
|
||||
|
||||
json.org
|
||||
Copyright (c) 2002 JSON.org
|
||||
All Rights Reserved.
|
||||
|
||||
JSON_checker
|
||||
Copyright (c) 2002 JSON.org
|
||||
All Rights Reserved.
|
||||
|
||||
|
||||
Terms of the JSON License:
|
||||
---------------------------------------------------
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
Terms of the MIT License:
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
145
resources/licenses/websocketpp.txt
Normal file
|
@ -0,0 +1,145 @@
|
|||
Main Library:
|
||||
|
||||
Copyright (c) 2014, Peter Thorson. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the WebSocket++ Project nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Bundled Libraries:
|
||||
|
||||
****** Base 64 Library (base64/base64.hpp) ******
|
||||
base64.hpp is a repackaging of the base64.cpp and base64.h files into a
|
||||
single header suitable for use as a header only library. This conversion was
|
||||
done by Peter Thorson (webmaster@zaphoyd.com) in 2012. All modifications to
|
||||
the code are redistributed under the same license as the original, which is
|
||||
listed below.
|
||||
|
||||
base64.cpp and base64.h
|
||||
|
||||
Copyright (C) 2004-2008 René Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
|
||||
****** SHA1 Library (sha1/sha1.hpp) ******
|
||||
sha1.hpp is a repackaging of the sha1.cpp and sha1.h files from the shallsha1
|
||||
library (http://code.google.com/p/smallsha1/) into a single header suitable for
|
||||
use as a header only library. This conversion was done by Peter Thorson
|
||||
(webmaster@zaphoyd.com) in 2013. All modifications to the code are redistributed
|
||||
under the same license as the original, which is listed below.
|
||||
|
||||
Copyright (c) 2011, Micael Hildenborg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Micael Hildenborg nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
****** MD5 Library (common/md5.hpp) ******
|
||||
md5.hpp is a reformulation of the md5.h and md5.c code from
|
||||
http://www.opensource.apple.com/source/cups/cups-59/cups/md5.c to allow it to
|
||||
function as a component of a header only library. This conversion was done by
|
||||
Peter Thorson (webmaster@zaphoyd.com) in 2012 for the WebSocket++ project. The
|
||||
changes are released under the same license as the original (listed below)
|
||||
|
||||
Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
****** UTF8 Validation logic (utf8_validation.hpp) ******
|
||||
utf8_validation.hpp is adapted from code originally written by Bjoern Hoehrmann
|
||||
<bjoern@hoehrmann.de>. See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for
|
||||
details.
|
||||
|
||||
The original license:
|
||||
|
||||
Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -51,6 +51,15 @@
|
|||
<file>images/split/splitright.png</file>
|
||||
<file>images/split/splitup.png</file>
|
||||
<file>images/split/splitmove.png</file>
|
||||
<file>licenses/boost_boost.txt</file>
|
||||
<file>licenses/fmt_bsd2.txt</file>
|
||||
<file>licenses/libcommuni_BSD3.txt</file>
|
||||
<file>licenses/openssl.txt</file>
|
||||
<file>licenses/pajlada_settings.txt</file>
|
||||
<file>licenses/pajlada_signals.txt</file>
|
||||
<file>licenses/qt_lgpl-3.0.txt</file>
|
||||
<file>licenses/rapidjson.txt</file>
|
||||
<file>licenses/websocketpp.txt</file>
|
||||
</qresource>
|
||||
<qresource prefix="/qt/etc">
|
||||
<file>qt.conf</file>
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
#include "application.hpp"
|
||||
|
||||
#include "controllers/accounts/accountcontroller.hpp"
|
||||
#include "controllers/commands/commandcontroller.hpp"
|
||||
#include "controllers/highlights/highlightcontroller.hpp"
|
||||
#include "controllers/ignores/ignorecontroller.hpp"
|
||||
#include "controllers/taggedusers/taggeduserscontroller.hpp"
|
||||
#include "providers/twitch/pubsub.hpp"
|
||||
#include "providers/twitch/twitchserver.hpp"
|
||||
#include "singletons/accountmanager.hpp"
|
||||
#include "singletons/emotemanager.hpp"
|
||||
#include "singletons/fontmanager.hpp"
|
||||
#include "singletons/loggingmanager.hpp"
|
||||
|
@ -19,12 +20,6 @@
|
|||
|
||||
#include <atomic>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
using namespace chatterino::singletons;
|
||||
|
||||
namespace chatterino {
|
||||
|
@ -34,7 +29,7 @@ namespace {
|
|||
bool isBigEndian()
|
||||
{
|
||||
int test = 1;
|
||||
char *p = (char *)&test;
|
||||
char *p = reinterpret_cast<char *>(&test);
|
||||
|
||||
return p[0] == 0;
|
||||
}
|
||||
|
@ -61,6 +56,7 @@ void Application::construct()
|
|||
isAppConstructed = true;
|
||||
|
||||
// 1. Instantiate all classes
|
||||
this->settings = new singletons::SettingManager;
|
||||
this->paths = new singletons::PathManager(this->argc, this->argv);
|
||||
this->themes = new singletons::ThemeManager;
|
||||
this->windows = new singletons::WindowManager;
|
||||
|
@ -68,9 +64,9 @@ void Application::construct()
|
|||
this->commands = new controllers::commands::CommandController;
|
||||
this->highlights = new controllers::highlights::HighlightController;
|
||||
this->ignores = new controllers::ignores::IgnoreController;
|
||||
this->accounts = new singletons::AccountManager;
|
||||
this->taggedUsers = new controllers::taggedusers::TaggedUsersController;
|
||||
this->accounts = new controllers::accounts::AccountController;
|
||||
this->emotes = new singletons::EmoteManager;
|
||||
this->settings = new singletons::SettingManager;
|
||||
this->fonts = new singletons::FontManager;
|
||||
this->resources = new singletons::ResourceManager;
|
||||
|
||||
|
@ -92,12 +88,13 @@ void Application::initialize()
|
|||
|
||||
// 2. Initialize/load classes
|
||||
this->settings->initialize();
|
||||
this->windows->initialize();
|
||||
|
||||
this->nativeMessaging->registerHost();
|
||||
|
||||
this->settings->load();
|
||||
this->commands->load();
|
||||
this->logging->initialize();
|
||||
this->windows->initialize();
|
||||
|
||||
this->resources->initialize();
|
||||
|
||||
|
@ -109,7 +106,6 @@ void Application::initialize()
|
|||
this->accounts->load();
|
||||
|
||||
this->twitch.server->initialize();
|
||||
this->logging->initialize();
|
||||
|
||||
// XXX
|
||||
this->settings->updateWordTypeMask();
|
||||
|
@ -182,8 +178,9 @@ void Application::initialize()
|
|||
}
|
||||
|
||||
auto msg = messages::Message::createTimeoutMessage(action);
|
||||
msg->flags |= messages::Message::PubSub;
|
||||
|
||||
util::postToThread([chan, msg] { chan->addMessage(msg); });
|
||||
util::postToThread([chan, msg] { chan->addOrReplaceTimeout(msg); });
|
||||
});
|
||||
|
||||
this->twitch.pubsub->sig.moderation.userUnbanned.connect([&](const auto &action) {
|
||||
|
@ -205,10 +202,10 @@ void Application::initialize()
|
|||
// TODO(pajlada): Unlisten to all authed topics instead of only moderation topics
|
||||
// this->twitch.pubsub->UnlistenAllAuthedTopics();
|
||||
|
||||
this->twitch.pubsub->listenToWhispers(this->accounts->Twitch.getCurrent()); //
|
||||
this->twitch.pubsub->listenToWhispers(this->accounts->twitch.getCurrent()); //
|
||||
};
|
||||
|
||||
this->accounts->Twitch.currentUserChanged.connect(RequestModerationActions);
|
||||
this->accounts->twitch.currentUserChanged.connect(RequestModerationActions);
|
||||
|
||||
RequestModerationActions();
|
||||
}
|
||||
|
@ -231,52 +228,6 @@ void Application::save()
|
|||
this->commands->save();
|
||||
}
|
||||
|
||||
void Application::runNativeMessagingHost()
|
||||
{
|
||||
auto app = getApp();
|
||||
|
||||
app->nativeMessaging = new singletons::NativeMessagingManager;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
bool bigEndian = isBigEndian();
|
||||
#endif
|
||||
|
||||
while (true) {
|
||||
char size_c[4];
|
||||
std::cin.read(size_c, 4);
|
||||
|
||||
if (std::cin.eof()) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t size = *reinterpret_cast<uint32_t *>(size_c);
|
||||
#if 0
|
||||
// To avoid breaking strict-aliasing rules and potentially inducing undefined behaviour, the following code can be run instead
|
||||
uint32_t size = 0;
|
||||
if (bigEndian) {
|
||||
size = size_c[3] | static_cast<uint32_t>(size_c[2]) << 8 |
|
||||
static_cast<uint32_t>(size_c[1]) << 16 | static_cast<uint32_t>(size_c[0]) << 24;
|
||||
} else {
|
||||
size = size_c[0] | static_cast<uint32_t>(size_c[1]) << 8 |
|
||||
static_cast<uint32_t>(size_c[2]) << 16 | static_cast<uint32_t>(size_c[3]) << 24;
|
||||
}
|
||||
#endif
|
||||
|
||||
char *b = (char *)malloc(size + 1);
|
||||
std::cin.read(b, size);
|
||||
*(b + size) = '\0';
|
||||
|
||||
app->nativeMessaging->sendToGuiProcess(QByteArray(b, size));
|
||||
|
||||
free(b);
|
||||
}
|
||||
}
|
||||
|
||||
Application *getApp()
|
||||
{
|
||||
assert(staticApp != nullptr);
|
||||
|
|
|
@ -26,6 +26,12 @@ class HighlightController;
|
|||
namespace ignores {
|
||||
class IgnoreController;
|
||||
}
|
||||
namespace taggedusers {
|
||||
class TaggedUsersController;
|
||||
}
|
||||
namespace accounts {
|
||||
class AccountController;
|
||||
}
|
||||
} // namespace controllers
|
||||
|
||||
namespace singletons {
|
||||
|
@ -67,7 +73,8 @@ public:
|
|||
controllers::commands::CommandController *commands = nullptr;
|
||||
controllers::highlights::HighlightController *highlights = nullptr;
|
||||
controllers::ignores::IgnoreController *ignores = nullptr;
|
||||
singletons::AccountManager *accounts = nullptr;
|
||||
controllers::taggedusers::TaggedUsersController *taggedUsers = nullptr;
|
||||
controllers::accounts::AccountController *accounts = nullptr;
|
||||
singletons::EmoteManager *emotes = nullptr;
|
||||
singletons::NativeMessagingManager *nativeMessaging = nullptr;
|
||||
singletons::SettingManager *settings = nullptr;
|
||||
|
|
|
@ -45,6 +45,11 @@ Channel::Type Channel::getType() const
|
|||
return this->type;
|
||||
}
|
||||
|
||||
bool Channel::isTwitchChannel() const
|
||||
{
|
||||
return this->type >= Twitch && this->type < TwitchEnd;
|
||||
}
|
||||
|
||||
bool Channel::isEmpty() const
|
||||
{
|
||||
return this->name.isEmpty();
|
||||
|
@ -60,37 +65,68 @@ void Channel::addMessage(MessagePtr message)
|
|||
auto app = getApp();
|
||||
MessagePtr deleted;
|
||||
|
||||
bool isTimeout = (message->flags & Message::Timeout) != 0;
|
||||
|
||||
if (!isTimeout) {
|
||||
const QString &username = message->loginName;
|
||||
if (!username.isEmpty()) {
|
||||
// TODO: Add recent chatters display name. This should maybe be a setting
|
||||
this->addRecentChatter(message);
|
||||
}
|
||||
}
|
||||
|
||||
app->logging->addMessage(this->name, message);
|
||||
|
||||
if (isTimeout) {
|
||||
if (this->messages.pushBack(message, deleted)) {
|
||||
this->messageRemovedFromStart.invoke(deleted);
|
||||
}
|
||||
|
||||
this->messageAppended.invoke(message);
|
||||
}
|
||||
|
||||
void Channel::addOrReplaceTimeout(messages::MessagePtr message)
|
||||
{
|
||||
LimitedQueueSnapshot<MessagePtr> snapshot = this->getMessageSnapshot();
|
||||
bool addMessage = true;
|
||||
int snapshotLength = snapshot.getLength();
|
||||
|
||||
int end = std::max(0, snapshotLength - 20);
|
||||
|
||||
bool addMessage = true;
|
||||
|
||||
QTime minimumTime = QTime::currentTime().addSecs(-5);
|
||||
|
||||
for (int i = snapshotLength - 1; i >= end; --i) {
|
||||
auto &s = snapshot[i];
|
||||
|
||||
qDebug() << s->parseTime << minimumTime;
|
||||
|
||||
if (s->parseTime < minimumTime) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (s->flags.HasFlag(Message::Untimeout) && s->timeoutUser == message->timeoutUser) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (s->flags.HasFlag(Message::Timeout) && s->timeoutUser == message->timeoutUser) {
|
||||
assert(message->banAction != nullptr);
|
||||
MessagePtr replacement(
|
||||
Message::createTimeoutMessage(*(message->banAction), s->count + 1));
|
||||
this->replaceMessage(s, replacement);
|
||||
if (message->flags.HasFlag(Message::PubSub) && !s->flags.HasFlag(Message::PubSub)) {
|
||||
this->replaceMessage(s, message);
|
||||
addMessage = false;
|
||||
break;
|
||||
}
|
||||
if (!message->flags.HasFlag(Message::PubSub) && s->flags.HasFlag(Message::PubSub)) {
|
||||
addMessage = false;
|
||||
break;
|
||||
}
|
||||
|
||||
int count = s->count + 1;
|
||||
|
||||
messages::MessagePtr replacement(Message::createSystemMessage(
|
||||
message->searchText + QString("(") + QString::number(count) + " times)"));
|
||||
|
||||
replacement->timeoutUser = message->timeoutUser;
|
||||
replacement->count = count;
|
||||
replacement->flags = message->flags;
|
||||
|
||||
this->replaceMessage(s, replacement);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,19 +139,12 @@ void Channel::addMessage(MessagePtr message)
|
|||
}
|
||||
}
|
||||
|
||||
if (addMessage) {
|
||||
this->addMessage(message);
|
||||
}
|
||||
|
||||
// XXX: Might need the following line
|
||||
// WindowManager::getInstance().repaintVisibleChatWidgets(this);
|
||||
|
||||
if (!addMessage) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->messages.pushBack(message, deleted)) {
|
||||
this->messageRemovedFromStart.invoke(deleted);
|
||||
}
|
||||
|
||||
this->messageAppended.invoke(message);
|
||||
}
|
||||
|
||||
void Channel::addMessagesAtStart(std::vector<messages::MessagePtr> &_messages)
|
||||
|
|
|
@ -29,6 +29,7 @@ public:
|
|||
TwitchWhispers,
|
||||
TwitchWatching,
|
||||
TwitchMentions,
|
||||
TwitchEnd,
|
||||
};
|
||||
|
||||
explicit Channel(const QString &_name, Type type);
|
||||
|
@ -43,11 +44,13 @@ public:
|
|||
pajlada::Signals::NoArgSignal destroyed;
|
||||
|
||||
Type getType() const;
|
||||
bool isTwitchChannel() const;
|
||||
virtual bool isEmpty() const;
|
||||
messages::LimitedQueueSnapshot<messages::MessagePtr> getMessageSnapshot();
|
||||
|
||||
void addMessage(messages::MessagePtr message);
|
||||
void addMessagesAtStart(std::vector<messages::MessagePtr> &messages);
|
||||
void addOrReplaceTimeout(messages::MessagePtr message);
|
||||
void replaceMessage(messages::MessagePtr message, messages::MessagePtr replacement);
|
||||
virtual void addRecentChatter(const std::shared_ptr<messages::Message> &message);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "debug/log.hpp"
|
||||
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
#include <boost/preprocessor.hpp>
|
||||
|
||||
#include <string>
|
||||
|
@ -20,4 +21,8 @@ inline QString qS(const std::string &string)
|
|||
return QString::fromStdString(string);
|
||||
}
|
||||
|
||||
const Qt::KeyboardModifiers showSplitOverlayModifiers = Qt::ControlModifier | Qt::AltModifier;
|
||||
const Qt::KeyboardModifiers showAddSplitRegions = Qt::ControlModifier | Qt::AltModifier;
|
||||
const Qt::KeyboardModifiers showResizeHandlesModifiers = Qt::ControlModifier;
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,11 +1,23 @@
|
|||
#include "account.hpp"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace accounts {
|
||||
|
||||
Account::Account(const QString &category)
|
||||
Account::Account(ProviderId _providerId)
|
||||
: providerId(_providerId)
|
||||
{
|
||||
static QString twitch("Twitch");
|
||||
|
||||
this->category = [&]() {
|
||||
switch (_providerId) {
|
||||
case ProviderId::Twitch:
|
||||
return twitch;
|
||||
}
|
||||
return QString("Unknown ProviderId");
|
||||
}();
|
||||
}
|
||||
|
||||
const QString &Account::getCategory() const
|
||||
|
@ -13,16 +25,17 @@ const QString &Account::getCategory() const
|
|||
return this->category;
|
||||
}
|
||||
|
||||
ProviderId Account::getProviderId() const
|
||||
{
|
||||
return this->providerId;
|
||||
}
|
||||
|
||||
bool Account::operator<(const Account &other) const
|
||||
{
|
||||
if (this->category < other.category) {
|
||||
return true;
|
||||
} else if (this->category == other.category) {
|
||||
if (this->toString() < other.toString()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
QString a = this->toString();
|
||||
QString b = other.toString();
|
||||
|
||||
return std::tie(this->category, a) < std::tie(other.category, b);
|
||||
}
|
||||
|
||||
} // namespace accounts
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "providerid.hpp"
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace chatterino {
|
||||
|
@ -9,14 +11,17 @@ namespace accounts {
|
|||
class Account
|
||||
{
|
||||
public:
|
||||
Account(const QString &category);
|
||||
Account(ProviderId providerId);
|
||||
virtual ~Account() = default;
|
||||
|
||||
virtual QString toString() const = 0;
|
||||
const QString &getCategory() const;
|
||||
ProviderId getProviderId() const;
|
||||
|
||||
bool operator<(const Account &other) const;
|
||||
|
||||
private:
|
||||
ProviderId providerId;
|
||||
QString category;
|
||||
};
|
||||
|
||||
|
|
|
@ -8,13 +8,42 @@ namespace accounts {
|
|||
|
||||
AccountController::AccountController()
|
||||
{
|
||||
this->twitch.accounts.itemInserted.connect([this](const auto &args) {
|
||||
this->accounts.insertItem(std::dynamic_pointer_cast<Account>(args.item));
|
||||
});
|
||||
|
||||
this->twitch.accounts.itemRemoved.connect([this](const auto &args) {
|
||||
if (args.caller != this) {
|
||||
auto &accs = this->twitch.accounts.getVector();
|
||||
auto it = std::find(accs.begin(), accs.end(), args.item);
|
||||
assert(it != accs.end());
|
||||
|
||||
this->accounts.removeItem(it - accs.begin());
|
||||
}
|
||||
});
|
||||
|
||||
this->accounts.itemRemoved.connect([this](const auto &args) {
|
||||
switch (args.item->getProviderId()) {
|
||||
case ProviderId::Twitch: {
|
||||
auto &accs = this->twitch.accounts.getVector();
|
||||
auto it = std::find(accs.begin(), accs.end(), args.item);
|
||||
assert(it != accs.end());
|
||||
|
||||
this->twitch.accounts.removeItem(it - accs.begin(), this);
|
||||
} break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AccountController::load()
|
||||
{
|
||||
this->twitch.load();
|
||||
}
|
||||
|
||||
AccountModel *AccountController::createModel(QObject *parent)
|
||||
{
|
||||
AccountModel *model = new AccountModel(parent);
|
||||
|
||||
//(util::BaseSignalVector<stdAccount> *)
|
||||
model->init(&this->accounts);
|
||||
return model;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <QObject>
|
||||
|
||||
#include "controllers/accounts/account.hpp"
|
||||
#include "providers/twitch/twitchaccountmanager.hpp"
|
||||
#include "util/sharedptrelementless.hpp"
|
||||
#include "util/signalvector2.hpp"
|
||||
|
||||
|
@ -19,6 +20,10 @@ public:
|
|||
|
||||
AccountModel *createModel(QObject *parent);
|
||||
|
||||
void load();
|
||||
|
||||
providers::twitch::TwitchAccountManager twitch;
|
||||
|
||||
private:
|
||||
util::SortedSignalVector<std::shared_ptr<Account>, util::SharedPtrElementLess<Account>>
|
||||
accounts;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "accountmodel.hpp"
|
||||
|
||||
#include "util/standarditemhelper.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace accounts {
|
||||
|
@ -10,16 +12,49 @@ AccountModel::AccountModel(QObject *parent)
|
|||
}
|
||||
|
||||
// turn a vector item into a model row
|
||||
std::shared_ptr<Account> AccountModel::getItemFromRow(std::vector<QStandardItem *> &row)
|
||||
std::shared_ptr<Account> AccountModel::getItemFromRow(std::vector<QStandardItem *> &,
|
||||
const std::shared_ptr<Account> &original)
|
||||
{
|
||||
return nullptr;
|
||||
return original;
|
||||
}
|
||||
|
||||
// turns a row in the model into a vector item
|
||||
void AccountModel::getRowFromItem(const std::shared_ptr<Account> &item,
|
||||
std::vector<QStandardItem *> &row)
|
||||
{
|
||||
row[0]->setData(item->toString(), Qt::DisplayRole);
|
||||
util::setStringItem(row[0], item->toString(), false);
|
||||
row[0]->setData(QFont("Segoe UI", 10), Qt::FontRole);
|
||||
}
|
||||
|
||||
int AccountModel::beforeInsert(const std::shared_ptr<Account> &item,
|
||||
std::vector<QStandardItem *> &row, int proposedIndex)
|
||||
{
|
||||
if (this->categoryCount[item->getCategory()]++ == 0) {
|
||||
auto row = this->createRow();
|
||||
|
||||
util::setStringItem(row[0], item->getCategory(), false, false);
|
||||
row[0]->setData(QFont("Segoe UI Light", 16), Qt::FontRole);
|
||||
|
||||
this->insertCustomRow(std::move(row), proposedIndex);
|
||||
|
||||
return proposedIndex + 1;
|
||||
}
|
||||
|
||||
return proposedIndex;
|
||||
}
|
||||
|
||||
void AccountModel::afterRemoved(const std::shared_ptr<Account> &item,
|
||||
std::vector<QStandardItem *> &row, int index)
|
||||
{
|
||||
auto it = this->categoryCount.find(item->getCategory());
|
||||
assert(it != this->categoryCount.end());
|
||||
|
||||
if (it->second <= 1) {
|
||||
this->categoryCount.erase(it);
|
||||
this->removeCustomRow(index - 1);
|
||||
} else {
|
||||
it->second--;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace accounts
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "controllers/accounts/account.hpp"
|
||||
#include "util/qstringhash.hpp"
|
||||
#include "util/signalvectormodel.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace accounts {
|
||||
|
@ -16,13 +19,23 @@ public:
|
|||
|
||||
protected:
|
||||
// turn a vector item into a model row
|
||||
virtual std::shared_ptr<Account> getItemFromRow(std::vector<QStandardItem *> &row) override;
|
||||
virtual std::shared_ptr<Account> getItemFromRow(
|
||||
std::vector<QStandardItem *> &row, const std::shared_ptr<Account> &original) override;
|
||||
|
||||
// turns a row in the model into a vector item
|
||||
virtual void getRowFromItem(const std::shared_ptr<Account> &item,
|
||||
std::vector<QStandardItem *> &row) override;
|
||||
|
||||
virtual int beforeInsert(const std::shared_ptr<Account> &item,
|
||||
std::vector<QStandardItem *> &row, int proposedIndex) override;
|
||||
|
||||
virtual void afterRemoved(const std::shared_ptr<Account> &item,
|
||||
std::vector<QStandardItem *> &row, int index) override;
|
||||
|
||||
friend class AccountController;
|
||||
|
||||
private:
|
||||
std::unordered_map<QString, int> categoryCount;
|
||||
};
|
||||
|
||||
} // namespace accounts
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include "commandcontroller.hpp"
|
||||
|
||||
#include "application.hpp"
|
||||
#include "controllers/accounts/accountcontroller.hpp"
|
||||
#include "controllers/commands/command.hpp"
|
||||
#include "controllers/commands/commandmodel.hpp"
|
||||
#include "messages/messagebuilder.hpp"
|
||||
#include "providers/twitch/twitchchannel.hpp"
|
||||
#include "providers/twitch/twitchserver.hpp"
|
||||
#include "singletons/accountmanager.hpp"
|
||||
#include "singletons/pathmanager.hpp"
|
||||
#include "util/signalvector2.hpp"
|
||||
|
||||
|
@ -110,7 +110,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
|
|||
|
||||
return "";
|
||||
} else if (commandName == "/uptime") {
|
||||
const auto &streamStatus = twitchChannel->GetStreamStatus();
|
||||
const auto &streamStatus = twitchChannel->getStreamStatus();
|
||||
|
||||
QString messageText =
|
||||
streamStatus.live ? streamStatus.uptime : "Channel is not live.";
|
||||
|
@ -121,7 +121,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
|
|||
} else if (commandName == "/ignore" && words.size() >= 2) {
|
||||
auto app = getApp();
|
||||
|
||||
auto user = app->accounts->Twitch.getCurrent();
|
||||
auto user = app->accounts->twitch.getCurrent();
|
||||
auto target = words.at(1);
|
||||
|
||||
if (user->isAnon()) {
|
||||
|
@ -138,7 +138,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
|
|||
} else if (commandName == "/unignore" && words.size() >= 2) {
|
||||
auto app = getApp();
|
||||
|
||||
auto user = app->accounts->Twitch.getCurrent();
|
||||
auto user = app->accounts->twitch.getCurrent();
|
||||
auto target = words.at(1);
|
||||
|
||||
if (user->isAnon()) {
|
||||
|
@ -161,7 +161,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
|
|||
|
||||
messages::MessageBuilder b;
|
||||
|
||||
b.emplace<messages::TextElement>(app->accounts->Twitch.getCurrent()->getUserName(),
|
||||
b.emplace<messages::TextElement>(app->accounts->twitch.getCurrent()->getUserName(),
|
||||
messages::MessageElement::Text);
|
||||
b.emplace<messages::TextElement>("->", messages::MessageElement::Text);
|
||||
b.emplace<messages::TextElement>(words[1], messages::MessageElement::Text);
|
||||
|
@ -169,7 +169,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel,
|
|||
QString rest = "";
|
||||
|
||||
for (int i = 2; i < words.length(); i++) {
|
||||
rest += words[i];
|
||||
rest += words[i] + " ";
|
||||
}
|
||||
|
||||
b.emplace<messages::TextElement>(rest, messages::MessageElement::Text);
|
||||
|
|
|
@ -11,7 +11,7 @@ CommandModel::CommandModel(QObject *parent)
|
|||
}
|
||||
|
||||
// turn a vector item into a model row
|
||||
Command CommandModel::getItemFromRow(std::vector<QStandardItem *> &row)
|
||||
Command CommandModel::getItemFromRow(std::vector<QStandardItem *> &row, const Command &original)
|
||||
{
|
||||
return Command(row[0]->data(Qt::EditRole).toString(), row[1]->data(Qt::EditRole).toString());
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ class CommandModel : public util::SignalVectorModel<Command>
|
|||
|
||||
protected:
|
||||
// turn a vector item into a model row
|
||||
virtual Command getItemFromRow(std::vector<QStandardItem *> &row) override;
|
||||
virtual Command getItemFromRow(std::vector<QStandardItem *> &row,
|
||||
const Command &command) override;
|
||||
|
||||
// turns a row in the model into a vector item
|
||||
virtual void getRowFromItem(const Command &item, std::vector<QStandardItem *> &row) override;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "application.hpp"
|
||||
#include "controllers/highlights/highlightmodel.hpp"
|
||||
#include "widgets/notificationpopup.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
|
@ -21,7 +22,6 @@ void HighlightController::initialize()
|
|||
}
|
||||
|
||||
this->phrases.delayedItemsChanged.connect([this] { //
|
||||
int xd = this->phrases.getVector().size();
|
||||
this->highlightsSetting.setValue(this->phrases.getVector());
|
||||
});
|
||||
}
|
||||
|
@ -34,6 +34,15 @@ HighlightModel *HighlightController::createModel(QObject *parent)
|
|||
return model;
|
||||
}
|
||||
|
||||
void HighlightController::addHighlight(const messages::MessagePtr &msg)
|
||||
{
|
||||
// static widgets::NotificationPopup popup;
|
||||
|
||||
// popup.updatePosition();
|
||||
// popup.addMessage(msg);
|
||||
// popup.show();
|
||||
}
|
||||
|
||||
} // namespace highlights
|
||||
} // namespace controllers
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "controllers/highlights/highlightphrase.hpp"
|
||||
#include "messages/message.hpp"
|
||||
#include "singletons/settingsmanager.hpp"
|
||||
#include "util/signalvector2.hpp"
|
||||
|
||||
|
@ -21,6 +22,8 @@ public:
|
|||
|
||||
HighlightModel *createModel(QObject *parent);
|
||||
|
||||
void addHighlight(const messages::MessagePtr &msg);
|
||||
|
||||
private:
|
||||
bool initialized = false;
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@ HighlightModel::HighlightModel(QObject *parent)
|
|||
}
|
||||
|
||||
// turn a vector item into a model row
|
||||
HighlightPhrase HighlightModel::getItemFromRow(std::vector<QStandardItem *> &row)
|
||||
HighlightPhrase HighlightModel::getItemFromRow(std::vector<QStandardItem *> &row,
|
||||
const HighlightPhrase &original)
|
||||
{
|
||||
// key, alert, sound, regex
|
||||
|
||||
|
|
|
@ -17,7 +17,8 @@ class HighlightModel : public util::SignalVectorModel<HighlightPhrase>
|
|||
|
||||
protected:
|
||||
// turn a vector item into a model row
|
||||
virtual HighlightPhrase getItemFromRow(std::vector<QStandardItem *> &row) override;
|
||||
virtual HighlightPhrase getItemFromRow(std::vector<QStandardItem *> &row,
|
||||
const HighlightPhrase &original) override;
|
||||
|
||||
// turns a row in the model into a vector item
|
||||
virtual void getRowFromItem(const HighlightPhrase &item,
|
||||
|
|
|
@ -15,7 +15,8 @@ IgnoreModel::IgnoreModel(QObject *parent)
|
|||
}
|
||||
|
||||
// turn a vector item into a model row
|
||||
IgnorePhrase IgnoreModel::getItemFromRow(std::vector<QStandardItem *> &row)
|
||||
IgnorePhrase IgnoreModel::getItemFromRow(std::vector<QStandardItem *> &row,
|
||||
const IgnorePhrase &original)
|
||||
{
|
||||
// key, regex
|
||||
|
||||
|
|
|
@ -17,7 +17,8 @@ class IgnoreModel : public util::SignalVectorModel<IgnorePhrase>
|
|||
|
||||
protected:
|
||||
// turn a vector item into a model row
|
||||
virtual IgnorePhrase getItemFromRow(std::vector<QStandardItem *> &row) override;
|
||||
virtual IgnorePhrase getItemFromRow(std::vector<QStandardItem *> &row,
|
||||
const IgnorePhrase &original) override;
|
||||
|
||||
// turns a row in the model into a vector item
|
||||
virtual void getRowFromItem(const IgnorePhrase &item,
|
||||
|
|
24
src/controllers/taggedusers/taggeduser.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "taggeduser.hpp"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace taggedusers {
|
||||
|
||||
TaggedUser::TaggedUser(ProviderId _provider, const QString &_name, const QString &_id)
|
||||
: provider(_provider)
|
||||
, name(_name)
|
||||
, id(_id)
|
||||
{
|
||||
}
|
||||
|
||||
bool TaggedUser::operator<(const TaggedUser &other) const
|
||||
{
|
||||
return std::tie(this->provider, this->name, this->id) <
|
||||
std::tie(other.provider, other.name, other.id);
|
||||
}
|
||||
|
||||
} // namespace taggedusers
|
||||
} // namespace controllers
|
||||
} // namespace chatterino
|
24
src/controllers/taggedusers/taggeduser.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <providerid.hpp>
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace taggedusers {
|
||||
|
||||
class TaggedUser
|
||||
{
|
||||
public:
|
||||
TaggedUser(ProviderId provider, const QString &name, const QString &id);
|
||||
|
||||
bool operator<(const TaggedUser &other) const;
|
||||
|
||||
ProviderId provider;
|
||||
QString name;
|
||||
QString id;
|
||||
};
|
||||
|
||||
} // namespace taggedusers
|
||||
} // namespace controllers
|
||||
} // namespace chatterino
|
23
src/controllers/taggedusers/taggeduserscontroller.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#include "taggeduserscontroller.hpp"
|
||||
|
||||
#include "controllers/taggedusers/taggedusersmodel.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace taggedusers {
|
||||
|
||||
TaggedUsersController::TaggedUsersController()
|
||||
{
|
||||
}
|
||||
|
||||
TaggedUsersModel *TaggedUsersController::createModel(QObject *parent)
|
||||
{
|
||||
TaggedUsersModel *model = new TaggedUsersModel(parent);
|
||||
model->init(&this->users);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
} // namespace taggedusers
|
||||
} // namespace controllers
|
||||
} // namespace chatterino
|
24
src/controllers/taggedusers/taggeduserscontroller.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "controllers/taggedusers/taggeduser.hpp"
|
||||
#include "util/signalvector2.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace taggedusers {
|
||||
|
||||
class TaggedUsersModel;
|
||||
|
||||
class TaggedUsersController
|
||||
{
|
||||
public:
|
||||
TaggedUsersController();
|
||||
|
||||
util::SortedSignalVector<TaggedUser, std::less<TaggedUser>> users;
|
||||
|
||||
TaggedUsersModel *createModel(QObject *parent = nullptr);
|
||||
};
|
||||
|
||||
} // namespace taggedusers
|
||||
} // namespace controllers
|
||||
} // namespace chatterino
|
66
src/controllers/taggedusers/taggedusersmodel.cpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
#include "taggedusersmodel.hpp"
|
||||
|
||||
#include "application.hpp"
|
||||
#include "util/standarditemhelper.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace taggedusers {
|
||||
|
||||
// commandmodel
|
||||
TaggedUsersModel::TaggedUsersModel(QObject *parent)
|
||||
: util::SignalVectorModel<TaggedUser>(1, parent)
|
||||
{
|
||||
}
|
||||
|
||||
// turn a vector item into a model row
|
||||
TaggedUser TaggedUsersModel::getItemFromRow(std::vector<QStandardItem *> &row,
|
||||
const TaggedUser &original)
|
||||
{
|
||||
return original;
|
||||
}
|
||||
|
||||
// turns a row in the model into a vector item
|
||||
void TaggedUsersModel::getRowFromItem(const TaggedUser &item, std::vector<QStandardItem *> &row)
|
||||
{
|
||||
util::setStringItem(row[0], item.name);
|
||||
}
|
||||
|
||||
void TaggedUsersModel::afterInit()
|
||||
{
|
||||
// std::vector<QStandardItem *> row = this->createRow();
|
||||
// util::setBoolItem(row[0], getApp()->settings->enableHighlightsSelf.getValue(), true,
|
||||
// false); row[0]->setData("Your username (automatic)", Qt::DisplayRole);
|
||||
// util::setBoolItem(row[1], getApp()->settings->enableHighlightTaskbar.getValue(), true,
|
||||
// false); util::setBoolItem(row[2], getApp()->settings->enableHighlightSound.getValue(),
|
||||
// true, false); row[3]->setFlags(0); this->insertCustomRow(row, 0);
|
||||
}
|
||||
|
||||
// void TaggedUserModel::customRowSetData(const std::vector<QStandardItem *> &row, int column,
|
||||
// const QVariant &value, int role)
|
||||
//{
|
||||
// switch (column) {
|
||||
// case 0: {
|
||||
// if (role == Qt::CheckStateRole) {
|
||||
// getApp()->settings->enableHighlightsSelf.setValue(value.toBool());
|
||||
// }
|
||||
// } break;
|
||||
// case 1: {
|
||||
// if (role == Qt::CheckStateRole) {
|
||||
// getApp()->settings->enableHighlightTaskbar.setValue(value.toBool());
|
||||
// }
|
||||
// } break;
|
||||
// case 2: {
|
||||
// if (role == Qt::CheckStateRole) {
|
||||
// getApp()->settings->enableHighlightSound.setValue(value.toBool());
|
||||
// }
|
||||
// } break;
|
||||
// case 3: {
|
||||
// // empty element
|
||||
// } break;
|
||||
// }
|
||||
//}
|
||||
|
||||
} // namespace taggedusers
|
||||
} // namespace controllers
|
||||
} // namespace chatterino
|
34
src/controllers/taggedusers/taggedusersmodel.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include "controllers/taggedusers/taggeduser.hpp"
|
||||
#include "util/signalvectormodel.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace controllers {
|
||||
namespace taggedusers {
|
||||
|
||||
class TaggedUsersController;
|
||||
|
||||
class TaggedUsersModel : public util::SignalVectorModel<TaggedUser>
|
||||
{
|
||||
explicit TaggedUsersModel(QObject *parent);
|
||||
|
||||
protected:
|
||||
// turn a vector item into a model row
|
||||
virtual TaggedUser getItemFromRow(std::vector<QStandardItem *> &row,
|
||||
const TaggedUser &original) override;
|
||||
|
||||
// turns a row in the model into a vector item
|
||||
virtual void getRowFromItem(const TaggedUser &item, std::vector<QStandardItem *> &row) override;
|
||||
|
||||
virtual void afterInit() override;
|
||||
|
||||
// virtual void customRowSetData(const std::vector<QStandardItem *> &row, int column,
|
||||
// const QVariant &value, int role) override;
|
||||
|
||||
friend class TaggedUsersController;
|
||||
};
|
||||
|
||||
} // namespace taggedusers
|
||||
} // namespace controllers
|
||||
} // namespace chatterino
|
114
src/main.cpp
|
@ -1,6 +1,7 @@
|
|||
#include "application.hpp"
|
||||
#include "singletons/nativemessagingmanager.hpp"
|
||||
#include "singletons/pathmanager.hpp"
|
||||
#include "singletons/updatemanager.hpp"
|
||||
#include "util/networkmanager.hpp"
|
||||
#include "widgets/lastruncrashdialog.hpp"
|
||||
|
||||
|
@ -9,6 +10,7 @@
|
|||
#include <QFile>
|
||||
#include <QLibrary>
|
||||
#include <QStringList>
|
||||
#include <QStyleFactory>
|
||||
|
||||
#ifdef USEWINSDK
|
||||
#include "util/nativeeventhelper.hpp"
|
||||
|
@ -17,7 +19,15 @@
|
|||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
int runGui(int argc, char *argv[]);
|
||||
void runNativeMessagingHost();
|
||||
void installCustomPalette();
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
@ -30,7 +40,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
// TODO: can be any argument
|
||||
if (args.size() > 0 && args[0].startsWith("chrome-extension://")) {
|
||||
chatterino::Application::runNativeMessagingHost();
|
||||
runNativeMessagingHost();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -47,14 +57,16 @@ int runGui(int argc, char *argv[])
|
|||
// QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL, true);
|
||||
QApplication a(argc, argv);
|
||||
|
||||
// Install native event handler for hidpi on windows
|
||||
#ifdef USEWINSDK
|
||||
a.installNativeEventFilter(new chatterino::util::DpiNativeEventFilter);
|
||||
#endif
|
||||
QApplication::setStyle(QStyleFactory::create("Fusion"));
|
||||
|
||||
installCustomPalette();
|
||||
|
||||
// Initialize NetworkManager
|
||||
chatterino::util::NetworkManager::init();
|
||||
|
||||
// Check for upates
|
||||
chatterino::singletons::UpdateManager::getInstance().checkForUpdates();
|
||||
|
||||
// Initialize application
|
||||
chatterino::Application::instantiate(argc, argv);
|
||||
auto app = chatterino::getApp();
|
||||
|
@ -68,7 +80,14 @@ int runGui(int argc, char *argv[])
|
|||
if (QFile::exists(runningPath)) {
|
||||
#ifndef DISABLE_CRASH_DIALOG
|
||||
chatterino::widgets::LastRunCrashDialog dialog;
|
||||
dialog.exec();
|
||||
|
||||
switch (dialog.exec()) {
|
||||
case QDialog::Accepted: {
|
||||
}; break;
|
||||
default: {
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
QFile runningFile(runningPath);
|
||||
|
@ -98,3 +117,86 @@ int runGui(int argc, char *argv[])
|
|||
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
void runNativeMessagingHost()
|
||||
{
|
||||
auto *nm = new chatterino::singletons::NativeMessagingManager;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
bool bigEndian = isBigEndian();
|
||||
#endif
|
||||
|
||||
std::atomic<bool> ping(false);
|
||||
|
||||
QTimer timer;
|
||||
QObject::connect(&timer, &QTimer::timeout, [&ping] {
|
||||
if (!ping.exchange(false)) {
|
||||
_exit(0);
|
||||
}
|
||||
});
|
||||
timer.setInterval(11000);
|
||||
timer.start();
|
||||
|
||||
while (true) {
|
||||
char size_c[4];
|
||||
std::cin.read(size_c, 4);
|
||||
|
||||
if (std::cin.eof()) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t size = *reinterpret_cast<uint32_t *>(size_c);
|
||||
#if 0
|
||||
// To avoid breaking strict-aliasing rules and potentially inducing undefined behaviour, the following code can be run instead
|
||||
uint32_t size = 0;
|
||||
if (bigEndian) {
|
||||
size = size_c[3] | static_cast<uint32_t>(size_c[2]) << 8 |
|
||||
static_cast<uint32_t>(size_c[1]) << 16 | static_cast<uint32_t>(size_c[0]) << 24;
|
||||
} else {
|
||||
size = size_c[0] | static_cast<uint32_t>(size_c[1]) << 8 |
|
||||
static_cast<uint32_t>(size_c[2]) << 16 | static_cast<uint32_t>(size_c[3]) << 24;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<char[]> b(new char[size + 1]);
|
||||
std::cin.read(b.get(), size);
|
||||
*(b.get() + size) = '\0';
|
||||
|
||||
nm->sendToGuiProcess(QByteArray::fromRawData(b.get(), static_cast<int32_t>(size)));
|
||||
}
|
||||
}
|
||||
|
||||
void installCustomPalette()
|
||||
{
|
||||
// borrowed from
|
||||
// https://stackoverflow.com/questions/15035767/is-the-qt-5-dark-fusion-theme-available-for-windows
|
||||
QPalette darkPalette = qApp->palette();
|
||||
|
||||
darkPalette.setColor(QPalette::Window, QColor(22, 22, 22));
|
||||
darkPalette.setColor(QPalette::WindowText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Text, Qt::white);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127));
|
||||
darkPalette.setColor(QPalette::Base, QColor("#333"));
|
||||
darkPalette.setColor(QPalette::AlternateBase, QColor("#444"));
|
||||
darkPalette.setColor(QPalette::ToolTipBase, Qt::white);
|
||||
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127));
|
||||
darkPalette.setColor(QPalette::Dark, QColor(35, 35, 35));
|
||||
darkPalette.setColor(QPalette::Shadow, QColor(20, 20, 20));
|
||||
darkPalette.setColor(QPalette::Button, QColor(70, 70, 70));
|
||||
darkPalette.setColor(QPalette::ButtonText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127));
|
||||
darkPalette.setColor(QPalette::BrightText, Qt::red);
|
||||
darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
|
||||
darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218));
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80));
|
||||
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127));
|
||||
|
||||
qApp->setPalette(darkPalette);
|
||||
}
|
||||
|
|
|
@ -147,10 +147,10 @@ void Image::loadImage()
|
|||
loadedEventQueued = true;
|
||||
|
||||
QTimer::singleShot(500, [] {
|
||||
getApp()->emotes->incGeneration();
|
||||
getApp()->windows->incGeneration();
|
||||
|
||||
auto app = getApp();
|
||||
app->windows->layoutVisibleChatWidgets();
|
||||
app->windows->layoutChannelViews();
|
||||
loadedEventQueued = false;
|
||||
});
|
||||
}
|
||||
|
@ -240,7 +240,8 @@ int Image::getWidth() const
|
|||
|
||||
int Image::getScaledWidth() const
|
||||
{
|
||||
return static_cast<int>(this->getWidth() * this->scale);
|
||||
return static_cast<int>((float)this->getWidth() * this->scale *
|
||||
getApp()->settings->emoteScale.getValue());
|
||||
}
|
||||
|
||||
int Image::getHeight() const
|
||||
|
@ -253,7 +254,8 @@ int Image::getHeight() const
|
|||
|
||||
int Image::getScaledHeight() const
|
||||
{
|
||||
return static_cast<int>(this->getHeight() * this->scale);
|
||||
return static_cast<int>((float)this->getHeight() * this->scale *
|
||||
getApp()->settings->emoteScale.getValue());
|
||||
}
|
||||
|
||||
} // namespace messages
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "application.hpp"
|
||||
#include "singletons/emotemanager.hpp"
|
||||
#include "singletons/settingsmanager.hpp"
|
||||
#include "singletons/windowmanager.hpp"
|
||||
#include "util/benchmark.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
|
@ -20,9 +22,9 @@ namespace chatterino {
|
|||
namespace messages {
|
||||
namespace layouts {
|
||||
|
||||
MessageLayout::MessageLayout(MessagePtr _message)
|
||||
: message(_message)
|
||||
, buffer(nullptr)
|
||||
MessageLayout::MessageLayout(MessagePtr message)
|
||||
: message_(message)
|
||||
, buffer_(nullptr)
|
||||
{
|
||||
util::DebugCount::increase("message layout");
|
||||
}
|
||||
|
@ -34,119 +36,116 @@ MessageLayout::~MessageLayout()
|
|||
|
||||
Message *MessageLayout::getMessage()
|
||||
{
|
||||
return this->message.get();
|
||||
return this->message_.get();
|
||||
}
|
||||
|
||||
// Height
|
||||
int MessageLayout::getHeight() const
|
||||
{
|
||||
return container.getHeight();
|
||||
return container_.getHeight();
|
||||
}
|
||||
|
||||
// Layout
|
||||
// return true if redraw is required
|
||||
bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags)
|
||||
{
|
||||
// BenchmarkGuard benchmark("MessageLayout::layout()");
|
||||
|
||||
auto app = getApp();
|
||||
|
||||
bool layoutRequired = false;
|
||||
|
||||
// check if width changed
|
||||
bool widthChanged = width != this->currentLayoutWidth;
|
||||
bool widthChanged = width != this->currentLayoutWidth_;
|
||||
layoutRequired |= widthChanged;
|
||||
this->currentLayoutWidth = width;
|
||||
this->currentLayoutWidth_ = width;
|
||||
|
||||
// check if emotes changed
|
||||
bool imagesChanged = this->emoteGeneration != app->emotes->getGeneration();
|
||||
layoutRequired |= imagesChanged;
|
||||
this->emoteGeneration = app->emotes->getGeneration();
|
||||
|
||||
// check if text changed
|
||||
bool textChanged = this->fontGeneration != app->fonts->getGeneration();
|
||||
layoutRequired |= textChanged;
|
||||
this->fontGeneration = app->fonts->getGeneration();
|
||||
// check if layout state changed
|
||||
if (this->layoutState_ != app->windows->getGeneration()) {
|
||||
layoutRequired = true;
|
||||
this->flags |= RequiresBufferUpdate;
|
||||
this->layoutState_ = app->windows->getGeneration();
|
||||
}
|
||||
|
||||
// check if work mask changed
|
||||
bool wordMaskChanged = this->currentWordFlags != flags; // app->settings->getWordTypeMask();
|
||||
layoutRequired |= wordMaskChanged;
|
||||
this->currentWordFlags = flags; // app->settings->getWordTypeMask();
|
||||
layoutRequired |= this->currentWordFlags_ != flags;
|
||||
this->currentWordFlags_ = flags; // app->settings->getWordTypeMask();
|
||||
|
||||
// check if timestamp format changed
|
||||
bool timestampFormatChanged = this->timestampFormat != app->settings->timestampFormat;
|
||||
|
||||
layoutRequired |= timestampFormatChanged;
|
||||
// check if layout was requested manually
|
||||
layoutRequired |= bool(this->flags & RequiresLayout);
|
||||
this->flags &= decltype(RequiresLayout)(~RequiresLayout);
|
||||
|
||||
// check if dpi changed
|
||||
bool scaleChanged = this->scale != scale;
|
||||
layoutRequired |= scaleChanged;
|
||||
this->scale = scale;
|
||||
imagesChanged |= scaleChanged;
|
||||
textChanged |= scaleChanged;
|
||||
layoutRequired |= this->scale_ != scale;
|
||||
this->scale_ = scale;
|
||||
|
||||
// update word sizes if needed
|
||||
if (imagesChanged) {
|
||||
// this->container.updateImages();
|
||||
this->flags |= MessageLayout::RequiresBufferUpdate;
|
||||
}
|
||||
if (textChanged) {
|
||||
// this->container.updateText();
|
||||
this->flags |= MessageLayout::RequiresBufferUpdate;
|
||||
}
|
||||
if (widthChanged || wordMaskChanged) {
|
||||
this->deleteBuffer();
|
||||
}
|
||||
|
||||
// return if no layout is required
|
||||
if (!layoutRequired) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int oldHeight = this->container_.getHeight();
|
||||
this->actuallyLayout(width, flags);
|
||||
if (widthChanged || this->container_.getHeight() != oldHeight) {
|
||||
this->deleteBuffer();
|
||||
}
|
||||
this->invalidateBuffer();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessageLayout::actuallyLayout(int width, MessageElement::Flags flags)
|
||||
void MessageLayout::actuallyLayout(int width, MessageElement::Flags _flags)
|
||||
{
|
||||
this->container.begin(width, this->scale, this->message->flags.value);
|
||||
auto messageFlags = this->message_->flags.value;
|
||||
|
||||
for (const std::unique_ptr<MessageElement> &element : this->message->getElements()) {
|
||||
element->addToContainer(this->container, flags);
|
||||
if (this->flags & MessageLayout::Expanded ||
|
||||
(_flags & MessageElement::ModeratorTools &&
|
||||
!(this->message_->flags & Message::MessageFlags::Disabled))) {
|
||||
messageFlags = Message::MessageFlags(messageFlags & ~Message::MessageFlags::Collapsed);
|
||||
}
|
||||
|
||||
if (this->height != this->container.getHeight()) {
|
||||
this->container_.begin(width, this->scale_, messageFlags);
|
||||
|
||||
for (const std::unique_ptr<MessageElement> &element : this->message_->getElements()) {
|
||||
element->addToContainer(this->container_, _flags);
|
||||
}
|
||||
|
||||
if (this->height_ != this->container_.getHeight()) {
|
||||
this->deleteBuffer();
|
||||
}
|
||||
|
||||
this->container.end();
|
||||
this->height = this->container.getHeight();
|
||||
this->container_.end();
|
||||
this->height_ = this->container_.getHeight();
|
||||
|
||||
// collapsed state
|
||||
this->flags &= ~Flags::Collapsed;
|
||||
if (this->container_.isCollapsed()) {
|
||||
this->flags |= Flags::Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
// Painting
|
||||
void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection &selection,
|
||||
bool isLastReadMessage, bool isWindowFocused)
|
||||
void MessageLayout::paint(QPainter &painter, int width, int y, int messageIndex,
|
||||
Selection &selection, bool isLastReadMessage, bool isWindowFocused)
|
||||
{
|
||||
auto app = getApp();
|
||||
QPixmap *pixmap = this->buffer.get();
|
||||
QPixmap *pixmap = this->buffer_.get();
|
||||
|
||||
// create new buffer if required
|
||||
if (!pixmap) {
|
||||
#ifdef Q_OS_MACOS
|
||||
pixmap =
|
||||
new QPixmap((int)(this->container.getWidth() * painter.device()->devicePixelRatioF()),
|
||||
(int)(this->container.getHeight() * painter.device()->devicePixelRatioF()));
|
||||
pixmap = new QPixmap(int(width * painter.device()->devicePixelRatioF()),
|
||||
int(container_.getHeight() * painter.device()->devicePixelRatioF()));
|
||||
pixmap->setDevicePixelRatio(painter.device()->devicePixelRatioF());
|
||||
#else
|
||||
pixmap = new QPixmap(this->container.getWidth(), std::max(16, this->container.getHeight()));
|
||||
pixmap = new QPixmap(width, std::max(16, this->container_.getHeight()));
|
||||
#endif
|
||||
|
||||
this->buffer = std::shared_ptr<QPixmap>(pixmap);
|
||||
this->bufferValid = false;
|
||||
this->buffer_ = std::shared_ptr<QPixmap>(pixmap);
|
||||
this->bufferValid_ = false;
|
||||
util::DebugCount::increase("message drawing buffers");
|
||||
}
|
||||
|
||||
if (!this->bufferValid || !selection.isEmpty()) {
|
||||
if (!this->bufferValid_ || !selection.isEmpty()) {
|
||||
this->updateBuffer(pixmap, messageIndex, selection);
|
||||
}
|
||||
|
||||
|
@ -155,21 +154,21 @@ void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection
|
|||
// painter.drawPixmap(0, y, this->container.width, this->container.getHeight(), *pixmap);
|
||||
|
||||
// draw gif emotes
|
||||
this->container.paintAnimatedElements(painter, y);
|
||||
this->container_.paintAnimatedElements(painter, y);
|
||||
|
||||
// draw disabled
|
||||
if (this->message->flags.HasFlag(Message::Disabled)) {
|
||||
if (this->message_->flags.HasFlag(Message::Disabled)) {
|
||||
painter.fillRect(0, y, pixmap->width(), pixmap->height(), app->themes->messages.disabled);
|
||||
}
|
||||
|
||||
// draw selection
|
||||
if (!selection.isEmpty()) {
|
||||
this->container.paintSelection(painter, messageIndex, selection, y);
|
||||
this->container_.paintSelection(painter, messageIndex, selection, y);
|
||||
}
|
||||
|
||||
// draw message seperation line
|
||||
if (app->settings->seperateMessages.getValue()) {
|
||||
painter.fillRect(0, y + this->container.getHeight() - 1, this->container.getWidth(), 1,
|
||||
painter.fillRect(0, y, this->container_.getWidth(), 1,
|
||||
app->themes->splits.messageSeperator);
|
||||
}
|
||||
|
||||
|
@ -180,14 +179,13 @@ void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection
|
|||
|
||||
QBrush brush(color, Qt::VerPattern);
|
||||
|
||||
painter.fillRect(0, y + this->container.getHeight() - 1, this->container.getWidth(), 1,
|
||||
brush);
|
||||
painter.fillRect(0, y + this->container_.getHeight() - 1, pixmap->width(), 1, brush);
|
||||
}
|
||||
|
||||
this->bufferValid = true;
|
||||
this->bufferValid_ = true;
|
||||
}
|
||||
|
||||
void MessageLayout::updateBuffer(QPixmap *buffer, int messageIndex, Selection &selection)
|
||||
void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/, Selection & /*selection*/)
|
||||
{
|
||||
auto app = getApp();
|
||||
|
||||
|
@ -197,8 +195,10 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int messageIndex, Selection &s
|
|||
|
||||
// draw background
|
||||
QColor backgroundColor;
|
||||
if (this->message->flags & Message::Highlighted) {
|
||||
if (this->message_->flags & Message::Highlighted) {
|
||||
backgroundColor = app->themes->messages.backgrounds.highlighted;
|
||||
} else if (this->message_->flags & Message::Subscription) {
|
||||
backgroundColor = app->themes->messages.backgrounds.subscription;
|
||||
} else if (app->settings->alternateMessageBackground.getValue() &&
|
||||
this->flags & MessageLayout::AlternateBackground) {
|
||||
backgroundColor = app->themes->messages.backgrounds.alternate;
|
||||
|
@ -208,7 +208,7 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int messageIndex, Selection &s
|
|||
painter.fillRect(buffer->rect(), backgroundColor);
|
||||
|
||||
// draw message
|
||||
this->container.paintElements(painter);
|
||||
this->container_.paintElements(painter);
|
||||
|
||||
#ifdef FOURTF
|
||||
// debug
|
||||
|
@ -226,15 +226,15 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int messageIndex, Selection &s
|
|||
|
||||
void MessageLayout::invalidateBuffer()
|
||||
{
|
||||
this->bufferValid = false;
|
||||
this->bufferValid_ = false;
|
||||
}
|
||||
|
||||
void MessageLayout::deleteBuffer()
|
||||
{
|
||||
if (this->buffer != nullptr) {
|
||||
if (this->buffer_ != nullptr) {
|
||||
util::DebugCount::decrease("message drawing buffers");
|
||||
|
||||
this->buffer = nullptr;
|
||||
this->buffer_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,7 +243,7 @@ void MessageLayout::deleteCache()
|
|||
this->deleteBuffer();
|
||||
|
||||
#ifdef XD
|
||||
this->container.clear();
|
||||
this->container_.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -256,22 +256,22 @@ void MessageLayout::deleteCache()
|
|||
const MessageLayoutElement *MessageLayout::getElementAt(QPoint point)
|
||||
{
|
||||
// go through all words and return the first one that contains the point.
|
||||
return this->container.getElementAt(point);
|
||||
return this->container_.getElementAt(point);
|
||||
}
|
||||
|
||||
int MessageLayout::getLastCharacterIndex() const
|
||||
{
|
||||
return this->container.getLastCharacterIndex();
|
||||
return this->container_.getLastCharacterIndex();
|
||||
}
|
||||
|
||||
int MessageLayout::getSelectionIndex(QPoint position)
|
||||
{
|
||||
return this->container.getSelectionIndex(position);
|
||||
return this->container_.getSelectionIndex(position);
|
||||
}
|
||||
|
||||
void MessageLayout::addSelectionText(QString &str, int from, int to)
|
||||
{
|
||||
this->container.addSelectionText(str, from, to);
|
||||
this->container_.addSelectionText(str, from, to);
|
||||
}
|
||||
|
||||
} // namespace layouts
|
||||
|
|
|
@ -22,10 +22,12 @@ public:
|
|||
enum Flags : uint8_t {
|
||||
RequiresBufferUpdate = 1 << 1,
|
||||
RequiresLayout = 1 << 2,
|
||||
AlternateBackground = 1 << 3
|
||||
AlternateBackground = 1 << 3,
|
||||
Collapsed = 1 << 4,
|
||||
Expanded = 1 << 5,
|
||||
};
|
||||
|
||||
MessageLayout(MessagePtr message);
|
||||
MessageLayout(MessagePtr message_);
|
||||
~MessageLayout();
|
||||
|
||||
Message *getMessage();
|
||||
|
@ -37,10 +39,10 @@ public:
|
|||
util::FlagsEnum<Flags> flags;
|
||||
|
||||
// Layout
|
||||
bool layout(int width, float scale, MessageElement::Flags flags);
|
||||
bool layout(int width, float scale_, MessageElement::Flags flags);
|
||||
|
||||
// Painting
|
||||
void paint(QPainter &painter, int y, int messageIndex, Selection &selection,
|
||||
void paint(QPainter &painter, int width, int y, int messageIndex, Selection &selection,
|
||||
bool isLastReadMessage, bool isWindowFocused);
|
||||
void invalidateBuffer();
|
||||
void deleteBuffer();
|
||||
|
@ -50,30 +52,28 @@ public:
|
|||
const MessageLayoutElement *getElementAt(QPoint point);
|
||||
int getLastCharacterIndex() const;
|
||||
int getSelectionIndex(QPoint position);
|
||||
void addSelectionText(QString &str, int from, int to);
|
||||
void addSelectionText(QString &str, int from = 0, int to = INT_MAX);
|
||||
|
||||
// Misc
|
||||
bool isDisabled() const;
|
||||
|
||||
private:
|
||||
// variables
|
||||
MessagePtr message;
|
||||
MessageLayoutContainer container;
|
||||
std::shared_ptr<QPixmap> buffer = nullptr;
|
||||
bool bufferValid = false;
|
||||
MessagePtr message_;
|
||||
MessageLayoutContainer container_;
|
||||
std::shared_ptr<QPixmap> buffer_ = nullptr;
|
||||
bool bufferValid_ = false;
|
||||
|
||||
int height = 0;
|
||||
int height_ = 0;
|
||||
|
||||
int currentLayoutWidth = -1;
|
||||
int fontGeneration = -1;
|
||||
int emoteGeneration = -1;
|
||||
QString timestampFormat;
|
||||
float scale = -1;
|
||||
unsigned int bufferUpdatedCount = 0;
|
||||
int currentLayoutWidth_ = -1;
|
||||
int layoutState_ = -1;
|
||||
float scale_ = -1;
|
||||
unsigned int bufferUpdatedCount_ = 0;
|
||||
|
||||
MessageElement::Flags currentWordFlags = MessageElement::None;
|
||||
MessageElement::Flags currentWordFlags_ = MessageElement::None;
|
||||
|
||||
int collapsedHeight = 32;
|
||||
int collapsedHeight_ = 32;
|
||||
|
||||
// methods
|
||||
void actuallyLayout(int width, MessageElement::Flags flags);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <QPainter>
|
||||
|
||||
#define COMPACT_EMOTES_OFFSET 6
|
||||
#define MAX_UNCOLLAPSED_LINES (getApp()->settings->collpseMessagesMinLines.getValue())
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
@ -36,6 +37,12 @@ void MessageLayoutContainer::begin(int _width, float _scale, Message::MessageFla
|
|||
this->width = _width;
|
||||
this->scale = _scale;
|
||||
this->flags = _flags;
|
||||
auto mediumFontMetrics = getApp()->fonts->getFontMetrics(FontStyle::ChatMedium, _scale);
|
||||
this->textLineHeight = mediumFontMetrics.height();
|
||||
this->spaceWidth = mediumFontMetrics.width(' ');
|
||||
this->dotdotdotWidth = mediumFontMetrics.width("...");
|
||||
this->_canAddMessages = true;
|
||||
this->_isCollapsed = false;
|
||||
}
|
||||
|
||||
void MessageLayoutContainer::clear()
|
||||
|
@ -68,12 +75,12 @@ void MessageLayoutContainer::addElementNoLineBreak(MessageLayoutElement *element
|
|||
|
||||
bool MessageLayoutContainer::canAddElements()
|
||||
{
|
||||
return !(this->flags & Message::MessageFlags::Collapsed && line >= 3);
|
||||
return this->_canAddMessages;
|
||||
}
|
||||
|
||||
void MessageLayoutContainer::_addElement(MessageLayoutElement *element)
|
||||
void MessageLayoutContainer::_addElement(MessageLayoutElement *element, bool forceAdd)
|
||||
{
|
||||
if (!this->canAddElements()) {
|
||||
if (!this->canAddElements() && !forceAdd) {
|
||||
delete element;
|
||||
return;
|
||||
}
|
||||
|
@ -129,6 +136,11 @@ void MessageLayoutContainer::breakLine()
|
|||
yExtra = (COMPACT_EMOTES_OFFSET / 2) * this->scale;
|
||||
}
|
||||
|
||||
// if (element->getCreator().getFlags() & MessageElement::Badges) {
|
||||
if (element->getRect().height() < this->textLineHeight) {
|
||||
yExtra -= (this->textLineHeight - element->getRect().height()) / 2;
|
||||
}
|
||||
|
||||
element->setPosition(QPoint(element->getRect().x() + xOffset + this->margin.left,
|
||||
element->getRect().y() + this->lineHeight + yExtra));
|
||||
}
|
||||
|
@ -146,6 +158,12 @@ void MessageLayoutContainer::breakLine()
|
|||
|
||||
this->lineStart = this->elements.size();
|
||||
// this->currentX = (int)(this->scale * 8);
|
||||
|
||||
if (this->canCollapse() && line + 1 >= MAX_UNCOLLAPSED_LINES) {
|
||||
this->_canAddMessages = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this->currentX = 0;
|
||||
this->currentY += this->lineHeight;
|
||||
this->height = this->currentY + (this->margin.bottom * this->scale);
|
||||
|
@ -160,15 +178,32 @@ bool MessageLayoutContainer::atStartOfLine()
|
|||
|
||||
bool MessageLayoutContainer::fitsInLine(int _width)
|
||||
{
|
||||
return this->currentX + _width <= this->width - this->margin.left - this->margin.right;
|
||||
return this->currentX + _width <=
|
||||
(this->width - this->margin.left - this->margin.right -
|
||||
(this->line + 1 == MAX_UNCOLLAPSED_LINES ? this->dotdotdotWidth : 0));
|
||||
}
|
||||
|
||||
void MessageLayoutContainer::end()
|
||||
{
|
||||
if (!this->canAddElements()) {
|
||||
static TextElement dotdotdot("...", MessageElement::Collapsed, MessageColor::Link);
|
||||
static QString dotdotdotText("...");
|
||||
|
||||
auto *element = new TextLayoutElement(
|
||||
dotdotdot, dotdotdotText, QSize(this->dotdotdotWidth, this->textLineHeight),
|
||||
QColor("#00D80A"), FontStyle::ChatMediumBold, this->scale);
|
||||
|
||||
// getApp()->themes->messages.textColors.system
|
||||
this->_addElement(element, true);
|
||||
this->_isCollapsed = true;
|
||||
}
|
||||
|
||||
if (!this->atStartOfLine()) {
|
||||
this->breakLine();
|
||||
}
|
||||
|
||||
this->height += this->lineHeight;
|
||||
|
||||
if (this->lines.size() != 0) {
|
||||
this->lines[0].rect.setTop(-100000);
|
||||
this->lines.back().rect.setBottom(100000);
|
||||
|
@ -177,6 +212,17 @@ void MessageLayoutContainer::end()
|
|||
}
|
||||
}
|
||||
|
||||
bool MessageLayoutContainer::canCollapse()
|
||||
{
|
||||
return getApp()->settings->collpseMessagesMinLines.getValue() > 0 &&
|
||||
this->flags & Message::MessageFlags::Collapsed;
|
||||
}
|
||||
|
||||
bool MessageLayoutContainer::isCollapsed()
|
||||
{
|
||||
return this->_isCollapsed;
|
||||
}
|
||||
|
||||
MessageLayoutElement *MessageLayoutContainer::getElementAt(QPoint point)
|
||||
{
|
||||
for (std::unique_ptr<MessageLayoutElement> &element : this->elements) {
|
||||
|
@ -429,8 +475,6 @@ void MessageLayoutContainer::addSelectionText(QString &str, int from, int to)
|
|||
for (std::unique_ptr<MessageLayoutElement> &ele : this->elements) {
|
||||
int c = ele->getSelectionIndexCount();
|
||||
|
||||
qDebug() << c;
|
||||
|
||||
if (first) {
|
||||
if (index + c > from) {
|
||||
ele->addCopyTextToString(str, from - index, to - index);
|
||||
|
|
|
@ -76,6 +76,8 @@ struct MessageLayoutContainer {
|
|||
int getLastCharacterIndex() const;
|
||||
void addSelectionText(QString &str, int from, int to);
|
||||
|
||||
bool isCollapsed();
|
||||
|
||||
private:
|
||||
struct Line {
|
||||
int startIndex;
|
||||
|
@ -86,7 +88,7 @@ private:
|
|||
};
|
||||
|
||||
// helpers
|
||||
void _addElement(MessageLayoutElement *element);
|
||||
void _addElement(MessageLayoutElement *element, bool forceAdd = false);
|
||||
|
||||
// variables
|
||||
float scale = 1.f;
|
||||
|
@ -100,6 +102,12 @@ private:
|
|||
size_t lineStart = 0;
|
||||
int lineHeight = 0;
|
||||
int spaceWidth = 4;
|
||||
int textLineHeight = 0;
|
||||
int dotdotdotWidth = 0;
|
||||
bool _canAddMessages = true;
|
||||
bool _isCollapsed = false;
|
||||
|
||||
bool canCollapse();
|
||||
|
||||
std::vector<std::unique_ptr<MessageLayoutElement>> elements;
|
||||
std::vector<Line> lines;
|
||||
|
|
|
@ -188,7 +188,7 @@ int TextLayoutElement::getMouseOverIndex(const QPoint &abs)
|
|||
|
||||
auto app = getApp();
|
||||
|
||||
QFontMetrics &metrics = app->fonts->getFontMetrics(this->style, this->scale);
|
||||
QFontMetrics metrics = app->fonts->getFontMetrics(this->style, this->scale);
|
||||
|
||||
int x = this->getRect().left();
|
||||
|
||||
|
@ -209,7 +209,7 @@ int TextLayoutElement::getXFromIndex(int index)
|
|||
{
|
||||
auto app = getApp();
|
||||
|
||||
QFontMetrics &metrics = app->fonts->getFontMetrics(this->style, this->scale);
|
||||
QFontMetrics metrics = app->fonts->getFontMetrics(this->style, this->scale);
|
||||
|
||||
if (index <= 0) {
|
||||
return this->getRect().left();
|
||||
|
|
|
@ -21,6 +21,8 @@ SBHighlight Message::getScrollBarHighlight() const
|
|||
{
|
||||
if (this->flags & Message::Highlighted) {
|
||||
return SBHighlight(SBHighlight::Highlight);
|
||||
} else if (this->flags & Message::Subscription) {
|
||||
return SBHighlight(SBHighlight::Subscription);
|
||||
}
|
||||
return SBHighlight();
|
||||
}
|
||||
|
@ -32,7 +34,19 @@ MessagePtr Message::createSystemMessage(const QString &text)
|
|||
|
||||
message->addElement(new TimestampElement(QTime::currentTime()));
|
||||
message->addElement(new TextElement(text, MessageElement::Text, MessageColor::System));
|
||||
message->flags.EnableFlag(MessageFlags::System);
|
||||
message->flags |= MessageFlags::System;
|
||||
message->flags |= MessageFlags::DoNotTriggerNotification;
|
||||
message->searchText = text;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
MessagePtr Message::createMessage(const QString &text)
|
||||
{
|
||||
MessagePtr message(new Message);
|
||||
|
||||
message->addElement(new TimestampElement(QTime::currentTime()));
|
||||
message->addElement(new TextElement(text, MessageElement::Text, MessageColor::Text));
|
||||
message->searchText = text;
|
||||
|
||||
return message;
|
||||
|
@ -63,7 +77,7 @@ MessagePtr Message::createTimeoutMessage(const QString &username, const QString
|
|||
|
||||
if (reason.length() > 0) {
|
||||
text.append(": \"");
|
||||
text.append(util::ParseTagString(reason));
|
||||
text.append(util::parseTagString(reason));
|
||||
text.append("\"");
|
||||
}
|
||||
text.append(".");
|
||||
|
@ -89,7 +103,6 @@ MessagePtr Message::createTimeoutMessage(const providers::twitch::BanAction &act
|
|||
|
||||
msg->timeoutUser = action.target.name;
|
||||
msg->count = count;
|
||||
msg->banAction.reset(new providers::twitch::BanAction(action));
|
||||
|
||||
QString text;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace messages {
|
|||
|
||||
struct Message {
|
||||
Message()
|
||||
: parseTime(QTime::currentTime())
|
||||
{
|
||||
util::DebugCount::increase("messages");
|
||||
}
|
||||
|
@ -39,6 +40,8 @@ struct Message {
|
|||
Collapsed = (1 << 7),
|
||||
DisconnectedMessage = (1 << 8),
|
||||
Untimeout = (1 << 9),
|
||||
PubSub = (1 << 10),
|
||||
Subscription = (1 << 11),
|
||||
};
|
||||
|
||||
util::FlagsEnum<MessageFlags> flags;
|
||||
|
@ -50,7 +53,6 @@ struct Message {
|
|||
QString localizedName;
|
||||
QString timeoutUser;
|
||||
|
||||
std::unique_ptr<providers::twitch::BanAction> banAction;
|
||||
uint32_t count = 1;
|
||||
|
||||
// Messages should not be added after the message is done initializing.
|
||||
|
@ -65,6 +67,7 @@ private:
|
|||
|
||||
public:
|
||||
static std::shared_ptr<Message> createSystemMessage(const QString &text);
|
||||
static std::shared_ptr<Message> createMessage(const QString &text);
|
||||
|
||||
static std::shared_ptr<Message> createTimeoutMessage(const QString &username,
|
||||
const QString &durationInSeconds,
|
||||
|
|
|
@ -70,8 +70,8 @@ ImageElement::ImageElement(Image *_image, MessageElement::Flags flags)
|
|||
void ImageElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags _flags)
|
||||
{
|
||||
if (_flags & this->getFlags()) {
|
||||
QSize size(this->image->getWidth() * this->image->getScale() * container.getScale(),
|
||||
this->image->getHeight() * this->image->getScale() * container.getScale());
|
||||
QSize size(this->image->getScaledWidth() * container.getScale(),
|
||||
this->image->getScaledHeight() * container.getScale());
|
||||
|
||||
container.addElement(
|
||||
(new ImageLayoutElement(*this, this->image, size))->setLink(this->getLink()));
|
||||
|
@ -97,19 +97,10 @@ void EmoteElement::addToContainer(MessageLayoutContainer &container, MessageElem
|
|||
return;
|
||||
}
|
||||
|
||||
int quality = getApp()->settings->preferredEmoteQuality;
|
||||
Image *_image = this->data.getImage(container.getScale());
|
||||
|
||||
Image *_image;
|
||||
if (quality == 3 && this->data.image3x != nullptr) {
|
||||
_image = this->data.image3x;
|
||||
} else if (quality >= 2 && this->data.image2x != nullptr) {
|
||||
_image = this->data.image2x;
|
||||
} else {
|
||||
_image = this->data.image1x;
|
||||
}
|
||||
|
||||
QSize size((int)(container.getScale() * _image->getScaledWidth()),
|
||||
(int)(container.getScale() * _image->getScaledHeight()));
|
||||
QSize size(int(container.getScale() * _image->getScaledWidth()),
|
||||
int(container.getScale() * _image->getScaledHeight()));
|
||||
|
||||
container.addElement(
|
||||
(new ImageLayoutElement(*this, _image, size))->setLink(this->getLink()));
|
||||
|
@ -139,7 +130,7 @@ void TextElement::addToContainer(MessageLayoutContainer &container, MessageEleme
|
|||
auto app = getApp();
|
||||
|
||||
if (_flags & this->getFlags()) {
|
||||
QFontMetrics &metrics = app->fonts->getFontMetrics(this->style, container.getScale());
|
||||
QFontMetrics metrics = app->fonts->getFontMetrics(this->style, container.getScale());
|
||||
|
||||
for (Word &word : this->words) {
|
||||
auto getTextLayoutElement = [&](QString text, int width, bool trailingSpace) {
|
||||
|
@ -242,7 +233,7 @@ TextElement *TimestampElement::formatTime(const QTime &time)
|
|||
|
||||
QString format = locale.toString(time, getApp()->settings->timestampFormat);
|
||||
|
||||
return new TextElement(format, Flags::Timestamp, MessageColor::System, FontStyle::Medium);
|
||||
return new TextElement(format, Flags::Timestamp, MessageColor::System, FontStyle::ChatMedium);
|
||||
}
|
||||
|
||||
// TWITCH MODERATION
|
||||
|
|
|
@ -158,7 +158,7 @@ class TextElement : public MessageElement
|
|||
public:
|
||||
TextElement(const QString &text, MessageElement::Flags flags,
|
||||
const MessageColor &color = MessageColor::Text,
|
||||
FontStyle style = FontStyle::Medium);
|
||||
FontStyle style = FontStyle::ChatMedium);
|
||||
~TextElement() override = default;
|
||||
|
||||
void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override;
|
||||
|
@ -169,7 +169,6 @@ public:
|
|||
// b) which size it wants
|
||||
class EmoteElement : public MessageElement
|
||||
{
|
||||
const util::EmoteData data;
|
||||
std::unique_ptr<TextElement> textElement;
|
||||
|
||||
public:
|
||||
|
@ -177,6 +176,8 @@ public:
|
|||
~EmoteElement() override = default;
|
||||
|
||||
void addToContainer(MessageLayoutContainer &container, MessageElement::Flags flags) override;
|
||||
|
||||
const util::EmoteData data;
|
||||
};
|
||||
|
||||
// contains a text, formated depending on the preferences
|
||||
|
|
|
@ -7,6 +7,7 @@ struct MessageParseArgs {
|
|||
bool disablePingSounds = false;
|
||||
bool isReceivedWhisper = false;
|
||||
bool isSentWhisper = false;
|
||||
bool trimSubscriberUsername = false;
|
||||
};
|
||||
|
||||
} // namespace messages
|
||||
|
|
|
@ -42,6 +42,11 @@ struct SelectionItem {
|
|||
{
|
||||
return this->messageIndex == b.messageIndex && this->charIndex == b.charIndex;
|
||||
}
|
||||
|
||||
bool operator!=(const SelectionItem &b) const
|
||||
{
|
||||
return this->operator==(b);
|
||||
}
|
||||
};
|
||||
|
||||
struct Selection {
|
||||
|
|
5
src/providerid.hpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
namespace chatterino {
|
||||
enum class ProviderId { Twitch };
|
||||
}
|
|
@ -4,6 +4,8 @@
|
|||
#include "messages/limitedqueuesnapshot.hpp"
|
||||
#include "messages/message.hpp"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
using namespace chatterino::messages;
|
||||
|
||||
namespace chatterino {
|
||||
|
@ -13,14 +15,14 @@ namespace irc {
|
|||
AbstractIrcServer::AbstractIrcServer()
|
||||
{
|
||||
// Initialize the connections
|
||||
this->writeConnection.reset(new Communi::IrcConnection);
|
||||
this->writeConnection.reset(new IrcConnection);
|
||||
this->writeConnection->moveToThread(QCoreApplication::instance()->thread());
|
||||
|
||||
QObject::connect(this->writeConnection.get(), &Communi::IrcConnection::messageReceived,
|
||||
[this](auto msg) { this->writeConnectionMessageReceived(msg); });
|
||||
|
||||
// Listen to read connection message signals
|
||||
this->readConnection.reset(new Communi::IrcConnection);
|
||||
this->readConnection.reset(new IrcConnection);
|
||||
this->readConnection->moveToThread(QCoreApplication::instance()->thread());
|
||||
|
||||
QObject::connect(this->readConnection.get(), &Communi::IrcConnection::messageReceived,
|
||||
|
@ -31,9 +33,13 @@ AbstractIrcServer::AbstractIrcServer()
|
|||
[this] { this->onConnected(); });
|
||||
QObject::connect(this->readConnection.get(), &Communi::IrcConnection::disconnected,
|
||||
[this] { this->onDisconnected(); });
|
||||
|
||||
// listen to reconnect request
|
||||
this->readConnection->reconnectRequested.connect([this] { this->connect(); });
|
||||
// this->writeConnection->reconnectRequested.connect([this] { this->connect(); });
|
||||
}
|
||||
|
||||
Communi::IrcConnection *AbstractIrcServer::getReadConnection() const
|
||||
IrcConnection *AbstractIrcServer::getReadConnection() const
|
||||
{
|
||||
return this->readConnection.get();
|
||||
}
|
||||
|
@ -231,7 +237,7 @@ void AbstractIrcServer::addFakeMessage(const QString &data)
|
|||
{
|
||||
auto fakeMessage = Communi::IrcMessage::fromData(data.toUtf8(), this->readConnection.get());
|
||||
|
||||
this->privateMessageReceived(qobject_cast<Communi::IrcPrivateMessage *>(fakeMessage));
|
||||
this->messageReceived(fakeMessage);
|
||||
}
|
||||
|
||||
void AbstractIrcServer::privateMessageReceived(Communi::IrcPrivateMessage *message)
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
#include "channel.hpp"
|
||||
|
||||
#include <IrcConnection>
|
||||
#include <IrcMessage>
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
#include <providers/irc/ircconnection2.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
@ -19,7 +19,7 @@ public:
|
|||
virtual ~AbstractIrcServer() = default;
|
||||
|
||||
// connection
|
||||
Communi::IrcConnection *getReadConnection() const;
|
||||
IrcConnection *getReadConnection() const;
|
||||
|
||||
void connect();
|
||||
void disconnect();
|
||||
|
@ -33,7 +33,7 @@ public:
|
|||
// signals
|
||||
pajlada::Signals::NoArgSignal connected;
|
||||
pajlada::Signals::NoArgSignal disconnected;
|
||||
pajlada::Signals::Signal<Communi::IrcPrivateMessage *> onPrivateMessage;
|
||||
// pajlada::Signals::Signal<Communi::IrcPrivateMessage *> onPrivateMessage;
|
||||
|
||||
void addFakeMessage(const QString &data);
|
||||
|
||||
|
@ -43,8 +43,7 @@ public:
|
|||
protected:
|
||||
AbstractIrcServer();
|
||||
|
||||
virtual void initializeConnection(Communi::IrcConnection *connection, bool isRead,
|
||||
bool isWrite) = 0;
|
||||
virtual void initializeConnection(IrcConnection *connection, bool isRead, bool isWrite) = 0;
|
||||
virtual std::shared_ptr<Channel> createChannel(const QString &channelName) = 0;
|
||||
|
||||
virtual void privateMessageReceived(Communi::IrcPrivateMessage *message);
|
||||
|
@ -64,10 +63,12 @@ protected:
|
|||
private:
|
||||
void initConnection();
|
||||
|
||||
std::unique_ptr<Communi::IrcConnection> writeConnection = nullptr;
|
||||
std::unique_ptr<Communi::IrcConnection> readConnection = nullptr;
|
||||
std::unique_ptr<IrcConnection> writeConnection = nullptr;
|
||||
std::unique_ptr<IrcConnection> readConnection = nullptr;
|
||||
|
||||
std::mutex connectionMutex;
|
||||
|
||||
QTimer pingTimer;
|
||||
};
|
||||
|
||||
} // namespace irc
|
||||
|
|
36
src/providers/irc/ircconnection2.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "ircconnection2.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace providers {
|
||||
namespace irc {
|
||||
|
||||
IrcConnection::IrcConnection(QObject *parent)
|
||||
: Communi::IrcConnection(parent)
|
||||
{
|
||||
this->pingTimer_.setInterval(5000);
|
||||
this->pingTimer_.start();
|
||||
QObject::connect(&this->pingTimer_, &QTimer::timeout, [this] {
|
||||
if (!this->recentlyReceivedMessage_.load()) {
|
||||
this->sendRaw("PING");
|
||||
this->reconnectTimer_.start();
|
||||
}
|
||||
this->recentlyReceivedMessage_ = false;
|
||||
});
|
||||
|
||||
this->reconnectTimer_.setInterval(5000);
|
||||
this->reconnectTimer_.setSingleShot(true);
|
||||
QObject::connect(&this->reconnectTimer_, &QTimer::timeout,
|
||||
[this] { reconnectRequested.invoke(); });
|
||||
|
||||
QObject::connect(this, &Communi::IrcConnection::messageReceived, [this](Communi::IrcMessage *) {
|
||||
this->recentlyReceivedMessage_ = true;
|
||||
|
||||
if (this->reconnectTimer_.isActive()) {
|
||||
this->reconnectTimer_.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace irc
|
||||
} // namespace providers
|
||||
} // namespace chatterino
|
27
src/providers/irc/ircconnection2.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
|
||||
#include <IrcConnection>
|
||||
#include <QTimer>
|
||||
|
||||
namespace chatterino {
|
||||
namespace providers {
|
||||
namespace irc {
|
||||
|
||||
class IrcConnection : public Communi::IrcConnection
|
||||
{
|
||||
public:
|
||||
IrcConnection(QObject *parent = nullptr);
|
||||
|
||||
pajlada::Signals::NoArgSignal reconnectRequested;
|
||||
|
||||
private:
|
||||
QTimer pingTimer_;
|
||||
QTimer reconnectTimer_;
|
||||
std::atomic<bool> recentlyReceivedMessage_{true};
|
||||
};
|
||||
|
||||
} // namespace irc
|
||||
} // namespace providers
|
||||
} // namespace chatterino
|
|
@ -1,6 +1,7 @@
|
|||
#include "ircmessagehandler.hpp"
|
||||
|
||||
#include "application.hpp"
|
||||
#include "controllers/highlights/highlightcontroller.hpp"
|
||||
#include "debug/log.hpp"
|
||||
#include "messages/limitedqueue.hpp"
|
||||
#include "messages/message.hpp"
|
||||
|
@ -10,6 +11,9 @@
|
|||
#include "providers/twitch/twitchserver.hpp"
|
||||
#include "singletons/resourcemanager.hpp"
|
||||
#include "singletons/windowmanager.hpp"
|
||||
#include "util/irchelpers.hpp"
|
||||
|
||||
#include <IrcMessage>
|
||||
|
||||
using namespace chatterino::singletons;
|
||||
using namespace chatterino::messages;
|
||||
|
@ -24,116 +28,145 @@ IrcMessageHandler &IrcMessageHandler::getInstance()
|
|||
return instance;
|
||||
}
|
||||
|
||||
void IrcMessageHandler::handlePrivMessage(Communi::IrcPrivateMessage *message, TwitchServer &server)
|
||||
{
|
||||
this->addMessage(message, message->target(), message->content(), server, false);
|
||||
}
|
||||
|
||||
void IrcMessageHandler::addMessage(Communi::IrcMessage *message, const QString &target,
|
||||
const QString &content, TwitchServer &server, bool isSub)
|
||||
{
|
||||
QString channelName;
|
||||
if (!trimChannelName(target, channelName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto chan = server.getChannelOrEmpty(channelName);
|
||||
|
||||
if (chan->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
messages::MessageParseArgs args;
|
||||
if (isSub) {
|
||||
args.trimSubscriberUsername = true;
|
||||
}
|
||||
|
||||
TwitchMessageBuilder builder(chan.get(), message, content, args);
|
||||
|
||||
if (isSub || !builder.isIgnored()) {
|
||||
messages::MessagePtr msg = builder.build();
|
||||
|
||||
if (isSub) {
|
||||
msg->flags |= messages::Message::Subscription;
|
||||
msg->flags &= ~messages::Message::Highlighted;
|
||||
} else {
|
||||
if (msg->flags & messages::Message::Highlighted) {
|
||||
server.mentionsChannel->addMessage(msg);
|
||||
getApp()->highlights->addHighlight(msg);
|
||||
}
|
||||
}
|
||||
|
||||
chan->addMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message)
|
||||
{
|
||||
const auto &tags = message->tags();
|
||||
auto iterator = tags.find("room-id");
|
||||
|
||||
if (iterator != tags.end()) {
|
||||
auto roomID = iterator.value().toString();
|
||||
|
||||
QStringList words = QString(message->toData()).split("#");
|
||||
|
||||
// ensure the format is valid
|
||||
if (words.length() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto app = getApp();
|
||||
|
||||
QString channelName = words.at(1);
|
||||
|
||||
auto channel = app->twitch.server->getChannelOrEmpty(channelName);
|
||||
|
||||
if (channel->isEmpty()) {
|
||||
// get twitch channel
|
||||
QString chanName;
|
||||
if (!trimChannelName(message->parameter(0), chanName)) {
|
||||
return;
|
||||
}
|
||||
auto chan = app->twitch.server->getChannelOrEmpty(chanName);
|
||||
TwitchChannel *twitchChannel = dynamic_cast<twitch::TwitchChannel *>(chan.get());
|
||||
|
||||
if (twitchChannel) {
|
||||
// room-id
|
||||
decltype(tags.find("xD")) it;
|
||||
|
||||
if ((it = tags.find("room-id")) != tags.end()) {
|
||||
auto roomID = it.value().toString();
|
||||
|
||||
if (auto twitchChannel = dynamic_cast<twitch::TwitchChannel *>(channel.get())) {
|
||||
// set the room id of the channel
|
||||
twitchChannel->setRoomID(roomID);
|
||||
}
|
||||
|
||||
app->resources->loadChannelData(roomID);
|
||||
}
|
||||
|
||||
// Room modes
|
||||
TwitchChannel::RoomModes roomModes = twitchChannel->getRoomModes();
|
||||
|
||||
if ((it = tags.find("emote-only")) != tags.end()) {
|
||||
roomModes.emoteOnly = it.value() == "1";
|
||||
}
|
||||
if ((it = tags.find("subs-only")) != tags.end()) {
|
||||
roomModes.submode = it.value() == "1";
|
||||
}
|
||||
if ((it = tags.find("slow")) != tags.end()) {
|
||||
roomModes.slowMode = it.value().toInt();
|
||||
}
|
||||
if ((it = tags.find("r9k")) != tags.end()) {
|
||||
roomModes.r9k = it.value() == "1";
|
||||
}
|
||||
if ((it = tags.find("broadcaster-lang")) != tags.end()) {
|
||||
roomModes.broadcasterLang = it.value().toString();
|
||||
}
|
||||
|
||||
twitchChannel->setRoomModes(roomModes);
|
||||
}
|
||||
}
|
||||
|
||||
void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
|
||||
{
|
||||
// check parameter count
|
||||
if (message->parameters().length() < 1) {
|
||||
return;
|
||||
// // check parameter count
|
||||
// if (message->parameters().length() < 1) {
|
||||
// return;
|
||||
// }
|
||||
}
|
||||
|
||||
// QString chanName;
|
||||
// if (!TrimChannelName(message->parameter(0), chanName)) {
|
||||
// return;
|
||||
// }
|
||||
QString chanName;
|
||||
if (!trimChannelName(message->parameter(0), chanName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// auto app = getApp();
|
||||
auto app = getApp();
|
||||
|
||||
// // get channel
|
||||
// auto chan = app->twitch.server->getChannelOrEmpty(chanName);
|
||||
// get channel
|
||||
auto chan = app->twitch.server->getChannelOrEmpty(chanName);
|
||||
|
||||
// if (chan->isEmpty()) {
|
||||
// debug::Log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not found",
|
||||
// chanName);
|
||||
// return;
|
||||
// }
|
||||
if (chan->isEmpty()) {
|
||||
debug::Log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not found",
|
||||
chanName);
|
||||
return;
|
||||
}
|
||||
|
||||
// // check if the chat has been cleared by a moderator
|
||||
// if (message->parameters().length() == 1) {
|
||||
// chan->addMessage(Message::createSystemMessage("Chat has been cleared by a
|
||||
// moderator."));
|
||||
// check if the chat has been cleared by a moderator
|
||||
if (message->parameters().length() == 1) {
|
||||
chan->addMessage(Message::createSystemMessage("Chat has been cleared by a moderator."));
|
||||
|
||||
// return;
|
||||
// }
|
||||
return;
|
||||
}
|
||||
|
||||
// // get username, duration and message of the timed out user
|
||||
// QString username = message->parameter(1);
|
||||
// QString durationInSeconds, reason;
|
||||
// QVariant v = message->tag("ban-duration");
|
||||
// if (v.isValid()) {
|
||||
// durationInSeconds = v.toString();
|
||||
// }
|
||||
// get username, duration and message of the timed out user
|
||||
QString username = message->parameter(1);
|
||||
QString durationInSeconds, reason;
|
||||
QVariant v = message->tag("ban-duration");
|
||||
if (v.isValid()) {
|
||||
durationInSeconds = v.toString();
|
||||
}
|
||||
|
||||
// v = message->tag("ban-reason");
|
||||
// if (v.isValid()) {
|
||||
// reason = v.toString();
|
||||
// }
|
||||
v = message->tag("ban-reason");
|
||||
if (v.isValid()) {
|
||||
reason = v.toString();
|
||||
}
|
||||
|
||||
// // add the notice that the user has been timed out
|
||||
// LimitedQueueSnapshot<MessagePtr> snapshot = chan->getMessageSnapshot();
|
||||
// bool addMessage = true;
|
||||
// int snapshotLength = snapshot.getLength();
|
||||
auto timeoutMsg = Message::createTimeoutMessage(username, durationInSeconds, reason, false);
|
||||
chan->addOrReplaceTimeout(timeoutMsg);
|
||||
|
||||
// for (int i = std::max(0, snapshotLength - 20); i < snapshotLength; i++) {
|
||||
// auto &s = snapshot[i];
|
||||
// if (s->flags.HasFlag(Message::Timeout) && s->timeoutUser == username) {
|
||||
// MessagePtr replacement(
|
||||
// Message::createTimeoutMessage(username, durationInSeconds, reason, true));
|
||||
// chan->replaceMessage(s, replacement);
|
||||
// addMessage = false;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (addMessage) {
|
||||
// chan->addMessage(Message::createTimeoutMessage(username, durationInSeconds, reason,
|
||||
// false));
|
||||
// }
|
||||
|
||||
// // disable the messages from the user
|
||||
// for (int i = 0; i < snapshotLength; i++) {
|
||||
// auto &s = snapshot[i];
|
||||
// if (!(s->flags & Message::Timeout) && s->loginName == username) {
|
||||
// s->flags.EnableFlag(Message::Disabled);
|
||||
// }
|
||||
// }
|
||||
|
||||
// // refresh all
|
||||
// app->windows->repaintVisibleChatWidgets(chan.get());
|
||||
// refresh all
|
||||
app->windows->repaintVisibleChatWidgets(chan.get());
|
||||
}
|
||||
|
||||
void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message)
|
||||
|
@ -144,7 +177,7 @@ void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message)
|
|||
auto app = getApp();
|
||||
|
||||
QString channelName;
|
||||
if (!TrimChannelName(message->parameter(0), channelName)) {
|
||||
if (!trimChannelName(message->parameter(0), channelName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -180,6 +213,8 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *message)
|
|||
app->twitch.server->mentionsChannel->addMessage(_message);
|
||||
}
|
||||
|
||||
app->twitch.server->lastUserThatWhisperedMe.set(builder.userName);
|
||||
|
||||
c->addMessage(_message);
|
||||
|
||||
if (app->settings->inlineWhispers) {
|
||||
|
@ -190,9 +225,51 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *message)
|
|||
}
|
||||
}
|
||||
|
||||
void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message)
|
||||
void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message, TwitchServer &server)
|
||||
{
|
||||
// do nothing
|
||||
auto data = message->toData();
|
||||
|
||||
auto tags = message->tags();
|
||||
auto parameters = message->parameters();
|
||||
|
||||
auto target = parameters[0];
|
||||
QString msgType = tags.value("msg-id", "").toString();
|
||||
QString content;
|
||||
if (parameters.size() >= 2) {
|
||||
content = parameters[1];
|
||||
}
|
||||
|
||||
if (msgType == "sub" || msgType == "resub" || msgType == "subgift") {
|
||||
// Sub-specific message. I think it's only allowed for "resub" messages atm
|
||||
if (!content.isEmpty()) {
|
||||
this->addMessage(message, target, content, server, true);
|
||||
}
|
||||
}
|
||||
|
||||
auto it = tags.find("system-msg");
|
||||
|
||||
if (it != tags.end()) {
|
||||
auto newMessage =
|
||||
messages::Message::createSystemMessage(util::parseTagString(it.value().toString()));
|
||||
|
||||
newMessage->flags |= messages::Message::Subscription;
|
||||
|
||||
QString channelName;
|
||||
|
||||
if (message->parameters().size() < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!trimChannelName(message->parameter(0), channelName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto chan = server.getChannelOrEmpty(channelName);
|
||||
|
||||
if (!chan->isEmpty()) {
|
||||
chan->addMessage(newMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IrcMessageHandler::handleModeMessage(Communi::IrcMessage *message)
|
||||
|
@ -231,7 +308,8 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
|
|||
// auto channel = app->twitch.server->getChannelOrEmpty(channelName);
|
||||
|
||||
// if (channel->isEmpty()) {
|
||||
// debug::Log("[IrcManager:handleNoticeMessage] Channel {} not found in channel manager",
|
||||
// debug::Log("[IrcManager:handleNoticeMessage] Channel {} not found in channel
|
||||
// manager",
|
||||
// channelName);
|
||||
// return;
|
||||
// }
|
||||
|
@ -257,6 +335,26 @@ void IrcMessageHandler::handleWriteConnectionNoticeMessage(Communi::IrcNoticeMes
|
|||
this->handleNoticeMessage(message);
|
||||
}
|
||||
|
||||
void IrcMessageHandler::handleJoinMessage(Communi::IrcMessage *message)
|
||||
{
|
||||
auto app = getApp();
|
||||
auto channel = app->twitch.server->getChannelOrEmpty(message->parameter(0).remove(0, 1));
|
||||
|
||||
if (TwitchChannel *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get())) {
|
||||
twitchChannel->addJoinedUser(message->nick());
|
||||
}
|
||||
}
|
||||
|
||||
void IrcMessageHandler::handlePartMessage(Communi::IrcMessage *message)
|
||||
{
|
||||
auto app = getApp();
|
||||
auto channel = app->twitch.server->getChannelOrEmpty(message->parameter(0).remove(0, 1));
|
||||
|
||||
if (TwitchChannel *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get())) {
|
||||
twitchChannel->addPartedUser(message->nick());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace twitch
|
||||
} // namespace providers
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -6,6 +6,8 @@ namespace chatterino {
|
|||
namespace providers {
|
||||
namespace twitch {
|
||||
|
||||
class TwitchServer;
|
||||
|
||||
class IrcMessageHandler
|
||||
{
|
||||
IrcMessageHandler() = default;
|
||||
|
@ -13,14 +15,23 @@ class IrcMessageHandler
|
|||
public:
|
||||
static IrcMessageHandler &getInstance();
|
||||
|
||||
void handlePrivMessage(Communi::IrcPrivateMessage *message, TwitchServer &server);
|
||||
|
||||
void handleRoomStateMessage(Communi::IrcMessage *message);
|
||||
void handleClearChatMessage(Communi::IrcMessage *message);
|
||||
void handleUserStateMessage(Communi::IrcMessage *message);
|
||||
void handleWhisperMessage(Communi::IrcMessage *message);
|
||||
void handleUserNoticeMessage(Communi::IrcMessage *message);
|
||||
void handleUserNoticeMessage(Communi::IrcMessage *message, TwitchServer &server);
|
||||
void handleModeMessage(Communi::IrcMessage *message);
|
||||
void handleNoticeMessage(Communi::IrcNoticeMessage *message);
|
||||
void handleWriteConnectionNoticeMessage(Communi::IrcNoticeMessage *message);
|
||||
|
||||
void handleJoinMessage(Communi::IrcMessage *message);
|
||||
void handlePartMessage(Communi::IrcMessage *message);
|
||||
|
||||
private:
|
||||
void addMessage(Communi::IrcMessage *message, const QString &target, const QString &content,
|
||||
TwitchServer &server, bool isResub);
|
||||
};
|
||||
|
||||
} // namespace twitch
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "debug/log.hpp"
|
||||
#include "providers/twitch/pubsubactions.hpp"
|
||||
#include "providers/twitch/pubsubhelpers.hpp"
|
||||
#include "singletons/accountmanager.hpp"
|
||||
#include "util/rapidjson-helpers.hpp"
|
||||
|
||||
#include <rapidjson/error/en.h>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "providers/twitch/pubsubhelpers.hpp"
|
||||
|
||||
#include "providers/twitch/pubsubactions.hpp"
|
||||
#include "singletons/accountmanager.hpp"
|
||||
#include "util/rapidjson-helpers.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace twitch {
|
|||
|
||||
TwitchAccount::TwitchAccount(const QString &_username, const QString &_oauthToken,
|
||||
const QString &_oauthClient, const QString &_userID)
|
||||
: controllers::accounts::Account("Twitch")
|
||||
: controllers::accounts::Account(ProviderId::Twitch)
|
||||
, oauthClient(_oauthClient)
|
||||
, oauthToken(_oauthToken)
|
||||
, userName(_username)
|
||||
|
|
|
@ -55,7 +55,6 @@ public:
|
|||
bool isAnon() const;
|
||||
|
||||
void loadIgnores();
|
||||
|
||||
void ignore(const QString &targetName,
|
||||
std::function<void(IgnoreResult, const QString &)> onFinished);
|
||||
void ignoreByID(const QString &targetUserID, const QString &targetName,
|
||||
|
|
|
@ -15,6 +15,9 @@ TwitchAccountManager::TwitchAccountManager()
|
|||
auto currentUser = this->getCurrent();
|
||||
currentUser->loadIgnores();
|
||||
});
|
||||
|
||||
this->accounts.itemRemoved.connect(
|
||||
[this](const auto &acc) { this->removeUser(acc.item.get()); });
|
||||
}
|
||||
|
||||
std::shared_ptr<TwitchAccount> TwitchAccountManager::getCurrent()
|
||||
|
@ -32,7 +35,7 @@ std::vector<QString> TwitchAccountManager::getUsernames() const
|
|||
|
||||
std::lock_guard<std::mutex> lock(this->mutex);
|
||||
|
||||
for (const auto &user : this->users) {
|
||||
for (const auto &user : this->accounts.getVector()) {
|
||||
userNames.push_back(user->getUserName());
|
||||
}
|
||||
|
||||
|
@ -44,7 +47,7 @@ std::shared_ptr<TwitchAccount> TwitchAccountManager::findUserByUsername(
|
|||
{
|
||||
std::lock_guard<std::mutex> lock(this->mutex);
|
||||
|
||||
for (const auto &user : this->users) {
|
||||
for (const auto &user : this->accounts.getVector()) {
|
||||
if (username.compare(user->getUserName(), Qt::CaseInsensitive) == 0) {
|
||||
return user;
|
||||
}
|
||||
|
@ -134,25 +137,16 @@ void TwitchAccountManager::load()
|
|||
});
|
||||
}
|
||||
|
||||
bool TwitchAccountManager::removeUser(const QString &username)
|
||||
bool TwitchAccountManager::removeUser(TwitchAccount *account)
|
||||
{
|
||||
if (!this->userExists(username)) {
|
||||
return false;
|
||||
}
|
||||
const auto &accs = this->accounts.getVector();
|
||||
|
||||
this->mutex.lock();
|
||||
this->users.erase(std::remove_if(this->users.begin(), this->users.end(), [username](auto user) {
|
||||
if (user->getUserName() == username) {
|
||||
std::string userID(user->getUserId().toStdString());
|
||||
assert(!userID.empty());
|
||||
std::string userID(account->getUserId().toStdString());
|
||||
if (!userID.empty()) {
|
||||
pajlada::Settings::SettingManager::removeSetting("/accounts/uid" + userID);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
this->mutex.unlock();
|
||||
|
||||
if (username == qS(this->currentUsername.getValue())) {
|
||||
if (account->getUserName() == qS(this->currentUsername.getValue())) {
|
||||
// The user that was removed is the current user, log into the anonymous user
|
||||
this->currentUsername = "";
|
||||
}
|
||||
|
@ -187,9 +181,9 @@ TwitchAccountManager::AddUserResponse TwitchAccountManager::addUser(
|
|||
auto newUser = std::make_shared<TwitchAccount>(userData.username, userData.oauthToken,
|
||||
userData.clientID, userData.userID);
|
||||
|
||||
std::lock_guard<std::mutex> lock(this->mutex);
|
||||
// std::lock_guard<std::mutex> lock(this->mutex);
|
||||
|
||||
this->users.push_back(newUser);
|
||||
this->accounts.insertItem(newUser);
|
||||
|
||||
return AddUserResponse::UserAdded;
|
||||
}
|
||||
|
|
|
@ -15,10 +15,11 @@
|
|||
//
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
namespace singletons {
|
||||
class AccountManager;
|
||||
} // namespace singletons
|
||||
namespace controllers {
|
||||
namespace accounts {
|
||||
class AccountController;
|
||||
}
|
||||
} // namespace controllers
|
||||
|
||||
namespace providers {
|
||||
namespace twitch {
|
||||
|
@ -46,8 +47,6 @@ public:
|
|||
void reloadUsers();
|
||||
void load();
|
||||
|
||||
bool removeUser(const QString &username);
|
||||
|
||||
pajlada::Settings::Setting<std::string> currentUsername = {"/accounts/current", ""};
|
||||
pajlada::Signals::NoArgSignal currentUserChanged;
|
||||
pajlada::Signals::NoArgSignal userListUpdated;
|
||||
|
@ -63,14 +62,14 @@ private:
|
|||
UserAdded,
|
||||
};
|
||||
AddUserResponse addUser(const UserData &data);
|
||||
bool removeUser(TwitchAccount *account);
|
||||
|
||||
std::shared_ptr<TwitchAccount> currentUser;
|
||||
|
||||
std::shared_ptr<TwitchAccount> anonymousUser;
|
||||
std::vector<std::shared_ptr<TwitchAccount>> users;
|
||||
mutable std::mutex mutex;
|
||||
|
||||
friend class chatterino::singletons::AccountManager;
|
||||
friend class chatterino::controllers::accounts::AccountController;
|
||||
};
|
||||
|
||||
} // namespace twitch
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include "messages/message.hpp"
|
||||
#include "providers/twitch/pubsub.hpp"
|
||||
#include "providers/twitch/twitchmessagebuilder.hpp"
|
||||
#include "singletons/accountmanager.hpp"
|
||||
#include "singletons/emotemanager.hpp"
|
||||
#include "singletons/ircmanager.hpp"
|
||||
#include "singletons/settingsmanager.hpp"
|
||||
|
@ -45,7 +44,8 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection
|
|||
this->refreshLiveStatus(); //
|
||||
});
|
||||
|
||||
this->managedConnect(app->accounts->Twitch.currentUserChanged, [this]() { this->setMod(false); });
|
||||
this->managedConnect(app->accounts->twitch.currentUserChanged,
|
||||
[this]() { this->setMod(false); });
|
||||
|
||||
auto refreshPubSubState = [=]() {
|
||||
if (!this->hasModRights()) {
|
||||
|
@ -56,7 +56,7 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection
|
|||
return;
|
||||
}
|
||||
|
||||
auto account = app->accounts->Twitch.getCurrent();
|
||||
auto account = app->accounts->twitch.getCurrent();
|
||||
if (account && !account->getUserId().isEmpty()) {
|
||||
app->twitch.pubsub->listenToChannelModerationActions(this->roomID, account);
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection
|
|||
|
||||
this->userStateChanged.connect(refreshPubSubState);
|
||||
this->roomIDchanged.connect(refreshPubSubState);
|
||||
this->managedConnect(app->accounts->Twitch.currentUserChanged, refreshPubSubState);
|
||||
this->managedConnect(app->accounts->twitch.currentUserChanged, refreshPubSubState);
|
||||
refreshPubSubState();
|
||||
|
||||
this->fetchMessages.connect([this] {
|
||||
|
@ -85,7 +85,7 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection
|
|||
};
|
||||
|
||||
auto doRefreshChatters = [=]() {
|
||||
const auto streamStatus = this->GetStreamStatus();
|
||||
const auto streamStatus = this->getStreamStatus();
|
||||
|
||||
if (app->settings->onlyFetchChattersForSmallerStreamers) {
|
||||
if (streamStatus.live && streamStatus.viewerCount > app->settings->smallStreamerLimit) {
|
||||
|
@ -102,6 +102,10 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection
|
|||
this->chattersListTimer = new QTimer;
|
||||
QObject::connect(this->chattersListTimer, &QTimer::timeout, doRefreshChatters);
|
||||
this->chattersListTimer->start(5 * 60 * 1000);
|
||||
|
||||
// for (int i = 0; i < 1000; i++) {
|
||||
// this->addMessage(messages::Message::createSystemMessage("asdf"));
|
||||
// }
|
||||
}
|
||||
|
||||
TwitchChannel::~TwitchChannel()
|
||||
|
@ -186,7 +190,7 @@ bool TwitchChannel::isBroadcaster()
|
|||
{
|
||||
auto app = getApp();
|
||||
|
||||
return this->name == app->accounts->Twitch.getCurrent()->getUserName();
|
||||
return this->name == app->accounts->twitch.getCurrent()->getUserName();
|
||||
}
|
||||
|
||||
bool TwitchChannel::hasModRights()
|
||||
|
@ -206,6 +210,93 @@ void TwitchChannel::addRecentChatter(const std::shared_ptr<messages::Message> &m
|
|||
this->completionModel.addUser(message->displayName);
|
||||
}
|
||||
|
||||
void TwitchChannel::addJoinedUser(const QString &user)
|
||||
{
|
||||
auto *app = getApp();
|
||||
if (user == app->accounts->twitch.getCurrent()->getUserName() ||
|
||||
!app->settings->showJoins.getValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(this->joinedUserMutex);
|
||||
|
||||
joinedUsers << user;
|
||||
|
||||
if (!this->joinedUsersMergeQueued) {
|
||||
this->joinedUsersMergeQueued = true;
|
||||
|
||||
QTimer::singleShot(500, &this->object, [this] {
|
||||
std::lock_guard<std::mutex> guard(this->joinedUserMutex);
|
||||
|
||||
auto message = messages::Message::createSystemMessage("Users joined: " +
|
||||
this->joinedUsers.join(", "));
|
||||
message->flags |= messages::Message::Collapsed;
|
||||
this->addMessage(message);
|
||||
this->joinedUsers.clear();
|
||||
this->joinedUsersMergeQueued = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void TwitchChannel::addPartedUser(const QString &user)
|
||||
{
|
||||
auto *app = getApp();
|
||||
|
||||
if (user == app->accounts->twitch.getCurrent()->getUserName() ||
|
||||
!app->settings->showJoins.getValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(this->partedUserMutex);
|
||||
|
||||
partedUsers << user;
|
||||
|
||||
if (!this->partedUsersMergeQueued) {
|
||||
this->partedUsersMergeQueued = true;
|
||||
|
||||
QTimer::singleShot(500, &this->object, [this] {
|
||||
std::lock_guard<std::mutex> guard(this->partedUserMutex);
|
||||
|
||||
auto message = messages::Message::createSystemMessage("Users parted: " +
|
||||
this->partedUsers.join(", "));
|
||||
message->flags |= messages::Message::Collapsed;
|
||||
this->addMessage(message);
|
||||
this->partedUsers.clear();
|
||||
|
||||
this->partedUsersMergeQueued = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TwitchChannel::RoomModes TwitchChannel::getRoomModes()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->roomModeMutex);
|
||||
|
||||
return this->roomModes;
|
||||
}
|
||||
|
||||
void TwitchChannel::setRoomModes(const RoomModes &_roomModes)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->roomModeMutex);
|
||||
this->roomModes = _roomModes;
|
||||
}
|
||||
|
||||
this->roomModesChanged.invoke();
|
||||
}
|
||||
|
||||
bool TwitchChannel::isLive() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->streamStatusMutex);
|
||||
return this->streamStatus.live;
|
||||
}
|
||||
|
||||
TwitchChannel::StreamStatus TwitchChannel::getStreamStatus() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->streamStatusMutex);
|
||||
return this->streamStatus;
|
||||
}
|
||||
|
||||
void TwitchChannel::setLive(bool newLiveStatus)
|
||||
{
|
||||
bool gotNewLiveStatus = false;
|
||||
|
@ -289,6 +380,11 @@ void TwitchChannel::refreshLiveStatus()
|
|||
QString::number(diff / 3600) + "h " + QString::number(diff % 3600 / 60) + "m";
|
||||
|
||||
channel->streamStatus.rerun = false;
|
||||
if (stream.HasMember("stream_type")) {
|
||||
channel->streamStatus.streamType = stream["stream_type"].GetString();
|
||||
} else {
|
||||
channel->streamStatus.streamType = QString();
|
||||
}
|
||||
|
||||
if (stream.HasMember("broadcast_platform")) {
|
||||
const auto &broadcastPlatformValue = stream["broadcast_platform"];
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "singletons/emotemanager.hpp"
|
||||
#include "singletons/ircmanager.hpp"
|
||||
#include "util/concurrentmap.hpp"
|
||||
#include "util/mutexvalue.hpp"
|
||||
|
||||
#include <pajlada/signals/signalholder.hpp>
|
||||
|
||||
|
@ -31,6 +32,7 @@ public:
|
|||
QString title;
|
||||
QString game;
|
||||
QString uptime;
|
||||
QString streamType;
|
||||
};
|
||||
|
||||
struct UserState {
|
||||
|
@ -38,6 +40,15 @@ public:
|
|||
bool broadcaster;
|
||||
};
|
||||
|
||||
struct RoomModes {
|
||||
bool submode = false;
|
||||
bool r9k = false;
|
||||
bool emoteOnly = false;
|
||||
// int folowerOnly = 0;
|
||||
int slowMode = 0;
|
||||
QString broadcasterLang;
|
||||
};
|
||||
|
||||
~TwitchChannel() final;
|
||||
|
||||
void reloadChannelEmotes();
|
||||
|
@ -52,6 +63,8 @@ public:
|
|||
bool hasModRights();
|
||||
|
||||
void addRecentChatter(const std::shared_ptr<messages::Message> &message) final;
|
||||
void addJoinedUser(const QString &user);
|
||||
void addPartedUser(const QString &user);
|
||||
|
||||
const std::shared_ptr<chatterino::util::EmoteMap> bttvChannelEmotes;
|
||||
const std::shared_ptr<chatterino::util::EmoteMap> ffzChannelEmotes;
|
||||
|
@ -66,25 +79,21 @@ public:
|
|||
|
||||
pajlada::Signals::NoArgBoltSignal fetchMessages;
|
||||
pajlada::Signals::NoArgSignal userStateChanged;
|
||||
pajlada::Signals::NoArgSignal roomModesChanged;
|
||||
|
||||
QString roomID;
|
||||
|
||||
StreamStatus GetStreamStatus() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->streamStatusMutex);
|
||||
return this->streamStatus;
|
||||
}
|
||||
RoomModes getRoomModes();
|
||||
void setRoomModes(const RoomModes &roomModes);
|
||||
|
||||
StreamStatus getStreamStatus() const;
|
||||
|
||||
struct NameOptions {
|
||||
QString displayName;
|
||||
QString localizedName;
|
||||
};
|
||||
|
||||
bool IsLive() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->streamStatusMutex);
|
||||
return this->streamStatus.live;
|
||||
}
|
||||
bool isLive() const;
|
||||
|
||||
private:
|
||||
explicit TwitchChannel(const QString &channelName, Communi::IrcConnection *readConnection);
|
||||
|
@ -103,6 +112,16 @@ private:
|
|||
bool mod;
|
||||
QByteArray messageSuffix;
|
||||
QString lastSentMessage;
|
||||
RoomModes roomModes;
|
||||
std::mutex roomModeMutex;
|
||||
|
||||
QObject object;
|
||||
std::mutex joinedUserMutex;
|
||||
QStringList joinedUsers;
|
||||
bool joinedUsersMergeQueued = false;
|
||||
std::mutex partedUserMutex;
|
||||
QStringList partedUsers;
|
||||
bool partedUsersMergeQueued = false;
|
||||
|
||||
Communi::IrcConnection *readConnection;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace chatterino {
|
|||
namespace providers {
|
||||
namespace twitch {
|
||||
|
||||
bool TrimChannelName(const QString &channelName, QString &outChannelName)
|
||||
bool trimChannelName(const QString &channelName, QString &outChannelName)
|
||||
{
|
||||
if (channelName.length() < 3) {
|
||||
debug::Log("channel name length below 3");
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace chatterino {
|
|||
namespace providers {
|
||||
namespace twitch {
|
||||
|
||||
bool TrimChannelName(const QString &channelName, QString &outChannelName);
|
||||
bool trimChannelName(const QString &channelName, QString &outChannelName);
|
||||
|
||||
} // namespace twitch
|
||||
} // namespace providers
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#include "providers/twitch/twitchmessagebuilder.hpp"
|
||||
|
||||
#include "application.hpp"
|
||||
#include "controllers/accounts/accountcontroller.hpp"
|
||||
#include "controllers/highlights/highlightcontroller.hpp"
|
||||
#include "controllers/ignores/ignorecontroller.hpp"
|
||||
#include "debug/log.hpp"
|
||||
#include "providers/twitch/twitchchannel.hpp"
|
||||
#include "singletons/accountmanager.hpp"
|
||||
#include "singletons/emotemanager.hpp"
|
||||
#include "singletons/ircmanager.hpp"
|
||||
#include "singletons/resourcemanager.hpp"
|
||||
|
@ -68,7 +68,18 @@ bool TwitchMessageBuilder::isIgnored() const
|
|||
if (app->settings->enableTwitchIgnoredUsers && this->tags.contains("user-id")) {
|
||||
auto sourceUserID = this->tags.value("user-id").toString();
|
||||
|
||||
for (const auto &user : app->accounts->Twitch.getCurrent()->getIgnores()) {
|
||||
for (const auto &user : app->accounts->twitch.getCurrent()->getIgnores()) {
|
||||
if (sourceUserID == user.id) {
|
||||
debug::Log("Blocking message because it's from blocked user {}", user.name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (app->settings->enableTwitchIgnoredUsers && this->tags.contains("user-id")) {
|
||||
auto sourceUserID = this->tags.value("user-id").toString();
|
||||
|
||||
for (const auto &user : app->accounts->twitch.getCurrent()->getIgnores()) {
|
||||
if (sourceUserID == user.id) {
|
||||
debug::Log("Blocking message because it's from blocked user {}", user.name);
|
||||
return true;
|
||||
|
@ -86,12 +97,14 @@ MessagePtr TwitchMessageBuilder::build()
|
|||
// PARSING
|
||||
this->parseUsername();
|
||||
|
||||
#ifdef XD
|
||||
if (this->originalMessage.length() > 100) {
|
||||
//#ifdef XD
|
||||
// if (this->originalMessage.length() > 100) {
|
||||
// this->message->flags |= Message::Collapsed;
|
||||
// this->emplace<EmoteElement>(getApp()->resources->badgeCollapsed,
|
||||
// MessageElement::Collapsed);
|
||||
// }
|
||||
//#endif
|
||||
this->message->flags |= Message::Collapsed;
|
||||
this->emplace<EmoteElement>(getApp()->resources->badgeCollapsed, MessageElement::Collapsed);
|
||||
}
|
||||
#endif
|
||||
|
||||
// PARSING
|
||||
this->parseMessageID();
|
||||
|
@ -141,15 +154,8 @@ MessagePtr TwitchMessageBuilder::build()
|
|||
this->appendTwitchEmote(ircMessage, emote, twitchEmotes);
|
||||
}
|
||||
|
||||
struct {
|
||||
bool operator()(const std::pair<long, util::EmoteData> &lhs,
|
||||
const std::pair<long, util::EmoteData> &rhs)
|
||||
{
|
||||
return lhs.first < rhs.first;
|
||||
}
|
||||
} customLess;
|
||||
|
||||
std::sort(twitchEmotes.begin(), twitchEmotes.end(), customLess);
|
||||
std::sort(twitchEmotes.begin(), twitchEmotes.end(),
|
||||
[](const auto &a, const auto &b) { return a.first < b.first; });
|
||||
}
|
||||
|
||||
auto currentTwitchEmote = twitchEmotes.begin();
|
||||
|
@ -282,10 +288,16 @@ void TwitchMessageBuilder::parseUsername()
|
|||
// username
|
||||
this->userName = this->ircMessage->nick();
|
||||
|
||||
if (this->userName.isEmpty()) {
|
||||
if (this->userName.isEmpty() || this->args.trimSubscriberUsername) {
|
||||
this->userName = this->tags.value(QLatin1String("login")).toString();
|
||||
}
|
||||
|
||||
// display name
|
||||
// auto displayNameVariant = this->tags.value("display-name");
|
||||
// if (displayNameVariant.isValid()) {
|
||||
// this->userName = displayNameVariant.toString() + " (" + this->userName + ")";
|
||||
// }
|
||||
|
||||
this->message->loginName = this->userName;
|
||||
}
|
||||
|
||||
|
@ -350,14 +362,14 @@ void TwitchMessageBuilder::appendUsername()
|
|||
} else if (this->args.isReceivedWhisper) {
|
||||
// Sender username
|
||||
this->emplace<TextElement>(usernameText, MessageElement::Text, this->usernameColor,
|
||||
FontStyle::MediumBold)
|
||||
FontStyle::ChatMediumBold)
|
||||
->setLink({Link::UserInfo, this->userName});
|
||||
|
||||
auto currentUser = app->accounts->Twitch.getCurrent();
|
||||
auto currentUser = app->accounts->twitch.getCurrent();
|
||||
|
||||
// Separator
|
||||
this->emplace<TextElement>("->", MessageElement::Text,
|
||||
app->themes->messages.textColors.system, FontStyle::Medium);
|
||||
app->themes->messages.textColors.system, FontStyle::ChatMedium);
|
||||
|
||||
QColor selfColor = currentUser->color;
|
||||
if (!selfColor.isValid()) {
|
||||
|
@ -366,14 +378,14 @@ void TwitchMessageBuilder::appendUsername()
|
|||
|
||||
// Your own username
|
||||
this->emplace<TextElement>(currentUser->getUserName() + ":", MessageElement::Text,
|
||||
selfColor, FontStyle::MediumBold);
|
||||
selfColor, FontStyle::ChatMediumBold);
|
||||
} else {
|
||||
if (!this->action) {
|
||||
usernameText += ":";
|
||||
}
|
||||
|
||||
this->emplace<TextElement>(usernameText, MessageElement::Text, this->usernameColor,
|
||||
FontStyle::MediumBold)
|
||||
FontStyle::ChatMediumBold)
|
||||
->setLink({Link::UserInfo, this->userName});
|
||||
}
|
||||
}
|
||||
|
@ -385,7 +397,7 @@ void TwitchMessageBuilder::parseHighlights()
|
|||
|
||||
auto app = getApp();
|
||||
|
||||
auto currentUser = app->accounts->Twitch.getCurrent();
|
||||
auto currentUser = app->accounts->twitch.getCurrent();
|
||||
|
||||
QString currentUsername = currentUser->getUserName();
|
||||
|
||||
|
@ -571,11 +583,13 @@ void TwitchMessageBuilder::appendTwitchBadges()
|
|||
|
||||
QString cheerAmountQS = badge.mid(5);
|
||||
std::string versionKey = cheerAmountQS.toStdString();
|
||||
QString tooltip = QString("Twitch cheer ") + cheerAmountQS;
|
||||
|
||||
// Try to fetch channel-specific bit badge
|
||||
try {
|
||||
const auto &badge = channelResources.badgeSets.at("bits").versions.at(versionKey);
|
||||
this->emplace<ImageElement>(badge.badgeImage1x, MessageElement::BadgeVanity);
|
||||
this->emplace<ImageElement>(badge.badgeImage1x, MessageElement::BadgeVanity)
|
||||
->setTooltip(tooltip);
|
||||
continue;
|
||||
} catch (const std::out_of_range &) {
|
||||
// Channel does not contain a special bit badge for this version
|
||||
|
@ -584,7 +598,8 @@ void TwitchMessageBuilder::appendTwitchBadges()
|
|||
// Use default bit badge
|
||||
try {
|
||||
const auto &badge = app->resources->badgeSets.at("bits").versions.at(versionKey);
|
||||
this->emplace<ImageElement>(badge.badgeImage1x, MessageElement::BadgeVanity);
|
||||
this->emplace<ImageElement>(badge.badgeImage1x, MessageElement::BadgeVanity)
|
||||
->setTooltip(tooltip);
|
||||
} catch (const std::out_of_range &) {
|
||||
debug::Log("No default bit badge for version {} found", versionKey);
|
||||
continue;
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
#include "twitchserver.hpp"
|
||||
|
||||
#include "application.hpp"
|
||||
#include "controllers/accounts/accountcontroller.hpp"
|
||||
#include "controllers/highlights/highlightcontroller.hpp"
|
||||
#include "providers/twitch/ircmessagehandler.hpp"
|
||||
#include "providers/twitch/twitchaccount.hpp"
|
||||
#include "providers/twitch/twitchhelpers.hpp"
|
||||
#include "providers/twitch/twitchmessagebuilder.hpp"
|
||||
#include "singletons/accountmanager.hpp"
|
||||
#include "util/posttothread.hpp"
|
||||
|
||||
#include <IrcCommand>
|
||||
#include <cassert>
|
||||
|
||||
using namespace Communi;
|
||||
// using namespace Communi;
|
||||
using namespace chatterino::singletons;
|
||||
|
||||
namespace chatterino {
|
||||
|
@ -27,13 +29,14 @@ TwitchServer::TwitchServer()
|
|||
|
||||
void TwitchServer::initialize()
|
||||
{
|
||||
getApp()->accounts->Twitch.currentUserChanged.connect(
|
||||
getApp()->accounts->twitch.currentUserChanged.connect(
|
||||
[this]() { util::postToThread([this] { this->connect(); }); });
|
||||
}
|
||||
|
||||
void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, bool isWrite)
|
||||
void TwitchServer::initializeConnection(providers::irc::IrcConnection *connection, bool isRead,
|
||||
bool isWrite)
|
||||
{
|
||||
std::shared_ptr<TwitchAccount> account = getApp()->accounts->Twitch.getCurrent();
|
||||
std::shared_ptr<TwitchAccount> account = getApp()->accounts->twitch.getCurrent();
|
||||
|
||||
qDebug() << "logging in as" << account->getUserName();
|
||||
|
||||
|
@ -56,9 +59,9 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead,
|
|||
// this->refreshIgnoredUsers(username, oauthClient, oauthToken);
|
||||
}
|
||||
|
||||
connection->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/membership"));
|
||||
connection->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/commands"));
|
||||
connection->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/tags"));
|
||||
connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/membership"));
|
||||
connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/commands"));
|
||||
connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/tags"));
|
||||
|
||||
connection->setHost("irc.chat.twitch.tv");
|
||||
connection->setPort(6667);
|
||||
|
@ -74,69 +77,53 @@ std::shared_ptr<Channel> TwitchServer::createChannel(const QString &channelName)
|
|||
return std::shared_ptr<Channel>(channel);
|
||||
}
|
||||
|
||||
void TwitchServer::privateMessageReceived(IrcPrivateMessage *message)
|
||||
void TwitchServer::privateMessageReceived(Communi::IrcPrivateMessage *message)
|
||||
{
|
||||
QString channelName;
|
||||
if (!TrimChannelName(message->target(), channelName)) {
|
||||
return;
|
||||
IrcMessageHandler::getInstance().handlePrivMessage(message, *this);
|
||||
}
|
||||
|
||||
this->onPrivateMessage.invoke(message);
|
||||
auto chan = this->getChannelOrEmpty(channelName);
|
||||
|
||||
if (chan->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
messages::MessageParseArgs args;
|
||||
|
||||
TwitchMessageBuilder builder(chan.get(), message, args);
|
||||
|
||||
if (!builder.isIgnored()) {
|
||||
messages::MessagePtr _message = builder.build();
|
||||
if (_message->flags & messages::Message::Highlighted) {
|
||||
this->mentionsChannel->addMessage(_message);
|
||||
}
|
||||
|
||||
chan->addMessage(_message);
|
||||
}
|
||||
}
|
||||
|
||||
void TwitchServer::messageReceived(IrcMessage *message)
|
||||
void TwitchServer::messageReceived(Communi::IrcMessage *message)
|
||||
{
|
||||
// this->readConnection
|
||||
if (message->type() == IrcMessage::Type::Private) {
|
||||
if (message->type() == Communi::IrcMessage::Type::Private) {
|
||||
// We already have a handler for private messages
|
||||
return;
|
||||
}
|
||||
|
||||
const QString &command = message->command();
|
||||
|
||||
auto &handler = IrcMessageHandler::getInstance();
|
||||
|
||||
if (command == "ROOMSTATE") {
|
||||
IrcMessageHandler::getInstance().handleRoomStateMessage(message);
|
||||
handler.handleRoomStateMessage(message);
|
||||
} else if (command == "CLEARCHAT") {
|
||||
IrcMessageHandler::getInstance().handleClearChatMessage(message);
|
||||
handler.handleClearChatMessage(message);
|
||||
} else if (command == "USERSTATE") {
|
||||
IrcMessageHandler::getInstance().handleUserStateMessage(message);
|
||||
handler.handleUserStateMessage(message);
|
||||
} else if (command == "WHISPER") {
|
||||
IrcMessageHandler::getInstance().handleWhisperMessage(message);
|
||||
handler.handleWhisperMessage(message);
|
||||
} else if (command == "USERNOTICE") {
|
||||
IrcMessageHandler::getInstance().handleUserNoticeMessage(message);
|
||||
handler.handleUserNoticeMessage(message, *this);
|
||||
} else if (command == "MODE") {
|
||||
IrcMessageHandler::getInstance().handleModeMessage(message);
|
||||
handler.handleModeMessage(message);
|
||||
} else if (command == "NOTICE") {
|
||||
IrcMessageHandler::getInstance().handleNoticeMessage(
|
||||
static_cast<IrcNoticeMessage *>(message));
|
||||
handler.handleNoticeMessage(static_cast<Communi::IrcNoticeMessage *>(message));
|
||||
} else if (command == "JOIN") {
|
||||
handler.handleJoinMessage(message);
|
||||
} else if (command == "PART") {
|
||||
handler.handlePartMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
void TwitchServer::writeConnectionMessageReceived(IrcMessage *message)
|
||||
void TwitchServer::writeConnectionMessageReceived(Communi::IrcMessage *message)
|
||||
{
|
||||
switch (message->type()) {
|
||||
case IrcMessage::Type::Notice: {
|
||||
case Communi::IrcMessage::Type::Notice: {
|
||||
IrcMessageHandler::getInstance().handleWriteConnectionNoticeMessage(
|
||||
static_cast<IrcNoticeMessage *>(message));
|
||||
static_cast<Communi::IrcNoticeMessage *>(message));
|
||||
} break;
|
||||
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "providers/irc/abstractircserver.hpp"
|
||||
#include "providers/twitch/twitchaccount.hpp"
|
||||
#include "providers/twitch/twitchchannel.hpp"
|
||||
#include "util/mutexvalue.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
@ -22,12 +23,17 @@ public:
|
|||
|
||||
std::shared_ptr<Channel> getChannelOrEmptyByID(const QString &channelID);
|
||||
|
||||
util::MutexValue<QString> lastUserThatWhisperedMe;
|
||||
|
||||
// QString getLastWhisperedPerson() const;
|
||||
// void setLastWhisperedPerson(const QString &person);
|
||||
|
||||
const ChannelPtr whispersChannel;
|
||||
const ChannelPtr mentionsChannel;
|
||||
IndirectChannel watchingChannel;
|
||||
|
||||
protected:
|
||||
void initializeConnection(Communi::IrcConnection *connection, bool isRead,
|
||||
void initializeConnection(providers::irc::IrcConnection *connection, bool isRead,
|
||||
bool isWrite) override;
|
||||
std::shared_ptr<Channel> createChannel(const QString &channelName) override;
|
||||
|
||||
|
@ -38,6 +44,10 @@ protected:
|
|||
std::shared_ptr<Channel> getCustomChannel(const QString &channelname) override;
|
||||
|
||||
QString cleanChannelName(const QString &dirtyChannelName) override;
|
||||
|
||||
private:
|
||||
// mutable std::mutex lastWhisperedPersonMutex;
|
||||
// QString lastWhisperedPerson;
|
||||
};
|
||||
|
||||
} // namespace twitch
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
#include "singletons/accountmanager.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace singletons {
|
||||
|
||||
void AccountManager::load()
|
||||
{
|
||||
this->Twitch.load();
|
||||
}
|
||||
|
||||
} // namespace singletons
|
||||
} // namespace chatterino
|
|
@ -1,21 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "providers/twitch/twitchaccountmanager.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace singletons {
|
||||
|
||||
class AccountManager
|
||||
{
|
||||
public:
|
||||
AccountManager() = default;
|
||||
|
||||
~AccountManager() = delete;
|
||||
|
||||
void load();
|
||||
|
||||
providers::twitch::TwitchAccountManager Twitch;
|
||||
};
|
||||
|
||||
} // namespace singletons
|
||||
} // namespace chatterino
|
|
@ -86,8 +86,8 @@ EmoteManager::EmoteManager()
|
|||
|
||||
void EmoteManager::initialize()
|
||||
{
|
||||
getApp()->accounts->Twitch.currentUserChanged.connect([this] {
|
||||
auto currentUser = getApp()->accounts->Twitch.getCurrent();
|
||||
getApp()->accounts->twitch.currentUserChanged.connect([this] {
|
||||
auto currentUser = getApp()->accounts->twitch.getCurrent();
|
||||
assert(currentUser);
|
||||
this->refreshTwitchEmotes(currentUser);
|
||||
});
|
||||
|
@ -130,13 +130,23 @@ void EmoteManager::reloadBTTVChannelEmotes(const QString &channelName,
|
|||
QString code = emoteObject.value("code").toString();
|
||||
// emoteObject.value("imageType").toString();
|
||||
|
||||
auto emote = this->getBTTVChannelEmoteFromCaches().getOrAdd(id, [&] {
|
||||
util::EmoteData emoteData;
|
||||
QString link = linkTemplate;
|
||||
link.detach();
|
||||
emoteData.image1x = new Image(link.replace("{{id}}", id).replace("{{image}}", "1x"),
|
||||
1, code, code + "<br />Channel BTTV Emote");
|
||||
link = linkTemplate;
|
||||
link.detach();
|
||||
emoteData.image2x = new Image(link.replace("{{id}}", id).replace("{{image}}", "2x"),
|
||||
0.5, code, code + "<br />Channel BTTV Emote");
|
||||
link = linkTemplate;
|
||||
link.detach();
|
||||
emoteData.image3x = new Image(link.replace("{{id}}", id).replace("{{image}}", "3x"),
|
||||
0.25, code, code + "<br />Channel BTTV Emote");
|
||||
emoteData.pageLink = "https://manage.betterttv.net/emotes/" + id;
|
||||
|
||||
link = link.replace("{{id}}", id).replace("{{image}}", "1x");
|
||||
|
||||
auto emote = this->getBTTVChannelEmoteFromCaches().getOrAdd(id, [&code, &link] {
|
||||
return util::EmoteData(new Image(link, 1, code, code + "<br/>Channel BTTV Emote"));
|
||||
return emoteData;
|
||||
});
|
||||
|
||||
this->bttvChannelEmotes.insert(code, emote);
|
||||
|
@ -182,9 +192,11 @@ void EmoteManager::reloadFFZChannelEmotes(const QString &channelName,
|
|||
|
||||
QJsonObject urls = emoteObject.value("urls").toObject();
|
||||
|
||||
auto emote = this->getFFZChannelEmoteFromCaches().getOrAdd(id, [&code, &urls] {
|
||||
auto emote = this->getFFZChannelEmoteFromCaches().getOrAdd(id, [id, &code, &urls] {
|
||||
util::EmoteData emoteData;
|
||||
FillInFFZEmoteData(urls, code, code + "<br/>Channel FFZ Emote", emoteData);
|
||||
emoteData.pageLink =
|
||||
QString("https://www.frankerfacez.com/emoticon/%1-%2").arg(id).arg(code);
|
||||
|
||||
return emoteData;
|
||||
});
|
||||
|
@ -436,6 +448,7 @@ void EmoteManager::refreshTwitchEmotes(const std::shared_ptr<TwitchAccount> &use
|
|||
[=, &emoteData](const QJsonObject &root) {
|
||||
emoteData.emoteSets.clear();
|
||||
emoteData.emoteCodes.clear();
|
||||
|
||||
auto emoticonSets = root.value("emoticon_sets").toObject();
|
||||
for (QJsonObject::iterator it = emoticonSets.begin(); it != emoticonSets.end(); ++it) {
|
||||
std::string emoteSetString = it.key().toStdString();
|
||||
|
@ -443,7 +456,7 @@ void EmoteManager::refreshTwitchEmotes(const std::shared_ptr<TwitchAccount> &use
|
|||
|
||||
for (QJsonValue emoteValue : emoteSetList) {
|
||||
QJsonObject emoticon = emoteValue.toObject();
|
||||
std::string id = emoticon["id"].toString().toStdString();
|
||||
std::string id = QString::number(emoticon["id"].toInt()).toStdString();
|
||||
std::string code = emoticon["code"].toString().toStdString();
|
||||
emoteData.emoteSets[emoteSetString].push_back({id, code});
|
||||
emoteData.emoteCodes.push_back(code);
|
||||
|
@ -483,6 +496,7 @@ void EmoteManager::loadBTTVEmotes()
|
|||
code + "<br />Global BTTV Emote");
|
||||
emoteData.image3x = new Image(GetBTTVEmoteLink(urlTemplate, id, "3x"), 0.25, code,
|
||||
code + "<br />Global BTTV Emote");
|
||||
emoteData.pageLink = "https://manage.betterttv.net/emotes/" + id;
|
||||
|
||||
this->bttvGlobalEmotes.insert(code, emoteData);
|
||||
codes.push_back(code.toStdString());
|
||||
|
@ -510,10 +524,13 @@ void EmoteManager::loadFFZEmotes()
|
|||
QJsonObject object = emote.toObject();
|
||||
|
||||
QString code = object.value("name").toString();
|
||||
int id = object.value("id").toInt();
|
||||
QJsonObject urls = object.value("urls").toObject();
|
||||
|
||||
util::EmoteData emoteData;
|
||||
FillInFFZEmoteData(urls, code, code + "<br/>Global FFZ Emote", emoteData);
|
||||
emoteData.pageLink =
|
||||
QString("https://www.frankerfacez.com/emoticon/%1-%2").arg(id).arg(code);
|
||||
|
||||
this->ffzGlobalEmotes.insert(code, emoteData);
|
||||
codes.push_back(code.toStdString());
|
||||
|
@ -530,6 +547,20 @@ util::EmoteData EmoteManager::getTwitchEmoteById(long id, const QString &emoteNa
|
|||
{
|
||||
QString _emoteName = emoteName;
|
||||
_emoteName.replace("<", "<");
|
||||
_emoteName.replace(">", ">");
|
||||
|
||||
static QMap<QString, QString> emoteNameReplacements{
|
||||
{"[oO](_|\\.)[oO]", "O_o"}, {"\\>\\;\\(", ">("}, {"\\<\\;3", "<3"},
|
||||
{"\\:-?(o|O)", ":O"}, {"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"},
|
||||
{"\\:-?[z|Z|\\|]", ":Z"}, {"\\:-?\\(", ":("}, {"\\:-?\\)", ":)"},
|
||||
{"\\:-?D", ":D"}, {"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"},
|
||||
{"R-?\\)", "R)"}, {"B-?\\)", "B)"},
|
||||
};
|
||||
|
||||
auto it = emoteNameReplacements.find(_emoteName);
|
||||
if (it != emoteNameReplacements.end()) {
|
||||
_emoteName = it.value();
|
||||
}
|
||||
|
||||
return _twitchEmoteFromCache.getOrAdd(id, [&emoteName, &_emoteName, &id] {
|
||||
util::EmoteData newEmoteData;
|
||||
|
|
|
@ -46,16 +46,6 @@ public:
|
|||
|
||||
util::EmoteData getTwitchEmoteById(long int id, const QString &emoteName);
|
||||
|
||||
int getGeneration()
|
||||
{
|
||||
return _generation;
|
||||
}
|
||||
|
||||
void incGeneration()
|
||||
{
|
||||
_generation++;
|
||||
}
|
||||
|
||||
pajlada::Signals::NoArgSignal &getGifUpdateSignal();
|
||||
|
||||
// Bit badge/emotes?
|
||||
|
@ -145,8 +135,6 @@ private:
|
|||
pajlada::Signals::NoArgSignal gifUpdateTimerSignal;
|
||||
QTimer gifUpdateTimer;
|
||||
bool gifUpdateTimerInitiated = false;
|
||||
|
||||
int _generation = 0;
|
||||
};
|
||||
|
||||
} // namespace singletons
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
#include <QDebug>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "application.hpp"
|
||||
#include "util/assertinguithread.hpp"
|
||||
#include "windowmanager.hpp"
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#define DEFAULT_FONT_FAMILY "Segoe UI"
|
||||
#define DEFAULT_FONT_SIZE 10
|
||||
|
@ -20,86 +24,111 @@ namespace chatterino {
|
|||
namespace singletons {
|
||||
|
||||
FontManager::FontManager()
|
||||
: currentFontFamily("/appearance/currentFontFamily", DEFAULT_FONT_FAMILY)
|
||||
, currentFontSize("/appearance/currentFontSize", DEFAULT_FONT_SIZE)
|
||||
// , currentFont(this->currentFontFamily.getValue().c_str(), currentFontSize.getValue())
|
||||
: chatFontFamily("/appearance/currentFontFamily", DEFAULT_FONT_FAMILY)
|
||||
, chatFontSize("/appearance/currentFontSize", DEFAULT_FONT_SIZE)
|
||||
{
|
||||
qDebug() << "init FontManager";
|
||||
|
||||
this->currentFontFamily.connect([this](const std::string &newValue, auto) {
|
||||
this->incGeneration();
|
||||
// this->currentFont.setFamily(newValue.c_str());
|
||||
this->currentFontByScale.clear();
|
||||
this->chatFontFamily.connect([this](const std::string &, auto) {
|
||||
util::assertInGuiThread();
|
||||
|
||||
if (getApp()->windows) {
|
||||
getApp()->windows->incGeneration();
|
||||
}
|
||||
|
||||
for (auto &map : this->fontsByType) {
|
||||
map.clear();
|
||||
}
|
||||
this->fontChanged.invoke();
|
||||
});
|
||||
|
||||
this->currentFontSize.connect([this](const int &newValue, auto) {
|
||||
this->incGeneration();
|
||||
// this->currentFont.setSize(newValue);
|
||||
this->currentFontByScale.clear();
|
||||
this->chatFontSize.connect([this](const int &, auto) {
|
||||
util::assertInGuiThread();
|
||||
|
||||
if (getApp()->windows) {
|
||||
getApp()->windows->incGeneration();
|
||||
}
|
||||
|
||||
for (auto &map : this->fontsByType) {
|
||||
map.clear();
|
||||
}
|
||||
this->fontChanged.invoke();
|
||||
});
|
||||
|
||||
this->fontsByType.resize(size_t(EndType));
|
||||
}
|
||||
|
||||
QFont &FontManager::getFont(FontManager::Type type, float scale)
|
||||
QFont FontManager::getFont(FontManager::Type type, float scale)
|
||||
{
|
||||
// return this->currentFont.getFont(type);
|
||||
return this->getCurrentFont(scale).getFont(type);
|
||||
return this->getOrCreateFontData(type, scale).font;
|
||||
}
|
||||
|
||||
QFontMetrics &FontManager::getFontMetrics(FontManager::Type type, float scale)
|
||||
QFontMetrics FontManager::getFontMetrics(FontManager::Type type, float scale)
|
||||
{
|
||||
// return this->currentFont.getFontMetrics(type);
|
||||
return this->getCurrentFont(scale).getFontMetrics(type);
|
||||
return this->getOrCreateFontData(type, scale).metrics;
|
||||
}
|
||||
|
||||
FontManager::FontData &FontManager::Font::getFontData(FontManager::Type type)
|
||||
FontManager::FontData &FontManager::getOrCreateFontData(Type type, float scale)
|
||||
{
|
||||
switch (type) {
|
||||
case Tiny:
|
||||
return this->tiny;
|
||||
case Small:
|
||||
return this->small;
|
||||
case MediumSmall:
|
||||
return this->mediumSmall;
|
||||
case Medium:
|
||||
return this->medium;
|
||||
case MediumBold:
|
||||
return this->mediumBold;
|
||||
case MediumItalic:
|
||||
return this->mediumItalic;
|
||||
case Large:
|
||||
return this->large;
|
||||
case VeryLarge:
|
||||
return this->veryLarge;
|
||||
default:
|
||||
qDebug() << "Unknown font type:" << type << ", defaulting to medium";
|
||||
return this->medium;
|
||||
}
|
||||
}
|
||||
util::assertInGuiThread();
|
||||
|
||||
QFont &FontManager::Font::getFont(Type type)
|
||||
{
|
||||
return this->getFontData(type).font;
|
||||
}
|
||||
assert(type >= 0 && type < EndType);
|
||||
|
||||
QFontMetrics &FontManager::Font::getFontMetrics(Type type)
|
||||
{
|
||||
return this->getFontData(type).metrics;
|
||||
}
|
||||
auto &map = this->fontsByType[size_t(type)];
|
||||
|
||||
// find element
|
||||
auto it = map.find(scale);
|
||||
if (it != map.end()) {
|
||||
// return if found
|
||||
|
||||
FontManager::Font &FontManager::getCurrentFont(float scale)
|
||||
{
|
||||
for (auto it = this->currentFontByScale.begin(); it != this->currentFontByScale.end(); it++) {
|
||||
if (it->first == scale) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
this->currentFontByScale.push_back(
|
||||
std::make_pair(scale, Font(this->currentFontFamily.getValue().c_str(),
|
||||
this->currentFontSize.getValue() * scale)));
|
||||
|
||||
return this->currentFontByScale.back().second;
|
||||
// emplace new element
|
||||
auto result = map.emplace(scale, this->createFontData(type, scale));
|
||||
assert(result.second);
|
||||
|
||||
return result.first->second;
|
||||
}
|
||||
|
||||
FontManager::FontData FontManager::createFontData(Type type, float scale)
|
||||
{
|
||||
// check if it's a chat (scale the setting)
|
||||
if (type >= ChatStart && type <= ChatEnd) {
|
||||
static std::unordered_map<Type, ChatFontData> sizeScale{
|
||||
{ChatSmall, {0.6f, false, QFont::Normal}},
|
||||
{ChatMediumSmall, {0.8f, false, QFont::Normal}},
|
||||
{ChatMedium, {1, false, QFont::Normal}},
|
||||
{ChatMediumBold, {1, false, QFont::Medium}},
|
||||
{ChatMediumItalic, {1, true, QFont::Normal}},
|
||||
{ChatLarge, {1.2f, false, QFont::Normal}},
|
||||
{ChatVeryLarge, {1.4f, false, QFont::Normal}},
|
||||
};
|
||||
|
||||
auto data = sizeScale[type];
|
||||
return FontData(QFont(QString::fromStdString(this->chatFontFamily.getValue()),
|
||||
int(this->chatFontSize.getValue() * data.scale * scale), data.weight,
|
||||
data.italic));
|
||||
}
|
||||
|
||||
// normal Ui font (use pt size)
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
constexpr float multiplier = 0.8f;
|
||||
#else
|
||||
constexpr float multiplier = 1.f;
|
||||
#endif
|
||||
|
||||
static std::unordered_map<Type, UiFontData> defaultSize{
|
||||
{Tiny, {8, "Monospace", false, QFont::Normal}},
|
||||
{UiMedium, {int(12 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}},
|
||||
{UiTabs, {int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}},
|
||||
};
|
||||
|
||||
UiFontData &data = defaultSize[type];
|
||||
QFont font(data.name, int(data.size * scale), data.weight, data.italic);
|
||||
return FontData(font);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace singletons
|
||||
|
|
|
@ -3,138 +3,79 @@
|
|||
#include <QFont>
|
||||
#include <QFontDatabase>
|
||||
#include <QFontMetrics>
|
||||
#include <array>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <pajlada/settings/setting.hpp>
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace chatterino {
|
||||
namespace singletons {
|
||||
|
||||
class FontManager
|
||||
class FontManager : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
FontManager();
|
||||
|
||||
FontManager(const FontManager &) = delete;
|
||||
FontManager(FontManager &&) = delete;
|
||||
~FontManager() = delete;
|
||||
|
||||
// font data gets set in createFontData(...)
|
||||
enum Type : uint8_t {
|
||||
Tiny,
|
||||
Small,
|
||||
MediumSmall,
|
||||
Medium,
|
||||
MediumBold,
|
||||
MediumItalic,
|
||||
Large,
|
||||
VeryLarge,
|
||||
ChatSmall,
|
||||
ChatMediumSmall,
|
||||
ChatMedium,
|
||||
ChatMediumBold,
|
||||
ChatMediumItalic,
|
||||
ChatLarge,
|
||||
ChatVeryLarge,
|
||||
|
||||
UiMedium,
|
||||
UiTabs,
|
||||
|
||||
// don't remove this value
|
||||
EndType,
|
||||
|
||||
// make sure to update these values accordingly!
|
||||
ChatStart = ChatSmall,
|
||||
ChatEnd = ChatVeryLarge,
|
||||
};
|
||||
|
||||
QFont &getFont(Type type, float scale);
|
||||
QFontMetrics &getFontMetrics(Type type, float scale);
|
||||
QFont getFont(Type type, float scale);
|
||||
QFontMetrics getFontMetrics(Type type, float scale);
|
||||
|
||||
int getGeneration() const
|
||||
{
|
||||
return this->generation;
|
||||
}
|
||||
|
||||
void incGeneration()
|
||||
{
|
||||
this->generation++;
|
||||
}
|
||||
|
||||
pajlada::Settings::Setting<std::string> currentFontFamily;
|
||||
pajlada::Settings::Setting<int> currentFontSize;
|
||||
pajlada::Settings::Setting<std::string> chatFontFamily;
|
||||
pajlada::Settings::Setting<int> chatFontSize;
|
||||
|
||||
pajlada::Signals::NoArgSignal fontChanged;
|
||||
|
||||
private:
|
||||
struct FontData {
|
||||
FontData(QFont &&_font)
|
||||
FontData(const QFont &_font)
|
||||
: font(_font)
|
||||
, metrics(this->font)
|
||||
, metrics(_font)
|
||||
{
|
||||
}
|
||||
|
||||
QFont font;
|
||||
QFontMetrics metrics;
|
||||
const QFont font;
|
||||
const QFontMetrics metrics;
|
||||
};
|
||||
|
||||
struct Font {
|
||||
Font() = delete;
|
||||
|
||||
Font(const char *fontFamilyName, int mediumSize)
|
||||
: tiny(QFont("Monospace", 8))
|
||||
, small(QFont(fontFamilyName, mediumSize - 4))
|
||||
, mediumSmall(QFont(fontFamilyName, mediumSize - 2))
|
||||
, medium(QFont(fontFamilyName, mediumSize))
|
||||
, mediumBold(QFont(fontFamilyName, mediumSize, QFont::DemiBold))
|
||||
, mediumItalic(QFont(fontFamilyName, mediumSize, -1, true))
|
||||
, large(QFont(fontFamilyName, mediumSize))
|
||||
, veryLarge(QFont(fontFamilyName, mediumSize))
|
||||
{
|
||||
tiny.font.setStyleHint(QFont::TypeWriter);
|
||||
}
|
||||
|
||||
void setFamily(const char *newFamily)
|
||||
{
|
||||
this->small.font.setFamily(newFamily);
|
||||
this->mediumSmall.font.setFamily(newFamily);
|
||||
this->medium.font.setFamily(newFamily);
|
||||
this->mediumBold.font.setFamily(newFamily);
|
||||
this->mediumItalic.font.setFamily(newFamily);
|
||||
this->large.font.setFamily(newFamily);
|
||||
this->veryLarge.font.setFamily(newFamily);
|
||||
|
||||
this->updateMetrics();
|
||||
}
|
||||
|
||||
void setSize(int newMediumSize)
|
||||
{
|
||||
this->small.font.setPointSize(newMediumSize - 4);
|
||||
this->mediumSmall.font.setPointSize(newMediumSize - 2);
|
||||
this->medium.font.setPointSize(newMediumSize);
|
||||
this->mediumBold.font.setPointSize(newMediumSize);
|
||||
this->mediumItalic.font.setPointSize(newMediumSize);
|
||||
this->large.font.setPointSize(newMediumSize + 2);
|
||||
this->veryLarge.font.setPointSize(newMediumSize + 4);
|
||||
|
||||
this->updateMetrics();
|
||||
}
|
||||
|
||||
void updateMetrics()
|
||||
{
|
||||
this->small.metrics = QFontMetrics(this->small.font);
|
||||
this->mediumSmall.metrics = QFontMetrics(this->mediumSmall.font);
|
||||
this->medium.metrics = QFontMetrics(this->medium.font);
|
||||
this->mediumBold.metrics = QFontMetrics(this->mediumBold.font);
|
||||
this->mediumItalic.metrics = QFontMetrics(this->mediumItalic.font);
|
||||
this->large.metrics = QFontMetrics(this->large.font);
|
||||
this->veryLarge.metrics = QFontMetrics(this->veryLarge.font);
|
||||
}
|
||||
|
||||
FontData &getFontData(Type type);
|
||||
|
||||
QFont &getFont(Type type);
|
||||
QFontMetrics &getFontMetrics(Type type);
|
||||
|
||||
FontData tiny;
|
||||
FontData small;
|
||||
FontData mediumSmall;
|
||||
FontData medium;
|
||||
FontData mediumBold;
|
||||
FontData mediumItalic;
|
||||
FontData large;
|
||||
FontData veryLarge;
|
||||
struct ChatFontData {
|
||||
float scale;
|
||||
bool italic;
|
||||
QFont::Weight weight;
|
||||
};
|
||||
|
||||
Font &getCurrentFont(float scale);
|
||||
struct UiFontData {
|
||||
float size;
|
||||
const char *name;
|
||||
bool italic;
|
||||
QFont::Weight weight;
|
||||
};
|
||||
|
||||
// Future plans:
|
||||
// Could have multiple fonts in here, such as "Menu font", "Application font", "Chat font"
|
||||
FontData &getOrCreateFontData(Type type, float scale);
|
||||
FontData createFontData(Type type, float scale);
|
||||
|
||||
std::list<std::pair<float, Font>> currentFontByScale;
|
||||
|
||||
int generation = 0;
|
||||
std::vector<std::unordered_map<float, FontData>> fontsByType;
|
||||
};
|
||||
|
||||
} // namespace singletons
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace ipc = boost::interprocess;
|
|||
namespace chatterino {
|
||||
namespace singletons {
|
||||
|
||||
// fourtf: don't add this class to the application class
|
||||
NativeMessagingManager::NativeMessagingManager()
|
||||
{
|
||||
qDebug() << "init NativeMessagingManager";
|
||||
|
@ -50,6 +51,10 @@ void NativeMessagingManager::registerHost()
|
|||
{
|
||||
auto app = getApp();
|
||||
|
||||
if (app->paths->isPortable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create manifest
|
||||
QJsonDocument document;
|
||||
QJsonObject root_obj;
|
||||
|
@ -113,13 +118,14 @@ void NativeMessagingManager::ReceiverThread::run()
|
|||
|
||||
while (true) {
|
||||
try {
|
||||
char *buf = (char *)malloc(MESSAGE_SIZE);
|
||||
std::unique_ptr<char> buf(static_cast<char *>(malloc(MESSAGE_SIZE)));
|
||||
ipc::message_queue::size_type retSize;
|
||||
unsigned int priority;
|
||||
|
||||
messageQueue.receive(buf, MESSAGE_SIZE, retSize, priority);
|
||||
messageQueue.receive(buf.get(), MESSAGE_SIZE, retSize, priority);
|
||||
|
||||
QJsonDocument document = QJsonDocument::fromJson(QByteArray(buf, retSize));
|
||||
QJsonDocument document =
|
||||
QJsonDocument::fromJson(QByteArray::fromRawData(buf.get(), retSize));
|
||||
|
||||
this->handleMessage(document.object());
|
||||
} catch (ipc::interprocess_exception &ex) {
|
||||
|
@ -143,26 +149,37 @@ void NativeMessagingManager::ReceiverThread::handleMessage(const QJsonObject &ro
|
|||
QString _type = root.value("type").toString();
|
||||
bool attach = root.value("attach").toBool();
|
||||
QString name = root.value("name").toString();
|
||||
QString winId = root.value("winId").toString();
|
||||
int yOffset = root.value("yOffset").toInt(-1);
|
||||
|
||||
if (_type.isNull() || name.isNull() || winId.isNull()) {
|
||||
#ifdef USEWINSDK
|
||||
widgets::AttachedWindow::GetArgs args;
|
||||
args.winId = root.value("winId").toString();
|
||||
args.yOffset = root.value("yOffset").toInt(-1);
|
||||
args.width = root.value("size").toObject().value("width").toInt(-1);
|
||||
args.height = root.value("size").toObject().value("height").toInt(-1);
|
||||
|
||||
if (_type.isNull() || args.winId.isNull()) {
|
||||
qDebug() << "NM type, name or winId missing";
|
||||
attach = false;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_type == "twitch") {
|
||||
util::postToThread([name, attach, winId, yOffset, app] {
|
||||
util::postToThread([=] {
|
||||
if (!name.isEmpty()) {
|
||||
app->twitch.server->watchingChannel.update(
|
||||
app->twitch.server->getOrAddChannel(name));
|
||||
}
|
||||
|
||||
if (attach) {
|
||||
#ifdef USEWINSDK
|
||||
auto *window =
|
||||
widgets::AttachedWindow::get(::GetForegroundWindow(), winId, yOffset);
|
||||
if (args.height != -1) {
|
||||
auto *window = widgets::AttachedWindow::get(::GetForegroundWindow(), args);
|
||||
if (!name.isEmpty()) {
|
||||
window->setChannel(app->twitch.server->getOrAddChannel(name));
|
||||
window->show();
|
||||
}
|
||||
}
|
||||
// window->show();
|
||||
#endif
|
||||
}
|
||||
});
|
||||
|
@ -179,12 +196,15 @@ void NativeMessagingManager::ReceiverThread::handleMessage(const QJsonObject &ro
|
|||
}
|
||||
|
||||
#ifdef USEWINSDK
|
||||
util::postToThread([winId] { widgets::AttachedWindow::detach(winId); });
|
||||
util::postToThread([winId] {
|
||||
qDebug() << "NW detach";
|
||||
widgets::AttachedWindow::detach(winId);
|
||||
});
|
||||
#endif
|
||||
} else {
|
||||
qDebug() << "NM unknown action " + action;
|
||||
}
|
||||
}
|
||||
} // namespace singletons
|
||||
|
||||
} // namespace singletons
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace singletons {
|
|||
class NativeMessagingManager
|
||||
{
|
||||
public:
|
||||
// fourtf: don't add this class to the application class
|
||||
NativeMessagingManager();
|
||||
|
||||
~NativeMessagingManager() = delete;
|
||||
|
|
|
@ -19,21 +19,21 @@ PathManager::PathManager(int argc, char **argv)
|
|||
.replace("/", "x");
|
||||
|
||||
// Options
|
||||
bool portable = false;
|
||||
this->portable = false;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "portable") == 0) {
|
||||
portable = true;
|
||||
this->portable = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (QFileInfo::exists(QCoreApplication::applicationDirPath() + "/portable")) {
|
||||
portable = true;
|
||||
if (QFileInfo::exists(QCoreApplication::applicationDirPath() + "/this->portable")) {
|
||||
this->portable = true;
|
||||
}
|
||||
|
||||
// Root path = %APPDATA%/Chatterino or the folder that the executable resides in
|
||||
QString rootPath;
|
||||
if (portable) {
|
||||
if (this->portable) {
|
||||
rootPath.append(QCoreApplication::applicationDirPath());
|
||||
} else {
|
||||
// Get settings path
|
||||
|
@ -91,5 +91,10 @@ bool PathManager::createFolder(const QString &folderPath)
|
|||
return QDir().mkpath(folderPath);
|
||||
}
|
||||
|
||||
bool PathManager::isPortable()
|
||||
{
|
||||
return this->portable;
|
||||
}
|
||||
|
||||
} // namespace singletons
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -28,6 +28,10 @@ public:
|
|||
QString appPathHash;
|
||||
|
||||
bool createFolder(const QString &folderPath);
|
||||
bool isPortable();
|
||||
|
||||
private:
|
||||
bool portable;
|
||||
};
|
||||
|
||||
} // namespace singletons
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "util/emotemap.hpp"
|
||||
|
||||
#include <QIcon>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include <map>
|
||||
|
|
|
@ -39,8 +39,18 @@ void SettingManager::initialize()
|
|||
|
||||
this->timestampFormat.connect([](auto, auto) {
|
||||
auto app = getApp();
|
||||
app->windows->layoutVisibleChatWidgets();
|
||||
app->windows->layoutChannelViews();
|
||||
});
|
||||
|
||||
this->emoteScale.connect([](auto, auto) { getApp()->windows->forceLayoutChannelViews(); });
|
||||
|
||||
this->timestampFormat.connect([](auto, auto) { getApp()->windows->forceLayoutChannelViews(); });
|
||||
this->alternateMessageBackground.connect(
|
||||
[](auto, auto) { getApp()->windows->forceLayoutChannelViews(); });
|
||||
this->seperateMessages.connect(
|
||||
[](auto, auto) { getApp()->windows->forceLayoutChannelViews(); });
|
||||
this->collpseMessagesMinLines.connect(
|
||||
[](auto, auto) { getApp()->windows->forceLayoutChannelViews(); });
|
||||
}
|
||||
|
||||
MessageElement::Flags SettingManager::getWordFlags()
|
||||
|
|
|
@ -40,6 +40,8 @@ public:
|
|||
BoolSetting hideEmptyInput = {"/appearance/hideEmptyInputBox", false};
|
||||
BoolSetting showMessageLength = {"/appearance/messages/showMessageLength", false};
|
||||
BoolSetting seperateMessages = {"/appearance/messages/separateMessages", false};
|
||||
// BoolSetting collapseLongMessages = {"/appearance/messages/collapseLongMessages", false};
|
||||
IntSetting collpseMessagesMinLines = {"/appearance/messages/collapseMessagesMinLines", 0};
|
||||
BoolSetting alternateMessageBackground = {"/appearance/messages/alternateMessageBackground",
|
||||
false};
|
||||
BoolSetting windowTopMost = {"/appearance/windowAlwaysOnTop", false};
|
||||
|
@ -54,6 +56,8 @@ public:
|
|||
/// Behaviour
|
||||
BoolSetting allowDuplicateMessages = {"/behaviour/allowDuplicateMessages", true};
|
||||
BoolSetting mentionUsersWithAt = {"/behaviour/mentionUsersWithAt", false};
|
||||
BoolSetting showJoins = {"/behaviour/showJoins", false};
|
||||
BoolSetting showParts = {"/behaviour/showParts", false};
|
||||
FloatSetting mouseScrollMultiplier = {"/behaviour/mouseScrollMultiplier", 1.0};
|
||||
|
||||
// Auto-completion
|
||||
|
@ -75,10 +79,10 @@ public:
|
|||
BoolSetting enableGifAnimations = {"/emotes/enableGifAnimations", true};
|
||||
FloatSetting emoteScale = {"/emotes/scale", 1.f};
|
||||
|
||||
// 0 = Smallest size
|
||||
// 1 = One size above 0 (usually size of 0 * 2)
|
||||
// 2 = One size above 1 (usually size of 1 * 2)
|
||||
// etc...
|
||||
// 0 = No preference
|
||||
// 1 = 1x
|
||||
// 2 = 2x
|
||||
// 3 = 3x
|
||||
IntSetting preferredEmoteQuality = {"/emotes/preferredEmoteQuality", 0};
|
||||
|
||||
/// Links
|
||||
|
|
|
@ -52,7 +52,6 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
|
|||
isLight = multiplier > 0;
|
||||
bool lightWin = isLight;
|
||||
|
||||
QColor none(0, 0, 0, 0);
|
||||
QColor themeColor = QColor::fromHslF(hue, 0.43, 0.5);
|
||||
QColor themeColorNoSat = QColor::fromHslF(hue, 0, 0.5);
|
||||
|
||||
|
@ -69,7 +68,7 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
|
|||
#ifdef Q_OS_LINUX
|
||||
this->window.background = lightWin ? "#fff" : QColor(61, 60, 56);
|
||||
#else
|
||||
this->window.background = lightWin ? "#fff" : "#444";
|
||||
this->window.background = lightWin ? "#fff" : "#111";
|
||||
#endif
|
||||
|
||||
QColor fg = this->window.text = lightWin ? "#000" : "#eee";
|
||||
|
@ -89,27 +88,52 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
|
|||
|
||||
/// TABS
|
||||
if (lightWin) {
|
||||
this->tabs.regular = {fg, {bg, QColor("#ccc"), bg}};
|
||||
this->tabs.regular = {QColor("#444"),
|
||||
{QColor("#fff"), QColor("#fff"), QColor("#fff")},
|
||||
{QColor("#fff"), QColor("#fff"), QColor("#fff")}};
|
||||
this->tabs.newMessage = {
|
||||
fg,
|
||||
{QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern),
|
||||
QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern),
|
||||
QBrush(blendColors(themeColorNoSat, "#ccc", 0.9), Qt::FDiagPattern)}};
|
||||
this->tabs.highlighted = {fg, {QColor("#ccc"), QColor("#ccc"), QColor("#bbb")}};
|
||||
this->tabs.selected = {QColor("#fff"),
|
||||
{QColor("#777"), QColor("#777"), QColor("#888")}};
|
||||
} else {
|
||||
this->tabs.regular = {fg, {bg, QColor("#555"), bg}};
|
||||
this->tabs.newMessage = {
|
||||
fg,
|
||||
{QBrush(blendColors(themeColor, "#666", 0.7), Qt::FDiagPattern),
|
||||
QBrush(blendColors(themeColor, "#666", 0.5), Qt::FDiagPattern),
|
||||
QBrush(blendColors(themeColorNoSat, "#666", 0.7), Qt::FDiagPattern)}};
|
||||
this->tabs.highlighted = {fg, {QColor("#777"), QColor("#777"), QColor("#666")}};
|
||||
fg, {bg, QColor("#ccc"), bg}, {QColor("#aaa"), QColor("#aaa"), QColor("#aaa")}};
|
||||
this->tabs.highlighted = {fg,
|
||||
{bg, QColor("#ccc"), bg},
|
||||
{QColor("#b60505"), QColor("#b60505"), QColor("#b60505")}};
|
||||
this->tabs.selected = {QColor("#000"),
|
||||
{QColor("#999"), QColor("#999"), QColor("#888")}};
|
||||
{QColor("#b4d7ff"), QColor("#b4d7ff"), QColor("#b4d7ff")},
|
||||
{QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}};
|
||||
} else {
|
||||
this->tabs.regular = {QColor("#aaa"),
|
||||
{QColor("#252525"), QColor("#252525"), QColor("#252525")},
|
||||
{QColor("#444"), QColor("#444"), QColor("#444")}};
|
||||
this->tabs.newMessage = {fg,
|
||||
{QColor("#252525"), QColor("#252525"), QColor("#252525")},
|
||||
{QColor("#888"), QColor("#888"), QColor("#888")}};
|
||||
this->tabs.highlighted = {fg,
|
||||
{QColor("#252525"), QColor("#252525"), QColor("#252525")},
|
||||
{QColor("#ee6166"), QColor("#ee6166"), QColor("#ee6166")}};
|
||||
|
||||
this->tabs.selected = {QColor("#fff"),
|
||||
{QColor("#555555"), QColor("#555555"), QColor("#555555")},
|
||||
{QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}};
|
||||
}
|
||||
|
||||
// scrollbar
|
||||
this->scrollbars.highlights.highlight = QColor("#ee6166");
|
||||
this->scrollbars.highlights.subscription = QColor("#C466FF");
|
||||
|
||||
// this->tabs.newMessage = {
|
||||
// fg,
|
||||
// {QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern),
|
||||
// QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern),
|
||||
// QBrush(blendColors(themeColorNoSat, "#ccc", 0.9), Qt::FDiagPattern)}};
|
||||
|
||||
// this->tabs.newMessage = {
|
||||
// fg,
|
||||
// {QBrush(blendColors(themeColor, "#666", 0.7), Qt::FDiagPattern),
|
||||
// QBrush(blendColors(themeColor, "#666", 0.5), Qt::FDiagPattern),
|
||||
// QBrush(blendColors(themeColorNoSat, "#666", 0.7),
|
||||
// Qt::FDiagPattern)}};
|
||||
// this->tabs.highlighted = {fg, {QColor("#777"), QColor("#777"),
|
||||
// QColor("#666")}};
|
||||
|
||||
this->tabs.bottomLine = this->tabs.selected.backgrounds.regular.color();
|
||||
}
|
||||
|
||||
|
@ -119,7 +143,12 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
|
|||
this->splits.messageSeperator = isLight ? QColor(127, 127, 127) : QColor(60, 60, 60);
|
||||
this->splits.background = getColor(0, sat, 1);
|
||||
this->splits.dropPreview = QColor(0, 148, 255, 0x30);
|
||||
this->splits.dropPreviewBorder = QColor(0, 148, 255, 0x70);
|
||||
this->splits.dropPreviewBorder = QColor(0, 148, 255, 0xff);
|
||||
this->splits.dropTargetRect = QColor(0, 148, 255, 0x00);
|
||||
this->splits.dropTargetRectBorder = QColor(0, 148, 255, 0x00);
|
||||
this->splits.resizeHandle = QColor(0, 148, 255, 0x70);
|
||||
this->splits.resizeHandleBackground = QColor(0, 148, 255, 0x20);
|
||||
|
||||
// this->splits.border
|
||||
// this->splits.borderFocused
|
||||
|
||||
|
@ -143,7 +172,10 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
|
|||
this->messages.backgrounds.regular = splits.background;
|
||||
this->messages.backgrounds.alternate = getColor(0, sat, 0.93);
|
||||
this->messages.backgrounds.highlighted =
|
||||
blendColors(themeColor, this->messages.backgrounds.regular, 0.8);
|
||||
blendColors(themeColor, this->messages.backgrounds.regular, 0.6);
|
||||
this->messages.backgrounds.subscription =
|
||||
blendColors(QColor("#C466FF"), this->messages.backgrounds.regular, 0.7);
|
||||
|
||||
// this->messages.backgrounds.resub
|
||||
// this->messages.backgrounds.whisper
|
||||
this->messages.disabled = getColor(0, sat, 1, 0.6);
|
||||
|
@ -151,7 +183,9 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
|
|||
// this->messages.seperatorInner =
|
||||
|
||||
// Scrollbar
|
||||
this->scrollbars.background = getColor(0, sat, 0.94);
|
||||
this->scrollbars.background = QColor(0, 0, 0, 0);
|
||||
// this->scrollbars.background = splits.background;
|
||||
// this->scrollbars.background.setAlphaF(qreal(0.2));
|
||||
this->scrollbars.thumb = getColor(0, sat, 0.80);
|
||||
this->scrollbars.thumbSelected = getColor(0, sat, 0.7);
|
||||
|
||||
|
@ -163,13 +197,13 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
|
|||
this->messages.selection = isLightTheme() ? QColor(0, 0, 0, 64) : QColor(255, 255, 255, 64);
|
||||
|
||||
this->updated.invoke();
|
||||
}
|
||||
} // namespace singletons
|
||||
|
||||
QColor ThemeManager::blendColors(const QColor &color1, const QColor &color2, qreal ratio)
|
||||
{
|
||||
int r = color1.red() * (1 - ratio) + color2.red() * ratio;
|
||||
int g = color1.green() * (1 - ratio) + color2.green() * ratio;
|
||||
int b = color1.blue() * (1 - ratio) + color2.blue() * ratio;
|
||||
int r = int(color1.red() * (1 - ratio) + color2.red() * ratio);
|
||||
int g = int(color1.green() * (1 - ratio) + color2.green() * ratio);
|
||||
int b = int(color1.blue() * (1 - ratio) + color2.blue() * ratio);
|
||||
|
||||
return QColor(r, g, b, 255);
|
||||
}
|
||||
|
@ -177,22 +211,22 @@ QColor ThemeManager::blendColors(const QColor &color1, const QColor &color2, qre
|
|||
void ThemeManager::normalizeColor(QColor &color)
|
||||
{
|
||||
if (this->isLight) {
|
||||
if (color.lightnessF() > 0.5f) {
|
||||
color.setHslF(color.hueF(), color.saturationF(), 0.5f);
|
||||
if (color.lightnessF() > 0.5) {
|
||||
color.setHslF(color.hueF(), color.saturationF(), 0.5);
|
||||
}
|
||||
|
||||
if (color.lightnessF() > 0.4f && color.hueF() > 0.1 && color.hueF() < 0.33333) {
|
||||
if (color.lightnessF() > 0.4 && color.hueF() > 0.1 && color.hueF() < 0.33333) {
|
||||
color.setHslF(
|
||||
color.hueF(), color.saturationF(),
|
||||
color.lightnessF() - sin((color.hueF() - 0.1) / (0.3333 - 0.1) * 3.14159) *
|
||||
color.saturationF() * 0.2);
|
||||
}
|
||||
} else {
|
||||
if (color.lightnessF() < 0.5f) {
|
||||
color.setHslF(color.hueF(), color.saturationF(), 0.5f);
|
||||
if (color.lightnessF() < 0.5) {
|
||||
color.setHslF(color.hueF(), color.saturationF(), 0.5);
|
||||
}
|
||||
|
||||
if (color.lightnessF() < 0.6f && color.hueF() > 0.54444 && color.hueF() < 0.83333) {
|
||||
if (color.lightnessF() < 0.6 && color.hueF() > 0.54444 && color.hueF() < 0.83333) {
|
||||
color.setHslF(
|
||||
color.hueF(), color.saturationF(),
|
||||
color.lightnessF() + sin((color.hueF() - 0.54444) / (0.8333 - 0.54444) * 3.14159) *
|
||||
|
|
|
@ -25,11 +25,16 @@ public:
|
|||
|
||||
struct TabColors {
|
||||
QColor text;
|
||||
struct Backgrounds {
|
||||
struct {
|
||||
QBrush regular;
|
||||
QBrush hover;
|
||||
QBrush unfocused;
|
||||
} backgrounds;
|
||||
struct {
|
||||
QColor regular;
|
||||
QColor hover;
|
||||
QColor unfocused;
|
||||
} line;
|
||||
};
|
||||
|
||||
/// WINDOW
|
||||
|
@ -43,9 +48,9 @@ public:
|
|||
/// TABS
|
||||
struct {
|
||||
TabColors regular;
|
||||
TabColors selected;
|
||||
TabColors highlighted;
|
||||
TabColors newMessage;
|
||||
TabColors highlighted;
|
||||
TabColors selected;
|
||||
QColor border;
|
||||
QColor bottomLine;
|
||||
} tabs;
|
||||
|
@ -58,6 +63,10 @@ public:
|
|||
QColor borderFocused;
|
||||
QColor dropPreview;
|
||||
QColor dropPreviewBorder;
|
||||
QColor dropTargetRect;
|
||||
QColor dropTargetRectBorder;
|
||||
QColor resizeHandle;
|
||||
QColor resizeHandleBackground;
|
||||
|
||||
struct {
|
||||
QColor border;
|
||||
|
@ -89,7 +98,7 @@ public:
|
|||
QColor regular;
|
||||
QColor alternate;
|
||||
QColor highlighted;
|
||||
// QColor resub;
|
||||
QColor subscription;
|
||||
// QColor whisper;
|
||||
} backgrounds;
|
||||
|
||||
|
@ -104,8 +113,10 @@ public:
|
|||
QColor background;
|
||||
QColor thumb;
|
||||
QColor thumbSelected;
|
||||
// const int highlightsCount = 3;
|
||||
// QColor highlights[3];
|
||||
struct {
|
||||
QColor highlight;
|
||||
QColor subscription;
|
||||
} highlights;
|
||||
} scrollbars;
|
||||
|
||||
/// TOOLTIP
|
||||
|
|
|
@ -7,25 +7,31 @@ namespace chatterino {
|
|||
namespace singletons {
|
||||
|
||||
UpdateManager::UpdateManager()
|
||||
: currentVersion(CHATTERINO_VERSION)
|
||||
: currentVersion_(CHATTERINO_VERSION)
|
||||
{
|
||||
qDebug() << "init UpdateManager";
|
||||
}
|
||||
|
||||
UpdateManager &UpdateManager::getInstance()
|
||||
{
|
||||
// fourtf: don't add this class to the application class
|
||||
static UpdateManager instance;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
const QString &UpdateManager::getCurrentVersion() const
|
||||
{
|
||||
return this->getCurrentVersion();
|
||||
return currentVersion_;
|
||||
}
|
||||
|
||||
const QString &UpdateManager::getOnlineVersion() const
|
||||
{
|
||||
return this->getOnlineVersion();
|
||||
return onlineVersion_;
|
||||
}
|
||||
|
||||
void UpdateManager::installUpdates()
|
||||
{
|
||||
}
|
||||
|
||||
void UpdateManager::checkForUpdates()
|
||||
|
@ -33,17 +39,38 @@ void UpdateManager::checkForUpdates()
|
|||
QString url = "https://notitia.chatterino.com/version/chatterino/" CHATTERINO_OS "/stable";
|
||||
|
||||
util::NetworkRequest req(url);
|
||||
req.setTimeout(20000);
|
||||
req.setTimeout(30000);
|
||||
req.getJSON([this](QJsonObject &object) {
|
||||
QJsonValue version_val = object.value("version");
|
||||
if (!version_val.isString()) {
|
||||
this->setStatus_(Error);
|
||||
qDebug() << "error updating";
|
||||
return;
|
||||
}
|
||||
|
||||
this->onlineVersion = version_val.toString();
|
||||
this->onlineVersionUpdated.invoke();
|
||||
this->onlineVersion_ = version_val.toString();
|
||||
|
||||
if (this->currentVersion_ != this->onlineVersion_) {
|
||||
this->setStatus_(UpdateAvailable);
|
||||
} else {
|
||||
this->setStatus_(NoUpdateAvailable);
|
||||
}
|
||||
});
|
||||
this->setStatus_(Searching);
|
||||
req.execute();
|
||||
}
|
||||
|
||||
UpdateManager::UpdateStatus UpdateManager::getStatus() const
|
||||
{
|
||||
return this->status_;
|
||||
}
|
||||
|
||||
void UpdateManager::setStatus_(UpdateStatus status)
|
||||
{
|
||||
if (this->status_ != status) {
|
||||
this->status_ = status;
|
||||
this->statusUpdated.invoke(status);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace singletons
|
||||
|
|