Merge branch 'master' of https://github.com/fourtf/chatterino2 into blacklist
|
@ -1,23 +1,14 @@
|
|||
IndentCaseLabels: true
|
||||
BasedOnStyle: Google
|
||||
IndentWidth: 4
|
||||
Standard: Auto
|
||||
PointerBindsToType: false
|
||||
Language: Cpp
|
||||
SpacesBeforeTrailingComments: 2
|
||||
|
||||
AccessModifierOffset: -1
|
||||
AccessModifierOffset: -4
|
||||
AlignEscapedNewlinesLeft: true
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: false
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
BreakConstructorInitializersBeforeComma: true
|
||||
# BreakBeforeBraces: Linux
|
||||
BreakBeforeBraces: Custom
|
||||
AccessModifierOffset: -4
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
DerivePointerBinding: false
|
||||
BasedOnStyle: Google
|
||||
BraceWrapping: {
|
||||
AfterNamespace: 'false'
|
||||
AfterClass: 'true'
|
||||
|
@ -26,5 +17,14 @@ BraceWrapping: {
|
|||
AfterFunction: 'true'
|
||||
BeforeCatch: 'false'
|
||||
}
|
||||
ColumnLimit: 100
|
||||
BreakBeforeBraces: Custom
|
||||
BreakConstructorInitializersBeforeComma: true
|
||||
ColumnLimit: 80
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
DerivePointerBinding: false
|
||||
FixNamespaceComments: true
|
||||
IndentCaseLabels: true
|
||||
IndentWidth: 4
|
||||
PointerBindsToType: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
Standard: Auto
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
1. Install Xcode and Xcode Command Line Utilites
|
||||
2. Start Xcode, settings -> Locations, activate your Command Line Tools
|
||||
3. Install brew https://brew.sh/
|
||||
4. `brew install boost openssl rapidjson qt`
|
||||
4. `brew install boost openssl rapidjson`
|
||||
5. `brew install qt`
|
||||
6. Step 5 should output some directions to add qt to your path, you will need to do this for qmake
|
||||
5. Go into project directory
|
||||
6. Create build folder `mkdir build && cd build`
|
||||
7. `qmake .. && make`
|
||||
|
|
20
Jenkinsfile
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
pipeline {
|
||||
agent any
|
||||
|
||||
stages {
|
||||
stage('Build') {
|
||||
parallel {
|
||||
stage('GCC') {
|
||||
steps {
|
||||
sh 'mkdir -p build-linux-gcc && cd build-linux-gcc && make distclean; qmake .. && make'
|
||||
}
|
||||
}
|
||||
stage('Clang') {
|
||||
steps {
|
||||
sh 'mkdir -p build-linux-clang && cd build-linux-clang && make distclean; qmake -spec linux-clang .. && make'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,8 +23,6 @@ Before building run `git submodule update --init --recursive` to get required su
|
|||
## 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).
|
||||
|
||||
### Get it automated with QT Creator + Beautifier + Clang Format
|
||||
1. Download LLVM: http://releases.llvm.org/6.0.1/LLVM-6.0.1-win64.exe
|
||||
2. During the installation, make sure to add it to your path
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
message(----)
|
||||
|
||||
QT += widgets core gui network multimedia svg
|
||||
QT += widgets core gui network multimedia svg concurrent
|
||||
CONFIG += communi
|
||||
COMMUNI += core model util
|
||||
CONFIG += c++14
|
||||
|
@ -17,6 +17,10 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
|||
PRECOMPILED_HEADER = src/PrecompiledHeader.hpp
|
||||
CONFIG += precompile_header
|
||||
|
||||
debug {
|
||||
DEFINES += QT_DEBUG
|
||||
}
|
||||
|
||||
useBreakpad {
|
||||
LIBS += -L$$PWD/lib/qBreakpad/handler/build
|
||||
include(lib/qBreakpad/qBreakpad.pri)
|
||||
|
@ -101,7 +105,6 @@ SOURCES += \
|
|||
src/Application.cpp \
|
||||
src/common/Channel.cpp \
|
||||
src/common/CompletionModel.cpp \
|
||||
src/common/Emotemap.cpp \
|
||||
src/common/NetworkData.cpp \
|
||||
src/common/NetworkManager.cpp \
|
||||
src/common/NetworkRequest.cpp \
|
||||
|
@ -132,9 +135,7 @@ SOURCES += \
|
|||
src/messages/MessageBuilder.cpp \
|
||||
src/messages/MessageColor.cpp \
|
||||
src/messages/MessageElement.cpp \
|
||||
src/providers/bttv/BttvEmotes.cpp \
|
||||
src/providers/emoji/Emojis.cpp \
|
||||
src/providers/ffz/FfzEmotes.cpp \
|
||||
src/providers/irc/AbstractIrcServer.cpp \
|
||||
src/providers/irc/IrcAccount.cpp \
|
||||
src/providers/irc/IrcChannel2.cpp \
|
||||
|
@ -184,8 +185,6 @@ SOURCES += \
|
|||
src/widgets/helper/NotebookButton.cpp \
|
||||
src/widgets/helper/NotebookTab.cpp \
|
||||
src/widgets/helper/ResizingTextEdit.cpp \
|
||||
src/widgets/helper/RippleEffectButton.cpp \
|
||||
src/widgets/helper/RippleEffectLabel.cpp \
|
||||
src/widgets/helper/ScrollbarHighlight.cpp \
|
||||
src/widgets/helper/SearchPopup.cpp \
|
||||
src/widgets/helper/SettingsDialogTab.cpp \
|
||||
|
@ -231,17 +230,35 @@ SOURCES += \
|
|||
src/util/InitUpdateButton.cpp \
|
||||
src/widgets/dialogs/UpdateDialog.cpp \
|
||||
src/widgets/settingspages/IgnoresPage.cpp \
|
||||
src/providers/twitch/PubsubClient.cpp
|
||||
src/providers/twitch/PubsubClient.cpp \
|
||||
src/providers/twitch/TwitchApi.cpp \
|
||||
src/messages/Emote.cpp \
|
||||
src/messages/ImageSet.cpp \
|
||||
src/providers/bttv/BttvEmotes.cpp \
|
||||
src/providers/ffz/FfzEmotes.cpp \
|
||||
src/autogenerated/ResourcesAutogen.cpp \
|
||||
src/singletons/Badges.cpp \
|
||||
src/providers/twitch/TwitchBadges.cpp \
|
||||
src/providers/chatterino/ChatterinoBadges.cpp \
|
||||
src/providers/twitch/TwitchParseCheerEmotes.cpp \
|
||||
src/providers/bttv/LoadBttvChannelEmote.cpp \
|
||||
src/util/JsonQuery.cpp \
|
||||
src/RunGui.cpp \
|
||||
src/BrowserExtension.cpp \
|
||||
src/util/FormatTime.cpp \
|
||||
src/util/FunctionEventFilter.cpp \
|
||||
src/widgets/helper/EffectLabel.cpp \
|
||||
src/widgets/helper/Button.cpp \
|
||||
src/messages/MessageContainer.cpp \
|
||||
src/debug/Benchmark.cpp
|
||||
|
||||
HEADERS += \
|
||||
src/Application.hpp \
|
||||
src/common/Channel.hpp \
|
||||
src/common/Common.hpp \
|
||||
src/common/CompletionModel.hpp \
|
||||
src/common/Emotemap.hpp \
|
||||
src/common/FlagsEnum.hpp \
|
||||
src/common/LockedObject.hpp \
|
||||
src/common/MutexValue.hpp \
|
||||
src/common/Atomic.hpp \
|
||||
src/common/NetworkCommon.hpp \
|
||||
src/common/NetworkData.hpp \
|
||||
src/common/NetworkManager.hpp \
|
||||
|
@ -253,9 +270,8 @@ HEADERS += \
|
|||
src/common/NullablePtr.hpp \
|
||||
src/common/Property.hpp \
|
||||
src/common/ProviderId.hpp \
|
||||
src/common/SerializeCustom.hpp \
|
||||
src/util/RapidJsonSerializeQString.hpp \
|
||||
src/common/SignalVectorModel.hpp \
|
||||
src/common/UrlFetch.hpp \
|
||||
src/common/Version.hpp \
|
||||
src/controllers/accounts/Account.hpp \
|
||||
src/controllers/accounts/AccountController.hpp \
|
||||
|
@ -289,12 +305,9 @@ HEADERS += \
|
|||
src/messages/MessageBuilder.hpp \
|
||||
src/messages/MessageColor.hpp \
|
||||
src/messages/MessageElement.hpp \
|
||||
src/messages/MessageParseArgs.hpp \
|
||||
src/messages/Selection.hpp \
|
||||
src/PrecompiledHeader.hpp \
|
||||
src/providers/bttv/BttvEmotes.hpp \
|
||||
src/providers/emoji/Emojis.hpp \
|
||||
src/providers/ffz/FfzEmotes.hpp \
|
||||
src/providers/irc/AbstractIrcServer.hpp \
|
||||
src/providers/irc/IrcAccount.hpp \
|
||||
src/providers/irc/IrcChannel2.hpp \
|
||||
|
@ -358,8 +371,6 @@ HEADERS += \
|
|||
src/widgets/helper/NotebookButton.hpp \
|
||||
src/widgets/helper/NotebookTab.hpp \
|
||||
src/widgets/helper/ResizingTextEdit.hpp \
|
||||
src/widgets/helper/RippleEffectButton.hpp \
|
||||
src/widgets/helper/RippleEffectLabel.hpp \
|
||||
src/widgets/helper/ScrollbarHighlight.hpp \
|
||||
src/widgets/helper/SearchPopup.hpp \
|
||||
src/widgets/helper/SettingsDialogTab.hpp \
|
||||
|
@ -402,7 +413,6 @@ HEADERS += \
|
|||
src/singletons/Updates.hpp \
|
||||
src/singletons/NativeMessaging.hpp \
|
||||
src/singletons/Theme.hpp \
|
||||
src/common/SimpleSignalVector.hpp \
|
||||
src/common/SignalVector.hpp \
|
||||
src/widgets/dialogs/LogsPopup.hpp \
|
||||
src/common/Singleton.hpp \
|
||||
|
@ -412,10 +422,34 @@ HEADERS += \
|
|||
src/util/InitUpdateButton.hpp \
|
||||
src/widgets/dialogs/UpdateDialog.hpp \
|
||||
src/widgets/settingspages/IgnoresPage.hpp \
|
||||
src/providers/twitch/PubsubClient.hpp
|
||||
src/providers/twitch/PubsubClient.hpp \
|
||||
src/providers/twitch/TwitchApi.hpp \
|
||||
src/messages/Emote.hpp \
|
||||
src/messages/EmoteCache.hpp \
|
||||
src/messages/ImageSet.hpp \
|
||||
src/common/Outcome.hpp \
|
||||
src/providers/bttv/BttvEmotes.hpp \
|
||||
src/providers/ffz/FfzEmotes.hpp \
|
||||
src/autogenerated/ResourcesAutogen.hpp \
|
||||
src/singletons/Badges.hpp \
|
||||
src/providers/twitch/TwitchBadges.hpp \
|
||||
src/providers/chatterino/ChatterinoBadges.hpp \
|
||||
src/common/Aliases.hpp \
|
||||
src/providers/twitch/TwitchParseCheerEmotes.hpp \
|
||||
src/providers/bttv/LoadBttvChannelEmote.hpp \
|
||||
src/util/JsonQuery.hpp \
|
||||
src/RunGui.hpp \
|
||||
src/BrowserExtension.hpp \
|
||||
src/util/FormatTime.hpp \
|
||||
src/util/FunctionEventFilter.hpp \
|
||||
src/widgets/helper/EffectLabel.hpp \
|
||||
src/util/LayoutHelper.hpp \
|
||||
src/widgets/helper/Button.hpp \
|
||||
src/messages/MessageContainer.hpp
|
||||
|
||||
RESOURCES += \
|
||||
resources/resources.qrc \
|
||||
resources/resources_autogenerated.qrc
|
||||
|
||||
DISTFILES +=
|
||||
|
||||
|
@ -450,7 +484,6 @@ win32-msvc* {
|
|||
QMAKE_CXXFLAGS_WARN_ON += -Wno-deprecated-declarations
|
||||
QMAKE_CXXFLAGS_WARN_ON += -Wno-sign-compare
|
||||
QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-variable
|
||||
QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-private-field
|
||||
|
||||
# Disabling strict-aliasing warnings for now, although we probably want to re-enable this in the future
|
||||
QMAKE_CXXFLAGS_WARN_ON += -Wno-strict-aliasing
|
||||
|
@ -459,6 +492,7 @@ win32-msvc* {
|
|||
|
||||
equals(QMAKE_CXX, "clang++") {
|
||||
QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-local-typedef
|
||||
QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-private-field
|
||||
} else {
|
||||
QMAKE_CXXFLAGS_WARN_ON += -Wno-class-memaccess
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 29accdf9dea05947d687112594ad06bf6001ee0a
|
||||
Subproject commit 7f0db95f245fb726e756ecde15a800c0928b054b
|
|
@ -1 +1 @@
|
|||
Subproject commit 3f6645c615ff7bf412c05fe322e589cbdd34ff9b
|
||||
Subproject commit e03c868ec922027a0e672b64388808beb1297816
|
BIN
resources/__pycache__/_generate_resources.cpython-36.pyc
Normal file
38
resources/_generate_resources.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
resources_header = \
|
||||
'''<RCC>
|
||||
<qresource prefix="/">'''
|
||||
|
||||
resources_footer = \
|
||||
''' </qresource>
|
||||
</RCC>'''
|
||||
|
||||
header_header = \
|
||||
'''#include <QPixmap>
|
||||
#include "common/Singleton.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class Resources2 : public Singleton {
|
||||
public:
|
||||
Resources2();
|
||||
|
||||
'''
|
||||
|
||||
header_footer = \
|
||||
'''};
|
||||
|
||||
} // namespace chatterino'''
|
||||
|
||||
source_header = \
|
||||
'''#include "ResourcesAutogen.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
Resources2::Resources2()
|
||||
{
|
||||
'''
|
||||
|
||||
source_footer = \
|
||||
'''}
|
||||
|
||||
} // namespace chatterino'''
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 847 B After Width: | Height: | Size: 847 B |
Before Width: | Height: | Size: 847 B After Width: | Height: | Size: 847 B |
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 307 B |
Before Width: | Height: | Size: 328 B After Width: | Height: | Size: 328 B |
Before Width: | Height: | Size: 376 B After Width: | Height: | Size: 376 B |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 349 B |
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 344 B |
Before Width: | Height: | Size: 332 B After Width: | Height: | Size: 332 B |
BIN
resources/error.png
Normal file
After Width: | Height: | Size: 685 B |
61
resources/generate_resources.py
Executable file
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
|
||||
from _generate_resources import *
|
||||
|
||||
ignored_files = ['qt.conf', 'resources.qrc', 'resources_autogenerated.qrc', 'windows.rc',
|
||||
'generate_resources.py', '_generate_resources.py']
|
||||
ignored_directories = ['__pycache__']
|
||||
|
||||
def isNotIgnored(file):
|
||||
return str(file) not in ignored_files
|
||||
|
||||
all_files = list(filter(isNotIgnored, \
|
||||
filter(Path.is_file, Path('.').glob('**/*'))))
|
||||
image_files = list(filter(isNotIgnored, \
|
||||
filter(Path.is_file, Path('.').glob('**/*.png'))))
|
||||
|
||||
with open('./resources_autogenerated.qrc', 'w') as out:
|
||||
out.write(resources_header)
|
||||
for file in all_files:
|
||||
out.write(f" <file>{str(file)}</file>\n")
|
||||
out.write(resources_footer)
|
||||
|
||||
with open('../src/autogenerated/ResourcesAutogen.cpp', 'w') as out:
|
||||
out.write(source_header)
|
||||
for file in sorted(image_files):
|
||||
var_name = str(file.with_suffix("")).replace("/",".")
|
||||
out.write(f' this->{var_name}')
|
||||
out.write(f' = QPixmap(":/{file}");\n')
|
||||
out.write(source_footer)
|
||||
|
||||
def writeHeader(out, name, element, indent):
|
||||
if isinstance(element, dict):
|
||||
if name != "":
|
||||
out.write(f"{indent}struct {{\n")
|
||||
for (key, value) in element.items():
|
||||
writeHeader(out, key, value, indent + ' ')
|
||||
if name != "":
|
||||
out.write(f"{indent}}} {name};\n");
|
||||
else:
|
||||
out.write(f"{indent}QPixmap {element};\n")
|
||||
|
||||
with open('../src/autogenerated/ResourcesAutogen.hpp', 'w') as out:
|
||||
out.write(header_header)
|
||||
|
||||
elements = {}
|
||||
for file in sorted(image_files):
|
||||
elements_ref = elements
|
||||
directories = str(file).split('/')[:-1]
|
||||
filename = file.stem
|
||||
for directory in directories:
|
||||
if directory not in elements_ref:
|
||||
if directory not in ignored_directories:
|
||||
elements_ref[directory] = {}
|
||||
elements_ref = elements_ref[directory]
|
||||
elements_ref[filename] = filename
|
||||
|
||||
writeHeader(out, "", elements, '')
|
||||
|
||||
out.write(header_footer)
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 347 B |
Before Width: | Height: | Size: 680 B |
Before Width: | Height: | Size: 403 B |
Before Width: | Height: | Size: 368 B |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 660 B |
Before Width: | Height: | Size: 280 B |
Before Width: | Height: | Size: 179 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 286 B |
Before Width: | Height: | Size: 480 B |
Before Width: | Height: | Size: 360 B |
Before Width: | Height: | Size: 351 B |
Before Width: | Height: | Size: 330 B |
Before Width: | Height: | Size: 285 B |
Before Width: | Height: | Size: 362 B |
Before Width: | Height: | Size: 236 B |
Before Width: | Height: | Size: 354 B |
Before Width: | Height: | Size: 274 B |
Before Width: | Height: | Size: 279 B |
21
resources/licenses/emoji-data-source.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Cal Henderson
|
||||
|
||||
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.
|
BIN
resources/pajaDank.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
|
@ -1,81 +1,5 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>images/AppearanceEditorPart_16x.png</file>
|
||||
<file>images/BrowserLink_16x.png</file>
|
||||
<file>images/cheer1.png</file>
|
||||
<file>images/cheer100.png</file>
|
||||
<file>images/cheer1000.png</file>
|
||||
<file>images/cheer10000.png</file>
|
||||
<file>images/cheer100000.png</file>
|
||||
<file>images/cheer5000.png</file>
|
||||
<file>images/verified.png</file>
|
||||
<file>images/CopyLongTextToClipboard_16x.png</file>
|
||||
<file>images/CustomActionEditor_16x.png</file>
|
||||
<file>images/Emoji_Color_1F60A_19.png</file>
|
||||
<file>images/Filter_16x.png</file>
|
||||
<file>images/format_Bold_16xLG.png</file>
|
||||
<file>images/Message_16xLG.png</file>
|
||||
<file>images/settings.png</file>
|
||||
<file>images/tool_moreCollapser_off16.png</file>
|
||||
<file>images/twitchprime_bg.png</file>
|
||||
<file>qss/settings.qss</file>
|
||||
<file>images/admin_bg.png</file>
|
||||
<file>images/broadcaster_bg.png</file>
|
||||
<file>images/globalmod_bg.png</file>
|
||||
<file>images/moderator_bg.png</file>
|
||||
<file>images/staff_bg.png</file>
|
||||
<file>images/turbo_bg.png</file>
|
||||
<file>emojidata.txt</file>
|
||||
<file>images/button_ban.png</file>
|
||||
<file>images/button_timeout.png</file>
|
||||
<file>images/StatusAnnotations_Blocked_16xLG_color.png</file>
|
||||
<file>images/UserProfile_22x.png</file>
|
||||
<file>images/VSO_Link_blue_16x.png</file>
|
||||
<file>sounds/ping2.wav</file>
|
||||
<file>images/subscriber.png</file>
|
||||
<file>images/collapse.png</file>
|
||||
<file>images/emote.svg</file>
|
||||
<file>images/notifications.svg</file>
|
||||
<file>images/behave.svg</file>
|
||||
<file>images/theme.svg</file>
|
||||
<file>images/accounts.svg</file>
|
||||
<file>images/chatterino2.icns</file>
|
||||
<file>images/icon.png</file>
|
||||
<file>images/commands.svg</file>
|
||||
<file>images/aboutlogo.png</file>
|
||||
<file>images/about.svg</file>
|
||||
<file>images/moderatormode_disabled.png</file>
|
||||
<file>images/moderatormode_enabled.png</file>
|
||||
<file>images/split/splitdown.png</file>
|
||||
<file>images/split/splitleft.png</file>
|
||||
<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>
|
||||
<file>emoji.json</file>
|
||||
<file>images/buttons/ban.png</file>
|
||||
<file>images/buttons/mod.png</file>
|
||||
<file>images/buttons/unban.png</file>
|
||||
<file>images/buttons/unmod.png</file>
|
||||
<file>images/emote_dark.svg</file>
|
||||
<file>tlds.txt</file>
|
||||
<file>images/menu_black.png</file>
|
||||
<file>images/menu_white.png</file>
|
||||
<file>contributors.txt</file>
|
||||
<file>avatars/fourtf.png</file>
|
||||
<file>avatars/pajlada.png</file>
|
||||
<file>images/download_update.png</file>
|
||||
<file>images/download_update_error.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/qt/etc">
|
||||
<file>qt.conf</file>
|
||||
</qresource>
|
||||
<qresource prefix="/qt/etc">
|
||||
<file>qt.conf</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
64
resources/resources_autogenerated.qrc
Normal file
|
@ -0,0 +1,64 @@
|
|||
<RCC>
|
||||
<qresource prefix="/"> <file>pajaDank.png</file>
|
||||
<file>icon.png</file>
|
||||
<file>emojidata.txt</file>
|
||||
<file>contributors.txt</file>
|
||||
<file>error.png</file>
|
||||
<file>emoji.json</file>
|
||||
<file>icon.ico</file>
|
||||
<file>tlds.txt</file>
|
||||
<file>chatterino2.icns</file>
|
||||
<file>qss/settings.qss</file>
|
||||
<file>__pycache__/_generate_resources.cpython-36.pyc</file>
|
||||
<file>licenses/fmt_bsd2.txt</file>
|
||||
<file>licenses/openssl.txt</file>
|
||||
<file>licenses/pajlada_settings.txt</file>
|
||||
<file>licenses/qt_lgpl-3.0.txt</file>
|
||||
<file>licenses/pajlada_signals.txt</file>
|
||||
<file>licenses/rapidjson.txt</file>
|
||||
<file>licenses/websocketpp.txt</file>
|
||||
<file>licenses/boost_boost.txt</file>
|
||||
<file>licenses/libcommuni_BSD3.txt</file>
|
||||
<file>settings/aboutlogo.png</file>
|
||||
<file>settings/behave.svg</file>
|
||||
<file>settings/accounts.svg</file>
|
||||
<file>settings/about.svg</file>
|
||||
<file>settings/notifications.svg</file>
|
||||
<file>settings/commands.svg</file>
|
||||
<file>settings/theme.svg</file>
|
||||
<file>split/up.png</file>
|
||||
<file>split/left.png</file>
|
||||
<file>split/move.png</file>
|
||||
<file>split/right.png</file>
|
||||
<file>split/down.png</file>
|
||||
<file>buttons/unban.png</file>
|
||||
<file>buttons/menuDark.png</file>
|
||||
<file>buttons/mod.png</file>
|
||||
<file>buttons/emote.svg</file>
|
||||
<file>buttons/modModeEnabled2.png</file>
|
||||
<file>buttons/ban.png</file>
|
||||
<file>buttons/unmod.png</file>
|
||||
<file>buttons/emoteDark.svg</file>
|
||||
<file>buttons/updateError.png</file>
|
||||
<file>buttons/modModeDisabled.png</file>
|
||||
<file>buttons/modModeDisabled2.png</file>
|
||||
<file>buttons/modModeEnabled.png</file>
|
||||
<file>buttons/menuLight.png</file>
|
||||
<file>buttons/update.png</file>
|
||||
<file>buttons/timeout.png</file>
|
||||
<file>buttons/banRed.png</file>
|
||||
<file>sounds/ping2.wav</file>
|
||||
<file>twitch/prime.png</file>
|
||||
<file>twitch/verified.png</file>
|
||||
<file>twitch/admin.png</file>
|
||||
<file>twitch/subscriber.png</file>
|
||||
<file>twitch/turbo.png</file>
|
||||
<file>twitch/moderator.png</file>
|
||||
<file>twitch/globalmod.png</file>
|
||||
<file>twitch/cheer1.png</file>
|
||||
<file>twitch/broadcaster.png</file>
|
||||
<file>twitch/staff.png</file>
|
||||
<file>avatars/fourtf.png</file>
|
||||
<file>avatars/pajlada.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 805 B After Width: | Height: | Size: 805 B |
Before Width: | Height: | Size: 820 B After Width: | Height: | Size: 820 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
12
resources/settings/emote.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M50,100c27.7,0,50-22.3,50-50S77.7,0,50,0S0,22.3,0,50S22.3,100,50,100z M33.3,46.2c4.2,0,8.3-3.3,8.3-8.3
|
||||
s-4.2-8.3-8.3-8.3S25,32.9,25,37.9S29.2,46.2,33.3,46.2z M50,91.7C27,91.7,8.3,73,8.3,50S27,8.3,50,8.3S91.7,27,91.7,50
|
||||
S73,91.7,50,91.7z M23.3,63.1c16.2,10.3,37.1,10.4,53.2,0.1l-4.3-7c-13.7,8.5-31,8.4-44.5-0.1L23.3,63.1z M67.1,46.2
|
||||
c4.2,0,8.3-3.3,8.3-8.3s-4.2-8.3-8.3-8.3s-8.3,3.3-8.3,8.3S62.9,46.2,67.1,46.2z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 847 B |
Before Width: | Height: | Size: 955 B After Width: | Height: | Size: 955 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 361 B After Width: | Height: | Size: 361 B |
Before Width: | Height: | Size: 437 B After Width: | Height: | Size: 437 B |
Before Width: | Height: | Size: 772 B After Width: | Height: | Size: 772 B |
Before Width: | Height: | Size: 494 B After Width: | Height: | Size: 494 B |
Before Width: | Height: | Size: 361 B After Width: | Height: | Size: 361 B |
Before Width: | Height: | Size: 439 B After Width: | Height: | Size: 439 B |
Before Width: | Height: | Size: 191 B After Width: | Height: | Size: 191 B |
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 255 B |
Before Width: | Height: | Size: 397 B After Width: | Height: | Size: 397 B |
Before Width: | Height: | Size: 376 B After Width: | Height: | Size: 376 B |
Before Width: | Height: | Size: 116 B After Width: | Height: | Size: 116 B |
Before Width: | Height: | Size: 269 B After Width: | Height: | Size: 269 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 257 B After Width: | Height: | Size: 257 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
@ -6,9 +6,11 @@
|
|||
#include "controllers/ignores/IgnoreController.hpp"
|
||||
#include "controllers/moderationactions/ModerationActions.hpp"
|
||||
#include "controllers/taggedusers/TaggedUsersController.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/bttv/BttvEmotes.hpp"
|
||||
#include "providers/ffz/FfzEmotes.hpp"
|
||||
#include "providers/twitch/PubsubClient.hpp"
|
||||
#include "providers/twitch/TwitchServer.hpp"
|
||||
#include "singletons/Emotes.hpp"
|
||||
#include "singletons/Fonts.hpp"
|
||||
#include "singletons/Logging.hpp"
|
||||
#include "singletons/NativeMessaging.hpp"
|
||||
|
@ -24,69 +26,77 @@
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
static std::atomic<bool> isAppConstructed{false};
|
||||
static std::atomic<bool> isAppInitialized{false};
|
||||
|
||||
static Application *staticApp = nullptr;
|
||||
Application *Application::instance = nullptr;
|
||||
|
||||
// this class is responsible for handling the workflow of Chatterino
|
||||
// It will create the instances of the major classes, and connect their signals to each other
|
||||
// It will create the instances of the major classes, and connect their signals
|
||||
// to each other
|
||||
|
||||
Application::Application(int _argc, char **_argv)
|
||||
: argc_(_argc)
|
||||
, argv_(_argv)
|
||||
Application::Application(Settings &_settings, Paths &_paths)
|
||||
: settings(&_settings)
|
||||
, paths(&_paths)
|
||||
, resources(&this->emplace<Resources2>())
|
||||
|
||||
, themes(&this->emplace<Theme>())
|
||||
, fonts(&this->emplace<Fonts>())
|
||||
, emotes(&this->emplace<Emotes>())
|
||||
, windows(&this->emplace<WindowManager>())
|
||||
|
||||
, accounts(&this->emplace<AccountController>())
|
||||
, commands(&this->emplace<CommandController>())
|
||||
, highlights(&this->emplace<HighlightController>())
|
||||
, ignores(&this->emplace<IgnoreController>())
|
||||
, taggedUsers(&this->emplace<TaggedUsersController>())
|
||||
, moderationActions(&this->emplace<ModerationActions>())
|
||||
, twitch2(&this->emplace<TwitchServer>())
|
||||
, logging(&this->emplace<Logging>())
|
||||
{
|
||||
getSettings()->initialize();
|
||||
getSettings()->load();
|
||||
}
|
||||
this->instance = this;
|
||||
|
||||
void Application::construct()
|
||||
{
|
||||
assert(isAppConstructed == false);
|
||||
isAppConstructed = true;
|
||||
this->fonts->fontChanged.connect(
|
||||
[this]() { this->windows->layoutChannelViews(); });
|
||||
|
||||
// 1. Instantiate all classes
|
||||
this->settings = getSettings();
|
||||
this->paths = getPaths();
|
||||
|
||||
this->addSingleton(this->themes = new Theme);
|
||||
this->addSingleton(this->windows = new WindowManager);
|
||||
this->addSingleton(this->logging = new Logging);
|
||||
this->addSingleton(this->commands = new CommandController);
|
||||
this->addSingleton(this->highlights = new HighlightController);
|
||||
this->addSingleton(this->ignores = new IgnoreController);
|
||||
this->addSingleton(this->taggedUsers = new TaggedUsersController);
|
||||
this->addSingleton(this->accounts = new AccountController);
|
||||
this->addSingleton(this->emotes = new Emotes);
|
||||
this->addSingleton(this->fonts = new Fonts);
|
||||
this->addSingleton(this->resources = new Resources);
|
||||
this->addSingleton(this->moderationActions = new ModerationActions);
|
||||
|
||||
this->addSingleton(this->twitch2 = new TwitchServer);
|
||||
this->twitch.server = this->twitch2;
|
||||
this->twitch.pubsub = this->twitch2->pubsub;
|
||||
}
|
||||
|
||||
void Application::instantiate(int argc, char **argv)
|
||||
{
|
||||
assert(staticApp == nullptr);
|
||||
|
||||
staticApp = new Application(argc, argv);
|
||||
}
|
||||
|
||||
void Application::initialize()
|
||||
void Application::initialize(Settings &settings, Paths &paths)
|
||||
{
|
||||
assert(isAppInitialized == false);
|
||||
isAppInitialized = true;
|
||||
|
||||
// 2. Initialize/load classes
|
||||
for (Singleton *singleton : this->singletons_) {
|
||||
singleton->initialize(*this);
|
||||
for (auto &singleton : this->singletons_) {
|
||||
singleton->initialize(settings, paths);
|
||||
}
|
||||
|
||||
// XXX
|
||||
this->windows->updateWordTypeMask();
|
||||
|
||||
this->initNm();
|
||||
this->initPubsub();
|
||||
}
|
||||
|
||||
int Application::run(QApplication &qtApp)
|
||||
{
|
||||
assert(isAppInitialized);
|
||||
|
||||
this->twitch.server->connect();
|
||||
|
||||
this->windows->getMainWindow().show();
|
||||
|
||||
return qtApp.exec();
|
||||
}
|
||||
|
||||
void Application::save()
|
||||
{
|
||||
for (auto &singleton : this->singletons_) {
|
||||
singleton->save();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::initNm()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef QT_DEBUG
|
||||
#ifdef C_DEBUG_NM
|
||||
|
@ -98,49 +108,61 @@ void Application::initialize()
|
|||
this->nativeMessaging->openGuiMessageQueue();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::initPubsub()
|
||||
{
|
||||
this->twitch.pubsub->signals_.whisper.sent.connect([](const auto &msg) {
|
||||
Log("WHISPER SENT LOL"); //
|
||||
log("WHISPER SENT LOL"); //
|
||||
});
|
||||
|
||||
this->twitch.pubsub->signals_.whisper.received.connect([](const auto &msg) {
|
||||
Log("WHISPER RECEIVED LOL"); //
|
||||
log("WHISPER RECEIVED LOL"); //
|
||||
});
|
||||
|
||||
this->twitch.pubsub->signals_.moderation.chatCleared.connect([this](const auto &action) {
|
||||
auto chan = this->twitch.server->getChannelOrEmptyByID(action.roomID);
|
||||
if (chan->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
this->twitch.pubsub->signals_.moderation.chatCleared.connect(
|
||||
[this](const auto &action) {
|
||||
auto chan =
|
||||
this->twitch.server->getChannelOrEmptyByID(action.roomID);
|
||||
if (chan->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString text = QString("%1 cleared the chat").arg(action.source.name);
|
||||
QString text =
|
||||
QString("%1 cleared the chat").arg(action.source.name);
|
||||
|
||||
auto msg = Message::createSystemMessage(text);
|
||||
postToThread([chan, msg] { chan->addMessage(msg); });
|
||||
});
|
||||
auto msg = makeSystemMessage(text);
|
||||
postToThread([chan, msg] { chan->addMessage(msg); });
|
||||
});
|
||||
|
||||
this->twitch.pubsub->signals_.moderation.modeChanged.connect([this](const auto &action) {
|
||||
auto chan = this->twitch.server->getChannelOrEmptyByID(action.roomID);
|
||||
if (chan->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
this->twitch.pubsub->signals_.moderation.modeChanged.connect(
|
||||
[this](const auto &action) {
|
||||
auto chan =
|
||||
this->twitch.server->getChannelOrEmptyByID(action.roomID);
|
||||
if (chan->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString text = QString("%1 turned %2 %3 mode") //
|
||||
.arg(action.source.name)
|
||||
.arg(action.state == ModeChangedAction::State::On ? "on" : "off")
|
||||
.arg(action.getModeName());
|
||||
QString text =
|
||||
QString("%1 turned %2 %3 mode") //
|
||||
.arg(action.source.name)
|
||||
.arg(action.state == ModeChangedAction::State::On ? "on"
|
||||
: "off")
|
||||
.arg(action.getModeName());
|
||||
|
||||
if (action.duration > 0) {
|
||||
text.append(" (" + QString::number(action.duration) + " seconds)");
|
||||
}
|
||||
if (action.duration > 0) {
|
||||
text.append(" (" + QString::number(action.duration) +
|
||||
" seconds)");
|
||||
}
|
||||
|
||||
auto msg = Message::createSystemMessage(text);
|
||||
postToThread([chan, msg] { chan->addMessage(msg); });
|
||||
});
|
||||
auto msg = makeSystemMessage(text);
|
||||
postToThread([chan, msg] { chan->addMessage(msg); });
|
||||
});
|
||||
|
||||
this->twitch.pubsub->signals_.moderation.moderationStateChanged.connect(
|
||||
[this](const auto &action) {
|
||||
auto chan = this->twitch.server->getChannelOrEmptyByID(action.roomID);
|
||||
auto chan =
|
||||
this->twitch.server->getChannelOrEmptyByID(action.roomID);
|
||||
if (chan->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -148,48 +170,57 @@ void Application::initialize()
|
|||
QString text;
|
||||
|
||||
if (action.modded) {
|
||||
text = QString("%1 modded %2").arg(action.source.name, action.target.name);
|
||||
text = QString("%1 modded %2")
|
||||
.arg(action.source.name, action.target.name);
|
||||
} else {
|
||||
text = QString("%1 unmodded %2").arg(action.source.name, action.target.name);
|
||||
text = QString("%1 unmodded %2")
|
||||
.arg(action.source.name, action.target.name);
|
||||
}
|
||||
|
||||
auto msg = Message::createSystemMessage(text);
|
||||
auto msg = makeSystemMessage(text);
|
||||
postToThread([chan, msg] { chan->addMessage(msg); });
|
||||
});
|
||||
|
||||
this->twitch.pubsub->signals_.moderation.userBanned.connect([&](const auto &action) {
|
||||
auto chan = this->twitch.server->getChannelOrEmptyByID(action.roomID);
|
||||
this->twitch.pubsub->signals_.moderation.userBanned.connect(
|
||||
[&](const auto &action) {
|
||||
auto chan =
|
||||
this->twitch.server->getChannelOrEmptyByID(action.roomID);
|
||||
|
||||
if (chan->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (chan->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto msg = Message::createTimeoutMessage(action);
|
||||
msg->flags |= Message::PubSub;
|
||||
MessageBuilder msg(action);
|
||||
msg->flags.set(MessageFlag::PubSub);
|
||||
|
||||
postToThread([chan, msg] { chan->addOrReplaceTimeout(msg); });
|
||||
});
|
||||
postToThread([chan, msg = msg.release()] {
|
||||
chan->addOrReplaceTimeout(msg);
|
||||
});
|
||||
});
|
||||
|
||||
this->twitch.pubsub->signals_.moderation.userUnbanned.connect([&](const auto &action) {
|
||||
auto chan = this->twitch.server->getChannelOrEmptyByID(action.roomID);
|
||||
this->twitch.pubsub->signals_.moderation.userUnbanned.connect(
|
||||
[&](const auto &action) {
|
||||
auto chan =
|
||||
this->twitch.server->getChannelOrEmptyByID(action.roomID);
|
||||
|
||||
if (chan->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (chan->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto msg = Message::createUntimeoutMessage(action);
|
||||
auto msg = MessageBuilder(action).release();
|
||||
|
||||
postToThread([chan, msg] { chan->addMessage(msg); });
|
||||
});
|
||||
postToThread([chan, msg] { chan->addMessage(msg); });
|
||||
});
|
||||
|
||||
this->twitch.pubsub->start();
|
||||
|
||||
auto RequestModerationActions = [=]() {
|
||||
this->twitch.server->pubsub->unlistenAllModerationActions();
|
||||
// TODO(pajlada): Unlisten to all authed topics instead of only moderation topics
|
||||
// this->twitch.pubsub->UnlistenAllAuthedTopics();
|
||||
// TODO(pajlada): Unlisten to all authed topics instead of only
|
||||
// moderation topics this->twitch.pubsub->UnlistenAllAuthedTopics();
|
||||
|
||||
this->twitch.server->pubsub->listenToWhispers(this->accounts->twitch.getCurrent()); //
|
||||
this->twitch.server->pubsub->listenToWhispers(
|
||||
this->accounts->twitch.getCurrent()); //
|
||||
};
|
||||
|
||||
this->accounts->twitch.currentUserChanged.connect(RequestModerationActions);
|
||||
|
@ -197,39 +228,11 @@ void Application::initialize()
|
|||
RequestModerationActions();
|
||||
}
|
||||
|
||||
int Application::run(QApplication &qtApp)
|
||||
{
|
||||
// Start connecting to the IRC Servers (Twitch only for now)
|
||||
this->twitch.server->connect();
|
||||
|
||||
// Show main window
|
||||
this->windows->getMainWindow().show();
|
||||
|
||||
return qtApp.exec();
|
||||
}
|
||||
|
||||
void Application::save()
|
||||
{
|
||||
for (Singleton *singleton : this->singletons_) {
|
||||
singleton->save();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::addSingleton(Singleton *singleton)
|
||||
{
|
||||
this->singletons_.push_back(singleton);
|
||||
}
|
||||
|
||||
Application *getApp()
|
||||
{
|
||||
assert(staticApp != nullptr);
|
||||
assert(Application::instance != nullptr);
|
||||
|
||||
return staticApp;
|
||||
}
|
||||
|
||||
bool appInitialized()
|
||||
{
|
||||
return isAppInitialized;
|
||||
return Application::instance;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/Singleton.hpp"
|
||||
#include "singletons/Resources.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <memory>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class Singleton;
|
||||
|
||||
class TwitchServer;
|
||||
class PubSub;
|
||||
|
||||
|
@ -24,68 +24,69 @@ class Logging;
|
|||
class Paths;
|
||||
class AccountManager;
|
||||
class Emotes;
|
||||
class NativeMessaging;
|
||||
class Settings;
|
||||
class Fonts;
|
||||
class Resources;
|
||||
|
||||
class Application
|
||||
{
|
||||
Application(int _argc, char **_argv);
|
||||
std::vector<std::unique_ptr<Singleton>> singletons_;
|
||||
int argc_;
|
||||
char **argv_;
|
||||
|
||||
public:
|
||||
static void instantiate(int argc_, char **argv_);
|
||||
static Application *instance;
|
||||
|
||||
~Application() = delete;
|
||||
Application(Settings &settings, Paths &paths);
|
||||
|
||||
void construct();
|
||||
void initialize();
|
||||
void initialize(Settings &settings, Paths &paths);
|
||||
void load();
|
||||
void save();
|
||||
|
||||
int run(QApplication &qtApp);
|
||||
|
||||
friend void test();
|
||||
|
||||
[[deprecated("use getSettings() instead")]] Settings *settings = nullptr;
|
||||
[[deprecated("use getPaths() instead")]] Paths *paths = nullptr;
|
||||
Settings *const settings{};
|
||||
Paths *const paths{};
|
||||
Resources2 *const resources;
|
||||
|
||||
Theme *themes = nullptr;
|
||||
WindowManager *windows = nullptr;
|
||||
Logging *logging = nullptr;
|
||||
CommandController *commands = nullptr;
|
||||
HighlightController *highlights = nullptr;
|
||||
IgnoreController *ignores = nullptr;
|
||||
TaggedUsersController *taggedUsers = nullptr;
|
||||
AccountController *accounts = nullptr;
|
||||
Emotes *emotes = nullptr;
|
||||
NativeMessaging *nativeMessaging = nullptr;
|
||||
Fonts *fonts = nullptr;
|
||||
Resources *resources = nullptr;
|
||||
ModerationActions *moderationActions = nullptr;
|
||||
TwitchServer *twitch2 = nullptr;
|
||||
Theme *const themes{};
|
||||
Fonts *const fonts{};
|
||||
Emotes *const emotes{};
|
||||
WindowManager *const windows{};
|
||||
|
||||
AccountController *const accounts{};
|
||||
CommandController *const commands{};
|
||||
HighlightController *const highlights{};
|
||||
IgnoreController *const ignores{};
|
||||
TaggedUsersController *const taggedUsers{};
|
||||
ModerationActions *const moderationActions{};
|
||||
TwitchServer *const twitch2{};
|
||||
|
||||
/*[[deprecated]]*/ Logging *const logging{};
|
||||
|
||||
/// Provider-specific
|
||||
struct {
|
||||
[[deprecated("use twitch2 instead")]] TwitchServer *server = nullptr;
|
||||
[[deprecated("use twitch2->pubsub instead")]] PubSub *pubsub = nullptr;
|
||||
/*[[deprecated("use twitch2 instead")]]*/ TwitchServer *server{};
|
||||
/*[[deprecated("use twitch2->pubsub instead")]]*/ PubSub *pubsub{};
|
||||
} twitch;
|
||||
|
||||
void save();
|
||||
|
||||
// Special application mode that only initializes the native messaging host
|
||||
static void runNativeMessagingHost();
|
||||
|
||||
private:
|
||||
void addSingleton(Singleton *singleton);
|
||||
void initPubsub();
|
||||
void initNm();
|
||||
|
||||
int argc_;
|
||||
char **argv_;
|
||||
|
||||
std::vector<Singleton *> singletons_;
|
||||
template <typename T,
|
||||
typename = std::enable_if_t<std::is_base_of<Singleton, T>::value>>
|
||||
T &emplace()
|
||||
{
|
||||
auto t = new T;
|
||||
this->singletons_.push_back(std::unique_ptr<T>(t));
|
||||
return *t;
|
||||
}
|
||||
};
|
||||
|
||||
Application *getApp();
|
||||
|
||||
bool appInitialized();
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
87
src/BrowserExtension.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
#include "BrowserExtension.hpp"
|
||||
|
||||
#include "singletons/NativeMessaging.hpp"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QTimer>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
namespace {
|
||||
void initFileMode()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
#endif
|
||||
}
|
||||
|
||||
void runLoop(NativeMessagingClient &client)
|
||||
{
|
||||
while (true) {
|
||||
char size_c[4];
|
||||
std::cin.read(size_c, 4);
|
||||
|
||||
if (std::cin.eof()) break;
|
||||
|
||||
auto size = *reinterpret_cast<uint32_t *>(size_c);
|
||||
|
||||
#if 0
|
||||
bool bigEndian = isBigEndian();
|
||||
// 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[]> buffer(new char[size + 1]);
|
||||
std::cin.read(buffer.get(), size);
|
||||
*(buffer.get() + size) = '\0';
|
||||
|
||||
client.sendMessage(
|
||||
QByteArray::fromRawData(buffer.get(), static_cast<int32_t>(size)));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool shouldRunBrowserExtensionHost(const QStringList &args)
|
||||
{
|
||||
return args.size() > 0 && (args[0].startsWith("chrome-extension://") ||
|
||||
args[0].endsWith(".json"));
|
||||
}
|
||||
|
||||
void runBrowserExtensionHost()
|
||||
{
|
||||
initFileMode();
|
||||
|
||||
std::atomic<bool> ping(false);
|
||||
|
||||
QTimer timer;
|
||||
QObject::connect(&timer, &QTimer::timeout, [&ping] {
|
||||
if (!ping.exchange(false)) {
|
||||
_Exit(0);
|
||||
}
|
||||
});
|
||||
timer.setInterval(11000);
|
||||
timer.start();
|
||||
|
||||
NativeMessagingClient client;
|
||||
|
||||
runLoop(client);
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
10
src/BrowserExtension.hpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
class QStringList;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
bool shouldRunBrowserExtensionHost(const QStringList &args);
|
||||
void runBrowserExtensionHost();
|
||||
|
||||
} // namespace chatterino
|
136
src/RunGui.cpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
#include "RunGui.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
#include <QPalette>
|
||||
#include <QStyleFactory>
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "common/NetworkManager.hpp"
|
||||
#include "singletons/Paths.hpp"
|
||||
#include "singletons/Updates.hpp"
|
||||
#include "widgets/dialogs/LastRunCrashDialog.hpp"
|
||||
|
||||
#ifdef C_USE_BREAKPAD
|
||||
#include <QBreakpadHandler.h>
|
||||
#endif
|
||||
|
||||
// void initQt();
|
||||
// void installCustomPalette();
|
||||
// void showLastCrashDialog();
|
||||
// void createRunningFile(const QString &path);
|
||||
// void removeRunningFile(const QString &path);
|
||||
|
||||
namespace chatterino {
|
||||
namespace {
|
||||
void installCustomPalette()
|
||||
{
|
||||
// borrowed from
|
||||
// https://stackoverflow.com/questions/15035767/is-the-qt-5-dark-fusion-theme-available-for-windows
|
||||
auto dark = qApp->palette();
|
||||
|
||||
dark.setColor(QPalette::Window, QColor(22, 22, 22));
|
||||
dark.setColor(QPalette::WindowText, Qt::white);
|
||||
dark.setColor(QPalette::Text, Qt::white);
|
||||
dark.setColor(QPalette::Disabled, QPalette::WindowText,
|
||||
QColor(127, 127, 127));
|
||||
dark.setColor(QPalette::Base, QColor("#333"));
|
||||
dark.setColor(QPalette::AlternateBase, QColor("#444"));
|
||||
dark.setColor(QPalette::ToolTipBase, Qt::white);
|
||||
dark.setColor(QPalette::ToolTipText, Qt::white);
|
||||
dark.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127));
|
||||
dark.setColor(QPalette::Dark, QColor(35, 35, 35));
|
||||
dark.setColor(QPalette::Shadow, QColor(20, 20, 20));
|
||||
dark.setColor(QPalette::Button, QColor(70, 70, 70));
|
||||
dark.setColor(QPalette::ButtonText, Qt::white);
|
||||
dark.setColor(QPalette::Disabled, QPalette::ButtonText,
|
||||
QColor(127, 127, 127));
|
||||
dark.setColor(QPalette::BrightText, Qt::red);
|
||||
dark.setColor(QPalette::Link, QColor(42, 130, 218));
|
||||
dark.setColor(QPalette::Highlight, QColor(42, 130, 218));
|
||||
dark.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80));
|
||||
dark.setColor(QPalette::HighlightedText, Qt::white);
|
||||
dark.setColor(QPalette::Disabled, QPalette::HighlightedText,
|
||||
QColor(127, 127, 127));
|
||||
|
||||
qApp->setPalette(dark);
|
||||
}
|
||||
|
||||
void initQt()
|
||||
{
|
||||
// set up the QApplication flags
|
||||
QApplication::setAttribute(Qt::AA_Use96Dpi, true);
|
||||
#ifdef Q_OS_WIN32
|
||||
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling, true);
|
||||
#endif
|
||||
|
||||
QApplication::setStyle(QStyleFactory::create("Fusion"));
|
||||
|
||||
installCustomPalette();
|
||||
}
|
||||
|
||||
void showLastCrashDialog()
|
||||
{
|
||||
#ifndef C_DISABLE_CRASH_DIALOG
|
||||
LastRunCrashDialog dialog;
|
||||
|
||||
switch (dialog.exec()) {
|
||||
case QDialog::Accepted: {
|
||||
}; break;
|
||||
default: {
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void createRunningFile(const QString &path)
|
||||
{
|
||||
QFile runningFile(path);
|
||||
|
||||
runningFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
|
||||
runningFile.flush();
|
||||
runningFile.close();
|
||||
}
|
||||
|
||||
void removeRunningFile(const QString &path)
|
||||
{
|
||||
QFile::remove(path);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void runGui(QApplication &a, Paths &paths, Settings &settings)
|
||||
{
|
||||
initQt();
|
||||
|
||||
chatterino::NetworkManager::init();
|
||||
chatterino::Updates::getInstance().checkForUpdates();
|
||||
|
||||
#ifdef C_USE_BREAKPAD
|
||||
QBreakpadInstance.setDumpPath(app->paths->settingsFolderPath + "/Crashes");
|
||||
#endif
|
||||
|
||||
// Running file
|
||||
auto runningPath =
|
||||
paths.miscDirectory + "/running_" + paths.applicationFilePathHash;
|
||||
|
||||
if (QFile::exists(runningPath)) {
|
||||
showLastCrashDialog();
|
||||
} else {
|
||||
createRunningFile(runningPath);
|
||||
}
|
||||
|
||||
Application app(settings, paths);
|
||||
app.initialize(settings, paths);
|
||||
app.run(a);
|
||||
app.save();
|
||||
|
||||
removeRunningFile(runningPath);
|
||||
|
||||
pajlada::Settings::SettingManager::gSave();
|
||||
|
||||
chatterino::NetworkManager::deinit();
|
||||
|
||||
_exit(0);
|
||||
}
|
||||
} // namespace chatterino
|
10
src/RunGui.hpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
class QApplication;
|
||||
|
||||
namespace chatterino {
|
||||
class Paths;
|
||||
class Settings;
|
||||
|
||||
void runGui(QApplication &a, Paths &paths, Settings &settings);
|
||||
} // namespace chatterino
|
44
src/autogenerated/ResourcesAutogen.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "ResourcesAutogen.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
Resources2::Resources2()
|
||||
{
|
||||
this->avatars.fourtf = QPixmap(":/avatars/fourtf.png");
|
||||
this->avatars.pajlada = QPixmap(":/avatars/pajlada.png");
|
||||
this->buttons.ban = QPixmap(":/buttons/ban.png");
|
||||
this->buttons.banRed = QPixmap(":/buttons/banRed.png");
|
||||
this->buttons.menuDark = QPixmap(":/buttons/menuDark.png");
|
||||
this->buttons.menuLight = QPixmap(":/buttons/menuLight.png");
|
||||
this->buttons.mod = QPixmap(":/buttons/mod.png");
|
||||
this->buttons.modModeDisabled = QPixmap(":/buttons/modModeDisabled.png");
|
||||
this->buttons.modModeDisabled2 = QPixmap(":/buttons/modModeDisabled2.png");
|
||||
this->buttons.modModeEnabled = QPixmap(":/buttons/modModeEnabled.png");
|
||||
this->buttons.modModeEnabled2 = QPixmap(":/buttons/modModeEnabled2.png");
|
||||
this->buttons.timeout = QPixmap(":/buttons/timeout.png");
|
||||
this->buttons.unban = QPixmap(":/buttons/unban.png");
|
||||
this->buttons.unmod = QPixmap(":/buttons/unmod.png");
|
||||
this->buttons.update = QPixmap(":/buttons/update.png");
|
||||
this->buttons.updateError = QPixmap(":/buttons/updateError.png");
|
||||
this->error = QPixmap(":/error.png");
|
||||
this->icon = QPixmap(":/icon.png");
|
||||
this->pajaDank = QPixmap(":/pajaDank.png");
|
||||
this->settings.aboutlogo = QPixmap(":/settings/aboutlogo.png");
|
||||
this->split.down = QPixmap(":/split/down.png");
|
||||
this->split.left = QPixmap(":/split/left.png");
|
||||
this->split.move = QPixmap(":/split/move.png");
|
||||
this->split.right = QPixmap(":/split/right.png");
|
||||
this->split.up = QPixmap(":/split/up.png");
|
||||
this->twitch.admin = QPixmap(":/twitch/admin.png");
|
||||
this->twitch.broadcaster = QPixmap(":/twitch/broadcaster.png");
|
||||
this->twitch.cheer1 = QPixmap(":/twitch/cheer1.png");
|
||||
this->twitch.globalmod = QPixmap(":/twitch/globalmod.png");
|
||||
this->twitch.moderator = QPixmap(":/twitch/moderator.png");
|
||||
this->twitch.prime = QPixmap(":/twitch/prime.png");
|
||||
this->twitch.staff = QPixmap(":/twitch/staff.png");
|
||||
this->twitch.subscriber = QPixmap(":/twitch/subscriber.png");
|
||||
this->twitch.turbo = QPixmap(":/twitch/turbo.png");
|
||||
this->twitch.verified = QPixmap(":/twitch/verified.png");
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
58
src/autogenerated/ResourcesAutogen.hpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
#include <QPixmap>
|
||||
#include "common/Singleton.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class Resources2 : public Singleton
|
||||
{
|
||||
public:
|
||||
Resources2();
|
||||
|
||||
struct {
|
||||
QPixmap fourtf;
|
||||
QPixmap pajlada;
|
||||
} avatars;
|
||||
struct {
|
||||
QPixmap ban;
|
||||
QPixmap banRed;
|
||||
QPixmap menuDark;
|
||||
QPixmap menuLight;
|
||||
QPixmap mod;
|
||||
QPixmap modModeDisabled;
|
||||
QPixmap modModeDisabled2;
|
||||
QPixmap modModeEnabled;
|
||||
QPixmap modModeEnabled2;
|
||||
QPixmap timeout;
|
||||
QPixmap unban;
|
||||
QPixmap unmod;
|
||||
QPixmap update;
|
||||
QPixmap updateError;
|
||||
} buttons;
|
||||
QPixmap error;
|
||||
QPixmap icon;
|
||||
QPixmap pajaDank;
|
||||
struct {
|
||||
QPixmap aboutlogo;
|
||||
} settings;
|
||||
struct {
|
||||
QPixmap down;
|
||||
QPixmap left;
|
||||
QPixmap move;
|
||||
QPixmap right;
|
||||
QPixmap up;
|
||||
} split;
|
||||
struct {
|
||||
QPixmap admin;
|
||||
QPixmap broadcaster;
|
||||
QPixmap cheer1;
|
||||
QPixmap globalmod;
|
||||
QPixmap moderator;
|
||||
QPixmap prime;
|
||||
QPixmap staff;
|
||||
QPixmap subscriber;
|
||||
QPixmap turbo;
|
||||
QPixmap verified;
|
||||
} twitch;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
34
src/common/Aliases.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
#include <functional>
|
||||
|
||||
#define QStringAlias(name) \
|
||||
namespace chatterino { \
|
||||
struct name { \
|
||||
QString string; \
|
||||
bool operator==(const name &other) const \
|
||||
{ \
|
||||
return this->string == other.string; \
|
||||
} \
|
||||
bool operator!=(const name &other) const \
|
||||
{ \
|
||||
return this->string != other.string; \
|
||||
} \
|
||||
}; \
|
||||
} /* namespace chatterino */ \
|
||||
namespace std { \
|
||||
template <> \
|
||||
struct hash<chatterino::name> { \
|
||||
size_t operator()(const chatterino::name &s) const \
|
||||
{ \
|
||||
return qHash(s.string); \
|
||||
} \
|
||||
}; \
|
||||
} /* namespace std */
|
||||
|
||||
QStringAlias(UserName);
|
||||
QStringAlias(UserId);
|
||||
QStringAlias(Url);
|
||||
QStringAlias(Tooltip);
|
|
@ -6,14 +6,14 @@
|
|||
namespace chatterino {
|
||||
|
||||
template <typename T>
|
||||
class MutexValue : boost::noncopyable
|
||||
class Atomic : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
MutexValue()
|
||||
Atomic()
|
||||
{
|
||||
}
|
||||
|
||||
MutexValue(T &&val)
|
||||
Atomic(T &&val)
|
||||
: value_(val)
|
||||
{
|
||||
}
|
||||
|
@ -32,6 +32,13 @@ public:
|
|||
this->value_ = val;
|
||||
}
|
||||
|
||||
void set(T &&val)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(this->mutex_);
|
||||
|
||||
this->value_ = std::move(val);
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::mutex mutex_;
|
||||
T value_;
|
|
@ -3,6 +3,7 @@
|
|||
#include "Application.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "singletons/Emotes.hpp"
|
||||
#include "singletons/Logging.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
|
@ -17,14 +18,18 @@
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
Channel::Channel(const QString &_name, Type type)
|
||||
: name(_name)
|
||||
, completionModel(this->name)
|
||||
//
|
||||
// Channel
|
||||
//
|
||||
Channel::Channel(const QString &name, Type type)
|
||||
: completionModel(name)
|
||||
, name_(name)
|
||||
, type_(type)
|
||||
{
|
||||
QObject::connect(&this->clearCompletionModelTimer_, &QTimer::timeout, [this]() {
|
||||
this->completionModel.clearExpiredStrings(); //
|
||||
});
|
||||
QObject::connect(&this->clearCompletionModelTimer_, &QTimer::timeout,
|
||||
[this]() {
|
||||
this->completionModel.clearExpiredStrings(); //
|
||||
});
|
||||
this->clearCompletionModelTimer_.start(60 * 1000);
|
||||
}
|
||||
|
||||
|
@ -38,6 +43,11 @@ Channel::Type Channel::getType() const
|
|||
return this->type_;
|
||||
}
|
||||
|
||||
const QString &Channel::getName() const
|
||||
{
|
||||
return this->name_;
|
||||
}
|
||||
|
||||
bool Channel::isTwitchChannel() const
|
||||
{
|
||||
return this->type_ >= Type::Twitch && this->type_ < Type::TwitchEnd;
|
||||
|
@ -45,7 +55,7 @@ bool Channel::isTwitchChannel() const
|
|||
|
||||
bool Channel::isEmpty() const
|
||||
{
|
||||
return this->name.isEmpty();
|
||||
return this->name_.isEmpty();
|
||||
}
|
||||
|
||||
LimitedQueueSnapshot<MessagePtr> Channel::getMessageSnapshot()
|
||||
|
@ -60,13 +70,13 @@ void Channel::addMessage(MessagePtr message)
|
|||
|
||||
const QString &username = message->loginName;
|
||||
if (!username.isEmpty()) {
|
||||
// TODO: Add recent chatters display name. This should maybe be a setting
|
||||
// TODO: Add recent chatters display name
|
||||
this->addRecentChatter(message);
|
||||
}
|
||||
|
||||
// FOURTF: change this when adding more providers
|
||||
if (this->isTwitchChannel()) {
|
||||
app->logging->addMessage(this->name, message);
|
||||
app->logging->addMessage(this->name_, message);
|
||||
}
|
||||
|
||||
if (this->messages_.pushBack(message, deleted)) {
|
||||
|
@ -90,37 +100,43 @@ void Channel::addOrReplaceTimeout(MessagePtr message)
|
|||
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) {
|
||||
if (s->flags.has(MessageFlag::Untimeout) &&
|
||||
s->timeoutUser == message->timeoutUser) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (s->flags.HasFlag(Message::Timeout) && s->timeoutUser == message->timeoutUser) {
|
||||
if (message->flags.HasFlag(Message::PubSub) && !s->flags.HasFlag(Message::PubSub)) {
|
||||
if (s->flags.has(MessageFlag::Timeout) &&
|
||||
s->timeoutUser == message->timeoutUser) //
|
||||
{
|
||||
if (message->flags.has(MessageFlag::PubSub) &&
|
||||
!s->flags.has(MessageFlag::PubSub)) //
|
||||
{
|
||||
this->replaceMessage(s, message);
|
||||
addMessage = false;
|
||||
break;
|
||||
}
|
||||
if (!message->flags.HasFlag(Message::PubSub) && s->flags.HasFlag(Message::PubSub)) {
|
||||
if (!message->flags.has(MessageFlag::PubSub) &&
|
||||
s->flags.has(MessageFlag::PubSub)) //
|
||||
{
|
||||
addMessage = false;
|
||||
break;
|
||||
}
|
||||
|
||||
int count = s->count + 1;
|
||||
|
||||
MessagePtr replacement(Message::createSystemMessage(
|
||||
message->searchText + QString(" (") + QString::number(count) + " times)"));
|
||||
MessageBuilder replacement(systemMessage,
|
||||
message->searchText + QString(" (") +
|
||||
QString::number(count) + " times)");
|
||||
|
||||
replacement->timeoutUser = message->timeoutUser;
|
||||
replacement->count = count;
|
||||
replacement->flags = message->flags;
|
||||
|
||||
this->replaceMessage(s, replacement);
|
||||
this->replaceMessage(s, replacement.release());
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -129,9 +145,10 @@ void Channel::addOrReplaceTimeout(MessagePtr message)
|
|||
// disable the messages from the user
|
||||
for (int i = 0; i < snapshotLength; i++) {
|
||||
auto &s = snapshot[i];
|
||||
if ((s->flags & (Message::Timeout | Message::Untimeout)) == 0 &&
|
||||
if (s->flags.hasNone({MessageFlag::Timeout, MessageFlag::Untimeout}) &&
|
||||
s->loginName == message->timeoutUser) {
|
||||
s->flags.EnableFlag(Message::Disabled);
|
||||
// FOURTF: disabled for now
|
||||
// s->flags.EnableFlag(MessageFlag::Disabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,17 +166,19 @@ void Channel::disableAllMessages()
|
|||
int snapshotLength = snapshot.getLength();
|
||||
for (int i = 0; i < snapshotLength; i++) {
|
||||
auto &s = snapshot[i];
|
||||
if (s->flags & Message::System || s->flags & Message::Timeout) {
|
||||
if (s->flags.hasAny({MessageFlag::System, MessageFlag::Timeout})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
s->flags.EnableFlag(Message::Disabled);
|
||||
// FOURTF: disabled for now
|
||||
// s->flags.EnableFlag(MessageFlag::Disabled);
|
||||
}
|
||||
}
|
||||
|
||||
void Channel::addMessagesAtStart(std::vector<MessagePtr> &_messages)
|
||||
{
|
||||
std::vector<MessagePtr> addedMessages = this->messages_.pushFront(_messages);
|
||||
std::vector<MessagePtr> addedMessages =
|
||||
this->messages_.pushFront(_messages);
|
||||
|
||||
if (addedMessages.size() != 0) {
|
||||
this->messagesAddedAtStart.invoke(addedMessages);
|
||||
|
@ -175,9 +194,8 @@ void Channel::replaceMessage(MessagePtr message, MessagePtr replacement)
|
|||
}
|
||||
}
|
||||
|
||||
void Channel::addRecentChatter(const std::shared_ptr<Message> &message)
|
||||
void Channel::addRecentChatter(const MessagePtr &message)
|
||||
{
|
||||
// Do nothing by default
|
||||
}
|
||||
|
||||
bool Channel::canSendMessage() const
|
||||
|
@ -215,4 +233,45 @@ void Channel::onConnected()
|
|||
{
|
||||
}
|
||||
|
||||
//
|
||||
// Indirect channel
|
||||
//
|
||||
IndirectChannel::Data::Data(ChannelPtr _channel, Channel::Type _type)
|
||||
: channel(_channel)
|
||||
, type(_type)
|
||||
{
|
||||
}
|
||||
|
||||
IndirectChannel::IndirectChannel(ChannelPtr channel, Channel::Type type)
|
||||
: data_(std::make_unique<Data>(channel, type))
|
||||
{
|
||||
}
|
||||
|
||||
ChannelPtr IndirectChannel::get()
|
||||
{
|
||||
return data_->channel;
|
||||
}
|
||||
|
||||
void IndirectChannel::reset(ChannelPtr channel)
|
||||
{
|
||||
assert(this->data_->type != Channel::Type::Direct);
|
||||
|
||||
this->data_->channel = channel;
|
||||
this->data_->changed.invoke();
|
||||
}
|
||||
|
||||
pajlada::Signals::NoArgSignal &IndirectChannel::getChannelChanged()
|
||||
{
|
||||
return this->data_->changed;
|
||||
}
|
||||
|
||||
Channel::Type IndirectChannel::getType()
|
||||
{
|
||||
if (this->data_->type == Channel::Type::Direct) {
|
||||
return this->get()->getType();
|
||||
} else {
|
||||
return this->data_->type;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "messages/Image.hpp"
|
||||
#include "messages/LimitedQueue.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
#include "util/ConcurrentMap.hpp"
|
||||
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
|
@ -32,7 +31,8 @@ public:
|
|||
explicit Channel(const QString &name, Type type);
|
||||
virtual ~Channel();
|
||||
|
||||
pajlada::Signals::Signal<const QString &, const QString &, bool &> sendMessageSignal;
|
||||
pajlada::Signals::Signal<const QString &, const QString &, bool &>
|
||||
sendMessageSignal;
|
||||
|
||||
pajlada::Signals::Signal<MessagePtr &> messageRemovedFromStart;
|
||||
pajlada::Signals::Signal<MessagePtr &> messageAppended;
|
||||
|
@ -41,6 +41,7 @@ public:
|
|||
pajlada::Signals::NoArgSignal destroyed;
|
||||
|
||||
Type getType() const;
|
||||
const QString &getName() const;
|
||||
bool isTwitchChannel() const;
|
||||
virtual bool isEmpty() const;
|
||||
LimitedQueueSnapshot<MessagePtr> getMessageSnapshot();
|
||||
|
@ -50,9 +51,8 @@ public:
|
|||
void addOrReplaceTimeout(MessagePtr message);
|
||||
void disableAllMessages();
|
||||
void replaceMessage(MessagePtr message, MessagePtr replacement);
|
||||
virtual void addRecentChatter(const std::shared_ptr<Message> &message);
|
||||
virtual void addRecentChatter(const MessagePtr &message);
|
||||
|
||||
QString name;
|
||||
QStringList modList;
|
||||
|
||||
virtual bool canSendMessage() const;
|
||||
|
@ -69,6 +69,7 @@ protected:
|
|||
virtual void onConnected();
|
||||
|
||||
private:
|
||||
const QString name_;
|
||||
LimitedQueue<MessagePtr> messages_;
|
||||
Type type_;
|
||||
QTimer clearCompletionModelTimer_;
|
||||
|
@ -83,46 +84,17 @@ class IndirectChannel
|
|||
Channel::Type type;
|
||||
pajlada::Signals::NoArgSignal changed;
|
||||
|
||||
Data() = delete;
|
||||
Data(ChannelPtr _channel, Channel::Type _type)
|
||||
: channel(_channel)
|
||||
, type(_type)
|
||||
{
|
||||
}
|
||||
Data(ChannelPtr channel, Channel::Type type);
|
||||
};
|
||||
|
||||
public:
|
||||
IndirectChannel(ChannelPtr channel, Channel::Type type = Channel::Type::Direct)
|
||||
: data_(new Data(channel, type))
|
||||
{
|
||||
}
|
||||
IndirectChannel(ChannelPtr channel,
|
||||
Channel::Type type = Channel::Type::Direct);
|
||||
|
||||
ChannelPtr get()
|
||||
{
|
||||
return data_->channel;
|
||||
}
|
||||
|
||||
void update(ChannelPtr ptr)
|
||||
{
|
||||
assert(this->data_->type != Channel::Type::Direct);
|
||||
|
||||
this->data_->channel = ptr;
|
||||
this->data_->changed.invoke();
|
||||
}
|
||||
|
||||
pajlada::Signals::NoArgSignal &getChannelChanged()
|
||||
{
|
||||
return this->data_->changed;
|
||||
}
|
||||
|
||||
Channel::Type getType()
|
||||
{
|
||||
if (this->data_->type == Channel::Type::Direct) {
|
||||
return this->get()->getType();
|
||||
} else {
|
||||
return this->data_->type;
|
||||
}
|
||||
}
|
||||
ChannelPtr get();
|
||||
void reset(ChannelPtr channel);
|
||||
pajlada::Signals::NoArgSignal &getChannelChanged();
|
||||
Channel::Type getType();
|
||||
|
||||
private:
|
||||
std::shared_ptr<Data> data_;
|
||||
|
|
|
@ -41,7 +41,7 @@ public:
|
|||
using pajlada::Settings::Setting<Type>::operator==;
|
||||
using pajlada::Settings::Setting<Type>::operator!=;
|
||||
|
||||
using pajlada::Settings::Setting<Type>::operator const Type;
|
||||
using pajlada::Settings::Setting<Type>::operator Type;
|
||||
};
|
||||
|
||||
using BoolSetting = ChatterinoSetting<bool>;
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/Aliases.hpp"
|
||||
#include "common/Outcome.hpp"
|
||||
#include "common/ProviderId.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/preprocessor.hpp>
|
||||
|
||||
#include <string>
|
||||
|
@ -21,20 +25,18 @@ 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 showSplitOverlayModifiers =
|
||||
Qt::ControlModifier | Qt::AltModifier;
|
||||
const Qt::KeyboardModifiers showAddSplitRegions =
|
||||
Qt::ControlModifier | Qt::AltModifier;
|
||||
const Qt::KeyboardModifiers showResizeHandlesModifiers = Qt::ControlModifier;
|
||||
|
||||
static const char *ANONYMOUS_USERNAME_LABEL ATTR_UNUSED = " - anonymous - ";
|
||||
|
||||
#define return_if(x) \
|
||||
if ((x)) { \
|
||||
return; \
|
||||
}
|
||||
|
||||
#define return_unless(x) \
|
||||
if (!(x)) { \
|
||||
return; \
|
||||
}
|
||||
template <typename T>
|
||||
std::weak_ptr<T> weakOf(T *element)
|
||||
{
|
||||
return element->shared_from_this();
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
#include "Application.hpp"
|
||||
#include "common/Common.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandController.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "providers/twitch/TwitchServer.hpp"
|
||||
#include "singletons/Emotes.hpp"
|
||||
|
||||
#include <QtAlgorithms>
|
||||
|
@ -102,46 +104,57 @@ int CompletionModel::rowCount(const QModelIndex &) const
|
|||
|
||||
void CompletionModel::refresh()
|
||||
{
|
||||
Log("[CompletionModel:{}] Refreshing...]", this->channelName_);
|
||||
log("[CompletionModel:{}] Refreshing...]", this->channelName_);
|
||||
|
||||
auto app = getApp();
|
||||
|
||||
// User-specific: Twitch Emotes
|
||||
// TODO: Fix this so it properly updates with the proper api. oauth token needs proper scope
|
||||
for (const auto &m : app->emotes->twitch.emotes) {
|
||||
for (const auto &emoteName : m.second.emoteCodes) {
|
||||
// XXX: No way to discern between a twitch global emote and sub emote right now
|
||||
this->addString(emoteName, TaggedString::Type::TwitchGlobalEmote);
|
||||
if (auto account = app->accounts->twitch.getCurrent()) {
|
||||
for (const auto &emote : account->accessEmotes()->allEmoteNames) {
|
||||
// XXX: No way to discern between a twitch global emote and sub
|
||||
// emote right now
|
||||
this->addString(emote.string,
|
||||
TaggedString::Type::TwitchGlobalEmote);
|
||||
}
|
||||
}
|
||||
|
||||
// Global: BTTV Global Emotes
|
||||
std::vector<QString> &bttvGlobalEmoteCodes = app->emotes->bttv.globalEmoteCodes;
|
||||
for (const auto &m : bttvGlobalEmoteCodes) {
|
||||
this->addString(m, TaggedString::Type::BTTVGlobalEmote);
|
||||
// // Global: BTTV Global Emotes
|
||||
// std::vector<QString> &bttvGlobalEmoteCodes =
|
||||
// app->emotes->bttv.globalEmoteNames_; for (const auto &m :
|
||||
// bttvGlobalEmoteCodes) {
|
||||
// this->addString(m, TaggedString::Type::BTTVGlobalEmote);
|
||||
// }
|
||||
|
||||
// // Global: FFZ Global Emotes
|
||||
// std::vector<QString> &ffzGlobalEmoteCodes =
|
||||
// app->emotes->ffz.globalEmoteCodes; for (const auto &m :
|
||||
// ffzGlobalEmoteCodes) {
|
||||
// this->addString(m, TaggedString::Type::FFZGlobalEmote);
|
||||
// }
|
||||
|
||||
// Channel emotes
|
||||
if (auto channel = dynamic_cast<TwitchChannel *>(
|
||||
getApp()
|
||||
->twitch2->getChannelOrEmptyByID(this->channelName_)
|
||||
.get())) {
|
||||
auto bttv = channel->bttvEmotes();
|
||||
// auto it = bttv->begin();
|
||||
// for (const auto &emote : *bttv) {
|
||||
// }
|
||||
// std::vector<QString> &bttvChannelEmoteCodes =
|
||||
// app->emotes->bttv.channelEmoteName_[this->channelName_];
|
||||
// for (const auto &m : bttvChannelEmoteCodes) {
|
||||
// this->addString(m, TaggedString::Type::BTTVChannelEmote);
|
||||
// }
|
||||
|
||||
// Channel-specific: FFZ Channel Emotes
|
||||
for (const auto &emote : *channel->ffzEmotes()) {
|
||||
this->addString(emote.second->name.string,
|
||||
TaggedString::Type::FFZChannelEmote);
|
||||
}
|
||||
}
|
||||
|
||||
// Global: FFZ Global Emotes
|
||||
std::vector<QString> &ffzGlobalEmoteCodes = app->emotes->ffz.globalEmoteCodes;
|
||||
for (const auto &m : ffzGlobalEmoteCodes) {
|
||||
this->addString(m, TaggedString::Type::FFZGlobalEmote);
|
||||
}
|
||||
|
||||
// Channel-specific: BTTV Channel Emotes
|
||||
std::vector<QString> &bttvChannelEmoteCodes =
|
||||
app->emotes->bttv.channelEmoteCodes[this->channelName_];
|
||||
for (const auto &m : bttvChannelEmoteCodes) {
|
||||
this->addString(m, TaggedString::Type::BTTVChannelEmote);
|
||||
}
|
||||
|
||||
// Channel-specific: FFZ Channel Emotes
|
||||
std::vector<QString> &ffzChannelEmoteCodes =
|
||||
app->emotes->ffz.channelEmoteCodes[this->channelName_];
|
||||
for (const auto &m : ffzChannelEmoteCodes) {
|
||||
this->addString(m, TaggedString::Type::FFZChannelEmote);
|
||||
}
|
||||
|
||||
// Global: Emojis
|
||||
// Emojis
|
||||
const auto &emojiShortCodes = app->emotes->emojis.shortCodes;
|
||||
for (const auto &m : emojiShortCodes) {
|
||||
this->addString(":" + m + ":", TaggedString::Type::Emoji);
|
||||
|
@ -158,7 +171,8 @@ void CompletionModel::refresh()
|
|||
|
||||
// Channel-specific: Usernames
|
||||
// fourtf: only works with twitch chat
|
||||
// auto c = ChannelManager::getInstance().getTwitchChannel(this->channelName);
|
||||
// auto c =
|
||||
// ChannelManager::getInstance().getTwitchChannel(this->channelName);
|
||||
// auto usernames = c->getUsernamesForCompletions();
|
||||
// for (const auto &name : usernames) {
|
||||
// assert(!name.displayName.isEmpty());
|
||||
|
@ -185,9 +199,11 @@ void CompletionModel::addUser(const QString &username)
|
|||
auto add = [this](const QString &str) {
|
||||
auto ts = this->createUser(str + " ");
|
||||
// Always add a space at the end of completions
|
||||
std::pair<std::set<TaggedString>::iterator, bool> p = this->emotes_.insert(ts);
|
||||
std::pair<std::set<TaggedString>::iterator, bool> p =
|
||||
this->emotes_.insert(ts);
|
||||
if (!p.second) {
|
||||
// No inseration was made, figure out if we need to replace the username.
|
||||
// No inseration was made, figure out if we need to replace the
|
||||
// username.
|
||||
|
||||
if (p.first->str > ts.str) {
|
||||
// Replace lowercase version of name with mixed-case version
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
class TwitchChannel;
|
||||
|
||||
class CompletionModel : public QAbstractListModel
|
||||
{
|
||||
struct TaggedString {
|
||||
|
|
|
@ -65,7 +65,8 @@ public:
|
|||
this->data.insert(name, value);
|
||||
}
|
||||
|
||||
void each(std::function<void(const TKey &name, const TValue &value)> func) const
|
||||
void each(
|
||||
std::function<void(const TKey &name, const TValue &value)> func) const
|
||||
{
|
||||
QMutexLocker lock(&this->mutex);
|
||||
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
#include "Emotemap.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
EmoteData::EmoteData(Image *image)
|
||||
: image1x(image)
|
||||
{
|
||||
}
|
||||
|
||||
// Emotes must have a 1x image to be valid
|
||||
bool EmoteData::isValid() const
|
||||
{
|
||||
return this->image1x != nullptr;
|
||||
}
|
||||
|
||||
Image *EmoteData::getImage(float scale) const
|
||||
{
|
||||
int quality = getApp()->settings->preferredEmoteQuality;
|
||||
|
||||
if (quality == 0) {
|
||||
scale *= getApp()->settings->emoteScale.getValue();
|
||||
quality = [&] {
|
||||
if (scale <= 1)
|
||||
return 1;
|
||||
if (scale <= 2)
|
||||
return 2;
|
||||
return 3;
|
||||
}();
|
||||
}
|
||||
|
||||
Image *_image;
|
||||
if (quality == 3 && this->image3x != nullptr) {
|
||||
_image = this->image3x;
|
||||
} else if (quality >= 2 && this->image2x != nullptr) {
|
||||
_image = this->image2x;
|
||||
} else {
|
||||
_image = this->image1x;
|
||||
}
|
||||
|
||||
return _image;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,27 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "messages/Image.hpp"
|
||||
#include "util/ConcurrentMap.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
struct EmoteData {
|
||||
EmoteData() = default;
|
||||
|
||||
EmoteData(Image *image);
|
||||
|
||||
// Emotes must have a 1x image to be valid
|
||||
bool isValid() const;
|
||||
Image *getImage(float scale) const;
|
||||
|
||||
// Link to the emote page i.e. https://www.frankerfacez.com/emoticon/144722-pajaCringe
|
||||
QString pageLink;
|
||||
|
||||
Image *image1x = nullptr;
|
||||
Image *image2x = nullptr;
|
||||
Image *image3x = nullptr;
|
||||
};
|
||||
|
||||
using EmoteMap = ConcurrentMap<QString, EmoteData>;
|
||||
|
||||
} // namespace chatterino
|
|
@ -4,62 +4,78 @@
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
// = std::enable_if<std::is_enum<T>::value>::type
|
||||
|
||||
template <typename T, typename Q = typename std::underlying_type<T>::type>
|
||||
class FlagsEnum
|
||||
{
|
||||
public:
|
||||
FlagsEnum()
|
||||
: value(static_cast<T>(0))
|
||||
: value_(static_cast<T>(0))
|
||||
{
|
||||
}
|
||||
|
||||
FlagsEnum(T _value)
|
||||
: value(_value)
|
||||
FlagsEnum(T value)
|
||||
: value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
inline T operator~() const
|
||||
FlagsEnum(std::initializer_list<T> flags)
|
||||
{
|
||||
return (T) ~(Q)this->value;
|
||||
}
|
||||
inline T operator|(Q a) const
|
||||
{
|
||||
return (T)((Q)a | (Q)this->value);
|
||||
}
|
||||
inline T operator&(Q a) const
|
||||
{
|
||||
return (T)((Q)a & (Q)this->value);
|
||||
}
|
||||
inline T operator^(Q a) const
|
||||
{
|
||||
return (T)((Q)a ^ (Q)this->value);
|
||||
}
|
||||
inline T &operator|=(const Q &a)
|
||||
{
|
||||
return (T &)((Q &)this->value |= (Q)a);
|
||||
}
|
||||
inline T &operator&=(const Q &a)
|
||||
{
|
||||
return (T &)((Q &)this->value &= (Q)a);
|
||||
}
|
||||
inline T &operator^=(const Q &a)
|
||||
{
|
||||
return (T &)((Q &)this->value ^= (Q)a);
|
||||
for (auto flag : flags) {
|
||||
this->set(flag);
|
||||
}
|
||||
}
|
||||
|
||||
void EnableFlag(T flag)
|
||||
bool operator==(const FlagsEnum<T> &other)
|
||||
{
|
||||
reinterpret_cast<Q &>(this->value) |= static_cast<Q>(flag);
|
||||
return this->value_ == other.value_;
|
||||
}
|
||||
|
||||
bool HasFlag(Q flag) const
|
||||
bool operator!=(const FlagsEnum &other)
|
||||
{
|
||||
return (this->value & flag) == flag;
|
||||
return this->value_ != other.value_;
|
||||
}
|
||||
|
||||
T value;
|
||||
void set(T flag)
|
||||
{
|
||||
reinterpret_cast<Q &>(this->value_) |= static_cast<Q>(flag);
|
||||
}
|
||||
|
||||
void unset(T flag)
|
||||
{
|
||||
reinterpret_cast<Q &>(this->value_) &= ~static_cast<Q>(flag);
|
||||
}
|
||||
|
||||
void set(T flag, bool value)
|
||||
{
|
||||
if (value)
|
||||
this->set(flag);
|
||||
else
|
||||
this->unset(flag);
|
||||
}
|
||||
|
||||
bool has(T flag) const
|
||||
{
|
||||
return static_cast<Q>(this->value_) & static_cast<Q>(flag);
|
||||
}
|
||||
|
||||
bool hasAny(FlagsEnum flags) const
|
||||
{
|
||||
return static_cast<Q>(this->value_) & static_cast<Q>(flags.value_);
|
||||
}
|
||||
|
||||
bool hasAll(FlagsEnum<T> flags) const
|
||||
{
|
||||
return (static_cast<Q>(this->value_) & static_cast<Q>(flags.value_)) &&
|
||||
static_cast<Q>(flags->value);
|
||||
}
|
||||
|
||||
bool hasNone(std::initializer_list<T> flags) const
|
||||
{
|
||||
return !this->hasAny(flags);
|
||||
}
|
||||
|
||||
private:
|
||||
T value_{};
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -5,58 +5,63 @@
|
|||
#include <QString>
|
||||
#include <QTextStream>
|
||||
|
||||
// ip 0.0.0.0 - 224.0.0.0
|
||||
#define IP \
|
||||
"(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" \
|
||||
"(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" \
|
||||
"(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))"
|
||||
#define PORT "(?::\\d{2,5})"
|
||||
#define WEB_CHAR1 "[_a-z\\x{00a1}-\\x{ffff}0-9]"
|
||||
#define WEB_CHAR2 "[a-z\\x{00a1}-\\x{ffff}0-9]"
|
||||
|
||||
#define SPOTIFY_1 "(?:artist|album|track|user:[^:]+:playlist):[a-zA-Z0-9]+"
|
||||
#define SPOTIFY_2 "user:[^:]+"
|
||||
#define SPOTIFY_3 "search:(?:[-\\w$\\.+!*'(),]+|%[a-fA-F0-9]{2})+"
|
||||
#define SPOTIFY_PARAMS "(?:" SPOTIFY_1 "|" SPOTIFY_2 "|" SPOTIFY_3 ")"
|
||||
#define SPOTIFY_LINK "(?x-mi:(spotify:" SPOTIFY_PARAMS "))"
|
||||
|
||||
#define WEB_PROTOCOL "(?:(?:https?|ftps?)://)?"
|
||||
#define WEB_USER "(?:\\S+(?::\\S*)?@)?"
|
||||
#define WEB_HOST "(?:(?:" WEB_CHAR1 "-*)*" WEB_CHAR2 "+)"
|
||||
#define WEB_DOMAIN "(?:\\.(?:" WEB_CHAR2 "-*)*" WEB_CHAR2 "+)*"
|
||||
#define WEB_TLD "(?:" + tldData + ")"
|
||||
#define WEB_RESOURCE_PATH "(?:[/?#]\\S*)"
|
||||
#define WEB_LINK \
|
||||
WEB_PROTOCOL WEB_USER "(?:" IP "|" WEB_HOST WEB_DOMAIN "\\." WEB_TLD PORT \
|
||||
"?" WEB_RESOURCE_PATH "?)"
|
||||
|
||||
#define LINK "^(?:" SPOTIFY_LINK "|" WEB_LINK ")$"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
LinkParser::LinkParser(const QString &unparsedString)
|
||||
{
|
||||
static QRegularExpression linkRegex = [] {
|
||||
static QRegularExpression newLineRegex("\r?\n");
|
||||
QFile tldFile(":/tlds.txt");
|
||||
tldFile.open(QFile::ReadOnly);
|
||||
QFile file(":/tlds.txt");
|
||||
file.open(QFile::ReadOnly);
|
||||
QTextStream tlds(&file);
|
||||
tlds.setCodec("UTF-8");
|
||||
|
||||
QTextStream t1(&tldFile);
|
||||
t1.setCodec("UTF-8");
|
||||
// tldData gets injected into the LINK macro
|
||||
auto tldData = tlds.readAll().replace(newLineRegex, "|");
|
||||
(void)tldData;
|
||||
|
||||
// Read the TLDs in and replace the newlines with pipes
|
||||
QString tldData = t1.readAll().replace(newLineRegex, "|");
|
||||
|
||||
const QString urlRegExp =
|
||||
"^"
|
||||
// protocol identifier
|
||||
"(?:(?:https?|ftps?)://)?"
|
||||
// user:pass authentication
|
||||
"(?:\\S+(?::\\S*)?@)?"
|
||||
"(?:"
|
||||
// IP address dotted notation octets
|
||||
// excludes loopback network 0.0.0.0
|
||||
// excludes reserved space >= 224.0.0.0
|
||||
// excludes network & broacast addresses
|
||||
// (first & last IP address of each class)
|
||||
"(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])"
|
||||
"(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}"
|
||||
"(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))"
|
||||
"|"
|
||||
// host name
|
||||
"(?:(?:[_a-z\\x{00a1}-\\x{ffff}0-9]-*)*[a-z\\x{00a1}-\\x{ffff}0-9]+)"
|
||||
// domain name
|
||||
"(?:\\.(?:[a-z\\x{00a1}-\\x{ffff}0-9]-*)*[a-z\\x{00a1}-\\x{ffff}0-9]+)*"
|
||||
// TLD identifier
|
||||
//"(?:\\.(?:[a-z\\x{00a1}-\\x{ffff}]{2,}))"
|
||||
"(?:[\\.](?:" +
|
||||
tldData +
|
||||
"))"
|
||||
"\\.?"
|
||||
")"
|
||||
// port number
|
||||
"(?::\\d{2,5})?"
|
||||
// resource path
|
||||
"(?:[/?#]\\S*)?"
|
||||
"$";
|
||||
|
||||
return QRegularExpression(urlRegExp, QRegularExpression::CaseInsensitiveOption);
|
||||
return QRegularExpression(LINK,
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
}();
|
||||
|
||||
this->match_ = linkRegex.match(unparsedString);
|
||||
}
|
||||
|
||||
bool LinkParser::hasMatch() const
|
||||
{
|
||||
return this->match_.hasMatch();
|
||||
}
|
||||
|
||||
QString LinkParser::getCaptured() const
|
||||
{
|
||||
return this->match_.captured();
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -10,15 +10,8 @@ class LinkParser
|
|||
public:
|
||||
explicit LinkParser(const QString &unparsedString);
|
||||
|
||||
bool hasMatch() const
|
||||
{
|
||||
return this->match_.hasMatch();
|
||||
}
|
||||
|
||||
QString getCaptured() const
|
||||
{
|
||||
return this->match_.captured();
|
||||
}
|
||||
bool hasMatch() const;
|
||||
QString getCaptured() const;
|
||||
|
||||
private:
|
||||
QRegularExpressionMatch match_;
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
template <typename Type>
|
||||
class LockedObject
|
||||
{
|
||||
public:
|
||||
LockedObject &operator=(const LockedObject<Type> &other)
|
||||
{
|
||||
this->mutex_.lock();
|
||||
|
||||
this->data = other.getValue();
|
||||
|
||||
this->mutex_.unlock();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
LockedObject &operator=(const Type &other)
|
||||
{
|
||||
this->mutex_.lock();
|
||||
|
||||
this->data = other;
|
||||
|
||||
this->mutex_.unlock();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
Type value_;
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -2,13 +2,15 @@
|
|||
|
||||
#include <functional>
|
||||
|
||||
#include "Common.hpp"
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class NetworkResult;
|
||||
|
||||
using NetworkSuccessCallback = std::function<bool(NetworkResult)>;
|
||||
using NetworkSuccessCallback = std::function<Outcome(NetworkResult)>;
|
||||
using NetworkErrorCallback = std::function<bool(int)>;
|
||||
using NetworkReplyCreatedCallback = std::function<void(QNetworkReply *)>;
|
||||
|
||||
|
|