From: Manuel Nickschas Date: Wed, 18 Feb 2015 20:03:51 +0000 (+0100) Subject: Merge pull request #107 from siduction-upstream/master X-Git-Tag: 0.12-beta1~5 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=eaa1bd30bc088e5cae6d8a742d7aedb3d8ff1897;hp=59695317b057f47ce620e99ac368e3b9b0accf8a Merge pull request #107 from siduction-upstream/master Set GIT_HEAD and GIT_DESCRIBE from environment --- diff --git a/.travis.yml b/.travis.yml index defbb623..476319e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,4 +26,9 @@ script: - cd build - if [ "$QT_VERSION" = "qt4" ]; then cmake ..; fi - if [ "$QT_VERSION" = "qt5" ]; then source /opt/qt52/bin/qt52-env.sh && cmake -DUSE_QT5=ON ..; fi - - make \ No newline at end of file + - make + +matrix: + exclude: + - compiler: clang + env: QT_VERSION=qt4 diff --git a/CMakeLists.txt b/CMakeLists.txt index d9a397f8..c65f6be0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,6 @@ # General setup ##################################################################### -cmake_minimum_required(VERSION 2.8.9) # You'll need at least 2.8.12 for KDE Frameworks integration project(QuasselIRC) # Versions @@ -34,11 +33,6 @@ include(FeatureSummary) include(QuasselCompileSettings) include(QuasselMacros) -# Setting COMPILE_DEFINITIONS_ is deprecated since CMake 3.0 in favor of generator expressions. -# These have existed since CMake 2.8.10; until we depend on that, we have to explicitly enable the old policy. -if (CMAKE_MAJOR_VERSION GREATER 2) - cmake_policy(SET CMP0043 OLD) -endif() # Options and variables that can be set on the command line ##################################################################### @@ -119,6 +113,22 @@ if (LINK_EXTRA) endif() +# Setup CMake +##################################################################### + +if (USE_QT5 AND WITH_KDE) + cmake_minimum_required(VERSION 2.8.12) +else() + cmake_minimum_required(VERSION 2.8.9) +endif() + +# Setting COMPILE_DEFINITIONS_ is deprecated since CMake 3.0 in favor of generator expressions. +# These have existed since CMake 2.8.10; until we depend on that, we have to explicitly enable the old policy. +if (CMAKE_MAJOR_VERSION GREATER 2) + cmake_policy(SET CMP0043 OLD) +endif() + + # Simplify later checks ##################################################################### @@ -139,11 +149,7 @@ if (USE_QT5) add_definitions(-DHAVE_QT5) else() message(STATUS "Building for Qt4...") - if (BUILD_GUI) - set(QT_MIN_VERSION "4.6.0") - else() - set(QT_MIN_VERSION "4.4.0") - endif() + set(QT_MIN_VERSION "4.8.0") # Select a Qt installation here, if you don't want to use system Qt if(QT_PATH) @@ -256,16 +262,17 @@ if (USE_QT5) endif() if (WITH_KDE) - find_package(KF5TextWidgets QUIET) - set_package_properties(KF5TextWidgets PROPERTIES TYPE REQUIRED - URL "http://inqlude.org/libraries/ktextwidgets.html" - DESCRIPTION "framework providing an assortment of widgets for displaying and editing text" - PURPOSE "Allows to use extra features provided by KDE Frameworks in input widgets" + find_package(KF5 COMPONENTS ConfigWidgets CoreAddons Notifications NotifyConfig TextWidgets XmlGui QUIET) + set_package_properties(KF5 PROPERTIES TYPE REQUIRED + URL "http://www.kde.org" + DESCRIPTION "KDE Frameworks" + PURPOSE "Required for integration into the Plasma desktop" ) endif() endif(BUILD_GUI) + if (BUILD_CORE) find_package(Qt5Script QUIET) set_package_properties(Qt5Script PROPERTIES TYPE REQUIRED @@ -276,14 +283,12 @@ if (USE_QT5) DESCRIPTION "the database support module for Qt5" ) - # While QCA2 seems to support Qt5, it is not actually co-installable or distinguishable from the Qt4 version... - # In order to avoid linking against the Qt4 version (which is probably the one installed), disable this for now - #find_package(QCA2 QUIET) - #set_package_properties(QCA2 PROPERTIES TYPE RECOMMENDED - # URL "https://projects.kde.org/projects/kdesupport/qca" - # DESCRIPTION "Qt Cryptographic Architecture" - # PURPOSE "Required for encryption support" - #) + find_package(QCA2-QT5) + set_package_properties(QCA2-QT5 PROPERTIES TYPE RECOMMENDED + URL "https://projects.kde.org/projects/kdesupport/qca" + DESCRIPTION "Qt Cryptographic Architecture" + PURPOSE "Required for encryption support" + ) endif(BUILD_CORE) @@ -292,6 +297,7 @@ if (USE_QT5) DESCRIPTION "contains tools for handling translation files" PURPOSE "Required for having translations" ) + # Some Qt5 versions do not define a target for lconvert, so we need to find it ourselves if (Qt5LinguistTools_FOUND) if (NOT TARGET Qt5::lconvert AND TARGET Qt5::lrelease) @@ -424,6 +430,11 @@ if (MINGW AND NOT KDE4_FOUND) add_definitions(-U__STRICT_ANSI__) endif() +# Sanitize compiler flags - old versions of KDE set -ansi, which breaks -std=c++11 +if (CMAKE_COMPILER_IS_GNUCXX) + string(REPLACE "-ansi" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) +endif() + # Setup KDE / KDE Frameworks ##################################################################### diff --git a/cmake/FindQCA2-QT5.cmake b/cmake/FindQCA2-QT5.cmake new file mode 100644 index 00000000..0d28590a --- /dev/null +++ b/cmake/FindQCA2-QT5.cmake @@ -0,0 +1,48 @@ +# - Try to find QCA2 (Qt Cryptography Architecture 2) for QT5 +# Once done this will define +# +# QCA2-QT5_FOUND - system has QCA2-QT5 +# QCA2-QT5_INCLUDE_DIR - the QCA2-QT5 include directory +# QCA2-QT5_LIBRARIES - the libraries needed to use QCA2-QT5 +# QCA2-QT5_DEFINITIONS - Compiler switches required for using QCA2-QT5 +# +# use pkg-config to get the directories and then use these values +# in the FIND_PATH() and FIND_LIBRARY() calls + +# Copyright (c) 2006, Michael Larouche, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(FindLibraryWithDebug) + +if (QCA2-QT5_INCLUDE_DIR AND QCA2-QT5_LIBRARIES) + + # in cache already + set(QCA2-QT5_FOUND TRUE) + +else (QCA2-QT5_INCLUDE_DIR AND QCA2-QT5_LIBRARIES) + + + if (NOT WIN32) + find_package(PkgConfig) + pkg_check_modules(PC_QCA2-QT5 QUIET qca2-qt5) + set(QCA2-QT5_DEFINITIONS ${PC_QCA2-QT5_CFLAGS_OTHER}) + endif (NOT WIN32) + + find_library_with_debug(QCA2-QT5_LIBRARIES + WIN32_DEBUG_POSTFIX d + NAMES qca-qt5 + HINTS ${PC_QCA2-QT5_LIBDIR} ${PC_QCA2-QT5_LIBRARY_DIRS} + ) + + find_path(QCA2-QT5_INCLUDE_DIR QtCrypto + HINTS ${PC_QCA2-QT5_INCLUDEDIR} ${PC_QCA2-QT5_INCLUDE_DIRS} + PATH_SUFFIXES QtCrypto) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(QCA2-QT5 DEFAULT_MSG QCA2-QT5_LIBRARIES QCA2-QT5_INCLUDE_DIR) + + mark_as_advanced(QCA2-QT5_INCLUDE_DIR QCA2-QT5_LIBRARIES) + +endif (QCA2-QT5_INCLUDE_DIR AND QCA2-QT5_LIBRARIES) diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 04b322b3..5c6762d5 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -8,9 +8,13 @@ if (BUILD_GUI) install(FILES quassel.desktop DESTINATION ${CMAKE_INSTALL_APPDIR}) endif() - if (KDE4_FOUND) + if (WITH_KDE4) install(FILES quassel.notifyrc DESTINATION ${CMAKE_INSTALL_DATADIR}/quassel) endif() + + if (WITH_KF5) + install(FILES quassel.notifyrc DESTINATION ${CMAKE_INSTALL_KNOTIFY5RCDIR}) + endif() endif() if (EMBED_DATA) diff --git a/data/quassel.desktop b/data/quassel.desktop index 158b9b94..a067eb60 100644 --- a/data/quassel.desktop +++ b/data/quassel.desktop @@ -73,3 +73,4 @@ Icon=quassel TryExec=quassel Exec=quassel Categories=Qt;Network;Chat;IRCClient; +X-DBUS-ServiceName=org.quassel-irc.quassel diff --git a/data/quasselclient.desktop b/data/quasselclient.desktop index c11cffe9..5be24cec 100644 --- a/data/quasselclient.desktop +++ b/data/quasselclient.desktop @@ -72,3 +72,4 @@ Icon=quassel TryExec=quasselclient Exec=quasselclient Categories=Qt;Network;Chat;IRCClient; +X-DBUS-ServiceName=org.quassel-irc.quasselclient diff --git a/icons/import/import_oxygen.pl b/icons/import/import_oxygen.pl index c6132d01..c6fb8177 100755 --- a/icons/import/import_oxygen.pl +++ b/icons/import/import_oxygen.pl @@ -72,8 +72,6 @@ foreach my $sizestr (readdir BASEDIR) { opendir (SIZEDIR, "$oxygen/$sizestr") or die "Could not open dir $sizestr\n"; foreach my $cat (readdir SIZEDIR) { next if $cat eq '.' or $cat eq '..'; - #system "mkdir -p $output/$sizestr/$cat" and die "Could not create category dir\n"; - system "mkdir -p $output/scalable/$cat" and die "Could not create category dir\n"; opendir (CATDIR, "$oxygen/$sizestr/$cat") or die "Could not open category dir\n"; foreach my $icon (readdir CATDIR) { $icon =~ s/\.png$//; @@ -105,13 +103,13 @@ foreach my $icon (keys %req_icons) { print "Warning: Missing icon $icon\n"; } +# Copy license etc. +system "cp $oxygen/AUTHORS $oxygen/CONTRIBUTING $oxygen/COPYING $oxygen/index.theme $output/"; + # Generate .qrc my @file_list; generate_qrc($output, $qrcfile_kde); -# Copy license etc. -system "cp $oxygen/AUTHORS $oxygen/CONTRIBUTING $oxygen/COPYING $oxygen/index.theme $output/"; - print "Done.\n"; ######################################################################################## @@ -135,7 +133,7 @@ sub generate_qrc { } sub push_icon_path { - return unless /\.png$/; + return unless /\.png$/ or /^index.theme$/; push @file_list, " $File::Find::name"; } diff --git a/icons/oxygen.qrc b/icons/oxygen.qrc index 3b019426..b451269f 100644 --- a/icons/oxygen.qrc +++ b/icons/oxygen.qrc @@ -1,5 +1,6 @@ + oxygen/index.theme oxygen/22x22/actions/irc-voice.png oxygen/22x22/actions/go-next.png oxygen/22x22/actions/im-user.png diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2e24ea40..62219306 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,6 +30,11 @@ if (WITH_OXYGEN) add_definitions(-DWITH_OXYGEN) endif() +# For KAboutData +if (WITH_KF5) + set(CLIENT_LIBRARIES ${CLIENT_LIBRARIES} KF5::CoreAddons) +endif() + # Needed for showing the cli option if appropriate if (HAVE_SYSLOG) add_definitions(-DHAVE_SYSLOG) diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 4f0106c4..4eef7d54 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -38,11 +38,6 @@ set(SOURCES clientcoreinfo.h ) -if (KDE4_FOUND) - include_directories(${KDE4_INCLUDES}) - add_definitions(-DHAVE_KDE ${KDE4_DEFINITIONS}) -endif() - if (USE_QT5) list(APPEND qt_modules Widgets) endif() @@ -53,7 +48,3 @@ add_library(mod_client STATIC ${SOURCES}) qt_use_modules(mod_client Network Core Gui ${qt_modules}) target_link_libraries(mod_client mod_common) - -if (KDE4_FOUND) - target_link_libraries(mod_client ${KDE4_SOLID_LIBS}) -endif() diff --git a/src/client/client.cpp b/src/client/client.cpp index 7bbbefe7..d8dbcbf5 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -48,6 +48,7 @@ #include "quassel.h" #include "signalproxy.h" #include "util.h" +#include "clientauthhandler.h" #include #include @@ -383,32 +384,39 @@ void Client::setSyncedToCore() connect(bufferSyncer(), SIGNAL(buffersPermanentlyMerged(BufferId, BufferId)), _messageModel, SLOT(buffersPermanentlyMerged(BufferId, BufferId))); connect(bufferSyncer(), SIGNAL(bufferMarkedAsRead(BufferId)), SIGNAL(bufferMarkedAsRead(BufferId))); connect(networkModel(), SIGNAL(requestSetLastSeenMsg(BufferId, MsgId)), bufferSyncer(), SLOT(requestSetLastSeenMsg(BufferId, const MsgId &))); - signalProxy()->synchronize(bufferSyncer()); + + SignalProxy *p = signalProxy(); + + if ((Client::coreFeatures() & Quassel::PasswordChange)) { + p->attachSignal(this, SIGNAL(clientChangePassword(QString))); + } + + p->synchronize(bufferSyncer()); // create a new BufferViewManager Q_ASSERT(!_bufferViewManager); - _bufferViewManager = new ClientBufferViewManager(signalProxy(), this); + _bufferViewManager = new ClientBufferViewManager(p, this); connect(_bufferViewManager, SIGNAL(initDone()), _bufferViewOverlay, SLOT(restore())); // create AliasManager Q_ASSERT(!_aliasManager); _aliasManager = new ClientAliasManager(this); connect(aliasManager(), SIGNAL(initDone()), SLOT(sendBufferedUserInput())); - signalProxy()->synchronize(aliasManager()); + p->synchronize(aliasManager()); // create NetworkConfig Q_ASSERT(!_networkConfig); _networkConfig = new NetworkConfig("GlobalNetworkConfig", this); - signalProxy()->synchronize(networkConfig()); + p->synchronize(networkConfig()); // create IgnoreListManager Q_ASSERT(!_ignoreListManager); _ignoreListManager = new ClientIgnoreListManager(this); - signalProxy()->synchronize(ignoreListManager()); + p->synchronize(ignoreListManager()); Q_ASSERT(!_transferManager); _transferManager = new ClientTransferManager(this); - signalProxy()->synchronize(transferManager()); + p->synchronize(transferManager()); // trigger backlog request once all active bufferviews are initialized connect(bufferViewOverlay(), SIGNAL(initDone()), this, SLOT(requestInitialBacklog())); @@ -645,6 +653,14 @@ void Client::markBufferAsRead(BufferId id) bufferSyncer()->requestMarkBufferAsRead(id); } +void Client::changePassword(QString newPassword) { + CoreAccount account = currentCoreAccount(); + account.setPassword(newPassword); + coreAccountModel()->createOrUpdateAccount(account); + coreAccountModel()->save(); + emit clientChangePassword(newPassword); +} + #if QT_VERSION < 0x050000 void Client::logMessage(QtMsgType type, const char *msg) diff --git a/src/client/client.h b/src/client/client.h index 8c707f34..445648b2 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -191,6 +191,8 @@ signals: */ void bufferMarkedAsRead(BufferId id); + void clientChangePassword(QString password); + public slots: void disconnectFromCore(); @@ -200,6 +202,8 @@ public slots: void markBufferAsRead(BufferId id); + void changePassword(QString newPassword); + private slots: void setSyncedToCore(); void setDisconnectedFromCore(); diff --git a/src/client/clientauthhandler.cpp b/src/client/clientauthhandler.cpp index b8a62efa..7ed231c2 100644 --- a/src/client/clientauthhandler.cpp +++ b/src/client/clientauthhandler.cpp @@ -169,6 +169,7 @@ void ClientAuthHandler::onSocketConnected() _probing = true; QDataStream stream(socket()); // stream handles the endianness for us + stream.setVersion(QDataStream::Qt_4_2); quint32 magic = Protocol::magic; #ifdef HAVE_SSL diff --git a/src/client/clientsettings.cpp b/src/client/clientsettings.cpp index c27e03a3..dc8e5c18 100644 --- a/src/client/clientsettings.cpp +++ b/src/client/clientsettings.cpp @@ -249,12 +249,10 @@ void CoreConnectionSettings::setNetworkDetectionMode(NetworkDetectionMode mode) CoreConnectionSettings::NetworkDetectionMode CoreConnectionSettings::networkDetectionMode() { -#ifdef HAVE_KDE4 - NetworkDetectionMode def = UseSolid; -#else - NetworkDetectionMode def = UsePingTimeout; -#endif - return (NetworkDetectionMode)localValue("NetworkDetectionMode", def).toInt(); + auto mode = localValue("NetworkDetectionMode", UseQNetworkConfigurationManager).toInt(); + if (mode == 0) + mode = UseQNetworkConfigurationManager; // UseSolid is gone, map that to the new default + return static_cast(mode); } diff --git a/src/client/clientsettings.h b/src/client/clientsettings.h index 755667da..dd413e0d 100644 --- a/src/client/clientsettings.h +++ b/src/client/clientsettings.h @@ -124,7 +124,7 @@ class CoreConnectionSettings : public ClientSettings { public: enum NetworkDetectionMode { - UseSolid, + UseQNetworkConfigurationManager = 1, // UseSolid is gone UsePingTimeout, NoActiveDetection }; diff --git a/src/client/coreconnection.cpp b/src/client/coreconnection.cpp index a4e24e02..0e4b4f94 100644 --- a/src/client/coreconnection.cpp +++ b/src/client/coreconnection.cpp @@ -57,10 +57,8 @@ void CoreConnection::init() _reconnectTimer.setSingleShot(true); connect(&_reconnectTimer, SIGNAL(timeout()), SLOT(reconnectTimeout())); -#ifdef HAVE_KDE4 - connect(Solid::Networking::notifier(), SIGNAL(statusChanged(Solid::Networking::Status)), - SLOT(solidNetworkStatusChanged(Solid::Networking::Status))); -#endif + _qNetworkConfigurationManager = new QNetworkConfigurationManager(this); + connect(_qNetworkConfigurationManager, SIGNAL(onlineStateChanged(bool)), SLOT(onlineStateChanged(bool))); CoreConnectionSettings s; s.initAndNotify("PingTimeoutInterval", this, SLOT(pingTimeoutIntervalChanged(QVariant)), 60); @@ -127,16 +125,12 @@ void CoreConnection::reconnectTimeout() if (!_peer) { CoreConnectionSettings s; if (_wantReconnect && s.autoReconnect()) { -#ifdef HAVE_KDE4 - // If using Solid, we don't want to reconnect if we're offline - if (s.networkDetectionMode() == CoreConnectionSettings::UseSolid) { - if (Solid::Networking::status() != Solid::Networking::Connected - && Solid::Networking::status() != Solid::Networking::Unknown) { + // If using QNetworkConfigurationManager, we don't want to reconnect if we're offline + if (s.networkDetectionMode() == CoreConnectionSettings::UseQNetworkConfigurationManager) { + if (!_qNetworkConfigurationManager->isOnline()) { return; - } + } } -#endif /* HAVE_KDE4 */ - reconnectToCore(); } } @@ -169,35 +163,26 @@ void CoreConnection::reconnectIntervalChanged(const QVariant &interval) } -#ifdef HAVE_KDE4 - -void CoreConnection::solidNetworkStatusChanged(Solid::Networking::Status status) +void CoreConnection::onlineStateChanged(bool isOnline) { CoreConnectionSettings s; - if (s.networkDetectionMode() != CoreConnectionSettings::UseSolid) + if (s.networkDetectionMode() != CoreConnectionSettings::UseQNetworkConfigurationManager) return; - switch (status) { - case Solid::Networking::Unknown: - case Solid::Networking::Connected: - //qDebug() << "Solid: Network status changed to connected or unknown"; + if(isOnline) { + // qDebug() << "QNetworkConfigurationManager reports Online"; if (state() == Disconnected) { if (_wantReconnect && s.autoReconnect()) { reconnectToCore(); } } - break; - case Solid::Networking::Disconnecting: - case Solid::Networking::Unconnected: + } else { + // qDebug() << "QNetworkConfigurationManager reports Offline"; if (state() != Disconnected && !isLocalConnection()) disconnectFromCore(tr("Network is down"), true); - break; - default: - break; } } -#endif bool CoreConnection::isEncrypted() const { diff --git a/src/client/coreconnection.h b/src/client/coreconnection.h index c6b2e261..0019a534 100644 --- a/src/client/coreconnection.h +++ b/src/client/coreconnection.h @@ -18,11 +18,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#ifndef CORECONNECTION_H_ -#define CORECONNECTION_H_ +#pragma once -#include "QPointer" -#include "QTimer" +#include +#include +#include #ifdef HAVE_SSL # include @@ -30,10 +30,6 @@ # include #endif -#ifdef HAVE_KDE4 -# include -#endif - #include "coreaccount.h" #include "remotepeer.h" #include "types.h" @@ -148,9 +144,7 @@ private slots: void reconnectIntervalChanged(const QVariant &interval); void reconnectTimeout(); -#ifdef HAVE_KDE4 - void solidNetworkStatusChanged(Solid::Networking::Status status); -#endif + void onlineStateChanged(bool isOnline); private: QPointer _authHandler; @@ -171,6 +165,8 @@ private: CoreAccount _account; CoreAccountModel *accountModel() const; + QPointer _qNetworkConfigurationManager; + friend class CoreConfigWizard; }; @@ -186,5 +182,3 @@ inline QString CoreConnection::progressText() const { return _progressText; } inline CoreConnection::ConnectionState CoreConnection::state() const { return _state; } inline bool CoreConnection::isConnected() const { return state() >= Connected; } inline CoreAccount CoreConnection::currentAccount() const { return _account; } - -#endif diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index fa48020d..90ccb5f3 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -9,7 +9,6 @@ set(SOURCES buffersyncer.cpp bufferviewconfig.cpp bufferviewmanager.cpp - cliparser.cpp compressor.cpp ctcpevent.cpp event.cpp @@ -46,8 +45,13 @@ set(SOURCES coreinfo.h ) +if (USE_QT5) + list(APPEND SOURCES qt5cliparser.cpp) +else() + list(APPEND SOURCES cliparser.cpp) +endif() -if (QCA2_FOUND) +if (QCA2_FOUND OR QCA2-QT5_FOUND) set(SOURCES ${SOURCES} keyevent.cpp) endif() diff --git a/src/common/abstractcliparser.h b/src/common/abstractcliparser.h index 2441a6e8..293ccd92 100644 --- a/src/common/abstractcliparser.h +++ b/src/common/abstractcliparser.h @@ -36,9 +36,9 @@ public: } - inline void addOption(const QString &longName, const char shortName = 0, const QString &help = QString(), const QString &def = QString()) + inline void addOption(const QString &longName, const char shortName = 0, const QString &help = QString(), const QString &valueName = QString(), const QString &def = QString()) { - addArgument(longName, CliParserArg(CliParserArg::CliArgOption, shortName, help, def)); + addArgument(longName, CliParserArg(CliParserArg::CliArgOption, shortName, help, valueName, def)); } @@ -54,20 +54,21 @@ protected: CliArgOption }; - CliParserArg(const CliArgType _type = CliArgInvalid, const char _shortName = 0, const QString _help = QString(), const QString _def = QString()) - : type(_type), - shortName(_shortName), - help(_help), - def(_def), - value(QString()), - boolValue(false) {}; + CliParserArg(const CliArgType type = CliArgInvalid, const char shortName = 0, const QString &help = QString(), const QString &valueName = QString(), const QString def = QString()) + : type(type) + , shortName(shortName) + , help(help) + , valueName(valueName) + , def(def) + {}; CliArgType type; char shortName; QString help; + QString valueName; QString def; QString value; - bool boolValue; + bool boolValue = false; }; virtual void addArgument(const QString &longName, const CliParserArg &arg) = 0; diff --git a/src/common/cliparser.cpp b/src/common/cliparser.cpp index ac70b4a1..2e94e559 100644 --- a/src/common/cliparser.cpp +++ b/src/common/cliparser.cpp @@ -32,10 +32,8 @@ CliParser::CliParser() : AbstractCliParser() } -void CliParser::addArgument(const QString &longName_, const CliParserArg &arg) +void CliParser::addArgument(const QString &longName, const CliParserArg &arg) { - QString longName = longName_; - longName.remove(QRegExp("\\s*<.*>\\s*")); // KCmdLineArgs takes args of the form "arg " if (argsMap.contains(longName)) qWarning() << "Warning: Multiple definition of argument" << longName; if (arg.shortName != 0 && !lnameOfShortArg(arg.shortName).isNull()) qWarning().nospace() << "Warning: Redefining shortName '" << arg.shortName << "' for " << longName << " previously defined for " << lnameOfShortArg(arg.shortName); @@ -189,8 +187,8 @@ void CliParser::usage() } else output.append(" "); lnameField.append(" --").append(arg.key()); - if (arg.value().type == CliParserArg::CliArgOption) { - lnameField.append("=[").append(arg.key().toUpper()).append("]"); + if (arg.value().type == CliParserArg::CliArgOption && !arg.value().valueName.isEmpty()) { + lnameField.append("=<").append(arg.value().valueName).append(">"); } output.append(lnameField.leftJustified(lnameFieldSize)); if (!arg.value().help.isEmpty()) { diff --git a/src/common/main.cpp b/src/common/main.cpp index 65525984..6847371d 100644 --- a/src/common/main.cpp +++ b/src/common/main.cpp @@ -20,11 +20,15 @@ #include +#include + #ifdef BUILD_CORE # include "coreapplication.h" #elif defined BUILD_QTUI +# include "aboutdata.h" # include "qtuiapplication.h" #elif defined BUILD_MONO +# include "aboutdata.h" # include "monoapplication.h" #else @@ -35,10 +39,22 @@ #if defined HAVE_KDE4 && defined BUILD_CORE # undef HAVE_KDE4 #endif +// We don't want quasselcore to depend on KDE +#if defined HAVE_KF5 && defined BUILD_CORE +# undef HAVE_KF5 +#endif #ifdef HAVE_KDE4 # include # include "kcmdlinewrapper.h" +#elif defined HAVE_KF5 +# include +# include +# include "qt5cliparser.h" +#elif defined HAVE_QT5 +# include "qt5cliparser.h" +#else +# include "cliparser.h" #endif #if !defined(BUILD_CORE) && defined(STATIC) @@ -47,13 +63,19 @@ Q_IMPORT_PLUGIN(qjpeg) Q_IMPORT_PLUGIN(qgif) #endif -#include "cliparser.h" #include "quassel.h" int main(int argc, char **argv) { +#if QT_VERSION < 0x050000 + // All our source files are in UTF-8, and Qt5 even requires that + QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); + QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); +#endif + Quassel::setupBuildInfo(); QCoreApplication::setApplicationName(Quassel::buildInfo().applicationName); + QCoreApplication::setApplicationVersion(Quassel::buildInfo().plainVersionString); QCoreApplication::setOrganizationName(Quassel::buildInfo().organizationName); QCoreApplication::setOrganizationDomain(Quassel::buildInfo().organizationDomain); @@ -81,11 +103,17 @@ int main(int argc, char **argv) # endif #endif + // Migrate settings from KDE4 to KF5 if appropriate +#ifdef HAVE_KF5 + Kdelibs4ConfigMigrator migrator(QCoreApplication::applicationName()); + migrator.setConfigFiles(QStringList() << "quasselrc" << "quassel.notifyrc"); + migrator.migrate(); +#endif + AbstractCliParser *cliParser; #ifdef HAVE_KDE4 // We need to init KCmdLineArgs first - // TODO: build an AboutData compat class to replace our aboutDlg strings KAboutData aboutData("quassel", "kdelibs4", ki18n("Quassel IRC"), Quassel::buildInfo().plainVersionString.toUtf8(), ki18n("A modern, distributed IRC client")); aboutData.addLicense(KAboutData::License_GPL_V2); @@ -95,6 +123,8 @@ int main(int argc, char **argv) KCmdLineArgs::init(argc, argv, &aboutData); cliParser = new KCmdLineWrapper(); +#elif defined HAVE_QT5 + cliParser = new Qt5CliParser(); #else cliParser = new CliParser(); #endif @@ -102,41 +132,42 @@ int main(int argc, char **argv) // Initialize CLI arguments // NOTE: We can't use tr() at this point, since app is not yet created + // TODO: Change this once we get rid of KDE4 and can initialize the parser after creating the app // put shared client&core arguments here cliParser->addSwitch("debug", 'd', "Enable debug output"); cliParser->addSwitch("help", 'h', "Display this help and exit"); cliParser->addSwitch("version", 'v', "Display version information"); #ifdef BUILD_QTUI - cliParser->addOption("configdir ", 'c', "Specify the directory holding the client configuration"); + cliParser->addOption("configdir", 'c', "Specify the directory holding the client configuration", "path"); #else - cliParser->addOption("configdir ", 'c', "Specify the directory holding configuration files, the SQlite database and the SSL certificate"); + cliParser->addOption("configdir", 'c', "Specify the directory holding configuration files, the SQlite database and the SSL certificate", "path"); #endif - cliParser->addOption("datadir ", 0, "DEPRECATED - Use --configdir instead"); + cliParser->addOption("datadir", 0, "DEPRECATED - Use --configdir instead", "path"); #ifndef BUILD_CORE // put client-only arguments here - cliParser->addOption("icontheme ", 0, "Override the system icon theme ('oxygen' is recommended)"); - cliParser->addOption("qss ", 0, "Load a custom application stylesheet"); + cliParser->addOption("icontheme", 0, "Override the system icon theme ('oxygen' is recommended)", "theme"); + cliParser->addOption("qss", 0, "Load a custom application stylesheet", "file.qss"); cliParser->addSwitch("debugbufferswitches", 0, "Enables debugging for bufferswitches"); cliParser->addSwitch("debugmodel", 0, "Enables debugging for models"); cliParser->addSwitch("hidewindow", 0, "Start the client minimized to the system tray"); #endif #ifndef BUILD_QTUI // put core-only arguments here - cliParser->addOption("listen
[,", 0, "The address(es) quasselcore will listen on", "::,0.0.0.0"); - cliParser->addOption("port ", 'p', "The port quasselcore will listen at", QString("4242")); + cliParser->addOption("listen", 0, "The address(es) quasselcore will listen on", "
[,
[,...]]", "::,0.0.0.0"); + cliParser->addOption("port", 'p', "The port quasselcore will listen at", "port", "4242"); cliParser->addSwitch("norestore", 'n', "Don't restore last core's state"); - cliParser->addOption("loglevel ", 'L', "Loglevel Debug|Info|Warning|Error", "Info"); + cliParser->addOption("loglevel", 'L', "Loglevel Debug|Info|Warning|Error", "level", "Info"); #ifdef HAVE_SYSLOG cliParser->addSwitch("syslog", 0, "Log to syslog"); #endif - cliParser->addOption("logfile ", 'l', "Log to a file"); - cliParser->addOption("select-backend ", 0, "Switch storage backend (migrating data if possible)"); + cliParser->addOption("logfile", 'l', "Log to a file", "path"); + cliParser->addOption("select-backend", 0, "Switch storage backend (migrating data if possible)", "backendidentifier"); cliParser->addSwitch("add-user", 0, "Starts an interactive session to add a new core user"); - cliParser->addOption("change-userpass ", 0, "Starts an interactive session to change the password of the user identified by username"); + cliParser->addOption("change-userpass", 0, "Starts an interactive session to change the password of the user identified by ", "username"); cliParser->addSwitch("oidentd", 0, "Enable oidentd integration"); - cliParser->addOption("oidentd-conffile ", 0, "Set path to oidentd configuration file"); + cliParser->addOption("oidentd-conffile", 0, "Set path to oidentd configuration file", "file"); #ifdef HAVE_SSL cliParser->addSwitch("require-ssl", 0, "Require SSL for client connections"); #endif @@ -167,6 +198,15 @@ int main(int argc, char **argv) } #endif - if (!app.init()) return EXIT_FAILURE; +#ifdef HAVE_KF5 + // FIXME: This should be done after loading the translation catalogue, but still in main() + AboutData aboutData; + AboutData::setQuasselPersons(&aboutData); + KAboutData::setApplicationData(aboutData.kAboutData()); +#endif + + if (!app.init()) + return EXIT_FAILURE; + return app.exec(); } diff --git a/src/common/qt5cliparser.cpp b/src/common/qt5cliparser.cpp new file mode 100644 index 00000000..e2249112 --- /dev/null +++ b/src/common/qt5cliparser.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (C) 2005-2015 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "qt5cliparser.h" + +#include +#include + +bool Qt5CliParser::init(const QStringList &arguments) +{ + _qCliParser.addHelpOption(); + _qCliParser.addVersionOption(); + _qCliParser.setApplicationDescription(QCoreApplication::translate("CliParser", "Quassel IRC is a modern, distributed IRC client.")); + + _qCliParser.process(arguments); + return true; // process() does error handling by itself +} + + +bool Qt5CliParser::isSet(const QString &longName) +{ + + return _qCliParser.isSet(longName); +} + + +QString Qt5CliParser::value(const QString &longName) +{ + return _qCliParser.value(longName); +} + + +void Qt5CliParser::usage() +{ + _qCliParser.showHelp(); +} + + +void Qt5CliParser::addArgument(const QString &longName, const AbstractCliParser::CliParserArg &arg) +{ + QStringList names(longName); + if (arg.shortName != 0) + names << QString(arg.shortName); + + switch(arg.type) { + case CliParserArg::CliArgSwitch: + _qCliParser.addOption(QCommandLineOption(names, arg.help)); + break; + case CliParserArg::CliArgOption: + _qCliParser.addOption(QCommandLineOption(names, arg.help, arg.valueName, arg.def)); + break; + default: + qWarning() << "Warning: Unrecognized argument:" << longName; + } +} diff --git a/src/common/qt5cliparser.h b/src/common/qt5cliparser.h new file mode 100644 index 00000000..1a7e7889 --- /dev/null +++ b/src/common/qt5cliparser.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2005-2015 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#pragma once + +#include + +#include "abstractcliparser.h" + + +class Qt5CliParser : public AbstractCliParser +{ +public: + bool init(const QStringList &arguments = QStringList()); + + QString value(const QString &longName); + bool isSet(const QString &longName); + void usage(); + +private: + void addArgument(const QString &longName, const CliParserArg &arg); + + QCommandLineParser _qCliParser; + +}; diff --git a/src/common/quassel.cpp b/src/common/quassel.cpp index c9da049c..f43423cc 100644 --- a/src/common/quassel.cpp +++ b/src/common/quassel.cpp @@ -427,43 +427,50 @@ QStringList Quassel::dataDirPaths() QStringList Quassel::findDataDirPaths() const { - QStringList dataDirNames = QString(qgetenv("XDG_DATA_DIRS")).split(':', QString::SkipEmptyParts); + // We don't use QStandardPaths for now, as we still need to provide fallbacks for Qt4 and + // want to stay consistent. - if (!dataDirNames.isEmpty()) { - for (int i = 0; i < dataDirNames.count(); i++) - dataDirNames[i].append("/apps/quassel/"); - } - else { - // Provide a fallback + QStringList dataDirNames; #ifdef Q_OS_WIN - dataDirNames << qgetenv("APPDATA") + QCoreApplication::organizationDomain() + "/share/apps/quassel/" - << qgetenv("APPDATA") + QCoreApplication::organizationDomain() - << QCoreApplication::applicationDirPath(); - } + dataDirNames << qgetenv("APPDATA") + QCoreApplication::organizationDomain() + "/share/apps/quassel/" + << qgetenv("APPDATA") + QCoreApplication::organizationDomain() + << QCoreApplication::applicationDirPath(); #elif defined Q_OS_MAC - dataDirNames << QDir::homePath() + "/Library/Application Support/Quassel/" - << QCoreApplication::applicationDirPath(); - } + dataDirNames << QDir::homePath() + "/Library/Application Support/Quassel/" + << QCoreApplication::applicationDirPath(); #else - dataDirNames.append("/usr/share/apps/quassel/"); - } - // on UNIX, we always check our install prefix - QString appDir = QCoreApplication::applicationDirPath(); - int binpos = appDir.lastIndexOf("/bin"); - if (binpos >= 0) { - appDir.replace(binpos, 4, "/share"); - appDir.append("/apps/quassel/"); - if (!dataDirNames.contains(appDir)) - dataDirNames.append(appDir); - } + // Linux et al + + // XDG_DATA_HOME is the location for users to override system-installed files, usually in .local/share + // This should thus come first. + QString xdgDataHome = QFile::decodeName(qgetenv("XDG_DATA_HOME")); + if (xdgDataHome.isEmpty()) + xdgDataHome = QDir::homePath() + QLatin1String("/.local/share"); + dataDirNames << xdgDataHome; + + // Now whatever is configured through XDG_DATA_DIRS + QString xdgDataDirs = QFile::decodeName(qgetenv("XDG_DATA_DIRS")); + if (xdgDataDirs.isEmpty()) + dataDirNames << "/usr/local/share" << "/usr/share"; + else + dataDirNames << xdgDataDirs.split(':', QString::SkipEmptyParts); + + // Just in case, also check our install prefix + dataDirNames << QCoreApplication::applicationDirPath() + "/../share"; + + // Normalize and append our application name + for (int i = 0; i < dataDirNames.count(); i++) + dataDirNames[i] = QDir::cleanPath(dataDirNames.at(i)) + "/quassel/"; + #endif - // add resource path and workdir just in case - dataDirNames << QCoreApplication::applicationDirPath() + "/data/" - << ":/data/"; + // Add resource path and workdir just in case. + // Workdir should have precedence + dataDirNames.prepend(QCoreApplication::applicationDirPath() + "/data/"); + dataDirNames.append(":/data/"); - // append trailing '/' and check for existence - QStringList::Iterator iter = dataDirNames.begin(); + // Append trailing '/' and check for existence + auto iter = dataDirNames.begin(); while (iter != dataDirNames.end()) { if (!iter->endsWith(QDir::separator()) && !iter->endsWith('/')) iter->append(QDir::separator()); @@ -473,6 +480,8 @@ QStringList Quassel::findDataDirPaths() const ++iter; } + dataDirNames.removeDuplicates(); + return dataDirNames; } diff --git a/src/common/quassel.h b/src/common/quassel.h index bcfad072..30d3d35f 100644 --- a/src/common/quassel.h +++ b/src/common/quassel.h @@ -71,8 +71,9 @@ public: SaslAuthentication = 0x0002, SaslExternal = 0x0004, HideInactiveNetworks = 0x0008, + PasswordChange = 0x0010, - NumFeatures = 0x0008 + NumFeatures = 0x0010 }; Q_DECLARE_FLAGS(Features, Feature); diff --git a/src/common/remotepeer.cpp b/src/common/remotepeer.cpp index a3fb2879..e8fa578d 100644 --- a/src/common/remotepeer.cpp +++ b/src/common/remotepeer.cpp @@ -252,11 +252,7 @@ void RemotePeer::handle(const HeartBeat &heartBeat) void RemotePeer::handle(const HeartBeatReply &heartBeatReply) { _heartBeatCount = 0; -#if QT_VERSION >= 0x040700 emit lagUpdated(heartBeatReply.timestamp.msecsTo(QDateTime::currentDateTime().toUTC()) / 2); -#else - emit lagUpdated(heartBeatReply.timestamp.time().msecsTo(QDateTime::currentDateTime().toUTC().time()) / 2); -#endif } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a9ea5ee7..43e68402 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -54,6 +54,13 @@ if (QCA2_FOUND) list(APPEND LIBS ${QCA2_LIBRARIES}) endif() +if (QCA2-QT5_FOUND) + add_definitions(-DHAVE_QCA2) + include_directories(${QCA2-QT5_INCLUDE_DIR}) + list(APPEND SOURCES cipher.cpp) + list(APPEND LIBS ${QCA2-QT5_LIBRARIES}) +endif() + include_directories(${CMAKE_SOURCE_DIR}/src/common) set(CORE_RCS ${CORE_RCS} ${CMAKE_CURRENT_SOURCE_DIR}/sql.qrc) diff --git a/src/core/core.cpp b/src/core/core.cpp index a86424f4..dfd2c95a 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -222,7 +222,7 @@ Core::~Core() foreach(CoreAuthHandler *handler, _connectingClients) { handler->deleteLater(); // disconnect non authed clients } - qDeleteAll(sessions); + qDeleteAll(_sessions); qDeleteAll(_storageBackends); } @@ -234,7 +234,8 @@ void Core::saveState() CoreSettings s; QVariantMap state; QVariantList activeSessions; - foreach(UserId user, instance()->sessions.keys()) activeSessions << QVariant::fromValue(user); + foreach(UserId user, instance()->_sessions.keys()) + activeSessions << QVariant::fromValue(user); state["CoreStateVersion"] = 1; state["ActiveSessions"] = activeSessions; s.setCoreState(state); @@ -247,7 +248,7 @@ void Core::restoreState() // qWarning() << qPrintable(tr("Cannot restore a state for an unconfigured core!")); return; } - if (instance()->sessions.count()) { + if (instance()->_sessions.count()) { qWarning() << qPrintable(tr("Calling restoreState() even though active sessions exist!")); return; } @@ -265,7 +266,7 @@ void Core::restoreState() quInfo() << "Restoring previous core state..."; foreach(QVariant v, activeSessions) { UserId user = v.value(); - instance()->createSession(user, true); + instance()->sessionForUser(user, true); } } } @@ -577,19 +578,7 @@ void Core::setupClientSession(RemotePeer *peer, UserId uid) handler->deleteLater(); // Find or create session for validated user - SessionThread *session; - if (sessions.contains(uid)) { - session = sessions[uid]; - } - else { - session = createSession(uid); - if (!session) { - qWarning() << qPrintable(tr("Could not initialize session for client:")) << qPrintable(peer->description()); - peer->close(); - peer->deleteLater(); - return; - } - } + sessionForUser(uid); // as we are currently handling an event triggered by incoming data on this socket // it is unsafe to directly move the socket to the client thread. @@ -610,14 +599,7 @@ void Core::customEvent(QEvent *event) void Core::addClientHelper(RemotePeer *peer, UserId uid) { // Find or create session for validated user - if (!sessions.contains(uid)) { - qWarning() << qPrintable(tr("Could not find a session for client:")) << qPrintable(peer->description()); - peer->close(); - peer->deleteLater(); - return; - } - - SessionThread *session = sessions[uid]; + SessionThread *session = sessionForUser(uid); session->addClient(peer); } @@ -643,26 +625,21 @@ void Core::setupInternalClientSession(InternalPeer *clientPeer) clientPeer->setPeer(corePeer); // Find or create session for validated user - SessionThread *sessionThread; - if (sessions.contains(uid)) - sessionThread = sessions[uid]; - else - sessionThread = createSession(uid); - + SessionThread *sessionThread = sessionForUser(uid); sessionThread->addClient(corePeer); } -SessionThread *Core::createSession(UserId uid, bool restore) +SessionThread *Core::sessionForUser(UserId uid, bool restore) { - if (sessions.contains(uid)) { - qWarning() << "Calling createSession() when a session for the user already exists!"; - return 0; - } - SessionThread *sess = new SessionThread(uid, restore, this); - sessions[uid] = sess; - sess->start(); - return sess; + if (_sessions.contains(uid)) + return _sessions[uid]; + + SessionThread *session = new SessionThread(uid, restore, this); + _sessions[uid] = session; + session->start(); + connect(session, SIGNAL(passwordChangeRequested(UserId, QString)), _storage, SLOT(updateUser(UserId, QString))); + return session; } diff --git a/src/core/core.h b/src/core/core.h index d544744a..deef9114 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -524,13 +524,15 @@ private slots: void socketError(QAbstractSocket::SocketError err, const QString &errorString); void setupClientSession(RemotePeer *, UserId); + void changeUserPass(const QString &username); + private: Core(); ~Core(); void init(); static Core *instanceptr; - SessionThread *createSession(UserId userId, bool restoreState = false); + SessionThread *sessionForUser(UserId userId, bool restoreState = false); void addClientHelper(RemotePeer *peer, UserId uid); //void processCoreSetup(QTcpSocket *socket, QVariantMap &msg); QString setupCoreForInternalUsage(); @@ -541,13 +543,12 @@ private: void unregisterStorageBackend(Storage *); bool selectBackend(const QString &backend); void createUser(); - void changeUserPass(const QString &username); void saveBackendSettings(const QString &backend, const QVariantMap &settings); QVariantMap promptForSettings(const Storage *storage); private: QSet _connectingClients; - QHash sessions; + QHash _sessions; Storage *_storage; QTimer _storageSyncTimer; diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp index 1ba393fe..7e9ce268 100644 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@ -232,17 +232,18 @@ void CoreNetwork::disconnectFromIrc(bool requested, const QString &reason, bool _quitReason = reason; displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Disconnecting. (%1)").arg((!requested && !withReconnect) ? tr("Core Shutdown") : _quitReason)); - switch (socket.state()) { - case QAbstractSocket::ConnectedState: - userInputHandler()->issueQuit(_quitReason); + if (socket.state() == QAbstractSocket::UnconnectedState) { + socketDisconnected(); + } else { + if (socket.state() == QAbstractSocket::ConnectedState) { + userInputHandler()->issueQuit(_quitReason); + } else { + socket.close(); + } if (requested || withReconnect) { // the irc server has 10 seconds to close the socket _socketCloseTimer.start(10000); - break; } - default: - socket.close(); - socketDisconnected(); } } @@ -416,11 +417,7 @@ void CoreNetwork::socketHasData() else if (s.endsWith("\n")) s.chop(1); NetworkDataEvent *event = new NetworkDataEvent(EventManager::NetworkIncoming, this, s); -#if QT_VERSION >= 0x040700 event->setTimestamp(QDateTime::currentDateTimeUtc()); -#else - event->setTimestamp(QDateTime::currentDateTime().toUTC()); -#endif emit newEvent(event); } } @@ -450,17 +447,15 @@ void CoreNetwork::socketInitialized() disconnectFromIrc(); return; } - + emit socketOpen(identity, localAddress(), localPort(), peerAddress(), peerPort()); - + Server server = usedServer(); #ifdef HAVE_SSL if (server.useSsl && !socket.isEncrypted()) return; #endif -#if QT_VERSION >= 0x040600 socket.setSocketOption(QAbstractSocket::KeepAliveOption, true); -#endif emit socketInitialized(identity, localAddress(), localPort(), peerAddress(), peerPort()); diff --git a/src/core/coresession.cpp b/src/core/coresession.cpp index 112aa856..1f6345d0 100644 --- a/src/core/coresession.cpp +++ b/src/core/coresession.cpp @@ -100,6 +100,8 @@ CoreSession::CoreSession(UserId uid, bool restoreState, QObject *parent) p->attachSlot(SIGNAL(createNetwork(const NetworkInfo &, const QStringList &)), this, SLOT(createNetwork(const NetworkInfo &, const QStringList &))); p->attachSlot(SIGNAL(removeNetwork(NetworkId)), this, SLOT(removeNetwork(NetworkId))); + p->attachSlot(SIGNAL(clientChangePassword(QString)), this, SLOT(changePassword(QString))); + loadSettings(); initScriptEngine(); @@ -638,3 +640,8 @@ void CoreSession::globalAway(const QString &msg) net->userInputHandler()->issueAway(msg, false /* no force away */); } } + +void CoreSession::changePassword(QString password) +{ + emit passwordChangeRequested(_user, password); +} diff --git a/src/core/coresession.h b/src/core/coresession.h index 3eecc4bb..0861b18c 100644 --- a/src/core/coresession.h +++ b/src/core/coresession.h @@ -132,6 +132,8 @@ public slots: //! Marks us away (or unaway) on all networks void globalAway(const QString &msg = QString()); + void changePassword(QString password); + signals: void initialized(); void sessionState(const Protocol::SessionState &sessionState); @@ -158,6 +160,8 @@ signals: void networkRemoved(NetworkId); void networkDisconnected(NetworkId); + void passwordChangeRequested(UserId user, QString password); + protected: virtual void customEvent(QEvent *event); diff --git a/src/core/coreuserinputhandler.cpp b/src/core/coreuserinputhandler.cpp index 6a53cad2..33d1f67a 100644 --- a/src/core/coreuserinputhandler.cpp +++ b/src/core/coreuserinputhandler.cpp @@ -169,11 +169,7 @@ void CoreUserInputHandler::handleCtcp(const BufferInfo &bufferInfo, const QStrin QString verboseMessage = tr("sending CTCP-%1 request to %2").arg(ctcpTag).arg(nick); if (ctcpTag == "PING") { -#if QT_VERSION >= 0x040700 message = QString::number(QDateTime::currentMSecsSinceEpoch()); -#else - message = QString::number(QDateTime::currentDateTime().toTime_t()); -#endif } // FIXME make this a proper event @@ -549,6 +545,16 @@ void CoreUserInputHandler::handlePing(const BufferInfo &bufferInfo, const QStrin } +void CoreUserInputHandler::handlePrint(const BufferInfo &bufferInfo, const QString &msg) +{ + if (bufferInfo.bufferName().isEmpty() || !bufferInfo.acceptsRegularMessages()) + return; // server buffer + + QByteArray encMsg = channelEncode(bufferInfo.bufferName(), msg); + emit displayMsg(Message::Info, bufferInfo.type(), bufferInfo.bufferName(), msg, network()->myNick(), Message::Self); +} + + // TODO: implement queries void CoreUserInputHandler::handleQuery(const BufferInfo &bufferInfo, const QString &msg) { diff --git a/src/core/coreuserinputhandler.h b/src/core/coreuserinputhandler.h index dd9696c4..69a429ee 100644 --- a/src/core/coreuserinputhandler.h +++ b/src/core/coreuserinputhandler.h @@ -63,6 +63,7 @@ public slots: void handleHalfop(const BufferInfo& bufferInfo, const QString &nicks); void handlePart(const BufferInfo &bufferInfo, const QString &text); void handlePing(const BufferInfo &bufferInfo, const QString &text); + void handlePrint(const BufferInfo &bufferInfo, const QString &text); void handleQuery(const BufferInfo &bufferInfo, const QString &text); void handleQuit(const BufferInfo &bufferInfo, const QString &text); void handleQuote(const BufferInfo &bufferInfo, const QString &text); diff --git a/src/core/eventstringifier.cpp b/src/core/eventstringifier.cpp index afb70c23..943727cd 100644 --- a/src/core/eventstringifier.cpp +++ b/src/core/eventstringifier.cpp @@ -733,12 +733,7 @@ void EventStringifier::handleCtcpPing(CtcpEvent *e) if (e->ctcpType() == CtcpEvent::Query) defaultHandler(e->ctcpCmd(), e); else { -#if QT_VERSION >= 0x040700 displayMsg(e, Message::Server, tr("Received CTCP-PING answer from %1 with %2 milliseconds round trip time") .arg(nickFromMask(e->prefix())).arg(QDateTime::fromMSecsSinceEpoch(e->param().toULongLong()).msecsTo(e->timestamp()))); -#else - displayMsg(e, Message::Server, tr("Received CTCP-PING answer from %1 with %2 seconds round trip time") - .arg(nickFromMask(e->prefix())).arg(QDateTime::fromTime_t(e->param().toInt()).secsTo(e->timestamp()))); -#endif } } diff --git a/src/core/sessionthread.cpp b/src/core/sessionthread.cpp index c2f9b5a2..1439bfce 100644 --- a/src/core/sessionthread.cpp +++ b/src/core/sessionthread.cpp @@ -121,6 +121,7 @@ void SessionThread::addInternalClientToSession(InternalPeer *internalPeer) void SessionThread::run() { _session = new CoreSession(user(), _restoreState); + connect(_session, SIGNAL(passwordChangeRequested(UserId, QString)), SIGNAL(passwordChangeRequested(UserId, QString))); connect(this, SIGNAL(addRemoteClient(RemotePeer*)), _session, SLOT(addClient(RemotePeer*))); connect(this, SIGNAL(addInternalClient(InternalPeer*)), _session, SLOT(addClient(InternalPeer*))); connect(_session, SIGNAL(sessionState(Protocol::SessionState)), Core::instance(), SIGNAL(sessionState(Protocol::SessionState))); diff --git a/src/core/sessionthread.h b/src/core/sessionthread.h index fee6e64f..9a4a1b6b 100644 --- a/src/core/sessionthread.h +++ b/src/core/sessionthread.h @@ -57,6 +57,8 @@ signals: void addRemoteClient(RemotePeer *peer); void addInternalClient(InternalPeer *peer); + void passwordChangeRequested(UserId user, QString newPassword); + private: CoreSession *_session; UserId _user; diff --git a/src/qtui/CMakeLists.txt b/src/qtui/CMakeLists.txt index c9274a22..f17cd454 100644 --- a/src/qtui/CMakeLists.txt +++ b/src/qtui/CMakeLists.txt @@ -81,13 +81,18 @@ set(FORMS set(LIBS ) set(QT_MODULES ) -if (KDE4_FOUND) +if (WITH_KDE4) add_definitions(-DHAVE_KDE ${KDE4_DEFINITIONS}) include_directories(${KDE4_INCLUDES}) list(APPEND SOURCES knotificationbackend.cpp) list(APPEND LIBS ${KDE4_KDECORE_LIBS} ${KDE4_KDEUI_LIBRARY} ${KDE4_KNOTIFYCONFIG_LIBRARY}) endif() +if (WITH_KF5) + list(APPEND SOURCES knotificationbackend.cpp) + list(APPEND LIBS KF5::ConfigWidgets KF5::Notifications KF5::NotifyConfig KF5::WidgetsAddons KF5::XmlGui) +endif() + if (LIBSNORE_FOUND) add_definitions(-DHAVE_LIBSNORE) include_directories(${LIBSNORE_INCLUDE_DIRS}) diff --git a/src/qtui/aboutdlg.cpp b/src/qtui/aboutdlg.cpp index 68fd6554..b5b7c76d 100644 --- a/src/qtui/aboutdlg.cpp +++ b/src/qtui/aboutdlg.cpp @@ -18,14 +18,20 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "aboutdlg.h" + #include #include -#include "aboutdlg.h" +#include "aboutdata.h" #include "quassel.h" -AboutDlg::AboutDlg(QWidget *parent) : QDialog(parent) +AboutDlg::AboutDlg(QWidget *parent) + : QDialog(parent) + , _aboutData(new AboutData(this)) { + AboutData::setQuasselPersons(_aboutData); + ui.setupUi(this); ui.quasselLogo->setPixmap(QIcon(":/icons/quassel-64.png").pixmap(64)); // don't let the icon theme affect our logo here @@ -63,16 +69,14 @@ QString AboutDlg::about() const QString AboutDlg::authors() const { QString res; - res = tr("Quassel IRC is mainly developed by:") + - "
" - "
Manuel \"Sputnick\" Nickschas
sput@quassel-irc.org
" - "Project Founder, Lead Developer
" - "
Marcus \"EgS\" Eggenberger
egs@quassel-irc.org
" - "Project Motivator, Lead Developer, Mac Maintainer
" - "
Alexander \"phon\" von Renteln
phon@quassel-irc.org
" - "Developer, Windows Maintainer
" - "
"; - + res = tr("Quassel IRC is mainly developed by:") + "
"; + for (const auto &person : _aboutData->authors()) { + res.append("
" + person.prettyName() + "
"); + if (!person.emailAddress().isEmpty()) + res.append("" + person.emailAddress() + "
"); + res.append("" + person.task() + "
"); + } + res.append("
"); return res; } @@ -80,126 +84,11 @@ QString AboutDlg::authors() const QString AboutDlg::contributors() const { QString res; - res = tr("We would like to thank the following contributors (in alphabetical order) and everybody we forgot to mention here:") - + QString::fromUtf8("
" - "
" - "
Daniel \"al\" Albers
Master Of Translation, many fixes and enhancements
" - "
Liudas Alisauskas
Lithuanian translation
" - "
Terje \"tan\" Andersen
Norwegian translation, documentation
" - "
Jens \"amiconn\" Arnold
Postgres migration fixes
" - "
Adolfo Jayme Barrientos
Spanish translation
" - "
Mattia Basaglia
Fixes
" - "
Pete \"elbeardmorez\" Beardmore
Linewrap for input line
" - "
Rafael \"EagleScreen\" Belmonte
Spanish translation
" - "
Sergiu Bivol
Romanian translation
" - "
Bruno Brigras
Crash fixes
" - "
Florent Castelli
Sanitize topic handling
" - "
Theo \"tampakrap\" Chatzimichos
Greek translation
" - "
Yuri Chornoivan
Ukrainian translation
" - "
Tomáš \"scarabeus\" Chvátal
Czech translation
" - "
\"Condex\"
Galician translation
" - "
Joshua \"tvakah\" Corbin
Various fixes
" - "
\"cordata\"
Esperanto translation
" - "
Matthias \"pennywise\" Coy
German translation
" - "
\"derpella\"
Polish translation
" - "
\"Dorian\"
French translation
" - "
Luke Faraone
Doc fixes
" - "
Chris \"stitch\" Fuenty
SASL support
" - "
Kevin \"KRF\" Funk
German translation
" - "
Fabiano \"elbryan\" Francesconi
Italian translation
" - "
Leo Franchi
OSX improvements
" - "
Sebastien Fricker
Audio backend improvements
" - "
Alf Gaida
Language improvements
" - "
Aurélien \"agateau\" Gâteau
Message Indicator support
" - "
Marco \"kaffeedoktor\" Genise
Ideas, hacking, motivation
" - "
Felix \"debfx\" Geyer
Certificate handling improvements
" - "
Volkan Gezer
Turkish translation
" - "
Sjors \"dazjorz\" Gielen
Fixes
" - "
Sebastian \"seezer\" Goth
Many improvements and features
" - "
Michael \"brot\" Groh
German translation, fixes
" - "
\"Gryllida\"
IRC parser improvements
" - "
H. İbrahim \"igungor\" Güngör
Turkish translation
" - "
Jiri Grönroos
Finnish translation
" - "
Chris \"Zren\" H
Various improvements
" - "
Edward Hades
Russian translation
" - "
John \"nox\" Hand
Former All-Seeing Eye logo
" - "
Adam \"2kah\" Harwood
ChatView improvements
" - "
Jonas \"Dante\" Heese
Project founder, various improvements
" - "
Thomas \"Datafreak\" Hogh
Windows builder
" - "
Johannes \"j0hu\" Huber
Many fixes and features, bug triaging
" - "
Theofilos Intzoglou
Greek translation
" - "
Jovan Jojkić
Serbian translation
" - "
Allan Jude
Documentation improvements
" - "
Michael \"ycros\" Kedzierski
Mac fixes
" - "
Scott \"ScottK\" Kitterman
Kubuntu nightly packager, (packaging/build system) bughunter
" - "
Paul \"Haudrauf\" Klumpp
Initial design and mainwindow layout
" - "
Maia Kozheva
Russian translation
" - "
Tae-Hoon Kwon
Korean translation
" - "
\"Larso\"
Finnish translation
" - "
Patrick \"bonsaikitten\" Lauer
Gentoo packaging
" - "
Chris \"Fish-Face\" Le Sueur
Various fixes and improvements
" - "
Jerome \"Adys\" Leclanche
Context menu fixes
" - "
Hendrik \"nevcairiel\" Leppkes
Various features
" - "
Jason Lynch
Bugfixes
" - "
Awad \"firesock\" Mackie
ChatView improvements
" - "
Michael \"mamarley\" Marley
Various fixes and improvements
" - "
Martin \"m4yer\" Mayer
German translation
" - "
Daniel \"hydrogen\" Meltzer
Various fixes and improvements
" - "
Sebastian Meyer
Fixes
" - "
Daniel E. Moctezuma
Japanese translation
" - "
Chris \"kode54\" Moeller
Various fixes and improvements
" - "
Thomas Müller
Fixes, Debian packaging
" - "
Gábor \"ELITE_x\" Németh
Hungarian translation
" - "
Per Nielsen
Danish translation
" - "
J-P Nurmi
Fixes
" - "
Marco \"Quizzlo\" Paolone
Italian translation
" - "
Bas \"Tucos\" Pape
Many fixes and improvements, bug and patch triaging, tireless community support
" - "
Bruno Patri
French translation
" - "
Drew \"LinuxDolt\" Patridge
BluesTheme stylesheet
" - "
Celeste \"seele\" Paul
Usability Queen
" - "
Vit Pelcak
Czech translation
" - "
Regis \"ZRegis\" Perrin
French translation
" - "
Diego \"Flameeyes\" Pettenò
Gentoo maintainer, build system improvements
" - "
Simon Philips
Dutch translation
" - "
Daniel \"billie\" Pielmeier
Gentoo maintainer
" - "
Nuno \"pinheiro\" Pinheiro
Tons of Oxygen icons including our application icon
" - "
David Planella
Translation system fixes
" - "
Jure \"JLP\" Repinc
Slovenian translation
" - "
Patrick \"TheOneRing\" von Reth
MinGW support, SNORE backend, Windows packager
" - "
Dirk \"MarcLandis\" Rettschlag
Various fixes and new features
" - "
Miguel Revilla
Spanish translation
" - "
Jaak Ristioja
Fixes
" - "
David \"Bombe\" Roden
Fixes
" - "
Henning \"honk\" Rohlfs
Various fixes
" - "
Stella \"differentreality\" Rouzi
Greek translation
" - "
\"salnx\"
Highlight configuration improvements
" - "
Martin \"sandsmark\" Sandsmark
Core fixes, Quasseldroid
" - "
David Sansome
OSX Notification Center support
" - "
Dennis \"DevUrandom\" Schridde
D-Bus notifications
" - "
Jussi \"jussi01\" Schultink
Tireless tester, {ku|U}buntu tester and lobbyist, liters of delicious Finnish alcohol
" - "
Tim \"xAFFE\" Schumacher
Fixes and feedback
" - "
\"sfionov\"
Russian translation
" - "
Harald \"apachelogger\" Sitter
{ku|U}buntu packager, motivator, promoter
" - "
Ramanathan Sivagurunathan
Fixes
" - "
Stefanos Sofroniou
Greek translation
" - "
Rüdiger \"ruediger\" Sonderfeld
Emacs keybindings
" - "
Alexander Stein
Tray icon fix
" - "
Daniel \"son\" Steinmetz
Early beta tester and bughunter (on Vista™!)
" - "
Jesper Thomschütz
Various fixes
" - "
Arthur \"roentgen\" Titeica
Romanian translation
" - "
\"ToBeFree\"
German translation
" - "
Edward \"Aides\" Toroshchin
Russian translation
" - "
Adam \"adamt\" Tulinius
Early beta tester and bughunter, Danish translation
" - "
Deniz Türkoglu
Mac fixes
" - "
Frederik M.J. \"freqmod\" Vestre
Norwegian translation
" - "
Atte Virtanen
Finnish translation
" - "
Pavel \"int\" Volkovitskiy
Early beta tester and bughunter
" - "
Roscoe van Wyk
Fixes
" - "
Zé
Portuguese translation
" - "
Benjamin \"zbenjamin\" Zeller
Windows build system fixes
" - "
\"zeugma\"
Turkish translation
" - "

" - "...and anybody else finding and reporting bugs, giving feedback, helping others and being part of the community!"); + res = tr("We would like to thank the following contributors (in alphabetical order) and everybody we forgot to mention here:") + "
"; + for (const auto &person : _aboutData->credits()) { + res.append("
" + person.prettyName() + "
" + person.task() + "
"); + } + res.append("
" + tr("...and anybody else finding and reporting bugs, giving feedback, helping others and being part of the community!")); return res; } @@ -211,13 +100,13 @@ QString AboutDlg::thanksTo() const res = tr("Special thanks goes to:
" "
" "
 John \"nox\" Hand
" - "
for the original Quassel icon - The All-Seeing Eye" + "
for the original Quassel icon - The All-Seeing Eye
" "
 The Oxygen Team
" - "
for creating all the artwork you see throughout Quassel
" + "
for creating all the artwork you see throughout Quassel
" "
 Qt Software formerly known as Trolltech
" - "
for creating Qt and Qtopia, and for sponsoring development of QuasselTopia with Greenphones and more
" + "
for creating Qt and Qtopia, and for sponsoring development of QuasselTopia with Greenphones and more
" "
" - "
for keeping Qt alive, and for sponsoring development of Quassel Mobile with N810s
" + "
for sponsoring development of Quassel Mobile with N810s
" ); return res; diff --git a/src/qtui/aboutdlg.h b/src/qtui/aboutdlg.h index d9fe3c28..d54df8c9 100644 --- a/src/qtui/aboutdlg.h +++ b/src/qtui/aboutdlg.h @@ -25,6 +25,8 @@ #include "ui_aboutdlg.h" +class AboutData; + class AboutDlg : public QDialog { Q_OBJECT @@ -39,6 +41,8 @@ private: QString authors() const; QString contributors() const; QString thanksTo() const; + + AboutData *_aboutData; }; diff --git a/src/qtui/chatviewsearchcontroller.h b/src/qtui/chatviewsearchcontroller.h index 294693de..2ad0edaa 100644 --- a/src/qtui/chatviewsearchcontroller.h +++ b/src/qtui/chatviewsearchcontroller.h @@ -21,7 +21,7 @@ #ifndef CHATVIEWSEARCHCONTROLLER_H #define CHATVIEWSEARCHCONTROLLER_H -#include +#include #include #include #include @@ -87,11 +87,11 @@ private: // Highlight Items -#include - class SearchHighlightItem : public QObject, public QGraphicsItem { Q_OBJECT + +// Apparently, there are broken Qt 4.8.2 mocs around that will fail without this (otherwise useless) #if... looking at you, Wheezy! #if QT_VERSION >= 0x040600 Q_INTERFACES(QGraphicsItem) #endif diff --git a/src/qtui/knotificationbackend.cpp b/src/qtui/knotificationbackend.cpp index 41d56385..1b09927a 100644 --- a/src/qtui/knotificationbackend.cpp +++ b/src/qtui/knotificationbackend.cpp @@ -18,13 +18,19 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include -#include +#include "knotificationbackend.h" + #include #include #include -#include "knotificationbackend.h" +#ifdef HAVE_KDE4 +# include +# include +#else +# include +# include +#endif #include "client.h" #include "mainwin.h" diff --git a/src/qtui/knotificationbackend.h b/src/qtui/knotificationbackend.h index 2f21c9ef..25abcf93 100644 --- a/src/qtui/knotificationbackend.h +++ b/src/qtui/knotificationbackend.h @@ -18,8 +18,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#ifndef KNOTIFICATIONBACKEND_H_ -#define KNOTIFICATIONBACKEND_H_ +#pragma once #include @@ -52,7 +51,7 @@ private: void removeNotificationById(uint id); void updateToolTip(); - QList > > _notifications; + QList>> _notifications; }; @@ -72,6 +71,3 @@ private slots: private: KNotifyConfigWidget *_widget; }; - - -#endif diff --git a/src/qtui/mainwin.cpp b/src/qtui/mainwin.cpp index cd4a5fe1..84c31597 100644 --- a/src/qtui/mainwin.cpp +++ b/src/qtui/mainwin.cpp @@ -25,17 +25,23 @@ #include #include #include +#include #ifdef HAVE_KDE4 -# include -# include # include # include # include # include # include # include -# include +#endif + +#ifdef HAVE_KF5 +# include +# include +# include +# include +# include #endif #ifdef Q_WS_X11 @@ -93,7 +99,7 @@ #include "topicwidget.h" #include "verticaldock.h" -#ifndef HAVE_KDE4 +#ifndef HAVE_KDE # ifdef HAVE_PHONON # include "phononnotificationbackend.h" # endif @@ -102,9 +108,9 @@ # endif # include "systraynotificationbackend.h" # include "taskbarnotificationbackend.h" -#else /* HAVE_KDE4 */ +#else /* HAVE_KDE */ # include "knotificationbackend.h" -#endif /* HAVE_KDE4 */ +#endif /* HAVE_KDE */ #ifdef HAVE_SSL # include "sslinfodlg.h" @@ -140,14 +146,13 @@ #include "settingspages/notificationssettingspage.h" #include "settingspages/topicwidgetsettingspage.h" -#ifndef HAVE_KDE4 +#ifndef HAVE_KDE # include "settingspages/shortcutssettingspage.h" #endif MainWin::MainWin(QWidget *parent) -#ifdef HAVE_KDE4 - : KMainWindow(parent), - _kHelpMenu(new KHelpMenu(this, KGlobal::mainComponent().aboutData())), +#ifdef HAVE_KDE + : KMainWindow(parent), _kHelpMenu(new KHelpMenu(this)), #else : QMainWindow(parent), #endif @@ -192,6 +197,8 @@ void MainWin::init() connect(Client::coreConnection(), SIGNAL(handleSslErrors(const QSslSocket *, bool *, bool *)), SLOT(handleSslErrors(const QSslSocket *, bool *, bool *))); #endif + connect(this, SIGNAL(changePassword(QString)), Client::instance(), SLOT(changePassword(QString))); + // Setup Dock Areas setDockNestingEnabled(true); setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea); @@ -214,7 +221,7 @@ void MainWin::init() setupTitleSetter(); setupHotList(); -#ifndef HAVE_KDE4 +#ifndef HAVE_KDE # ifdef HAVE_PHONON QtUi::registerNotificationBackend(new PhononNotificationBackend(this)); # endif @@ -226,9 +233,9 @@ void MainWin::init() QtUi::registerNotificationBackend(new TaskbarNotificationBackend(this)); -#else /* HAVE_KDE4 */ +#else /* HAVE_KDE */ QtUi::registerNotificationBackend(new KNotificationBackend(this)); -#endif /* HAVE_KDE4 */ +#endif /* HAVE_KDE */ #ifdef HAVE_INDICATEQT QtUi::registerNotificationBackend(new IndicatorNotificationBackend(this)); @@ -249,7 +256,7 @@ void MainWin::init() setDisconnectedState(); // Disable menus and stuff -#ifdef HAVE_KDE4 +#ifdef HAVE_KDE setAutoSaveSettings(); #endif @@ -295,7 +302,7 @@ void MainWin::saveStateToSettings(UiSettings &s) if (lastBufId.isValid()) s.setValue("LastUsedBufferId", lastBufId.toInt()); -#ifdef HAVE_KDE4 +#ifdef HAVE_KDE saveAutoSaveSettings(); #endif } @@ -307,7 +314,7 @@ void MainWin::restoreStateFromSettings(UiSettings &s) _normalPos = s.value("MainWinPos", pos()).toPoint(); bool maximized = s.value("MainWinMaximized", false).toBool(); -#ifndef HAVE_KDE4 +#ifndef HAVE_KDE restoreGeometry(s.value("MainWinGeometry").toByteArray()); if (maximized) { @@ -355,6 +362,8 @@ void MainWin::setupActions() this, SLOT(showCoreConnectionDlg()))); coll->addAction("DisconnectCore", new Action(QIcon::fromTheme("network-disconnect"), tr("&Disconnect from Core"), coll, Client::instance(), SLOT(disconnectFromCore()))); + coll->addAction("ChangePassword", new Action(QIcon::fromTheme("dialog-password"), tr("Change &Password..."), coll, + this, SLOT(showChangePasswordDialog()))); coll->addAction("CoreInfo", new Action(QIcon::fromTheme("help-about"), tr("Core &Info..."), coll, this, SLOT(showCoreInfoDlg()))); coll->addAction("ConfigureNetworks", new Action(QIcon::fromTheme("configure"), tr("Configure &Networks..."), coll, @@ -381,14 +390,14 @@ void MainWin::setupActions() coll->addAction("ToggleStatusBar", new Action(tr("Show Status &Bar"), coll, 0, 0))->setCheckable(true); -#ifdef HAVE_KDE4 - QAction *fullScreenAct = KStandardAction::fullScreen(this, SLOT(onFullScreenToggled()), this, coll); +#ifdef HAVE_KDE + _fullScreenAction = KStandardAction::fullScreen(this, SLOT(onFullScreenToggled()), this, coll); #else - QAction *fullScreenAct = new Action(QIcon::fromTheme("view-fullscreen"), tr("&Full Screen Mode"), coll, + _fullScreenAction = new Action(QIcon::fromTheme("view-fullscreen"), tr("&Full Screen Mode"), coll, this, SLOT(onFullScreenToggled()), QKeySequence(Qt::Key_F11)); - fullScreenAct->setCheckable(true); + _fullScreenAction->setCheckable(true); + coll->addAction("ToggleFullScreen", _fullScreenAction); #endif - coll->addAction("ToggleFullScreen", fullScreenAct); // Settings QAction *configureShortcutsAct = new Action(QIcon::fromTheme("configure-shortcuts"), tr("Configure &Shortcuts..."), coll, @@ -396,14 +405,14 @@ void MainWin::setupActions() configureShortcutsAct->setMenuRole(QAction::NoRole); coll->addAction("ConfigureShortcuts", configureShortcutsAct); - #ifdef Q_OS_MAC +#ifdef Q_OS_MAC QAction *configureQuasselAct = new Action(QIcon::fromTheme("configure"), tr("&Configure Quassel..."), coll, this, SLOT(showSettingsDlg())); configureQuasselAct->setMenuRole(QAction::PreferencesRole); - #else +#else QAction *configureQuasselAct = new Action(QIcon::fromTheme("configure"), tr("&Configure Quassel..."), coll, this, SLOT(showSettingsDlg()), QKeySequence(Qt::Key_F7)); - #endif +#endif coll->addAction("ConfigureQuassel", configureQuasselAct); // Help @@ -508,7 +517,7 @@ void MainWin::setupMenus() _fileMenu = menuBar()->addMenu(tr("&File")); static const QStringList coreActions = QStringList() - << "ConnectCore" << "DisconnectCore" << "CoreInfo"; + << "ConnectCore" << "DisconnectCore" << "ChangePassword" << "CoreInfo"; QAction *coreAction; foreach(QString actionName, coreActions) { @@ -542,7 +551,7 @@ void MainWin::setupMenus() _viewMenu->addAction(coll->action("LockLayout")); _settingsMenu = menuBar()->addMenu(tr("&Settings")); -#ifdef HAVE_KDE4 +#ifdef HAVE_KDE _settingsMenu->addAction(KStandardAction::configureNotifications(this, SLOT(showNotificationsDlg()), this)); _settingsMenu->addAction(KStandardAction::keyBindings(this, SLOT(showShortcutsDlg()), this)); #else @@ -550,9 +559,11 @@ void MainWin::setupMenus() #endif _settingsMenu->addAction(coll->action("ConfigureQuassel")); + _helpMenu = menuBar()->addMenu(tr("&Help")); + _helpMenu->addAction(coll->action("AboutQuassel")); -#ifndef HAVE_KDE4 +#ifndef HAVE_KDE _helpMenu->addAction(coll->action("AboutQt")); #else _helpMenu->addAction(KStandardAction::aboutKDE(_kHelpMenu, SLOT(aboutKDE()), this)); @@ -727,6 +738,25 @@ void MainWin::changeActiveBufferView(int bufferViewId) } +void MainWin::showChangePasswordDialog() +{ + if((Client::coreFeatures() & Quassel::PasswordChange)) { + bool ok; + QString newPassword = QInputDialog::getText(this, tr("Set Core Password"), tr("New password for your Quassel Core:"), QLineEdit::Password, QString(), &ok); + if (ok && !newPassword.isEmpty()) { + emit changePassword(newPassword); + } + } + else { + QMessageBox box(QMessageBox::Warning, tr("Feature Not Supported"), + tr("Your Quassel Core does not support this feature"), + QMessageBox::Ok, this); + box.setInformativeText(tr("You need a Quassel Core v0.12.0 or newer in order to be able to remotely change your password.")); + box.exec(); + } +} + + void MainWin::changeActiveBufferView(bool backwards) { BufferView *current = activeBufferView(); @@ -917,7 +947,7 @@ void MainWin::setupTopicWidget() void MainWin::setupViewMenuTail() { _viewMenu->addSeparator(); - _viewMenu->addAction(QtUi::actionCollection("General")->action("ToggleFullScreen")); + _viewMenu->addAction(_fullScreenAction); } @@ -998,7 +1028,7 @@ void MainWin::setupToolBars() setUnifiedTitleAndToolBarOnMac(true); #endif -#ifdef HAVE_KDE4 +#ifdef HAVE_KDE _mainToolBar = new KToolBar("MainToolBar", this, Qt::TopToolBarArea, false, true, true); #else _mainToolBar = new QToolBar(this); @@ -1049,6 +1079,7 @@ void MainWin::setConnectedState() coll->action("ConnectCore")->setEnabled(false); coll->action("DisconnectCore")->setEnabled(true); + coll->action("ChangePassword")->setEnabled(true); coll->action("CoreInfo")->setEnabled(true); foreach(QAction *action, _fileMenu->actions()) { @@ -1165,6 +1196,7 @@ void MainWin::setDisconnectedState() coll->action("ConnectCore")->setEnabled(true); coll->action("DisconnectCore")->setEnabled(false); coll->action("CoreInfo")->setEnabled(false); + coll->action("ChangePassword")->setEnabled(false); //_viewMenu->setEnabled(false); statusBar()->showMessage(tr("Not connected to core.")); if (_msgProcessorStatusWidget) @@ -1363,11 +1395,11 @@ void MainWin::showAboutDlg() void MainWin::showShortcutsDlg() { -#ifdef HAVE_KDE4 +#ifdef HAVE_KDE KShortcutsDialog dlg(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsDisallowed, this); foreach(KActionCollection *coll, QtUi::actionCollections()) dlg.addCollection(coll, coll->property("Category").toString()); - dlg.exec(); + dlg.configure(true); #else SettingsPageDlg dlg(new ShortcutsSettingsPage(QtUi::actionCollections(), this), this); dlg.exec(); @@ -1387,15 +1419,10 @@ void MainWin::onFullScreenToggled() // Relying on QWidget::isFullScreen is discouraged, see the KToggleFullScreenAction docs // Also, one should not use showFullScreen() or showNormal(), as those reset all other window flags - QAction *action = QtUi::actionCollection("General")->action("ToggleFullScreen"); - if (!action) - return; - -#ifdef HAVE_KDE4 - KToggleFullScreenAction *kAct = static_cast(action); - kAct->setFullScreen(this, kAct->isChecked()); +#ifdef HAVE_KDE + static_cast(_fullScreenAction)->setFullScreen(this, _fullScreenAction->isChecked()); #else - if (action->isChecked()) + if (_fullScreenAction->isChecked()) setWindowState(windowState() | Qt::WindowFullScreen); else setWindowState(windowState() & ~Qt::WindowFullScreen); diff --git a/src/qtui/mainwin.h b/src/qtui/mainwin.h index 30e57269..44444266 100644 --- a/src/qtui/mainwin.h +++ b/src/qtui/mainwin.h @@ -18,11 +18,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#ifndef MAINWIN_H_ -#define MAINWIN_H_ +#pragma once #ifdef HAVE_KDE4 # include +#elif defined HAVE_KF5 +# include #else # include #endif @@ -56,11 +57,10 @@ class KHelpMenu; //!\brief The main window of Quassel's QtUi. class MainWin -#ifdef HAVE_KDE4 - : public KMainWindow -{ +#ifdef HAVE_KDE + : public KMainWindow { #else -: public QMainWindow { + : public QMainWindow { #endif Q_OBJECT @@ -164,12 +164,15 @@ private slots: void changeActiveBufferView(bool backwards); void changeActiveBufferView(int bufferViewId); + void showChangePasswordDialog(); + signals: void connectToCore(const QVariantMap &connInfo); void disconnectFromCore(); + void changePassword(QString newPassword); private: -#ifdef HAVE_KDE4 +#ifdef HAVE_KDE KHelpMenu *_kHelpMenu; #endif @@ -203,6 +206,7 @@ private: ChatMonitorView *_chatMonitorView; TopicWidget *_topicWidget; + QAction *_fullScreenAction; QMenu *_fileMenu, *_networksMenu, *_viewMenu, *_bufferViewsMenu, *_settingsMenu, *_helpMenu, *_helpDebugMenu; QMenu *_toolbarMenu; QToolBar *_mainToolBar, *_chatViewToolBar, *_nickToolBar; @@ -220,6 +224,3 @@ private: friend class QtUi; }; - - -#endif diff --git a/src/qtui/qtuiapplication.cpp b/src/qtui/qtuiapplication.cpp index a279442b..88b8bdb1 100644 --- a/src/qtui/qtuiapplication.cpp +++ b/src/qtui/qtuiapplication.cpp @@ -33,9 +33,10 @@ #include "qtui.h" #include "qtuisettings.h" + QtUiApplication::QtUiApplication(int &argc, char **argv) #ifdef HAVE_KDE4 - : KApplication(), + : KApplication(), // KApplication is deprecated in KF5 #else : QApplication(argc, argv), #endif @@ -45,11 +46,34 @@ QtUiApplication::QtUiApplication(int &argc, char **argv) #ifdef HAVE_KDE4 Q_UNUSED(argc); Q_UNUSED(argv); - // We need to setup KDE's data dirs + // Setup KDE's data dirs + // Because we can't use KDE stuff in (the class) Quassel directly, we need to do this here... QStringList dataDirs = KGlobal::dirs()->findDirs("data", ""); + + // Just in case, also check our install prefix + dataDirs << QCoreApplication::applicationDirPath() + "/../share/apps/"; + + // Normalize and append our application name for (int i = 0; i < dataDirs.count(); i++) - dataDirs[i].append("quassel/"); + dataDirs[i] = QDir::cleanPath(dataDirs.at(i)) + "/quassel/"; + + // Add resource path and just in case. + // Workdir should have precedence + dataDirs.prepend(QCoreApplication::applicationDirPath() + "/data/"); dataDirs.append(":/data/"); + + // Append trailing '/' and check for existence + auto iter = dataDirs.begin(); + while (iter != dataDirs.end()) { + if (!iter->endsWith(QDir::separator()) && !iter->endsWith('/')) + iter->append(QDir::separator()); + if (!QFile::exists(*iter)) + iter = dataDirs.erase(iter); + else + ++iter; + } + + dataDirs.removeDuplicates(); setDataDirPaths(dataDirs); #else /* HAVE_KDE4 */ diff --git a/src/qtui/settingspages/coreconnectionsettingspage.cpp b/src/qtui/settingspages/coreconnectionsettingspage.cpp index a9d0bca7..d73c0107 100644 --- a/src/qtui/settingspages/coreconnectionsettingspage.cpp +++ b/src/qtui/settingspages/coreconnectionsettingspage.cpp @@ -24,13 +24,10 @@ CoreConnectionSettingsPage::CoreConnectionSettingsPage(QWidget *parent) : SettingsPage(tr("Remote Cores"), tr("Connection"), parent) { ui.setupUi(this); -#ifndef HAVE_KDE4 - ui.useSolid->hide(); -#endif initAutoWidgets(); - connect(ui.useSolid, SIGNAL(toggled(bool)), SLOT(widgetHasChanged())); + connect(ui.useQNetworkConfigurationManager, SIGNAL(toggled(bool)), SLOT(widgetHasChanged())); connect(ui.usePingTimeout, SIGNAL(toggled(bool)), SLOT(widgetHasChanged())); connect(ui.useNoTimeout, SIGNAL(toggled(bool)), SLOT(widgetHasChanged())); } @@ -49,11 +46,7 @@ void CoreConnectionSettingsPage::widgetHasChanged() void CoreConnectionSettingsPage::defaults() { -#ifdef HAVE_KDE4 - setRadioButtons(CoreConnectionSettings::UseSolid); -#else - setRadioButtons(CoreConnectionSettings::UsePingTimeout); -#endif + setRadioButtons(CoreConnectionSettings::UseQNetworkConfigurationManager); SettingsPage::defaults(); } @@ -80,11 +73,9 @@ void CoreConnectionSettingsPage::save() void CoreConnectionSettingsPage::setRadioButtons(CoreConnectionSettings::NetworkDetectionMode mode) { switch (mode) { -#ifdef HAVE_KDE4 - case CoreConnectionSettings::UseSolid: - ui.useSolid->setChecked(true); + case CoreConnectionSettings::UseQNetworkConfigurationManager: + ui.useQNetworkConfigurationManager->setChecked(true); break; -#endif case CoreConnectionSettings::UsePingTimeout: ui.usePingTimeout->setChecked(true); break; @@ -96,10 +87,8 @@ void CoreConnectionSettingsPage::setRadioButtons(CoreConnectionSettings::Network CoreConnectionSettings::NetworkDetectionMode CoreConnectionSettingsPage::modeFromRadioButtons() const { -#ifdef HAVE_KDE4 - if (ui.useSolid->isChecked()) - return CoreConnectionSettings::UseSolid; -#endif + if (ui.useQNetworkConfigurationManager->isChecked()) + return CoreConnectionSettings::UseQNetworkConfigurationManager; if (ui.usePingTimeout->isChecked()) return CoreConnectionSettings::UsePingTimeout; diff --git a/src/qtui/settingspages/coreconnectionsettingspage.ui b/src/qtui/settingspages/coreconnectionsettingspage.ui index 0e0308d4..15891847 100644 --- a/src/qtui/settingspages/coreconnectionsettingspage.ui +++ b/src/qtui/settingspages/coreconnectionsettingspage.ui @@ -6,7 +6,7 @@ 0 0 - 446 + 476 465 @@ -21,12 +21,12 @@ - + - Rely on KDE's hardware layer to detect if we're online. Recommended for most KDE users + Rely on Qt's network configuration manager to detect if we're online - Use KDE's network status detection (via Solid) + Automatic false @@ -151,7 +151,7 @@ - Actively ping the remote core and disconnect if we didn't get a reply after a certain time + Interval between consecutive connection attempts seconds @@ -206,6 +206,14 @@ + + useQNetworkConfigurationManager + usePingTimeout + pingTimeout + useNoTimeout + autoReconnect + reconnectInterval + diff --git a/src/qtui/settingspages/highlightsettingspage.cpp b/src/qtui/settingspages/highlightsettingspage.cpp index c932d9f6..2fe2a0b4 100644 --- a/src/qtui/settingspages/highlightsettingspage.cpp +++ b/src/qtui/settingspages/highlightsettingspage.cpp @@ -252,9 +252,7 @@ void HighlightSettingsPage::save() NotificationSettings notificationSettings; notificationSettings.setHighlightList(highlightList); - NotificationSettings::HighlightNickType highlightNickType; - if (ui.highlightNoNick->isChecked()) - highlightNickType = NotificationSettings::NoNick; + NotificationSettings::HighlightNickType highlightNickType = NotificationSettings::NoNick; if (ui.highlightCurrentNick->isChecked()) highlightNickType = NotificationSettings::CurrentNick; if (ui.highlightAllNicks->isChecked()) @@ -279,18 +277,18 @@ bool HighlightSettingsPage::testHasChanged() { NotificationSettings notificationSettings; - NotificationSettings::HighlightNickType highlightNickType; - if (ui.highlightNoNick->isChecked()) - highlightNickType = NotificationSettings::NoNick; + NotificationSettings::HighlightNickType highlightNickType = NotificationSettings::NoNick; if (ui.highlightCurrentNick->isChecked()) highlightNickType = NotificationSettings::CurrentNick; if (ui.highlightAllNicks->isChecked()) highlightNickType = NotificationSettings::AllNicks; - if (notificationSettings.highlightNick() != highlightNickType) return true; - if (notificationSettings.nicksCaseSensitive() != ui.nicksCaseSensitive->isChecked()) return true; - - if (notificationSettings.highlightList() != highlightList) return true; + if (notificationSettings.highlightNick() != highlightNickType) + return true; + if (notificationSettings.nicksCaseSensitive() != ui.nicksCaseSensitive->isChecked()) + return true; + if (notificationSettings.highlightList() != highlightList) + return true; return false; } diff --git a/src/qtui/settingspages/networkssettingspage.cpp b/src/qtui/settingspages/networkssettingspage.cpp index c9ba18a8..011328d5 100644 --- a/src/qtui/settingspages/networkssettingspage.cpp +++ b/src/qtui/settingspages/networkssettingspage.cpp @@ -865,6 +865,7 @@ ServerEditDlg::ServerEditDlg(const Network::Server &server, QWidget *parent) : Q ui.setupUi(this); ui.useSSL->setIcon(QIcon::fromTheme("document-encrypt")); ui.host->setText(server.host); + ui.host->setFocus(); ui.port->setValue(server.port); ui.password->setText(server.password); ui.useSSL->setChecked(server.useSsl); diff --git a/src/qtui/statusnotifieritem.cpp b/src/qtui/statusnotifieritem.cpp index a1d2916a..a75c3255 100644 --- a/src/qtui/statusnotifieritem.cpp +++ b/src/qtui/statusnotifieritem.cpp @@ -52,14 +52,7 @@ protected: virtual QString iconNameForAction(QAction *action) // TODO Qt 4.7: fixme when we have converted our iconloader { QIcon icon(action->icon()); -#if QT_VERSION >= 0x040701 - // QIcon::name() is in the 4.7 git branch, but it is not in 4.7 TP. - // If you get a build error here, you need to update your pre-release - // of Qt 4.7. return icon.isNull() ? QString() : icon.name(); -#else - return QString(); -#endif } }; diff --git a/src/uisupport/CMakeLists.txt b/src/uisupport/CMakeLists.txt index c0d6e4bd..37ea3114 100644 --- a/src/uisupport/CMakeLists.txt +++ b/src/uisupport/CMakeLists.txt @@ -1,6 +1,7 @@ # Builds the uisupport module set(SOURCES + aboutdata.cpp abstractbuffercontainer.cpp abstractitemview.cpp action.cpp @@ -58,5 +59,5 @@ if (WITH_KDE4) endif() if (WITH_KF5) - target_link_libraries(mod_uisupport KF5::TextWidgets) + target_link_libraries(mod_uisupport KF5::CoreAddons KF5::TextWidgets KF5::XmlGui) endif() diff --git a/src/uisupport/aboutdata.cpp b/src/uisupport/aboutdata.cpp new file mode 100644 index 00000000..17356fda --- /dev/null +++ b/src/uisupport/aboutdata.cpp @@ -0,0 +1,300 @@ +/*************************************************************************** + * Copyright (C) 2005-2015 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "aboutdata.h" + +#include + +#include "quassel.h" + + +AboutPerson::AboutPerson(const QString &name, const QString &nick, const QString &task, const QString &emailAddress, QLocale::Language translatedLanguage) + : _name(name) + , _nick(nick) + , _task(task) + , _emailAddress(emailAddress) + , _language(translatedLanguage) +{ + +} + + +QString AboutPerson::name() const +{ + return _name; +} + + +QString AboutPerson::nick() const +{ + return _nick; +} + + +QString AboutPerson::task() const +{ + return _task; +} + + +QString AboutPerson::emailAddress() const +{ + return _emailAddress; +} + + +QLocale::Language AboutPerson::translatedLanguage() const +{ + return _language; +} + + +QString AboutPerson::prettyName() const +{ + if (!name().isEmpty() && !nick().isEmpty()) + return name() + " (" + nick() + ')'; + + if (name().isEmpty() && !nick().isEmpty()) + return nick(); + + return name(); +} + + +/**************************************************************************************************/ + + +AboutData::AboutData(QObject *parent) + : QObject(parent) +{ + +} + + +QList AboutData::authors() const +{ + return _authors; +} + + +QList< AboutPerson > AboutData::credits() const +{ + return _credits; +} + + +AboutData &AboutData::addAuthor(const AboutPerson &author) +{ + _authors.append(author); + return *this; +} + + +AboutData &AboutData::addAuthors(std::initializer_list authors) +{ + _authors.append(authors); + return *this; +} + + +AboutData &AboutData::addCredit(const AboutPerson &credit) +{ + _credits.append(credit); + return *this; +} + + +AboutData &AboutData::addCredits(std::initializer_list credits) +{ + _credits.append(credits); + return *this; +} + +#ifdef HAVE_KF5 + +KAboutData AboutData::kAboutData() const +{ + KAboutData aboutData( + Quassel::buildInfo().applicationName, + tr("Quassel IRC"), + Quassel::buildInfo().plainVersionString + ); + aboutData.addLicense(KAboutLicense::GPL_V2); + aboutData.addLicense(KAboutLicense::GPL_V3); + aboutData.setShortDescription(tr("A modern, distributed IRC client")); + aboutData.setProgramLogo(QVariant::fromValue(QImage(":/pics/quassel-logo.png"))); + aboutData.setBugAddress("http://bugs.quassel-irc.org/projects/quassel-irc/issues/new"); + aboutData.setOrganizationDomain(Quassel::buildInfo().organizationDomain.toUtf8()); + + for (const auto &person : authors()) { + aboutData.addAuthor(person.prettyName(), person.task(), person.emailAddress()); + } + + for (const auto &person : credits()) { + aboutData.addCredit(person.prettyName(), person.task(), person.emailAddress()); + } + + return aboutData; +} + +#endif + + +/**************************************************************************************************/ + +/* + * NOTE: The list of contributors was retrieved from the Git history, but sometimes things fall + * through the cracks... especially for translations, we don't have an easy way to track + * contributors' names. + * If you find wrong data for yourself, want your nickname and/or mail addresses added or + * removed, or feel left out or unfairly credited, please don't hesitate to let us know! We + * do want to credit everyone who has contributed to Quassel development. + */ + +void AboutData::setQuasselPersons(AboutData *aboutData) +{ + aboutData->addAuthors({ + { "Manuel Nickschas", "Sputnick", tr("Project Founder, Lead Developer"), "sputnick@quassel-irc.org" }, + { "Marcus Eggenberger", "EgS", tr("Project Motivator, Lead Developer"), "egs@quassel-irc.org" }, + { "Alexander von Renteln", "phon", tr("Former Lead Developer"), "phon@quassel-irc.org" }, + { "Daniel Albers", "al", tr("Master of Translation, many fixes and enhancements") }, + { "Sebastian Goth", "seezer", tr("Many features, fixes and improvements") }, + { "Bas Pape", "Tucos", tr("Many fixes and improvements, bug and patch triaging, community support") }, + }); + + aboutData->addCredits({ + { "Adam Harwood", "2kah", tr("Chatview improvements") }, + { "Adam Tulinius", "adamt", tr("Early beta tester and bughunter, Danish translation"), "", QLocale::Danish }, + { "Adolfo Jayme Barrientos", "", tr("Spanish translation"), "", QLocale::Spanish }, + { "Alexander Stein", "", tr("Tray icon fix") }, + { "Alf Gaida", "agaida", tr("Language improvements") }, + { "Allan Jude", "", tr("Documentation improvements") }, + { "Arthur Titeica", "roentgen", tr("Romanian translation"), "", QLocale::Romanian }, + { "Atte Virtanen", "", tr("Finnish translation"), "", QLocale::Finnish }, + { "Aurélien Gâteau", "agateau", tr("Message indicator support") }, + { "Awad Mackie", "firesock", tr("Chatview improvements") }, + { "Benjamin Zeller", "zbenjamin", tr("Windows build system fixes") }, + { "Bruno Brigras", "", tr("Crash fixes") }, + { "Bruno Patri", "", tr("French translation"), "", QLocale::French }, + { "Celeste Paul", "seele", tr("Usability review") }, + { "Chris Fuenty", "stitch", tr("SASL support") }, + { "Chris H", "Zren", tr("Various improvements") }, + { "Chris Le Sueur", "Fish-Face", tr("Various fixes and improvements") }, + { "Chris Moeller", "kode54", tr("Various fixes and improvements") }, + { "", "Condex", tr("Galician translation"), "", QLocale::Galician }, + { "", "cordata", tr("Esperanto translation"), "", QLocale::Esperanto }, + { "Daniel E. Moctezuma", "", tr("Japanese translation"), "", QLocale::Japanese }, + { "Daniel Meltzer", "hydrogen", tr("Various fixes and improvements") }, + { "Daniel Pielmeier", "billie", tr("Gentoo maintainer") }, + { "Daniel Steinmetz", "son", tr("Early beta tester and bughunter (on Vista™!)") }, + { "David Planella", "", tr("Translation system fixes") }, + { "David Sansome", "", tr("OSX Notification Center support") }, + { "David Roden", "Bombe", tr("Fixes") }, + { "Deniz Türkoglu", "", tr("Mac fixes") }, + { "Dennis Schridde", "devurandom", tr("D-Bus notifications") }, + { "", "derpella", tr("Polish translation"), "", QLocale::Polish }, + { "Diego Pettenò", "Flameeyes", tr("Build system improvements") }, + { "Dirk Rettschlag", "MarcLandis", tr("Formatting support and other input line improvements, many other fixes") }, + { "", "Dorian", tr("French translation"), "", QLocale::French }, + { "Drew Patridge", "LinuxDolt", tr("BluesTheme stylesheet") }, + { "Edward Hades", "", tr("Russian translation"), "", QLocale::Russian }, + { "Fabiano Francesconi", "elbryan", tr("Italian translation"), "", QLocale::Italian }, + { "Felix Geyer", "debfx", tr("Certificate handling improvements") }, + { "Florent Castelli", "", tr("Sanitize topic handling") }, + { "Frederik M.J. Vestre", "freqmod", tr("Norwegian translation"), "", QLocale::Norwegian }, + { "Gábor Németh", "ELITE_x", tr("Hungarian translation"), "", QLocale::Hungarian }, + { "Gryllida A", "gry", tr("IRC parser improvements") }, + { "H. İbrahim Güngör", "igungor", tr("Turkish translation"), "", QLocale::Turkish }, + { "Harald Fernengel", "harryF", tr("Initial Qt5 support") }, + { "Harald Sitter", "apachelogger", tr("{Ku|U}buntu packager, motivator, promoter") }, + { "Hendrik Leppkes", "nevcairiel", tr("Various features") }, + { "Henning Rohlfs", "honk", tr("Various fixes") }, + { "J-P Nurmi", "", tr("Various fixes") }, + { "Jaak Ristioja", "", tr("Bugfixes") }, + { "Jason Lynch", "", tr("Bugfixes") }, + { "Jens Arnold", "amiconn", tr("Postgres migration fixes") }, + { "Jerome Leclanche", "Adys", tr("Context menu fixes") }, + { "Jesper Thomschütz", "", tr("Various fixes") }, + { "Jiri Grönroos", "", tr("Finnish translation"), "", QLocale::Finnish }, + { "Johannes Huber", "johu", tr("Many fixes and improvements, bug triaging") }, + { "John Hand", "nox", tr("Original \"All-Seeing Eye\" logo") }, + { "Jonas Heese", "Dante", tr("Project founder, various improvements") }, + { "Joshua T Corbin", "tvakah", tr("Various fixes") }, + { "Jovan Jojkić", "", tr("Serbian translation"), "", QLocale::Serbian }, + { "Jure Repinc", "JLP", tr("Slovenian translation"), "", QLocale::Slovenian }, + { "Jussi Schultink", "jussi01", tr("Tireless tester, {Ku|U}buntu tester and lobbyist, liters of delicious Finnish alcohol") }, + { "K. Ernest Lee", "iFire", tr("Qt5 porting help, Travis CI setup") }, + { "Kevin Funk", "KRF", tr("German translation"), "", QLocale::German }, + { "", "Larso", tr("Finnish translation"), "", QLocale::Finnish }, + { "Lasse Liehu", "", tr("Finnish translation"), "", QLocale::Finnish }, + { "Leo Franchi", "", tr("OSX improvements") }, + { "Liudas Alisauskas", "", tr("Lithuanian translation"), "", QLocale::Lithuanian }, + { "Luke Faraone", "", tr("Documentation fixes") }, + { "Maia Kozheva", "", tr("Russian translation"), "", QLocale::Russian }, + { "Marco Genise", "kaffeedoktor", tr("Ideas, hacking, initial motivation") }, + { "Marco Paolone", "Quizzlo", tr("Italian translation"), "", QLocale::Italian }, + { "Martin Mayer", "m4yer", tr("German translation"), "", QLocale::German }, + { "Martin Sandsmark", "sandsmark", tr("Core and other fixes, QuasselDroid") }, + { "Matthias Coy", "pennywise", tr("German translation"), "", QLocale::German }, + { "Mattia Basaglia", "", tr("Fixes") }, + { "Michael Groh", "brot", tr("German translation, fixes"), "", QLocale::German }, + { "Michael Kedzierski", "ycros", tr("Mac fixes") }, + { "Michael Marley", "mamarley", tr("Many fixes and improvements; Ubuntu live packages") }, + { "Miguel Revilla", "", tr("Spanish translation"), "", QLocale::Spanish }, + { "Nuno Pinheiro", "", tr("Tons of Oxygen icons including the Quassel logo") }, + { "Patrick Lauer", "bonsaikitten", tr("Gentoo maintainer") }, + { "Patrick von Reth", "TheOneRing", tr("MinGW support, SNORE backend, Windows packaging") }, + { "Paul Klumpp", "Haudrauf", tr("Initial design and main window layout") }, + { "Pavel Volkovitskiy", "int", tr("Early beta tester and bughunter") }, + { "Per Nielsen", "", tr("Danish translation"), "", QLocale::Danish }, + { "Pete Beardmore", "elbeardmorez", tr("Linewrap for input line") }, + { "Ramanathan Sivagurunathan", "", tr("Bugfixes") }, + { "Regis Perrin", "ZRegis", tr("French translation"), "", QLocale::French }, + { "Rolf Eike Beer", "", tr("Build system fixes") }, + { "Roscoe van Wyk", "", tr("Bugfixes") }, + { "Rüdiger Sonderfeld", "ruediger", tr("Emacs keybindings") }, + { "", "salnx", tr("Highlight configuration improvements") }, + { "Scott Kitterman", "ScottK", tr("Kubuntu packager, (packaging/build system) bughunter") }, + { "Sebastian Meyer", "", tr("Bugfixes") }, + { "Sebastien Fricker", "", tr("Audio backend improvements") }, + { "", "sfionov", tr("Russian translation"), "", QLocale::Russian }, + { "Simon Philips", "", tr("Dutch translation"), "", QLocale::Dutch }, + { "Sjors Gielen", "dazjorz", tr("Bugfixes") }, + { "Stefanos Sofroniou", "", tr("Greek translation"), "", QLocale::Greek }, + { "Stella Rouzi", "differentreality", tr("Greek translation"), "", QLocale::Greek }, + { "Rafael Belmonte", "EagleScreen", tr("Spanish translation"), "", QLocale::Spanish }, + { "Sergiu Bivol", "", tr("Romanian translation"), "", QLocale::Romanian }, + { "Tae-Hoon Kwon", "", tr("Korean translation"), "", QLocale::Korean }, + { "Terje Andersen", "tan", tr("Norwegian translation, documentation") }, + { "Theo Chatzimichos", "tampakrap", tr("Greek translation"), "", QLocale::Greek }, + { "Theofilos Intzoglou", "", tr("Greek translation"), "", QLocale::Greek }, + { "Thomas Hogh", "Datafreak", tr("Former Windows builder") }, + { "Thomas Müller", "", tr("Fixes, Debian packaging") }, + { "Tim Schumacher", "xAFFE", tr("Fixes and feedback") }, + { "", "ToBeFree", tr("German translation"), "", QLocale::German }, + { "Tomáš Chvátal", "scarabeus", tr("Czech translation"), "", QLocale::Czech }, + { "Vit Pelcak", "", tr("Czech translation"), "", QLocale::Czech }, + { "Volkan Gezer", "", tr("Turkish translation"), "", QLocale::Turkish }, + { "Yaohan Chen", "hagabaka", tr("Network detection improvements") }, + { "Yuri Chornoivan", "", tr("Ukrainian translation"), "", QLocale::Ukrainian }, + { "Zé", "", tr("Portuguese translation"), "", QLocale::Portuguese }, + { "", "zeugma", tr("Turkish translation"), "", QLocale::Turkish } + }); +} diff --git a/src/uisupport/aboutdata.h b/src/uisupport/aboutdata.h new file mode 100644 index 00000000..6c306be9 --- /dev/null +++ b/src/uisupport/aboutdata.h @@ -0,0 +1,194 @@ +/*************************************************************************** + * Copyright (C) 2005-2015 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#pragma once + +#include + +#ifdef HAVE_KF5 +# include +#endif + + +/** + * Represents a contributor or author for Quassel. + * + * This is used to show a list of contributors in the About Quassel dialog. + */ +class AboutPerson +{ +public: + /** + * Constructor. + * + * @param[in] name The person's name (in the form "Firstname Surname") + * @param[in] nick The person's nickname, if applicable + * @param[in] task Things the person does or has done for the project + * @param[in] emailAddress The person's email address, if applicable + * @param[in] translatedLanguage The language the person helped translate (only applicable for translators) + */ + AboutPerson(const QString &name, const QString &nick, const QString &task, const QString &emailAddress = QString(), QLocale::Language translatedLanguage = QLocale::C); + + /** + * Gets the person's name. + * + * @returns The person's name + */ + QString name() const; + + /** + * Gets the person's nick. + * + * @returns The person's nick + */ + QString nick() const; + + /** + * Gets the person's task. + * + * @returns The person's task + */ + QString task() const; + + /** + * Gets the person's e-mail address. + * + * @returns The person's e-mail address + */ + QString emailAddress() const; + + /** + * Gets the language this person helped translate. + * + * @returns The language this person helped translate + */ + QLocale::Language translatedLanguage() const; + + /** + * Gets the person's formatted name and nick. + * + * @returns The person's name and nick formatted for combined output + */ + QString prettyName() const; + +private: + QString _name; ///< The person's name + QString _nick; ///< The person's nick + QString _task; ///< The person's task + QString _emailAddress; ///< The person's email address + QLocale::Language _language; ///< The language the person helps translate +}; + + +/** + * Holds a list of authors, contributors and translators. + * + * This class is meant to hold the list of people who contributed to Quassel, used for displaying + * the About Quassel dialog. Additionally, this class can provide a KAboutData object to be shown + * if KDE integration is enabled. + */ +class AboutData : public QObject +{ + Q_OBJECT +public: + /** + * Default constructor. + * + * @param[in] parent The parent object, if applicable + */ + AboutData(QObject *parent = nullptr); + + /** + * Adds an author to the list of contributors. + * + * Authors are people who contributed a significant amount of code to Quassel. + * + * @param[in] author The author to add + * @returns A reference to this AboutData instance + */ + AboutData &addAuthor(const AboutPerson &author); + + /** + * Adds a list of authors to the list of contributors. + * + * This method allows the use of a brace initializer in order to easily add a long list of + * people. + * + * @param[in] authors A list of authors to add + * @returns A reference to this AboutData instance + */ + AboutData &addAuthors(std::initializer_list authors); + + /** + * Adds a contributor. + * + * @param[in] author The contributor to add + * @returns A reference to this AboutData instance + */ + AboutData &addCredit(const AboutPerson &credit); + + /** + * Adds a list of contributors. + * + * This method allows the use of brace initializers in order to easily add a long list of + * people. + * + * @param[in] authors A list of contributors to add + * @returns A reference to this AboutData instance + */ + AboutData &addCredits(std::initializer_list credits); + + /** + * Gets the list of authors stored in this AboutData instance. + * + * @returns A list of authors + */ + QList authors() const; + + /** + * Gets the list of non-author contributors stored in this AboutData instance. + * + * @returns A list of contributors + */ + QList credits() const; + +#ifdef HAVE_KF5 + /** + * Creates a KAboutData instance based on the contents of this AboutData instance. + * + * @returns A KAboutData instance holding the list of contributors as well as any additional + * data required for KAboutDialog and friends + */ + KAboutData kAboutData() const; +#endif + + /** + * Fills the given AboutData instance with data relevant for Quassel itself. + * + * This method adds a (hardcoded) list of contributors to the given AboutData instance. + * + * @param[in,out] aboutData An existing AboutData instance to add Quassel's contributors to + */ + static void setQuasselPersons(AboutData *aboutData); + +private: + QList _authors; ///< The list of authors + QList _credits; ///< The list of other contributors +}; diff --git a/src/uisupport/actioncollection.cpp b/src/uisupport/actioncollection.cpp index b862ae85..593b236e 100644 --- a/src/uisupport/actioncollection.cpp +++ b/src/uisupport/actioncollection.cpp @@ -20,7 +20,7 @@ * Parts of this implementation are based on KDE's KActionCollection. * ***************************************************************************/ -#ifndef HAVE_KDE4 +#ifndef HAVE_KDE #include #include @@ -305,4 +305,4 @@ bool ActionCollection::unlistAction(QAction *action) } -#endif /* HAVE_KDE4 */ +#endif /* HAVE_KDE */ diff --git a/src/uisupport/actioncollection.h b/src/uisupport/actioncollection.h index a76fc04a..c920adc5 100644 --- a/src/uisupport/actioncollection.h +++ b/src/uisupport/actioncollection.h @@ -20,10 +20,9 @@ * This is a subset of the API of KDE's KActionCollection. * ***************************************************************************/ -#ifndef ACTIONCOLLECTION_H_ -#define ACTIONCOLLECTION_H_ +#pragma once -#ifndef HAVE_KDE4 +#ifndef HAVE_KDE #include #include @@ -123,9 +122,12 @@ private: int ActionCollection::count() const { return actions().count(); } bool ActionCollection::isEmpty() const { return actions().count(); } -#else /* HAVE_KDE4 */ - -#include +#else /* HAVE_KDE */ +# ifdef HAVE_KDE4 +# include +# else +# include +# endif class ActionCollection : public KActionCollection { @@ -135,7 +137,4 @@ public: explicit ActionCollection(QObject *parent) : KActionCollection(parent) {}; }; - -#endif - -#endif +#endif /* HAVE_KDE */ diff --git a/src/uisupport/contextmenuactionprovider.cpp b/src/uisupport/contextmenuactionprovider.cpp index 0d9e483a..87d104fd 100644 --- a/src/uisupport/contextmenuactionprovider.cpp +++ b/src/uisupport/contextmenuactionprovider.cpp @@ -44,6 +44,7 @@ ContextMenuActionProvider::ContextMenuActionProvider(QObject *parent) : NetworkM registerAction(BufferRemove, tr("Delete Chat(s)...")); registerAction(BufferSwitchTo, tr("Go to Chat")); + registerAction(HideJoinPartQuit, tr("Joins/Parts/Quits")); registerAction(HideJoin, tr("Joins"), true); registerAction(HidePart, tr("Parts"), true); registerAction(HideQuit, tr("Quits"), true); @@ -92,6 +93,8 @@ ContextMenuActionProvider::ContextMenuActionProvider(QObject *parent) : NetworkM registerAction(ShowIgnoreList, tr("Show Ignore List")); QMenu *hideEventsMenu = new QMenu(); + hideEventsMenu->addAction(action(HideJoinPartQuit)); + hideEventsMenu->addSeparator(); hideEventsMenu->addAction(action(HideJoin)); hideEventsMenu->addAction(action(HidePart)); hideEventsMenu->addAction(action(HideQuit)); diff --git a/src/uisupport/flatproxymodel.cpp b/src/uisupport/flatproxymodel.cpp index 80d14f2b..bd6dcd08 100644 --- a/src/uisupport/flatproxymodel.cpp +++ b/src/uisupport/flatproxymodel.cpp @@ -204,8 +204,9 @@ QItemSelection FlatProxyModel::mapSelectionToSource(const QItemSelection &proxyS row++; } - Q_ASSERT(topLeftItem && bottomRightItem); // there should be one range left. - sourceSelection << QItemSelectionRange(mapToSource(createIndex(topLeftItem->pos(), left, topLeftItem)), mapToSource(createIndex(bottomRightItem->pos(), right, bottomRightItem))); + if (topLeftItem && bottomRightItem) { // there should be one range left. + sourceSelection << QItemSelectionRange(mapToSource(createIndex(topLeftItem->pos(), left, topLeftItem)), mapToSource(createIndex(bottomRightItem->pos(), right, bottomRightItem))); + } } return sourceSelection; diff --git a/src/uisupport/graphicalui.cpp b/src/uisupport/graphicalui.cpp index 0fb34b04..d1e106d1 100644 --- a/src/uisupport/graphicalui.cpp +++ b/src/uisupport/graphicalui.cpp @@ -146,6 +146,9 @@ bool GraphicalUi::eventFilter(QObject *obj, QEvent *event) } +// NOTE: Window activation stuff seems to work just fine in Plasma 5 without requiring X11 hacks. +// TODO: Evaluate cleaning all this up once we can get rid of Qt4/KDE4 + // Code taken from KStatusNotifierItem for handling minimize/restore bool GraphicalUi::checkMainWidgetVisibility(bool perform) diff --git a/src/uisupport/kcmdlinewrapper.cpp b/src/uisupport/kcmdlinewrapper.cpp index fc102ccf..a263e3c3 100644 --- a/src/uisupport/kcmdlinewrapper.cpp +++ b/src/uisupport/kcmdlinewrapper.cpp @@ -27,11 +27,16 @@ KCmdLineWrapper::KCmdLineWrapper() } -void KCmdLineWrapper::addArgument(const QString &longName, const CliParserArg &arg) +void KCmdLineWrapper::addArgument(const QString &longName_, const CliParserArg &arg) { + QString longName = longName_; + if (arg.type == CliParserArg::CliArgOption && !arg.valueName.isEmpty()) + longName += " <" + arg.valueName + ">"; + if (arg.shortName != 0) { - _cmdLineOptions.add(QByteArray().append(arg.shortName)); + _cmdLineOptions.add(QByteArray(1, arg.shortName)); } + _cmdLineOptions.add(longName.toUtf8(), ki18n(arg.help.toUtf8()), arg.def.toUtf8()); } diff --git a/src/uisupport/multilineedit.cpp b/src/uisupport/multilineedit.cpp index 48ffdbac..ed0dbb60 100644 --- a/src/uisupport/multilineedit.cpp +++ b/src/uisupport/multilineedit.cpp @@ -551,22 +551,18 @@ QString MultiLineEdit::convertRichtextToMircCodes() cursor.clearSelection(); } - if (color) { - color = false; + + if (color) mircText.append('\x03'); - } - if (underline) { - underline = false; + + if (underline) mircText.append('\x1f'); - } - if (italic) { - italic = false; + + if (italic) mircText.append('\x1d'); - } - if (bold) { - bold = false; + + if (bold) mircText.append('\x02'); - } return mircText; } diff --git a/src/uisupport/networkmodelcontroller.cpp b/src/uisupport/networkmodelcontroller.cpp index e4472380..a8946ede 100644 --- a/src/uisupport/networkmodelcontroller.cpp +++ b/src/uisupport/networkmodelcontroller.cpp @@ -307,6 +307,19 @@ void NetworkModelController::handleHideAction(ActionType type, QAction *action) { Q_UNUSED(action) + if (type == HideJoinPartQuit) { + bool anyChecked = NetworkModelController::action(HideJoin)->isChecked(); + anyChecked |= NetworkModelController::action(HidePart)->isChecked(); + anyChecked |= NetworkModelController::action(HideQuit)->isChecked(); + + // If any are checked, uncheck them all. + // If none are checked, check them all. + bool newCheckedState = !anyChecked; + NetworkModelController::action(HideJoin)->setChecked(newCheckedState); + NetworkModelController::action(HidePart)->setChecked(newCheckedState); + NetworkModelController::action(HideQuit)->setChecked(newCheckedState); + } + int filter = 0; if (NetworkModelController::action(HideJoin)->isChecked()) filter |= Message::Join | Message::NetsplitJoin; @@ -324,6 +337,7 @@ void NetworkModelController::handleHideAction(ActionType type, QAction *action) filter |= Message::Topic; switch (type) { + case HideJoinPartQuit: case HideJoin: case HidePart: case HideQuit: diff --git a/src/uisupport/networkmodelcontroller.h b/src/uisupport/networkmodelcontroller.h index 3f1b29fc..f83e23f3 100644 --- a/src/uisupport/networkmodelcontroller.h +++ b/src/uisupport/networkmodelcontroller.h @@ -64,6 +64,7 @@ public: HideMode = 0x0500, HideDayChange = 0x0600, HideTopic = 0x0700, + HideJoinPartQuit = 0xd00, HideUseDefaults = 0xe00, HideApplyToAll = 0xf00, diff --git a/src/uisupport/uistyle.cpp b/src/uisupport/uistyle.cpp index 944b6049..ea0cb19d 100644 --- a/src/uisupport/uistyle.cpp +++ b/src/uisupport/uistyle.cpp @@ -104,8 +104,22 @@ void UiStyle::loadStyleSheet() QString styleSheet; styleSheet += loadStyleSheet("file:///" + Quassel::findDataFilePath("stylesheets/default.qss")); styleSheet += loadStyleSheet("file:///" + Quassel::configDirPath() + "settings.qss"); - if (s.value("UseCustomStyleSheet", false).toBool()) - styleSheet += loadStyleSheet("file:///" + s.value("CustomStyleSheetPath").toString(), true); + if (s.value("UseCustomStyleSheet", false).toBool()) { + QString customSheetPath(s.value("CustomStyleSheetPath").toString()); + QString customSheet = loadStyleSheet("file:///" + customSheetPath, true); + if (customSheet.isEmpty()) { + // MIGRATION: changed default install path for data from /usr/share/apps to /usr/share + if (customSheetPath.startsWith("/usr/share/apps/quassel")) { + customSheetPath.replace(QRegExp("^/usr/share/apps"), "/usr/share"); + customSheet = loadStyleSheet("file:///" + customSheetPath, true); + if (!customSheet.isEmpty()) { + s.setValue("CustomStyleSheetPath", customSheetPath); + qDebug() << "Custom stylesheet path migrated to" << customSheetPath; + } + } + } + styleSheet += customSheet; + } styleSheet += loadStyleSheet("file:///" + Quassel::optionValue("qss"), true); if (!styleSheet.isEmpty()) {