diff --git a/src/application.cpp b/src/application.cpp index dc9ffc84..54a9c09b 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -1,1403 +1,1403 @@ /* 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) any later version. */ /* Copyright (C) 2002 Dario Abatianni Copyright (C) 2005 Ismail Donmez Copyright (C) 2005 Peter Simonsson Copyright (C) 2005 John Tapsell Copyright (C) 2005-2008 Eike Hein */ #include "application.h" #include "connectionmanager.h" #include "scriptlauncher.h" #include "transfermanager.h" #include "viewcontainer.h" #include "urlcatcher.h" #include "highlight.h" #include "server.h" #include "sound.h" #include "quickconnectdialog.h" #include "dbus.h" #include "servergroupsettings.h" #include "serversettings.h" #include "channel.h" #include "images.h" #include "notificationhandler.h" #include "awaymanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Konversation; Application::Application(int &argc, char **argv) : QApplication(argc, argv) { - mainWindow = 0; + mainWindow = nullptr; m_restartScheduled = false; - m_connectionManager = 0; - m_awayManager = 0; - m_scriptLauncher = 0; - quickConnectDialog = 0; - osd = 0; - m_wallet = NULL; - m_images = 0; - m_sound = 0; - m_dccTransferManager = 0; - m_notificationHandler = 0; - m_urlModel = 0; - dbusObject = 0; - identDBus = 0; - m_networkConfigurationManager = 0; + m_connectionManager = nullptr; + m_awayManager = nullptr; + m_scriptLauncher = nullptr; + quickConnectDialog = nullptr; + osd = nullptr; + m_wallet = nullptr; + m_images = nullptr; + m_sound = nullptr; + m_dccTransferManager = nullptr; + m_notificationHandler = nullptr; + m_urlModel = nullptr; + dbusObject = nullptr; + identDBus = nullptr; + m_networkConfigurationManager = nullptr; } Application::~Application() { qDebug(); if (!m_images) return; // Nothing to do, newInstance() has never been called. stashQueueRates(); Preferences::self()->save(); // FIXME i can't figure out why this isn't in saveOptions --argonel saveOptions(false); // Delete m_dccTransferManager here as its destructor depends on the main loop being in tact which it // won't be if if we wait till Qt starts deleting parent pointers. delete m_dccTransferManager; delete m_images; delete m_sound; //delete dbusObject; //delete prefsDCOP; //delete identDBus; delete osd; - osd = 0; + osd = nullptr; closeWallet(); delete m_networkConfigurationManager; if (m_restartScheduled) implementRestart(); } void Application::implementRestart() { // Pop off the executable name. May not be the first argument in argv // everywhere, so verify first. if (QFileInfo(m_restartArguments.first()) == QFileInfo(QCoreApplication::applicationFilePath())) m_restartArguments.removeFirst(); // Don't round-trip --restart. m_restartArguments.removeAll(QStringLiteral("--restart")); // Avoid accumulating multiple --startupdelay arguments across multiple // uses of restart(). if (m_restartArguments.contains(QStringLiteral("--startupdelay"))) { int index = m_restartArguments.lastIndexOf(QStringLiteral("--startupdelay")); if (index < m_restartArguments.count() - 1 && !m_restartArguments.at(index + 1).startsWith(QLatin1Char('-'))) { QString delayArgument = m_restartArguments.at(index + 1); bool ok; uint delay = delayArgument.toUInt(&ok, 10); // If the argument is invalid or too low, raise to at least 2000 msecs. if (!ok || delay < 2000) m_restartArguments.replace(index + 1, QStringLiteral("2000")); } } else m_restartArguments << QStringLiteral("--startupdelay") << QStringLiteral("2000"); KProcess::startDetached(QCoreApplication::applicationFilePath(), m_restartArguments); } void Application::newInstance(QCommandLineParser *args) { QString url; if (args->positionalArguments().count() > 1) url = args->positionalArguments().at(1); if (!mainWindow) { connect(this, &Application::aboutToQuit, this, &Application::prepareShutdown); m_connectionManager = new ConnectionManager(this); m_awayManager = new AwayManager(this); connect(m_connectionManager, &ConnectionManager::identityOnline, m_awayManager, &AwayManager::identityOnline); connect(m_connectionManager, &ConnectionManager::identityOffline, m_awayManager, &AwayManager::identityOffline); connect(m_connectionManager, &ConnectionManager::connectionChangedAwayState, m_awayManager, &AwayManager::updateGlobalAwayAction); m_networkConfigurationManager = new QNetworkConfigurationManager(); connect(m_networkConfigurationManager, SIGNAL(onlineStateChanged(bool)), m_connectionManager, SLOT(onOnlineStateChanged(bool))); m_scriptLauncher = new ScriptLauncher(this); // an instance of DccTransferManager needs to be created before GUI class instances' creation. m_dccTransferManager = new DCC::TransferManager(this); // make sure all vars are initialized properly - quickConnectDialog = 0; + quickConnectDialog = nullptr; // Sound object used to play sound is created when needed. - m_sound = NULL; + m_sound = nullptr; // initialize OSD display here, so we can read the Preferences::properly osd = new OSDWidget( QStringLiteral("Konversation") ); Preferences::self(); readOptions(); // Images object providing LEDs, NickIcons m_images = new Images(); m_urlModel = new QStandardItemModel(0, 3, this); // Auto-alias scripts. This adds any missing aliases QStringList aliasList(Preferences::self()->aliasList()); const QStringList scripts(Preferences::defaultAliasList()); bool changed = false; for ( QStringList::ConstIterator it = scripts.constBegin(); it != scripts.constEnd(); ++it ) { if(!aliasList.contains(*it)) { changed = true; aliasList.append(*it); } } if(changed) Preferences::self()->setAliasList(aliasList); // open main window mainWindow = new MainWindow(); connect(mainWindow.data(), &MainWindow::showQuickConnectDialog, this, &Application::openQuickConnectDialog); connect(Preferences::self(), &Preferences::updateTrayIcon, mainWindow.data(), &MainWindow::updateTrayIcon); connect(mainWindow.data(), &MainWindow::endNotification, osd, &OSDWidget::hide); // take care of user style changes, setting back colors and stuff // apply GUI settings emit appearanceChanged(); if (Preferences::self()->showTrayIcon() && Preferences::self()->hideToTrayOnStartup()) mainWindow->hide(); else mainWindow->show(); bool openServerList = Preferences::self()->showServerList(); // handle autoconnect on startup Konversation::ServerGroupHash serverGroups = Preferences::serverGroupHash(); if (!args->isSet(QStringLiteral("noautoconnect")) && url.isEmpty() && !args->isSet(QStringLiteral("server"))) { QList serversToAutoconnect; QHashIterator it(serverGroups); while(it.hasNext()) { it.next(); if (it.value()->autoConnectEnabled()) { openServerList = false; serversToAutoconnect << it.value(); } } std::sort(serversToAutoconnect.begin(), serversToAutoconnect.end(), [] (const ServerGroupSettingsPtr &left, const ServerGroupSettingsPtr &right) { return left->sortIndex() < right->sortIndex(); }); for (QList::iterator it = serversToAutoconnect.begin(); it != serversToAutoconnect.end(); ++it) { m_connectionManager->connectTo(Konversation::CreateNewConnection, (*it)->id()); } } if (openServerList) mainWindow->openServerList(); connect(this, SIGNAL(serverGroupsChanged(Konversation::ServerGroupSettingsPtr)), this, SLOT(saveOptions())); // prepare dbus interface dbusObject = new Konversation::DBus(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/irc"), dbusObject, QDBusConnection::ExportNonScriptableSlots); identDBus = new Konversation::IdentDBus(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/identity"), identDBus, QDBusConnection::ExportNonScriptableSlots); if (dbusObject) { connect(dbusObject,SIGNAL (dbusMultiServerRaw(QString)), this,SLOT (dbusMultiServerRaw(QString)) ); connect(dbusObject,SIGNAL (dbusRaw(QString,QString)), this,SLOT (dbusRaw(QString,QString)) ); connect(dbusObject,SIGNAL (dbusSay(QString,QString,QString)), this,SLOT (dbusSay(QString,QString,QString)) ); connect(dbusObject,SIGNAL (dbusInfo(QString)), this,SLOT (dbusInfo(QString)) ); connect(dbusObject,SIGNAL (dbusInsertMarkerLine()), mainWindow,SIGNAL(insertMarkerLine())); connect(dbusObject, SIGNAL(connectTo(Konversation::ConnectionFlag,QString,QString,QString,QString,QString,bool)), m_connectionManager, SLOT(connectTo(Konversation::ConnectionFlag,QString,QString,QString,QString,QString,bool))); } m_notificationHandler = new Konversation::NotificationHandler(this); connect(this, &Application::appearanceChanged, this, &Application::updateProxySettings); } else if (args->isSet(QStringLiteral("restart"))) { restart(); return; } if (!url.isEmpty()) getConnectionManager()->connectTo(Konversation::SilentlyReuseConnection, url); else if (args->isSet(QStringLiteral("server"))) { getConnectionManager()->connectTo(Konversation::SilentlyReuseConnection, args->value(QStringLiteral("server")), args->value(QStringLiteral("port")), args->value(QStringLiteral("password")), args->value(QStringLiteral("nick")), args->value(QStringLiteral("channel")), args->isSet(QStringLiteral("ssl"))); } return; } Application* Application::instance() { return static_cast(QApplication::instance()); } void Application::restart() { m_restartScheduled = true; mainWindow->quitProgram(); } void Application::prepareShutdown() { if (mainWindow) mainWindow->getViewContainer()->prepareShutdown(); if (m_awayManager) { m_awayManager->blockSignals(true); delete m_awayManager; - m_awayManager = 0; + m_awayManager = nullptr; } if (m_connectionManager) { m_connectionManager->quitServers(); m_connectionManager->blockSignals(true); delete m_connectionManager; - m_connectionManager = 0; + m_connectionManager = nullptr; } } bool Application::event(QEvent* event) { if (event->type() == QEvent::ApplicationPaletteChange || event->type() == QEvent::ApplicationFontChange) { emit appearanceChanged(); } return QApplication::event(event); } void Application::showQueueTuner(bool p) { getMainWindow()->getViewContainer()->showQueueTuner(p); } void Application::dbusMultiServerRaw(const QString &command) { sendMultiServerCommand(command.section(QLatin1Char(' '), 0,0), command.section(QLatin1Char(' '), 1)); } void Application::dbusRaw(const QString& connection, const QString &command) { Server* server = getConnectionManager()->getServerByName(connection, ConnectionManager::MatchByIdThenName); if (server) server->dbusRaw(command); } void Application::dbusSay(const QString& connection, const QString& target, const QString& command) { Server* server = getConnectionManager()->getServerByName(connection, ConnectionManager::MatchByIdThenName); if (server) server->dbusSay(target, command); } void Application::dbusInfo(const QString& string) { - mainWindow->getViewContainer()->appendToFrontmost(i18n("D-Bus"), string, 0); + mainWindow->getViewContainer()->appendToFrontmost(i18n("D-Bus"), string, nullptr); } void Application::readOptions() { // get standard config file // read nickname sorting order for channel nick lists KConfigGroup cgSortNicknames(KSharedConfig::openConfig()->group("Sort Nicknames")); QString sortOrder=cgSortNicknames.readEntry("SortOrder"); QStringList sortOrderList=sortOrder.split(QString()); sortOrderList.sort(); if (sortOrderList.join(QString())!=QStringLiteral("-hopqv")) { sortOrder=Preferences::defaultNicknameSortingOrder(); Preferences::self()->setSortOrder(sortOrder); } // Identity list QStringList identityList=KSharedConfig::openConfig()->groupList().filter(QRegExp(QStringLiteral("Identity [0-9]+"))); if (!identityList.isEmpty()) { Preferences::clearIdentityList(); for(int index=0;indexgroup(identityList[index])); newIdentity->setName(cgIdentity.readEntry("Name")); newIdentity->setIdent(cgIdentity.readEntry("Ident")); newIdentity->setRealName(cgIdentity.readEntry("Realname")); newIdentity->setNicknameList(cgIdentity.readEntry("Nicknames",QStringList())); newIdentity->setAuthType(cgIdentity.readEntry("AuthType", "nickserv")); newIdentity->setAuthPassword(cgIdentity.readEntry("Password")); newIdentity->setNickservNickname(cgIdentity.readEntry("Bot")); newIdentity->setNickservCommand(cgIdentity.readEntry("NickservCommand", "identify")); newIdentity->setSaslAccount(cgIdentity.readEntry("SaslAccount")); newIdentity->setPemClientCertFile(cgIdentity.readEntry("PemClientCertFile", QUrl())); newIdentity->setInsertRememberLineOnAway(cgIdentity.readEntry("InsertRememberLineOnAway", false)); newIdentity->setRunAwayCommands(cgIdentity.readEntry("ShowAwayMessage", false)); newIdentity->setAwayCommand(cgIdentity.readEntry("AwayMessage")); newIdentity->setReturnCommand(cgIdentity.readEntry("ReturnMessage")); newIdentity->setAutomaticAway(cgIdentity.readEntry("AutomaticAway", false)); newIdentity->setAwayInactivity(cgIdentity.readEntry("AwayInactivity", 10)); newIdentity->setAutomaticUnaway(cgIdentity.readEntry("AutomaticUnaway", false)); newIdentity->setQuitReason(cgIdentity.readEntry("QuitReason")); newIdentity->setPartReason(cgIdentity.readEntry("PartReason")); newIdentity->setKickReason(cgIdentity.readEntry("KickReason")); newIdentity->setShellCommand(cgIdentity.readEntry("PreShellCommand")); newIdentity->setCodecName(cgIdentity.readEntry("Codec")); newIdentity->setAwayMessage(cgIdentity.readEntry("AwayReason")); newIdentity->setAwayNickname(cgIdentity.readEntry("AwayNick")); Preferences::addIdentity(newIdentity); } } osd->setEnabled(Preferences::self()->useOSD()); //How to load the font from the text? osd->setFont(Preferences::self()->oSDFont()); osd->setDuration(Preferences::self()->oSDDuration()); osd->setScreen(Preferences::self()->oSDScreen()); osd->setShadow(Preferences::self()->oSDDrawShadow()); osd->setOffset(Preferences::self()->oSDOffsetX(), Preferences::self()->oSDOffsetY()); osd->setAlignment((OSDWidget::Alignment)Preferences::self()->oSDAlignment()); if(Preferences::self()->oSDUseCustomColors()) { osd->setTextColor(Preferences::self()->oSDTextColor()); QPalette p = osd->palette(); p.setColor(osd->backgroundRole(), Preferences::self()->oSDBackgroundColor()); osd->setPalette(p); } // Check if there is old server list config //TODO FIXME why are we doing this here? KConfigGroup cgServerList(KSharedConfig::openConfig()->group("Server List")); // Read the new server settings QStringList groups = KSharedConfig::openConfig()->groupList().filter(QRegExp(QStringLiteral("ServerGroup [0-9]+"))); QMap notifyList; QList sgKeys; if(!groups.isEmpty()) { Konversation::ServerGroupHash serverGroups; QStringList::iterator it; QStringList tmp1; QStringList::iterator it2; int index = 0; Konversation::ChannelList channelHistory; Konversation::ServerSettings server; Konversation::ChannelSettings channel; for (it = groups.begin(); it != groups.end(); ++it) { KConfigGroup cgServerGroup(KSharedConfig::openConfig()->group(*it)); Konversation::ServerGroupSettingsPtr serverGroup(new Konversation::ServerGroupSettings); serverGroup->setName(cgServerGroup.readEntry("Name")); serverGroup->setSortIndex(groups.at(index).section(' ', -1).toInt() ); serverGroup->setIdentityId(Preferences::identityByName(cgServerGroup.readEntry("Identity"))->id()); serverGroup->setConnectCommands(cgServerGroup.readEntry("ConnectCommands")); serverGroup->setAutoConnectEnabled(cgServerGroup.readEntry("AutoConnect", false)); serverGroup->setNotificationsEnabled(cgServerGroup.readEntry("EnableNotifications", true)); serverGroup->setExpanded(cgServerGroup.readEntry("Expanded", false)); notifyList.insert((*serverGroup).id(), cgServerGroup.readEntry("NotifyList", QString()).split(QLatin1Char(' '), QString::SkipEmptyParts)); tmp1 = cgServerGroup.readEntry("ServerList", QStringList()); for (it2 = tmp1.begin(); it2 != tmp1.end(); ++it2) { KConfigGroup cgServer(KSharedConfig::openConfig()->group(*it2)); server.setHost(cgServer.readEntry("Server")); server.setPort(cgServer.readEntry("Port", 0)); server.setPassword(cgServer.readEntry("Password")); server.setSSLEnabled(cgServer.readEntry("SSLEnabled", false)); server.setBypassProxy(cgServer.readEntry("BypassProxy", false)); serverGroup->addServer(server); } //config->setGroup((*it)); tmp1 = cgServerGroup.readEntry("AutoJoinChannels", QStringList()); for (it2 = tmp1.begin(); it2 != tmp1.end(); ++it2) { KConfigGroup cgJoin(KSharedConfig::openConfig()->group(*it2)); if (!cgJoin.readEntry("Name").isEmpty()) { channel.setName(cgJoin.readEntry("Name")); channel.setPassword(cgJoin.readEntry("Password")); serverGroup->addChannel(channel); } } //config->setGroup((*it)); tmp1 = cgServerGroup.readEntry("ChannelHistory", QStringList()); channelHistory.clear(); for (it2 = tmp1.begin(); it2 != tmp1.end(); ++it2) { KConfigGroup cgChanHistory(KSharedConfig::openConfig()->group(*it2)); if (!cgChanHistory.readEntry("Name").isEmpty()) { channel.setName(cgChanHistory.readEntry("Name")); channel.setPassword(cgChanHistory.readEntry("Password")); channel.setNotificationsEnabled(cgChanHistory.readEntry("EnableNotifications", true)); channelHistory.append(channel); } } serverGroup->setChannelHistory(channelHistory); serverGroups.insert(serverGroup->id(), serverGroup); sgKeys.append(serverGroup->id()); index++; } Preferences::setServerGroupHash(serverGroups); } // Notify Settings and lists. Must follow Server List. Preferences::setNotifyList(notifyList); Preferences::self()->setNotifyDelay(Preferences::self()->notifyDelay()); Preferences::self()->setUseNotify(Preferences::self()->useNotify()); // Quick Buttons List // if there are button definitions in the config file, remove default buttons if (KSharedConfig::openConfig()->hasGroup("Button List")) Preferences::clearQuickButtonList(); KConfigGroup cgQuickButtons(KSharedConfig::openConfig()->group("Button List")); // Read all default buttons QStringList buttonList(Preferences::quickButtonList()); // Read all quick buttons int index=0; while (cgQuickButtons.hasKey(QString(QStringLiteral("Button%1")).arg(index))) { buttonList.append(cgQuickButtons.readEntry(QString(QStringLiteral("Button%1")).arg(index++))); } // while // Put back the changed button list Preferences::setQuickButtonList(buttonList); // Autoreplace List // if there are autoreplace definitions in the config file, remove default entries if (KSharedConfig::openConfig()->hasGroup("Autoreplace List")) Preferences::clearAutoreplaceList(); KConfigGroup cgAutoreplace(KSharedConfig::openConfig()->group("Autoreplace List")); // Read all default entries QList autoreplaceList(Preferences::autoreplaceList()); // Read all entries index=0; // legacy code for old autoreplace format 4/6/09 QString autoReplaceString(QStringLiteral("Autoreplace")); while (cgAutoreplace.hasKey(autoReplaceString + QString::number(index))) { // read entry and get length of the string QString entry=cgAutoreplace.readEntry(autoReplaceString + QString::number(index++)); int length=entry.length()-1; // if there's a "#" in the end, strip it (used to preserve blanks at the end of the replacement text) // there should always be one, but older versions did not do it, so we check first if (entry.at(length)==QLatin1Char('#')) entry=entry.left(length); QString regex = entry.section(QLatin1Char(','),0,0); QString direction = entry.section(QLatin1Char(','),1,1); QString pattern = entry.section(QLatin1Char(','),2,2); QString replace = entry.section(QLatin1Char(','),3); // add entry to internal list autoreplaceList.append(QStringList() << regex << direction << pattern << replace); } // while //end legacy code for old autoreplace format index=0; //new code for autoreplace config QString indexString(QString::number(index)); QString regexString(QStringLiteral("Regex")); QString directString(QStringLiteral("Direction")); QString patternString(QStringLiteral("Pattern")); QString replaceString(QStringLiteral("Replace")); while (cgAutoreplace.hasKey(patternString + indexString)) { QString pattern = cgAutoreplace.readEntry(patternString + indexString); QString regex = cgAutoreplace.readEntry(regexString + indexString, QStringLiteral("0")); QString direction = cgAutoreplace.readEntry(directString + indexString, QStringLiteral("o")); QString replace = cgAutoreplace.readEntry(replaceString + indexString, QString()); if (replace.length()>0) { int repLen=replace.length()-1; if (replace.at(repLen)==QLatin1Char('#')) replace=replace.left(repLen); } if (pattern.length()>0) { int patLen=pattern.length()-1; if (pattern.at(patLen)==QLatin1Char('#')) pattern=pattern.left(patLen); } index++; indexString = QString::number(index); autoreplaceList.append(QStringList() << regex << direction << pattern << replace); } // Put back the changed autoreplace list Preferences::setAutoreplaceList(autoreplaceList); //TODO FIXME I assume this is in the group, but I have a hunch we just don't care about <1.0.1 // Highlight List KConfigGroup cgDefault(KSharedConfig::openConfig()->group("")); if (cgDefault.hasKey("Highlight")) // Stay compatible with versions < 0.14 { QString highlight=cgDefault.readEntry("Highlight"); QStringList hiList = highlight.split(QLatin1Char(' '), QString::SkipEmptyParts); for (int hiIndex=0; hiIndex < hiList.count(); hiIndex+=2) { Preferences::addHighlight(hiList[hiIndex], false, QString(QLatin1Char('#')+hiList[hiIndex+1]), QString(), QString(), QString(), true); } cgDefault.deleteEntry("Highlight"); } else { int i = 0; while (KSharedConfig::openConfig()->hasGroup(QString(QStringLiteral("Highlight%1")).arg(i))) { KConfigGroup cgHilight(KSharedConfig::openConfig()->group(QString(QStringLiteral("Highlight%1")).arg(i))); Preferences::addHighlight( cgHilight.readEntry("Pattern"), cgHilight.readEntry("RegExp", false), cgHilight.readEntry("Color", QColor(Qt::black)), cgHilight.readPathEntry("Sound", QString()), cgHilight.readEntry("AutoText"), cgHilight.readEntry("ChatWindows"), cgHilight.readEntry("Notify", true) ); i++; } } // Ignore List KConfigGroup cgIgnoreList(KSharedConfig::openConfig()->group("Ignore List")); // Remove all default entries if there is at least one Ignore in the Preferences::file if (cgIgnoreList.hasKey("Ignore0")) Preferences::clearIgnoreList(); // Read all ignores index=0; while (cgIgnoreList.hasKey(QString(QStringLiteral("Ignore%1")).arg(index))) { Preferences::addIgnore(cgIgnoreList.readEntry(QString(QStringLiteral("Ignore%1")).arg(index++))); } // Aliases KConfigGroup cgAliases(KSharedConfig::openConfig()->group("Aliases")); QStringList newList=cgAliases.readEntry("AliasList", QStringList()); if (!newList.isEmpty()) Preferences::self()->setAliasList(newList); // Channel Encodings //Legacy channel encodings read in Jun. 29, 2009 KConfigGroup cgChannelEncodings(KSharedConfig::openConfig()->group("Channel Encodings")); QMap channelEncodingEntries=cgChannelEncodings.entryMap(); QRegExp re(QStringLiteral("^(.+) ([^\\s]+)$")); QList channelEncodingEntryKeys=channelEncodingEntries.keys(); for(QList::const_iterator itStr=channelEncodingEntryKeys.constBegin(); itStr != channelEncodingEntryKeys.constEnd(); ++itStr) { if(re.indexIn(*itStr) > -1) { Preferences::setChannelEncoding(re.cap(1),re.cap(2),channelEncodingEntries[*itStr]); } } //End legacy channel encodings read in Jun 29, 2009 KConfigGroup cgEncodings(KSharedConfig::openConfig()->group("Encodings")); QMap encodingEntries=cgEncodings.entryMap(); QList encodingEntryKeys=encodingEntries.keys(); QRegExp reg(QStringLiteral("^([^\\s]+) ([^\\s]+)\\s?([^\\s]*)$")); for(QList::const_iterator itStr=encodingEntryKeys.constBegin(); itStr != encodingEntryKeys.constEnd(); ++itStr) { if(reg.indexIn(*itStr) > -1) { if(reg.cap(1) == QStringLiteral("ServerGroup") && !reg.cap(3).isEmpty()) Preferences::setChannelEncoding(sgKeys.at(reg.cap(2).toInt()), reg.cap(3), encodingEntries[*itStr]); else Preferences::setChannelEncoding(reg.cap(1), reg.cap(2), encodingEntries[*itStr]); } } // Spell Checking Languages KConfigGroup cgSpellCheckingLanguages(KSharedConfig::openConfig()->group("Spell Checking Languages")); QMap spellCheckingLanguageEntries=cgSpellCheckingLanguages.entryMap(); QList spellCheckingLanguageEntryKeys=spellCheckingLanguageEntries.keys(); for (QList::const_iterator itStr=spellCheckingLanguageEntryKeys.constBegin(); itStr != spellCheckingLanguageEntryKeys.constEnd(); ++itStr) { if (reg.indexIn(*itStr) > -1) { if (reg.cap(1) == QStringLiteral("ServerGroup") && !reg.cap(3).isEmpty()) { ServerGroupSettingsPtr serverGroup = Preferences::serverGroupById(sgKeys.at(reg.cap(2).toInt())); if (serverGroup) Preferences::setSpellCheckingLanguage(serverGroup, reg.cap(3), spellCheckingLanguageEntries[*itStr]); } else Preferences::setSpellCheckingLanguage(reg.cap(1), reg.cap(2), spellCheckingLanguageEntries[*itStr]); } } fetchQueueRates(); updateProxySettings(); } void Application::saveOptions(bool updateGUI) { // template: KConfigGroup (KSharedConfig::openConfig()->group( )); //KConfig* config=KSharedConfig::openConfig(); // Should be handled in NicklistBehaviorConfigController now // config->setGroup("Sort Nicknames"); // Clean up identity list QStringList identities=KSharedConfig::openConfig()->groupList().filter(QRegExp(QStringLiteral("Identity [0-9]+"))); if (identities.count()) { // remove old identity list from Preferences::file to keep numbering under control for (int index=0; index < identities.count(); index++) KSharedConfig::openConfig()->deleteGroup(identities[index]); } IdentityList identityList = Preferences::identityList(); int index = 0; for (IdentityList::ConstIterator it = identityList.constBegin(); it != identityList.constEnd(); ++it) { IdentityPtr identity = (*it); KConfigGroup cgIdentity(KSharedConfig::openConfig()->group(QString(QStringLiteral("Identity %1")).arg(index))); cgIdentity.writeEntry("Name",identity->getName()); cgIdentity.writeEntry("Ident",identity->getIdent()); cgIdentity.writeEntry("Realname",identity->getRealName()); cgIdentity.writeEntry("Nicknames",identity->getNicknameList()); cgIdentity.writeEntry("AuthType",identity->getAuthType()); cgIdentity.writeEntry("Password",identity->getAuthPassword()); cgIdentity.writeEntry("Bot",identity->getNickservNickname()); cgIdentity.writeEntry("NickservCommand",identity->getNickservCommand()); cgIdentity.writeEntry("SaslAccount",identity->getSaslAccount()); cgIdentity.writeEntry("PemClientCertFile", identity->getPemClientCertFile().toString()); cgIdentity.writeEntry("InsertRememberLineOnAway", identity->getInsertRememberLineOnAway()); cgIdentity.writeEntry("ShowAwayMessage",identity->getRunAwayCommands()); cgIdentity.writeEntry("AwayMessage",identity->getAwayCommand()); cgIdentity.writeEntry("ReturnMessage",identity->getReturnCommand()); cgIdentity.writeEntry("AutomaticAway", identity->getAutomaticAway()); cgIdentity.writeEntry("AwayInactivity", identity->getAwayInactivity()); cgIdentity.writeEntry("AutomaticUnaway", identity->getAutomaticUnaway()); cgIdentity.writeEntry("QuitReason",identity->getQuitReason()); cgIdentity.writeEntry("PartReason",identity->getPartReason()); cgIdentity.writeEntry("KickReason",identity->getKickReason()); cgIdentity.writeEntry("PreShellCommand",identity->getShellCommand()); cgIdentity.writeEntry("Codec",identity->getCodecName()); cgIdentity.writeEntry("AwayReason",identity->getAwayMessage()); cgIdentity.writeEntry("AwayNick", identity->getAwayNickname()); index++; } // endfor // Remove the old servergroups from the config QStringList groups = KSharedConfig::openConfig()->groupList().filter(QRegExp(QStringLiteral("ServerGroup [0-9]+"))); if (groups.count()) { QStringList::iterator it; for(it = groups.begin(); it != groups.end(); ++it) { KSharedConfig::openConfig()->deleteGroup((*it)); } } // Remove the old servers from the config groups = KSharedConfig::openConfig()->groupList().filter(QRegExp(QStringLiteral("Server [0-9]+"))); if (groups.count()) { QStringList::iterator it; for(it = groups.begin(); it != groups.end(); ++it) { KSharedConfig::openConfig()->deleteGroup((*it)); } } // Remove the old channels from the config groups = KSharedConfig::openConfig()->groupList().filter(QRegExp(QStringLiteral("Channel [0-9]+"))); if (groups.count()) { QStringList::iterator it; for(it = groups.begin(); it != groups.end(); ++it) { KSharedConfig::openConfig()->deleteGroup((*it)); } } // Add the new servergroups to the config Konversation::ServerGroupHash serverGroupHash = Preferences::serverGroupHash(); QHashIterator hashIt(serverGroupHash); QMap sortedServerGroupMap; // Make the indices in the group headers reflect the server list dialog sorting. while (hashIt.hasNext()) { hashIt.next(); sortedServerGroupMap.insert(hashIt.value()->sortIndex(), hashIt.value()); } QMapIterator it(sortedServerGroupMap); index = 0; int index2 = 0; int index3 = 0; int width = 0; QList keys = serverGroupHash.keys(); for(int i=0; i sgKeys; while(it.hasNext()) { it.next(); serverlist = (it.value())->serverList(); servers.clear(); sgKeys.append(it.value()->id()); for(it2 = serverlist.begin(); it2 != serverlist.end(); ++it2) { groupName = QString(QStringLiteral("Server %1")).arg(index2); servers.append(groupName); KConfigGroup cgServer(KSharedConfig::openConfig()->group(groupName)); cgServer.writeEntry("Server", (*it2).host()); cgServer.writeEntry("Port", (*it2).port()); cgServer.writeEntry("Password", (*it2).password()); cgServer.writeEntry("SSLEnabled", (*it2).SSLEnabled()); cgServer.writeEntry("BypassProxy", (*it2).bypassProxy()); index2++; } channelList = it.value()->channelList(); channels.clear(); for(it3 = channelList.begin(); it3 != channelList.end(); ++it3) { groupName = QString(QStringLiteral("Channel %1")).arg(index3); channels.append(groupName); KConfigGroup cgChannel(KSharedConfig::openConfig()->group(groupName)); cgChannel.writeEntry("Name", (*it3).name()); cgChannel.writeEntry("Password", (*it3).password()); index3++; } channelList = it.value()->channelHistory(); channelHistory.clear(); for(it3 = channelList.begin(); it3 != channelList.end(); ++it3) { // TODO FIXME: is it just me or is this broken? groupName = QString(QStringLiteral("Channel %1")).arg(index3); channelHistory.append(groupName); KConfigGroup cgChannelHistory(KSharedConfig::openConfig()->group(groupName)); cgChannelHistory.writeEntry("Name", (*it3).name()); cgChannelHistory.writeEntry("Password", (*it3).password()); cgChannelHistory.writeEntry("EnableNotifications", (*it3).enableNotifications()); index3++; } QString sgn = QString(QStringLiteral("ServerGroup %1")).arg(QString::number(index).rightJustified(width,QLatin1Char('0'))); KConfigGroup cgServerGroup(KSharedConfig::openConfig()->group(sgn)); cgServerGroup.writeEntry("Name", it.value()->name()); cgServerGroup.writeEntry("Identity", it.value()->identity()->getName()); cgServerGroup.writeEntry("ServerList", servers); cgServerGroup.writeEntry("AutoJoinChannels", channels); cgServerGroup.writeEntry("ConnectCommands", it.value()->connectCommands()); cgServerGroup.writeEntry("AutoConnect", it.value()->autoConnectEnabled()); cgServerGroup.writeEntry("ChannelHistory", channelHistory); cgServerGroup.writeEntry("EnableNotifications", it.value()->enableNotifications()); cgServerGroup.writeEntry("Expanded", it.value()->expanded()); cgServerGroup.writeEntry("NotifyList",Preferences::notifyStringByGroupId(it.value()->id())); index++; } KSharedConfig::openConfig()->deleteGroup("Server List"); // Ignore List KSharedConfig::openConfig()->deleteGroup("Ignore List"); KConfigGroup cgIgnoreList(KSharedConfig::openConfig()->group("Ignore List")); QList ignoreList=Preferences::ignoreList(); for (int i = 0; i < ignoreList.size(); ++i) { cgIgnoreList.writeEntry(QString(QStringLiteral("Ignore%1")).arg(i),QString(QStringLiteral("%1,%2")).arg(ignoreList.at(i)->getName()).arg(ignoreList.at(i)->getFlags())); } // Channel Encodings // remove all entries once KSharedConfig::openConfig()->deleteGroup("Channel Encodings"); // legacy Jun 29, 2009 KSharedConfig::openConfig()->deleteGroup("Encodings"); KConfigGroup cgEncoding(KSharedConfig::openConfig()->group("Encodings")); QList encServers=Preferences::channelEncodingsServerGroupIdList(); //i have no idea these would need to be sorted //encServers.sort(); QList::iterator encServer; for ( encServer = encServers.begin(); encServer != encServers.end(); ++encServer ) { Konversation::ServerGroupSettingsPtr sgsp = Preferences::serverGroupById(*encServer); if ( sgsp ) // sgsp == 0 when the entry is of QuickConnect or something? { QStringList encChannels=Preferences::channelEncodingsChannelList(*encServer); //ditto //encChannels.sort(); QStringList::iterator encChannel; for ( encChannel = encChannels.begin(); encChannel != encChannels.end(); ++encChannel ) { QString enc = Preferences::channelEncoding(*encServer, *encChannel); QString key = QLatin1Char(' ') + (*encChannel); if(sgKeys.contains(*encServer)) key.prepend(QStringLiteral("ServerGroup ")+QString::number(sgKeys.indexOf(*encServer))); else key.prepend(sgsp->name()); cgEncoding.writeEntry(key, enc); } } } // Spell Checking Languages KSharedConfig::openConfig()->deleteGroup("Spell Checking Languages"); KConfigGroup cgSpellCheckingLanguages(KSharedConfig::openConfig()->group("Spell Checking Languages")); QHashIterator > i(Preferences::serverGroupSpellCheckingLanguages()); while (i.hasNext()) { i.next(); QHashIterator i2(i.value()); while (i2.hasNext()) { i2.next(); int serverGroupIndex = sgKeys.indexOf(i.key()->id()); if (serverGroupIndex != -1) cgSpellCheckingLanguages.writeEntry(QStringLiteral("ServerGroup ") + QString::number(serverGroupIndex) + QLatin1Char(' ') + i2.key(), i2.value()); } } QHashIterator > i3(Preferences::serverSpellCheckingLanguages()); while (i3.hasNext()) { i3.next(); QHashIterator i4(i3.value()); while (i4.hasNext()) { i4.next(); cgSpellCheckingLanguages.writeEntry(i3.key() + QLatin1Char(' ') + i4.key(), i4.value()); } } KSharedConfig::openConfig()->sync(); if(updateGUI) emit appearanceChanged(); } void Application::fetchQueueRates() { //The following rate was found in the rc for all queues, which were deliberately bad numbers chosen for debugging. //Its possible that the static array was constructed or deconstructed at the wrong time, and so those values saved //in the rc. When reading the values out of the rc, we must check to see if they're this specific value, //and if so, reset to defaults. --argonel IRCQueue::EmptyingRate shit(6, 50000, IRCQueue::EmptyingRate::Lines); int bad = 0; for (int i=0; i <= countOfQueues(); i++) { QList r = Preferences::self()->queueRate(i); staticrates[i] = IRCQueue::EmptyingRate(r[0], r[1]*1000, IRCQueue::EmptyingRate::RateType(r[2])); if (staticrates[i] == shit) bad++; } if (bad == 3) resetQueueRates(); } void Application::stashQueueRates() { for (int i=0; i <= countOfQueues(); i++) { QList r; r.append(staticrates[i].m_rate); r.append(staticrates[i].m_interval / 1000); r.append(int(staticrates[i].m_type)); Preferences::self()->setQueueRate(i, r); } } void Application::resetQueueRates() { for (int i=0; i <= countOfQueues(); i++) { Preferences::self()->queueRateItem(i)->setDefault(); QList r=Preferences::self()->queueRate(i); staticrates[i]=IRCQueue::EmptyingRate(r[0], r[1]*1000, IRCQueue::EmptyingRate::RateType(r[2])); } } void Application::storeUrl(const QString& origin, const QString& newUrl, const QDateTime& dateTime) { QString url(newUrl); url = url.replace(QStringLiteral("&"), QStringLiteral("&")); QList existing = m_urlModel->findItems(url, Qt::MatchExactly, 1); foreach(QStandardItem* item, existing) { if (m_urlModel->item(item->row(), 0)->data(Qt::DisplayRole).toString() == origin) m_urlModel->removeRow(item->row()); } m_urlModel->insertRow(0); m_urlModel->setData(m_urlModel->index(0, 0), origin, Qt::DisplayRole); m_urlModel->setData(m_urlModel->index(0, 1), url, Qt::DisplayRole); UrlDateItem* dateItem = new UrlDateItem(dateTime); m_urlModel->setItem(0, 2, dateItem); } void Application::openQuickConnectDialog() { quickConnectDialog = new QuickConnectDialog(mainWindow); connect(quickConnectDialog, SIGNAL(connectClicked(Konversation::ConnectionFlag,QString,QString,QString,QString,QString,bool)), m_connectionManager, SLOT(connectTo(Konversation::ConnectionFlag,QString,QString,QString,QString,QString,bool))); quickConnectDialog->show(); } void Application::sendMultiServerCommand(const QString& command, const QString& parameter) { const QList serverList = getConnectionManager()->getServerList(); foreach (Server* server, serverList) server->executeMultiServerCommand(command, parameter); } void Application::splitNick_Server(const QString& nick_server, QString &ircnick, QString &serverOrGroup) { //kaddresbook uses the utf separator 0xE120, so treat that as a separator as well QString nickServer = nick_server; nickServer.replace(QChar(0xE120), QStringLiteral("@")); ircnick = nickServer.section(QLatin1Char('@'),0,0); serverOrGroup = nickServer.section(QLatin1Char('@'),1); } NickInfoPtr Application::getNickInfo(const QString &ircnick, const QString &serverOrGroup) { const QList serverList = getConnectionManager()->getServerList(); NickInfoPtr nickInfo; QString lserverOrGroup = serverOrGroup.toLower(); foreach (Server* lookServer, serverList) { if (lserverOrGroup.isEmpty() || lookServer->getServerName().toLower()==lserverOrGroup || lookServer->getDisplayName().toLower()==lserverOrGroup) { nickInfo = lookServer->getNickInfo(ircnick); if (nickInfo) return nickInfo; //If we found one } } - return (NickInfoPtr)0; + return (NickInfoPtr)nullptr; } // auto replace on input/output QPair Application::doAutoreplace(const QString& text, bool output, int cursorPos) { // get autoreplace list QList autoreplaceList=Preferences::autoreplaceList(); // working copy QString line=text; // loop through the list of replacement patterns for (int index=0;index -1 && cursorPos >= index) { if (cursorPos < index + captures[0].length()) cursorPos = newIndex; else { if (captures[0].length() > replaceWith.length()) cursorPos -= captures[0].length() - replaceWith.length(); else cursorPos += replaceWith.length() - captures[0].length(); } } index = newIndex; } } while (index >= 0 && index < line.length()); } else { QRegExp needleReg(pattern); needleReg.setPatternSyntax(QRegExp::FixedString); int index=line.indexOf(needleReg); while (index>=0) { int length,nextLength,patLen,repLen; patLen=pattern.length(); repLen=replacement.length(); length=index; length+=patLen; nextLength=length; //nextlength is used to account for the replacement taking up less space QChar before,after; if (index!=0) before = line.at(index-1); if (line.length() > length) after = line.at(length); if (index==0 || before.isSpace() || before.isPunct()) { if (line.length() == length || after.isSpace() || after.isPunct()) { // allow for var expansion in autoreplace replacement = Konversation::doVarExpansion(replacement); line.replace(index,patLen,replacement); nextLength = index+repLen; } } if (cursorPos > -1 && cursorPos >= index) { if (cursorPos < length) cursorPos = nextLength; else { if (patLen > repLen) cursorPos -= patLen - repLen; else cursorPos += repLen - patLen; } } index=line.indexOf(needleReg,nextLength); } } } } return QPair(line, cursorPos); } void Application::doInlineAutoreplace(KTextEdit* textEdit) { QTextCursor cursor(textEdit->document()); cursor.beginEditBlock(); const QPair& replace = Application::instance()->doAutoreplace(textEdit->toPlainText(), true, textEdit->textCursor().position()); cursor.select(QTextCursor::Document); cursor.insertText(replace.first); cursor.setPosition(replace.second); cursor.endEditBlock(); textEdit->setTextCursor(cursor); } void Application::openUrl(const QString& url) { if (!Preferences::self()->useCustomBrowser() || url.startsWith(QLatin1String("mailto:")) || url.startsWith(QLatin1String("amarok:"))) { if (url.startsWith(QLatin1String("irc://")) || url.startsWith(QLatin1String("ircs://"))) Application::instance()->getConnectionManager()->connectTo(Konversation::SilentlyReuseConnection, url); else if (url.startsWith(QLatin1String("mailto:"))) QDesktopServices::openUrl(QUrl(url)); else #ifndef Q_OS_WIN new KRun(QUrl(url), Application::instance()->getMainWindow()); #else QDesktopServices::openUrl(QUrl::fromUserInput(url)); #endif } else { QHash map; map.insert(QLatin1Char('u'), url); const QString cmd = KMacroExpander::expandMacrosShellQuote(Preferences::self()->webBrowserCmd(), map); const QStringList args = KShell::splitArgs(cmd); if (!args.isEmpty()) { KProcess::startDetached(args); return; } } } Konversation::Sound* Application::sound() { if (!m_sound) m_sound = new Konversation::Sound; return m_sound; } void Application::updateProxySettings() { if (Preferences::self()->proxyEnabled()) { QNetworkProxy proxy; if (Preferences::self()->proxyType() == Preferences::Socksv5Proxy) { proxy.setType(QNetworkProxy::Socks5Proxy); } else { proxy.setType(QNetworkProxy::HttpProxy); } proxy.setHostName(Preferences::self()->proxyAddress()); proxy.setPort(Preferences::self()->proxyPort()); proxy.setUser(Preferences::self()->proxyUsername()); QString password; if(wallet()) { int ret = wallet()->readPassword(QStringLiteral("ProxyPassword"), password); if(ret != 0) { qCritical() << "Failed to read the proxy password from the wallet, error code:" << ret; } } proxy.setPassword(password); QNetworkProxy::setApplicationProxy(proxy); } else { QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); } } KWallet::Wallet* Application::wallet() { if(!m_wallet) { WId winid = 0; if(mainWindow) winid = mainWindow->winId(); m_wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), winid); if(!m_wallet) - return NULL; + return nullptr; connect(m_wallet, &KWallet::Wallet::walletClosed, this, &Application::closeWallet); if(!m_wallet->hasFolder(QStringLiteral("Konversation"))) { if(!m_wallet->createFolder(QStringLiteral("Konversation"))) { qCritical() << "Failed to create folder Konversation in the network wallet."; closeWallet(); - return NULL; + return nullptr; } } if(!m_wallet->setFolder(QStringLiteral("Konversation"))) { qCritical() << "Failed to set active folder to Konversation in the network wallet."; closeWallet(); - return NULL; + return nullptr; } } return m_wallet; } void Application::closeWallet() { delete m_wallet; - m_wallet = NULL; + m_wallet = nullptr; } void Application::handleActivate(const QStringList& arguments) { m_commandLineParser->parse(QStringList(applicationFilePath()) << arguments); if(m_commandLineParser->isSet(QStringLiteral("restart"))) { m_restartArguments = arguments; } newInstance(m_commandLineParser); KStartupInfo::setNewStartupId(mainWindow, KStartupInfo::startupId()); mainWindow->show(); mainWindow->raise(); } // kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on; // vim: set et sw=4 ts=4 cino=l1,cs,U1: diff --git a/src/config/alias_config.cpp b/src/config/alias_config.cpp index 8677fd62..ceb21e79 100644 --- a/src/config/alias_config.cpp +++ b/src/config/alias_config.cpp @@ -1,210 +1,210 @@ /* 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) any later version. */ /* Copyright (C) 2006 Dario Abatianni Copyright (C) 2006 John Tapsell */ #include "alias_config.h" #include "preferences.h" Alias_Config::Alias_Config(QWidget* parent, const char* name) : QWidget(parent) { setObjectName(QString::fromLatin1(name)); setupUi(this); // reset flag to defined state (used to block signals when just selecting a new item) m_newItemSelected = false; // populate listview loadSettings(); connect(aliasListView, &QTreeWidget::currentItemChanged, this, &Alias_Config::entrySelected); connect(aliasInput, &KLineEdit::textChanged, this, &Alias_Config::nameChanged); connect(replacementInput, &KLineEdit::textChanged, this, &Alias_Config::actionChanged); connect(newButton, &QPushButton::clicked, this, &Alias_Config::addEntry); connect(removeButton, &QPushButton::clicked, this, &Alias_Config::removeEntry); } Alias_Config::~Alias_Config() { } void Alias_Config::loadSettings() { setAliasListView(Preferences::self()->aliasList()); } void Alias_Config::saveSettings() { QStringList newList=currentAliasList(); Preferences::self()->setAliasList(newList); // saved list is now old list, to check for changes m_oldAliasList=newList; } void Alias_Config::restorePageToDefaults() { aliasListView->clear(); setAliasListView(Preferences::defaultAliasList()); } bool Alias_Config::hasChanged() { return (currentAliasList() != m_oldAliasList); } void Alias_Config::setAliasListView(const QStringList& aliasList) { aliasListView->clear(); // Insert alias items backwards to get them sorted properly for(int index=aliasList.count(); index!=0; index--) { QString item=aliasList[index-1]; QTreeWidgetItem *treeWidgetItem = new QTreeWidgetItem(aliasListView, QStringList() << item.section(' ',0,0) << item.section(' ',1)); treeWidgetItem->setFlags(treeWidgetItem->flags() &~ Qt::ItemIsDropEnabled); } aliasListView->setCurrentItem(aliasListView->topLevelItem(0)); // remember alias list m_oldAliasList=aliasList; } QStringList Alias_Config::currentAliasList() { QStringList newList; QTreeWidgetItem* item=aliasListView->topLevelItem(0); while(item) { newList.append(item->text(0)+' '+item->text(1)); item=aliasListView->itemBelow(item); } return newList; } // what to do when the user selects an item void Alias_Config::entrySelected(QTreeWidgetItem* aliasEntry) { // play it safe, assume disabling all widgets first bool enabled = false; // check if there really was an item selected if (aliasEntry) { // remember to enable the editing widgets enabled = true; // tell the editing widgets not to emit modified() on signals now m_newItemSelected = true; // update editing widget contents aliasInput->setText(aliasEntry->text(0)); replacementInput->setText(aliasEntry->text(1)); // re-enable modified() signal on text changes in edit widgets m_newItemSelected = false; } // enable or disable editing widgets removeButton->setEnabled(enabled); aliasLabel->setEnabled(enabled); aliasInput->setEnabled(enabled); replacementLabel->setEnabled(enabled); replacementInput->setEnabled(enabled); } // what to do when the user change the name of a quick button void Alias_Config::nameChanged(const QString& newName) { // get possible first selected item QTreeWidgetItem* item = aliasListView->currentItem(); // sanity check if (item) { // rename item item->setText(0,newName); // tell the config system that something has changed if (!m_newItemSelected) emit modified(); } } // what to do when the user change the action definition of a quick button void Alias_Config::actionChanged(const QString& newAction) { // get possible first selected item QTreeWidgetItem* item = aliasListView->currentItem(); // sanity check if (item) { // rename item item->setText(1,newAction); // tell the config system that something has changed if(!m_newItemSelected) emit modified(); } } // add button pressed void Alias_Config::addEntry() { // add new item at the bottom of list view QTreeWidgetItem* newItem = new QTreeWidgetItem(aliasListView, aliasListView->topLevelItemCount()); if (newItem) { newItem->setFlags(newItem->flags() &~ Qt::ItemIsDropEnabled); newItem->setText(0, i18n("New")); // select new item and make it the current one aliasListView->setCurrentItem(newItem); // set input focus on item name edit aliasInput->setFocus(); // select all text to make overwriting easier aliasInput->selectAll(); // tell the config system that something has changed emit modified(); } } // remove button pressed void Alias_Config::removeEntry() { // get possible first selected item QTreeWidgetItem* item = aliasListView->currentItem(); // sanity check if (item) { // get item below the current one QTreeWidgetItem* nextItem = aliasListView->itemBelow(item); // if there was none, get the one above if(!nextItem) nextItem = aliasListView->itemAbove(item); // remove the item from the list delete item; // check if we found the next item if (nextItem) { // select the item and make it the current item aliasListView->setCurrentItem(nextItem); } else { // no next item found, this means the list is empty - entrySelected(0); + entrySelected(nullptr); } // tell the config system that somethig has changed emit modified(); } } diff --git a/src/config/autoreplace_config.cpp b/src/config/autoreplace_config.cpp index e0aafca2..70fa212f 100644 --- a/src/config/autoreplace_config.cpp +++ b/src/config/autoreplace_config.cpp @@ -1,346 +1,346 @@ /* 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) any later version. */ /* Copyright (C) 2006 Dario Abatianni Copyright (C) 2006 Eike Hein */ #include "autoreplace_config.h" #include "preferences.h" #include #define DIRECTION_OUTPUT 0 #define DIRECTION_INPUT 1 #define DIRECTION_BOTH 2 Autoreplace_Config::Autoreplace_Config(QWidget* parent, const char* name) : QWidget(parent) { setObjectName(QString::fromLatin1(name)); setupUi(this); // reset flag to defined state (used to block signals when just selecting a new item) m_newItemSelected=false; // populate combobox directionCombo->insertItem(DIRECTION_OUTPUT, i18n("Outgoing")); directionCombo->insertItem(DIRECTION_INPUT, i18n("Incoming")); directionCombo->insertItem(DIRECTION_BOTH, i18n("Both")); // populate listview loadSettings(); connect(patternListView, &QTreeWidget::currentItemChanged, this, &Autoreplace_Config::entrySelected); connect(directionCombo, static_cast(&KComboBox::activated), this, &Autoreplace_Config::directionChanged); connect(patternInput, &KLineEdit::textChanged, this, &Autoreplace_Config::patternChanged); connect(replacementInput, &KLineEdit::textChanged, this, &Autoreplace_Config::replacementChanged); connect(newButton, &QPushButton::clicked, this, &Autoreplace_Config::addEntry); connect(removeButton, &QPushButton::clicked, this, &Autoreplace_Config::removeEntry); } Autoreplace_Config::~Autoreplace_Config() { } void Autoreplace_Config::loadSettings() { setAutoreplaceListView(Preferences::autoreplaceList()); // remember autoreplace list for hasChanged() m_oldAutoreplaceList=Preferences::autoreplaceList(); } // fill listview with autoreplace definitions void Autoreplace_Config::setAutoreplaceListView(const QList &autoreplaceList) { // clear listView patternListView->clear(); // go through the list for (int index=0;indexsetFlags(newItem->flags() &~ Qt::ItemIsDropEnabled); newItem->setCheckState(0, Qt::Unchecked); // Regular expression? if (definition.at(0)=="1") newItem->setCheckState(0, Qt::Checked); // direction input/output/both if (definition.at(1)=="i") { newItem->setText(1,directionCombo->itemText(DIRECTION_INPUT)); } else if (definition.at(1)=="o") { newItem->setText(1,directionCombo->itemText(DIRECTION_OUTPUT)); } else if (definition.at(1)=="io") { newItem->setText(1,directionCombo->itemText(DIRECTION_BOTH)); } // pattern newItem->setText(2,definition.at(2)); // replacement newItem->setText(3,definition.at(3)); // hidden column, so we are independent of the i18n()ed display string newItem->setText(4,definition.at(1)); } // for patternListView->setCurrentItem(patternListView->topLevelItem(0)); } // save autoreplace entries to configuration void Autoreplace_Config::saveSettings() { // get configuration object KSharedConfigPtr config=KSharedConfig::openConfig(); // delete all patterns config->deleteGroup("Autoreplace List"); // create new empty autoreplace group KConfigGroup grp = config->group("Autoreplace List"); // create empty list QList newList=currentAutoreplaceList(); // check if there are any patterns in the list view if(newList.count()) { // go through all patterns and save them into the configuration QString regexString("Regex"); QString directString("Direction"); QString patternString("Pattern"); QString replaceString("Replace"); for(int index=0;index Autoreplace_Config::currentAutoreplaceList() { // get first item of the autoreplace listview QTreeWidgetItem* item=patternListView->topLevelItem(0); // create empty list QList newList; QChar regex; // go through all items and save them into the configuration while(item) { regex = (item->checkState(0) == Qt::Checked) ? '1' : '0'; // remember entry in internal list (col 4 is hidden for input/output) newList.append(QStringList() << regex << item->text(4) << item->text(2) << item->text(3)); // get next item in the listview item=patternListView->itemBelow(item); } // while // return list return newList; } bool Autoreplace_Config::hasChanged() { return(m_oldAutoreplaceList!=currentAutoreplaceList()); } // slots // what to do when the user selects an item void Autoreplace_Config::entrySelected(QTreeWidgetItem* autoreplaceEntry) { // play it safe, assume disabling all widgets first bool enabled=false; // check if there really was an item selected if(autoreplaceEntry) { // remember to enable the editing widgets enabled=true; // tell the editing widgets not to emit modified() on signals now m_newItemSelected=true; // update editing widget contents patternInput->setText(autoreplaceEntry->text(2)); replacementInput->setText(autoreplaceEntry->text(3)); // set combobox to selected item int itemIndex=0; QString direction=autoreplaceEntry->text(4); if(direction=="i") itemIndex=DIRECTION_INPUT; else if(direction=="o") itemIndex=DIRECTION_OUTPUT; else if(direction=="io") itemIndex=DIRECTION_BOTH; directionCombo->setCurrentIndex(itemIndex); // re-enable modified() signal on text changes in edit widgets m_newItemSelected=false; } // enable or disable editing widgets removeButton->setEnabled(enabled); directionLabel->setEnabled(enabled); directionCombo->setEnabled(enabled); patternLabel->setEnabled(enabled); patternInput->setEnabled(enabled); replacementLabel->setEnabled(enabled); replacementInput->setEnabled(enabled); // make checkboxes work emit modified(); } // what to do when the user changes the direction of an entry void Autoreplace_Config::directionChanged(int newDirection) { // get possible selected item QTreeWidgetItem* item=patternListView->currentItem(); // sanity check if(item) { // prepare hidden identifier string QString id; // find the direction strings to set up in the item if(newDirection==DIRECTION_INPUT) id='i'; else if(newDirection==DIRECTION_OUTPUT) id='o'; else if(newDirection==DIRECTION_BOTH) id="io"; // rename direction item->setText(1,directionCombo->itemText(newDirection)); item->setText(4,id); // tell the config system that something has changed if(!m_newItemSelected) emit modified(); } } // what to do when the user changes the pattern of an entry void Autoreplace_Config::patternChanged(const QString& newPattern) { // get possible selected item QTreeWidgetItem* item=patternListView->currentItem(); // sanity check if(item) { // rename pattern if (newPattern.length()>0) { item->setText(2,newPattern); } else { item->setText(2,i18nc("Fallback content for the \"Find:\" field of an auto-replace rule that gets set when the user leaves the field empty and e.g. switches to another rule in the list. It's identical to the default content of the field after adding a new auto-replace rule.", "New")); } // tell the config system that something has changed if(!m_newItemSelected) emit modified(); } } // what to do when the user changes the replacement of an entry void Autoreplace_Config::replacementChanged(const QString& newReplacement) { // get possible selected item QTreeWidgetItem* item=patternListView->currentItem(); // sanity check if(item) { // rename item item->setText(3,newReplacement); // tell the config system that something has changed if(!m_newItemSelected) emit modified(); } } // add button pressed void Autoreplace_Config::addEntry() { // add new item at the bottom of list view QTreeWidgetItem* newItem=new QTreeWidgetItem(patternListView); if (newItem) { newItem->setFlags(newItem->flags() &~ Qt::ItemIsDropEnabled); newItem->setCheckState(0, Qt::Unchecked); // set default direction newItem->setText(1,directionCombo->itemText(DIRECTION_OUTPUT)); // set default pattern name newItem->setText(2,i18nc("Default content of the \"Find:\" field of a newly-added auto-replace rule.", "New")); // set default direction newItem->setText(4,"o"); // select new item and make it the current one patternListView->setCurrentItem(newItem); // set input focus on item pattern edit patternInput->setFocus(); // select all text to make overwriting easier patternInput->selectAll(); // tell the config system that something has changed emit modified(); } } // remove button pressed void Autoreplace_Config::removeEntry() { // get possible first selected item QTreeWidgetItem* item=patternListView->currentItem(); // sanity check if(item) { // get item below the current one QTreeWidgetItem* nextItem=patternListView->itemBelow(item); // if there was none, get the one above if(!nextItem) nextItem=patternListView->itemAbove(item); // remove the item from the list delete item; // check if we found the next item if(nextItem) { // select the item and make it the current item patternListView->setCurrentItem(nextItem); } else { // no next item found, this means the list is empty - entrySelected(0); + entrySelected(nullptr); } // tell the config system that somethig has changed emit modified(); } } diff --git a/src/config/configdialog.cpp b/src/config/configdialog.cpp index 932ebd3c..860f9f75 100644 --- a/src/config/configdialog.cpp +++ b/src/config/configdialog.cpp @@ -1,429 +1,429 @@ /* * This file is part of the KDE libraries * Copyright (C) 2003 Benjamin C Meyer (ben+kdelibs at meyerhome dot net) * Copyright (C) 2003 Waldo Bastian * Copyright (C) 2004 Michael Brade * * Forked from KConfigDialog from KF5::ConfigWidgets to add tree support. * Copyright (C) 2014 Eike Hein * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "configdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include class ConfigDialog::ConfigDialogPrivate { public: ConfigDialogPrivate(ConfigDialog *q, const QString &name, KCoreConfigSkeleton *config) - : q(q), shown(false), manager(0) + : q(q), shown(false), manager(nullptr) { q->setObjectName(name); q->setWindowTitle(tr("Configure")); q->setFaceType(List); if (!name.isEmpty()) { openDialogs.insert(name, q); } else { QString genericName; genericName.sprintf("SettingsDialog-%p", static_cast(q)); openDialogs.insert(genericName, q); q->setObjectName(genericName); } QDialogButtonBox *buttonBox = q->buttonBox(); buttonBox->setStandardButtons(QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel | QDialogButtonBox::Help); connect(buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), q, SLOT(updateSettings())); connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), q, SLOT(updateSettings())); connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), q, SLOT(_k_updateButtons())); connect(buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), q, SLOT(updateWidgets())); connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), q, SLOT(updateWidgetsDefault())); connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), q, SLOT(_k_updateButtons())); connect(buttonBox->button(QDialogButtonBox::Help), SIGNAL(clicked()), q, SLOT(showHelp())); connect(q, SIGNAL(pageRemoved(KPageWidgetItem*)), q, SLOT(onPageRemoved(KPageWidgetItem*))); manager = new KConfigDialogManager(q, config); setupManagerConnections(manager); setApplyButtonEnabled(false); } KPageWidgetItem *addPageInternal(KPageWidgetItem *parent, QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header); void setupManagerConnections(KConfigDialogManager *manager); void setApplyButtonEnabled(bool enabled); void setRestoreDefaultsButtonEnabled(bool enabled); void _k_updateButtons(); void _k_settingsChangedSlot(); ConfigDialog *q; QString mAnchor; QString mHelpApp; bool shown; KConfigDialogManager *manager; QMap managerForPage; /** * The list of existing dialogs. */ static QHash openDialogs; }; QHash ConfigDialog::ConfigDialogPrivate::openDialogs; ConfigDialog::ConfigDialog(QWidget *parent, const QString &name, KCoreConfigSkeleton *config) : KPageDialog(parent), d(new ConfigDialogPrivate(this, name, config)) { } ConfigDialog::~ConfigDialog() { ConfigDialogPrivate::openDialogs.remove(objectName()); delete d; } KPageWidgetItem *ConfigDialog::addPage(QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header, bool manage) { Q_ASSERT(page); if (!page) { - return 0; + return nullptr; } - KPageWidgetItem *item = d->addPageInternal(0, page, itemName, pixmapName, header); + KPageWidgetItem *item = d->addPageInternal(nullptr, page, itemName, pixmapName, header); if (manage) { d->manager->addWidget(page); } if (d->shown && manage) { // update the default button if the dialog is shown QPushButton *defaultButton = buttonBox()->button(QDialogButtonBox::RestoreDefaults); if (defaultButton) { bool is_default = defaultButton->isEnabled() && d->manager->isDefault(); defaultButton->setEnabled(!is_default); } } return item; } KPageWidgetItem *ConfigDialog::konviAddSubPage(KPageWidgetItem *parent, QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header, bool manage) { Q_ASSERT(parent); if (!parent) { - return 0; + return nullptr; } Q_ASSERT(page); if (!page) { - return 0; + return nullptr; } KPageWidgetItem *item = d->addPageInternal(parent, page, itemName, pixmapName, header); if (manage) { d->manager->addWidget(page); } if (d->shown && manage) { // update the default button if the dialog is shown QPushButton *defaultButton = buttonBox()->button(QDialogButtonBox::RestoreDefaults); if (defaultButton) { bool is_default = defaultButton->isEnabled() && d->manager->isDefault(); defaultButton->setEnabled(!is_default); } } return item; } KPageWidgetItem *ConfigDialog::addPage(QWidget *page, KCoreConfigSkeleton *config, const QString &itemName, const QString &pixmapName, const QString &header) { Q_ASSERT(page); if (!page) { - return 0; + return nullptr; } - KPageWidgetItem *item = d->addPageInternal(0, page, itemName, pixmapName, header); + KPageWidgetItem *item = d->addPageInternal(nullptr, page, itemName, pixmapName, header); d->managerForPage[page] = new KConfigDialogManager(page, config); d->setupManagerConnections(d->managerForPage[page]); if (d->shown) { // update the default button if the dialog is shown QPushButton *defaultButton = buttonBox()->button(QDialogButtonBox::RestoreDefaults); if (defaultButton) { bool is_default = defaultButton->isEnabled() && d->managerForPage[page]->isDefault(); defaultButton->setEnabled(!is_default); } } return item; } KPageWidgetItem *ConfigDialog::ConfigDialogPrivate::addPageInternal(KPageWidgetItem *parent, QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header) { QWidget *frame = new QWidget(q); QVBoxLayout *boxLayout = new QVBoxLayout(frame); boxLayout->setMargin(0); QScrollArea *scroll = new QScrollArea(q); scroll->setFrameShape(QFrame::NoFrame); scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scroll->setWidget(page); scroll->setWidgetResizable(true); scroll->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); boxLayout->addWidget(scroll); KPageWidgetItem *item = new KPageWidgetItem(frame, itemName); item->setHeader(header); if (!pixmapName.isEmpty()) { item->setIcon(QIcon::fromTheme(pixmapName)); } if (parent) { q->KPageDialog::addSubPage(parent, item); } else { q->KPageDialog::addPage(item); } return item; } void ConfigDialog::ConfigDialogPrivate::setupManagerConnections(KConfigDialogManager *manager) { q->connect(manager, SIGNAL(settingsChanged()), q, SLOT(_k_settingsChangedSlot())); q->connect(manager, SIGNAL(widgetModified()), q, SLOT(_k_updateButtons())); QDialogButtonBox *buttonBox = q->buttonBox(); q->connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, manager, &KConfigDialogManager::updateSettings); q->connect(buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, manager, &KConfigDialogManager::updateSettings); q->connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, manager, &KConfigDialogManager::updateWidgets); q->connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, manager, &KConfigDialogManager::updateWidgetsDefault); } void ConfigDialog::ConfigDialogPrivate::setApplyButtonEnabled(bool enabled) { QPushButton *applyButton = q->buttonBox()->button(QDialogButtonBox::Apply); if (applyButton) { applyButton->setEnabled(enabled); } } void ConfigDialog::ConfigDialogPrivate::setRestoreDefaultsButtonEnabled(bool enabled) { QPushButton *restoreDefaultsButton = q->buttonBox()->button(QDialogButtonBox::RestoreDefaults); if (restoreDefaultsButton) { restoreDefaultsButton->setEnabled(enabled); } } void ConfigDialog::onPageRemoved(KPageWidgetItem *item) { QMap::iterator j = d->managerForPage.begin(); while (j != d->managerForPage.end()) { // there is a manager for this page, so remove it if (item->widget()->isAncestorOf(j.key())) { KConfigDialogManager *manager = j.value(); d->managerForPage.erase(j); delete manager; d->_k_updateButtons(); break; } ++j; } } ConfigDialog *ConfigDialog::exists(const QString &name) { QHash::const_iterator it = ConfigDialogPrivate::openDialogs.constFind(name); if (it != ConfigDialogPrivate::openDialogs.constEnd()) { return *it; } - return 0; + return nullptr; } bool ConfigDialog::showDialog(const QString &name) { ConfigDialog *dialog = exists(name); if (dialog) { dialog->show(); } - return (dialog != NULL); + return (dialog != nullptr); } void ConfigDialog::ConfigDialogPrivate::_k_updateButtons() { static bool only_once = false; if (only_once) { return; } only_once = true; QMap::iterator it; bool has_changed = manager->hasChanged() || q->hasChanged(); for (it = managerForPage.begin(); it != managerForPage.end() && !has_changed; ++it) { has_changed |= (*it)->hasChanged(); } setApplyButtonEnabled(has_changed); bool is_default = manager->isDefault() && q->isDefault(); for (it = managerForPage.begin(); it != managerForPage.end() && is_default; ++it) { is_default &= (*it)->isDefault(); } setRestoreDefaultsButtonEnabled(!is_default); emit q->widgetModified(); only_once = false; } void ConfigDialog::ConfigDialogPrivate::_k_settingsChangedSlot() { // Update the buttons _k_updateButtons(); emit q->settingsChanged(q->objectName()); } void ConfigDialog::showEvent(QShowEvent *e) { if (!d->shown) { QMap::iterator it; updateWidgets(); d->manager->updateWidgets(); for (it = d->managerForPage.begin(); it != d->managerForPage.end(); ++it) { (*it)->updateWidgets(); } bool has_changed = d->manager->hasChanged() || hasChanged(); for (it = d->managerForPage.begin(); it != d->managerForPage.end() && !has_changed; ++it) { has_changed |= (*it)->hasChanged(); } d->setApplyButtonEnabled(has_changed); bool is_default = d->manager->isDefault() && isDefault(); for (it = d->managerForPage.begin(); it != d->managerForPage.end() && is_default; ++it) { is_default &= (*it)->isDefault(); } d->setRestoreDefaultsButtonEnabled(!is_default); d->shown = true; } const QSize & availableSize = QApplication::desktop()->availableGeometry(this).size(); this->setMaximumSize(availableSize); KPageDialog::showEvent(e); } void ConfigDialog::moveEvent(QMoveEvent *e) { const QSize & availableSize = QApplication::desktop()->availableGeometry(this).size(); this->setMaximumSize(availableSize); KPageDialog::moveEvent(e); } void ConfigDialog::updateSettings() { } void ConfigDialog::updateWidgets() { } void ConfigDialog::updateWidgetsDefault() { } bool ConfigDialog::hasChanged() { return false; } bool ConfigDialog::isDefault() { return true; } void ConfigDialog::updateButtons() { d->_k_updateButtons(); } void ConfigDialog::settingsChangedSlot() { d->_k_settingsChangedSlot(); } void ConfigDialog::setHelp(const QString &anchor, const QString &appname) { d->mAnchor = anchor; d->mHelpApp = appname; } void ConfigDialog::showHelp() { KHelpClient::invokeHelp(d->mAnchor, d->mHelpApp); } #include "moc_configdialog.cpp" diff --git a/src/config/dcc_config.cpp b/src/config/dcc_config.cpp index 6163cbda..2c023152 100644 --- a/src/config/dcc_config.cpp +++ b/src/config/dcc_config.cpp @@ -1,90 +1,90 @@ /* 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) any later version. */ /* Copyright (C) 2005 Ismail Donmez Copyright (C) 2006 John Tapsell Copyright (C) 2009 Michael Kreitzer */ #include "dcc_config.h" #include "application.h" #include "transfermanager.h" #include #include using namespace Konversation; DCC_Config::DCC_Config(QWidget *parent, const char* name) : QWidget(parent) { setObjectName(QString::fromLatin1(name)); setupUi(this); kcfg_DccPath->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); languageChange(); connect(kcfg_DccMethodToGetOwnIp, static_cast(&KComboBox::activated), this, &DCC_Config::dccMethodChanged); connect(kcfg_DccUPnP, &QCheckBox::stateChanged, this, &DCC_Config::dccUPnPChanged); dccMethodChanged(kcfg_DccMethodToGetOwnIp->currentIndex()); kcfg_DccBufferSize->setSuffix(ki18np(" byte", " bytes")); kcfg_DccSendTimeout->setSuffix(ki18np(" second", " seconds")); QNetworkConfigurationManager manager; foreach (const QNetworkConfiguration& conf, manager.allConfigurations()) { kcfg_DccIPv4FallbackIface->addItem(conf.name()); } #ifdef Q_OS_WIN //This option does nothing under windows, it just confuses the user //reenable if ipv6FallbackAddress is implemented for windows in dcc/dcccommon.cpp kcfg_DccIPv4Fallback->setEnabled(false); kcfg_DccIPv4FallbackIface->setEnabled(false); #endif } void DCC_Config::showEvent(QShowEvent *event) { QWidget::showEvent(event); kcfg_DccSpecificOwnIp->setEnabled(kcfg_DccMethodToGetOwnIp->currentIndex() == 2); } void DCC_Config::dccMethodChanged(int index) { kcfg_DccSpecificOwnIp->setEnabled( index == 2 ); } void DCC_Config::dccUPnPChanged(int state) { DCC::TransferManager *transferManager = Application::instance()->getDccTransferManager(); - if (state == Qt::Checked && transferManager->getUPnPRouter() == NULL) + if (state == Qt::Checked && transferManager->getUPnPRouter() == nullptr) { transferManager->startupUPnP(); } - else if (state == Qt::Unchecked && transferManager->getUPnPRouter() != NULL) + else if (state == Qt::Unchecked && transferManager->getUPnPRouter() != nullptr) { transferManager->shutdownUPnP(); } } void DCC_Config::languageChange() { kcfg_DccMethodToGetOwnIp->clear(); kcfg_DccMethodToGetOwnIp->addItem(i18n("Network Interface")); kcfg_DccMethodToGetOwnIp->addItem(i18n("Reply From IRC Server")); kcfg_DccMethodToGetOwnIp->addItem(i18n("Specify Manually")); } DCC_Config::~DCC_Config() { } diff --git a/src/config/highlight_config.cpp b/src/config/highlight_config.cpp index 82555ad1..ef420f66 100644 --- a/src/config/highlight_config.cpp +++ b/src/config/highlight_config.cpp @@ -1,322 +1,322 @@ /* 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) any later version. */ /* Copyright (C) 2006 Dario Abatianni Copyright (C) 2006 John Tapsell */ #include "highlight_config.h" #include "highlightviewitem.h" #include "application.h" #include "sound.h" #include "preferences.h" #include #include #include Highlight_Config::Highlight_Config(QWidget* parent, const char* name) : QWidget(parent) { setObjectName(name); setupUi(this); // reset flag to defined state (used to block signals when just selecting a new item) newItemSelected = false; loadSettings(); soundPlayBtn->setIcon(QIcon::fromTheme("media-playback-start")); soundURL->setWhatsThis(i18n("Select Sound File")); // This code was copied from KNotifyWidget::openSoundDialog() (knotifydialog.cpp) [it's under LGPL v2] // find the first "sound"-resource that contains files QStringList soundDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "konversation/sounds"); soundDirs += QStandardPaths::locate(QStandardPaths::GenericDataLocation, "sounds", QStandardPaths::LocateDirectory); if (!soundDirs.isEmpty()) { QDir dir; dir.setFilter( QDir::Files | QDir::Readable ); QStringList::ConstIterator it = soundDirs.constBegin(); while ( it != soundDirs.constEnd() ) { dir = *it; if ( dir.isReadable() && dir.count() > 2 ) { soundURL->setStartDir(QUrl(*it)); break; } ++it; } } // End copy connect(highlightListView, &QTreeWidget::currentItemChanged, this, &Highlight_Config::highlightSelected); connect(highlightListView, &QTreeWidget::itemChanged, this, &Highlight_Config::modified); connect(highlightListView, &HighlightTreeWidget::itemDropped, this, &Highlight_Config::modified); connect(patternInput, &KLineEdit::textChanged, this, &Highlight_Config::patternChanged); connect(enableNotificationsCheckbox, &QCheckBox::toggled, this, &Highlight_Config::notifyModeChanged); connect(patternColor, &KColorButton::changed, this, &Highlight_Config::colorChanged); connect(soundURL, &KUrlRequester::textChanged, this, &Highlight_Config::soundURLChanged); connect(soundPlayBtn, &QToolButton::clicked, this, &Highlight_Config::playSound); connect(autoTextInput, &KLineEdit::textChanged, this, &Highlight_Config::autoTextChanged); connect(chatWindowsInput, &KLineEdit::textChanged, this, &Highlight_Config::chatWindowsChanged); connect(newButton, &QPushButton::clicked, this, &Highlight_Config::addHighlight); connect(removeButton, &QPushButton::clicked, this, &Highlight_Config::removeHighlight); updateButtons(); } Highlight_Config::~Highlight_Config() { } void Highlight_Config::restorePageToDefaults() { if (highlightListView->topLevelItemCount() != 0) { highlightListView->clear(); emit modified(); } } void Highlight_Config::loadSettings() { highlightListView->clear(); foreach (Highlight* currentHighlight, Preferences::highlightList()) { HighlightViewItem *item = new HighlightViewItem(highlightListView,currentHighlight); item->setFlags(item->flags() &~ Qt::ItemIsDropEnabled); } highlightListView->setCurrentItem(highlightListView->topLevelItem(0)); // remember current list for hasChanged() m_oldHighlightList=currentHighlightList(); } bool Highlight_Config::hasChanged() { return (m_oldHighlightList!=currentHighlightList()); } // Slots: void Highlight_Config::highlightSelected(QTreeWidgetItem* item) { // check if there was a widget selected at all if (item) { // make a highlight item out of the generic qlistviewitem HighlightViewItem* highlightItem = static_cast(item); // tell all now emitted signals that we just clicked on a new item, so they should // not emit the modified() signal. newItemSelected = true; patternColor->setColor(highlightItem->getColor()); patternInput->setText(highlightItem->getPattern()); enableNotificationsCheckbox->setChecked(highlightItem->getNotify()); soundURL->setUrl(highlightItem->getSoundURL()); autoTextInput->setText(highlightItem->getAutoText()); chatWindowsInput->setText(highlightItem->getChatWindows()); // all signals will now emit the modified() signal again newItemSelected = false; // remember to enable all edit widgets } updateButtons(); } void Highlight_Config::updateButtons() { - bool enabled = highlightListView->currentItem() != NULL; + bool enabled = highlightListView->currentItem() != nullptr; // enable or disable edit widgets patternLabel->setEnabled(enabled); patternInput->setEnabled(enabled); colorLabel->setEnabled(enabled); patternColor->setEnabled(enabled); enableNotificationsLabel->setEnabled(enabled); enableNotificationsCheckbox->setEnabled(enabled); soundURL->setEnabled(enabled); soundLabel->setEnabled(enabled); soundPlayBtn->setEnabled(enabled); autoTextLabel->setEnabled(enabled); autoTextInput->setEnabled(enabled); chatWindowsLabel->setEnabled(enabled); chatWindowsInput->setEnabled(enabled); } void Highlight_Config::patternChanged(const QString& newPattern) { HighlightViewItem* item = static_cast(highlightListView->currentItem()); if (!newItemSelected && item) { item->setPattern(newPattern); emit modified(); } } void Highlight_Config::notifyModeChanged(bool enabled) { HighlightViewItem* item = static_cast(highlightListView->currentItem()); if (!newItemSelected && item) { item->setNotify(enabled); emit modified(); } } void Highlight_Config::colorChanged(const QColor& newColor) { HighlightViewItem* item = static_cast(highlightListView->currentItem()); if (!newItemSelected && item) { item->setColor(newColor); emit modified(); } } void Highlight_Config::soundURLChanged(const QString& newURL) { HighlightViewItem* item = static_cast(highlightListView->currentItem()); if (!newItemSelected && item) { item->setSoundURL(QUrl(newURL)); emit modified(); } } void Highlight_Config::autoTextChanged(const QString& newText) { HighlightViewItem* item = static_cast(highlightListView->currentItem()); if (!newItemSelected && item) { item->setAutoText(newText); emit modified(); } } void Highlight_Config::chatWindowsChanged(const QString& newChatWindows) { HighlightViewItem* item = static_cast(highlightListView->currentItem()); if (!newItemSelected && item) { item->setChatWindows(newChatWindows); emit modified(); } } void Highlight_Config::addHighlight() { Highlight* newHighlight = new Highlight(i18n("New"), false, QColor("#ff0000"), QUrl(), QString(), QString(), true); HighlightViewItem* item = new HighlightViewItem(highlightListView, newHighlight); item->setFlags(item->flags() &~ Qt::ItemIsDropEnabled); highlightListView->setCurrentItem(item); patternInput->setFocus(); patternInput->selectAll(); emit modified(); } void Highlight_Config::removeHighlight() { HighlightViewItem* item = static_cast(highlightListView->currentItem()); if (item) { delete item; item = static_cast(highlightListView->currentItem()); if (item) { highlightListView->setCurrentItem(item); } emit modified(); } updateButtons(); } QList Highlight_Config::getHighlightList() { QList newList; HighlightViewItem* item = static_cast(highlightListView->topLevelItem(0)); while (item) { newList.append(new Highlight(item->getPattern(), item->getRegExp(), item->getColor(), item->getSoundURL(), item->getAutoText(), item->getChatWindows(), item->getNotify())); item = static_cast(highlightListView->itemBelow(item)); } return newList; } QStringList Highlight_Config::currentHighlightList() { QStringList newList; HighlightViewItem* item = static_cast(highlightListView->topLevelItem(0)); while (item) { newList.append(item->getPattern() + QString(item->getRegExp()) + item->getColor().name() + item->getSoundURL().url() + item->getAutoText() + item->getChatWindows() + QString::number(item->getNotify())); item = static_cast(highlightListView->itemBelow(item)); } return newList; } void Highlight_Config::playSound() { Application *konvApp = Application::instance(); konvApp->sound()->play(soundURL->url()); } void Highlight_Config::saveSettings() { KSharedConfigPtr config = KSharedConfig::openConfig(); // Write all highlight entries QList hiList = getHighlightList(); int i = 0; foreach (Highlight* hl, hiList) { KConfigGroup grp = config->group(QString("Highlight%1").arg(i)); grp.writeEntry("Pattern", hl->getPattern()); grp.writeEntry("RegExp", hl->getRegExp()); grp.writeEntry("Color", hl->getColor()); grp.writePathEntry("Sound", hl->getSoundURL().url()); grp.writeEntry("AutoText", hl->getAutoText()); grp.writeEntry("ChatWindows", hl->getChatWindows()); grp.writeEntry("Notify", hl->getNotify()); i++; } Preferences::setHighlightList(hiList); // Remove unused entries... while (config->hasGroup(QString("Highlight%1").arg(i))) { config->deleteGroup(QString("Highlight%1").arg(i)); i++; } // remember current list for hasChanged() m_oldHighlightList=currentHighlightList(); } diff --git a/src/config/ignore_config.cpp b/src/config/ignore_config.cpp index 365c6a61..78ad74da 100644 --- a/src/config/ignore_config.cpp +++ b/src/config/ignore_config.cpp @@ -1,210 +1,210 @@ /* 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) any later version. */ /* Copyright (C) 2006 Dario Abatianni Copyright (C) 2006 John Tapsell */ #include "ignore_config.h" #include "ignorelistviewitem.h" #include "ignore.h" #include "preferences.h" #include Ignore_Config::Ignore_Config( QWidget* parent, const char* name, Qt::WindowFlags fl ) : QWidget( parent, fl ) { setObjectName(QString::fromLatin1(name)); setupUi(this); connect(newButton, &QPushButton::clicked, this, &Ignore_Config::newIgnore); connect(removeButton, &QPushButton::clicked, this, &Ignore_Config::removeIgnore); connect(removeAllButton, &QPushButton::clicked, this, &Ignore_Config::removeAllIgnore); connect(ignoreListView, &QTreeWidget::currentItemChanged, this, &Ignore_Config::select); connect(chkChannel, &QCheckBox::clicked, this, &Ignore_Config::flagCheckboxChanged); connect(chkQuery, &QCheckBox::clicked, this, &Ignore_Config::flagCheckboxChanged); connect(chkNotice, &QCheckBox::clicked, this, &Ignore_Config::flagCheckboxChanged); connect(chkCTCP, &QCheckBox::clicked, this, &Ignore_Config::flagCheckboxChanged); connect(chkDCC, &QCheckBox::clicked, this, &Ignore_Config::flagCheckboxChanged); connect(chkInvite, &QCheckBox::clicked, this, &Ignore_Config::flagCheckboxChanged); connect(txtPattern, &KLineEdit::textChanged, this, &Ignore_Config::flagCheckboxChanged); // connect(chkException, SIGNAL(clicked()), this, SLOT(flagCheckboxChanged())); loadSettings(); ignoreListView->header()->setSectionsMovable(false); } Ignore_Config::~Ignore_Config() { } void Ignore_Config::newIgnore() { QTreeWidgetItem *item = new IgnoreListViewItem(ignoreListView, "new!new@new.new", Ignore::Channel | Ignore::Query | Ignore::Notice | Ignore::CTCP | Ignore::DCC | Ignore::Invite); ignoreListView->setCurrentItem(item); txtPattern->setFocus(); txtPattern->selectAll(); updateEnabledness(); emit modified(); } void Ignore_Config::removeAllIgnore() { ignoreListView->clear(); updateEnabledness(); emit modified(); } void Ignore_Config::removeIgnore() { delete ignoreListView->currentItem(); updateEnabledness(); emit modified(); } QList Ignore_Config::getIgnoreList() { QList newList; QTreeWidgetItem *root = ignoreListView->invisibleRootItem(); for (int i = 0; i < root->childCount(); ++i) { IgnoreListViewItem* item = static_cast(root->child(i)); Ignore* newItem=new Ignore(item->text(0),item->getFlags()); newList.append(newItem); } return newList; } // returns the currently visible ignore list as QStringList to make comparing easy QStringList Ignore_Config::currentIgnoreList() { QStringList newList; QTreeWidgetItem *root = ignoreListView->invisibleRootItem(); for (int i = 0; i < root->childCount(); ++i) { IgnoreListViewItem* item = static_cast(root->child(i)); newList.append(item->text(0)+QLatin1Char(' ')+QString(item->getFlags())); } return newList; } // checks if the currently visible ignore list differs from the currently saved one bool Ignore_Config::hasChanged() { return(m_oldIgnoreList!=currentIgnoreList()); } void Ignore_Config::restorePageToDefaults() { if(ignoreListView->topLevelItemCount() > 0) { ignoreListView->clear(); updateEnabledness(); emit modified(); } } void Ignore_Config::saveSettings() { Preferences::setIgnoreList(getIgnoreList()); // remember the list for hasChanged() m_oldIgnoreList=currentIgnoreList(); } void Ignore_Config::loadSettings() { ignoreListView->clear(); foreach (Ignore* item, Preferences::ignoreList()) { new IgnoreListViewItem(ignoreListView, item->getName(), item->getFlags()); } ignoreListView->sortItems(0, Qt::AscendingOrder); // remember the list for hasChanged() m_oldIgnoreList=currentIgnoreList(); updateEnabledness(); } void Ignore_Config::updateEnabledness() { IgnoreListViewItem* selectedItem=static_cast(ignoreListView->currentItem()); - chkChannel->setEnabled(selectedItem != NULL); - chkQuery->setEnabled(selectedItem != NULL); - chkNotice->setEnabled(selectedItem != NULL); - chkCTCP->setEnabled(selectedItem != NULL); - chkDCC->setEnabled(selectedItem != NULL); - chkInvite->setEnabled(selectedItem != NULL); + chkChannel->setEnabled(selectedItem != nullptr); + chkQuery->setEnabled(selectedItem != nullptr); + chkNotice->setEnabled(selectedItem != nullptr); + chkCTCP->setEnabled(selectedItem != nullptr); + chkDCC->setEnabled(selectedItem != nullptr); + chkInvite->setEnabled(selectedItem != nullptr); // chkExceptions->setEnabled(selectedItem != NULL); - txtPattern->setEnabled(selectedItem != NULL); - removeButton->setEnabled(selectedItem != NULL); + txtPattern->setEnabled(selectedItem != nullptr); + removeButton->setEnabled(selectedItem != nullptr); removeAllButton->setEnabled(ignoreListView->topLevelItemCount() > 0); } void Ignore_Config::select(QTreeWidgetItem* item) { updateEnabledness(); IgnoreListViewItem* selectedItem=static_cast(item); if(selectedItem) { int flags = selectedItem->getFlags(); chkChannel->setChecked(flags & Ignore::Channel); chkQuery->setChecked(flags & Ignore::Query); chkNotice->setChecked(flags & Ignore::Notice); chkCTCP->setChecked(flags & Ignore::CTCP); chkDCC->setChecked(flags & Ignore::DCC); chkInvite->setChecked(flags & Ignore::Invite); txtPattern->blockSignals(true); txtPattern->setText(selectedItem->getName()); txtPattern->blockSignals(false); // chkExceptions->setChecked(flags & Ignore::Exception) ; } } void Ignore_Config::flagCheckboxChanged() { int flags = 0; if(chkChannel->isChecked()) flags |= Ignore::Channel; if(chkQuery->isChecked()) flags |= Ignore::Query; if(chkNotice->isChecked()) flags |= Ignore::Notice; if(chkCTCP->isChecked()) flags |= Ignore::CTCP; if(chkDCC->isChecked()) flags |= Ignore::DCC; if(chkInvite->isChecked()) flags |= Ignore::Invite; // if(chkExceptions->isChecked()) flags |= Ignore::Exceptions; IgnoreListViewItem* selectedItem=static_cast(ignoreListView->currentItem()); if(selectedItem) { selectedItem->setFlags(flags); selectedItem->setName(txtPattern->text()); } emit modified(); } /* * Sets the strings of the subwidgets using the current * language. */ void Ignore_Config::languageChange() { loadSettings(); } diff --git a/src/config/nicklistbehavior_config.cpp b/src/config/nicklistbehavior_config.cpp index 1d65ca38..835fd5c3 100644 --- a/src/config/nicklistbehavior_config.cpp +++ b/src/config/nicklistbehavior_config.cpp @@ -1,99 +1,99 @@ /* 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) any later version. */ /* Copyright (C) 2006 Dario Abatianni Copyright (C) 2006 John Tapsell Copyright (C) 2006 Eike Hein */ #include "nicklistbehavior_config.h" #include "preferences.h" NicklistBehavior_Config::NicklistBehavior_Config(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(QString::fromLatin1(name)); setupUi(this); // get page widget and populate listview loadSettings(); } NicklistBehavior_Config::~NicklistBehavior_Config() { } void NicklistBehavior_Config::restorePageToDefaults() { setNickList(Preferences::defaultNicknameSortingOrder()); } void NicklistBehavior_Config::loadSettings() { // get sorting order string from preferences setNickList(Preferences::self()->sortOrder()); m_oldSortingOrder=currentSortingOrder(); } void NicklistBehavior_Config::setNickList(const QString &sortingOrder) { sortOrder->clear(); for(int index = 0; index < sortingOrder.length() ; ++index) { // get next mode char QChar mode=sortingOrder[index]; - QTreeWidgetItem *item = 0; + QTreeWidgetItem *item = nullptr; // find appropriate description if(mode=='-') item = new QTreeWidgetItem(sortOrder, QStringList() << mode << i18n("Normal Users")); if(mode=='v') item = new QTreeWidgetItem(sortOrder, QStringList() << mode << i18n("Voice (+v)")); if(mode=='h') item = new QTreeWidgetItem(sortOrder, QStringList() << mode << i18n("Halfops (+h)")); if(mode=='o') item = new QTreeWidgetItem(sortOrder, QStringList() << mode << i18n("Operators (+o)")); if(mode=='p') item = new QTreeWidgetItem(sortOrder, QStringList() << mode << i18n("Channel Admins (+p)")); if(mode=='q') item = new QTreeWidgetItem(sortOrder, QStringList() << mode << i18n("Channel Owners (+q)")); item->setFlags(item->flags() &~ Qt::ItemIsDropEnabled); } } QString NicklistBehavior_Config::currentSortingOrder() { // get the uppermost entry of the sorting list QTreeWidgetItem* item=sortOrder->topLevelItem(0); // prepare the new sorting order string QString currentSortingOrder; // iterate through all items of the listview while(item) { // add mode char to the sorting order string currentSortingOrder+=item->text(0); // go to next item in the listview item=sortOrder->itemBelow(item); } // while return currentSortingOrder; } // save settings permanently void NicklistBehavior_Config::saveSettings() { // get the current sorting order QString newSortingOrder=currentSortingOrder(); // update sorting order on in-memory preferences Preferences::self()->setSortOrder(newSortingOrder); // save current sorting order as a reference to hasChanged() m_oldSortingOrder=currentSortingOrder(); } bool NicklistBehavior_Config::hasChanged() { return(m_oldSortingOrder!=currentSortingOrder()); } diff --git a/src/config/quickbuttons_config.cpp b/src/config/quickbuttons_config.cpp index 585b266f..51774a06 100644 --- a/src/config/quickbuttons_config.cpp +++ b/src/config/quickbuttons_config.cpp @@ -1,251 +1,251 @@ /* 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) any later version. */ /* Copyright (C) 2006 Dario Abatianni Copyright (C) 2006 John Tapsell */ #include "quickbuttons_config.h" #include "preferences.h" #include #include QuickButtons_Config::QuickButtons_Config(QWidget* parent, const char* name) : QWidget(parent) { setObjectName(QString::fromLatin1(name)); setupUi(this); // reset flag to defined state (used to block signals when just selecting a new item) m_newItemSelected=false; // populate listview loadSettings(); connect(buttonListView, &QTreeWidget::currentItemChanged, this, &QuickButtons_Config::entrySelected); connect(nameInput, &KLineEdit::textChanged, this, &QuickButtons_Config::nameChanged); connect(actionInput, &KLineEdit::textChanged, this, &QuickButtons_Config::actionChanged); connect(newButton, &QPushButton::clicked, this, &QuickButtons_Config::addEntry); connect(removeButton, &QPushButton::clicked, this, &QuickButtons_Config::removeEntry); } QuickButtons_Config::~QuickButtons_Config() { } void QuickButtons_Config::loadSettings() { setButtonsListView(Preferences::quickButtonList()); // remember button list for hasChanged() m_oldButtonList=Preferences::quickButtonList(); } // fill listview with button definitions void QuickButtons_Config::setButtonsListView(const QStringList &buttonList) { buttonListView->clear(); QStringListIterator it(buttonList); while (it.hasNext()) { QString definition = it.next(); QTreeWidgetItem *item = new QTreeWidgetItem(buttonListView, QStringList() << definition.section(',',0,0) << definition.section(',',1)); item->setFlags(item->flags() &~ Qt::ItemIsDropEnabled); } buttonListView->setCurrentItem(buttonListView->topLevelItem(0)); } // save quick buttons to configuration void QuickButtons_Config::saveSettings() { // get configuration object KSharedConfigPtr config=KSharedConfig::openConfig(); // delete all buttons config->deleteGroup("Button List"); // create new empty button group KConfigGroup grp = config->group("Button List"); // create empty list QStringList newList=currentButtonList(); // check if there are any quick buttons in the list view if(newList.count()) { // go through all buttons and save them into the configuration for(int index=0;indextopLevelItemCount(); index++) { item = buttonListView->topLevelItem(index); newList.append(item->text(0)+','+item->text(1)); } return newList; } bool QuickButtons_Config::hasChanged() { return(m_oldButtonList!=currentButtonList()); } // slots // what to do when the user selects an item void QuickButtons_Config::entrySelected(QTreeWidgetItem* quickButtonEntry) { // play it safe, assume disabling all widgets first bool enabled=false; // check if there really was an item selected if(quickButtonEntry) { // remember to enable the editing widgets enabled=true; // tell the editing widgets not to emit modified() on signals now m_newItemSelected=true; // update editing widget contents nameInput->setText(quickButtonEntry->text(0)); actionInput->setText(quickButtonEntry->text(1)); // re-enable modified() signal on text changes in edit widgets m_newItemSelected=false; } // enable or disable editing widgets removeButton->setEnabled(enabled); nameLabel->setEnabled(enabled); nameInput->setEnabled(enabled); actionLabel->setEnabled(enabled); actionInput->setEnabled(enabled); } // what to do when the user change the name of a quick button void QuickButtons_Config::nameChanged(const QString& newName) { // get possible first selected item QTreeWidgetItem* item=buttonListView->currentItem(); // sanity check if(item) { // rename item item->setText(0,newName); // tell the config system that something has changed if(!m_newItemSelected) emit modified(); } } // what to do when the user change the action definition of a quick button void QuickButtons_Config::actionChanged(const QString& newAction) { // get possible first selected item QTreeWidgetItem* item=buttonListView->currentItem(); // sanity check if(item) { // rename item item->setText(1,newAction); // tell the config system that something has changed if(!m_newItemSelected) emit modified(); } } // add button pressed void QuickButtons_Config::addEntry() { // add new item at the bottom of list view QTreeWidgetItem* newItem = new QTreeWidgetItem(buttonListView, buttonListView->topLevelItemCount()); if (newItem) { newItem->setFlags(newItem->flags() &~ Qt::ItemIsDropEnabled); newItem->setText(0, i18n("New")); // select new item and make it the current one buttonListView->setCurrentItem(newItem); // set input focus on item name edit nameInput->setFocus(); // select all text to make overwriting easier nameInput->selectAll(); // tell the config system that something has changed emit modified(); } } // remove button pressed void QuickButtons_Config::removeEntry() { // get possible first selected item QTreeWidgetItem* item=buttonListView->currentItem(); // sanity check if(item) { // get item below the current one QTreeWidgetItem* nextItem=buttonListView->itemBelow(item); // if there was none, get the one above if(!nextItem) nextItem=buttonListView->itemAbove(item); // remove the item from the list delete item; // check if we found the next item if(nextItem) { // select the item and make it the current item buttonListView->setCurrentItem(nextItem); } else { // no next item found, this means the list is empty - entrySelected(0); + entrySelected(nullptr); } // tell the config system that somethig has changed emit modified(); } } diff --git a/src/config/settingsdialog.cpp b/src/config/settingsdialog.cpp index f2dc2be1..5b4949b1 100644 --- a/src/config/settingsdialog.cpp +++ b/src/config/settingsdialog.cpp @@ -1,252 +1,252 @@ /* 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) any later version. */ /* Copyright (C) 2006 John Tapsell Copyright (C) 2006 Eike Hein */ #include "settingsdialog.h" #include "preferences.h" #include "ui_chatwindowappearance_config.h" #include "connectionbehavior_config.h" #include "highlight_config.h" #include "warnings_config.h" #include "ui_log_config.h" #include "quickbuttons_config.h" #include "autoreplace_config.h" #include "ui_chatwindowbehaviour_config.h" #include "ui_fontappearance_config.h" #include "nicklistbehavior_config.h" #include "tabs_config.h" #include "ui_colorsappearance_config.h" #include "ui_generalbehavior_configui.h" #include "dcc_config.h" #include "osd_config.h" #include "theme_config.h" #include "alias_config.h" #include "ignore_config.h" #include "ui_watchednicknames_configui.h" #include "ui_tabnotifications_config.h" #include #include #include #include KonviSettingsDialog::KonviSettingsDialog( QWidget *parent) : ConfigDialog( parent, QLatin1String("settings"), Preferences::self()) { setFaceType(KPageDialog::Tree); m_modified = false; KPageWidgetItem* interfaceGroup = new KPageWidgetItem(new QWidget(this), i18n("Interface")); interfaceGroup->setIcon(QIcon::fromTheme("preferences-desktop-theme")); KPageDialog::addPage(interfaceGroup); KPageWidgetItem* behaviorGroup = new KPageWidgetItem(new QWidget(this), i18n("Behavior")); behaviorGroup->setIcon(QIcon::fromTheme("configure")); KPageDialog::addPage(behaviorGroup); KPageWidgetItem* notificationGroup = new KPageWidgetItem(new QWidget(this), i18n("Notifications")); notificationGroup->setIcon(QIcon::fromTheme("preferences-desktop-notification")); KPageDialog::addPage(notificationGroup); - QWidget *w = 0; + QWidget *w = nullptr; //Interface/Chat Window Ui::ChatWindowAppearance_Config confChatWindowAppearance; w = new QWidget(); confChatWindowAppearance.setupUi(w); confChatWindowAppearance.kcfg_TimestampFormat->addItem("hh:mm"); confChatWindowAppearance.kcfg_TimestampFormat->addItem("hh:mm:ss"); confChatWindowAppearance.kcfg_TimestampFormat->addItem("h:m ap"); konviAddSubPage(interfaceGroup, w, i18n("Chat Window"), QLatin1String("view-list-text")); //Interface/Themes m_confThemeWdg = new Theme_Config( this, "Theme" ); konviAddSubPage(interfaceGroup, m_confThemeWdg, i18n("Nicklist Themes"), QLatin1String("preferences-desktop-icons")); m_pages.append(m_confThemeWdg); connect(m_confThemeWdg, &Theme_Config::modified, this, &KonviSettingsDialog::modifiedSlot); //Interface/Colors Ui::ColorsAppearance_Config confColorsAppearance; w = new QWidget(); confColorsAppearance.setupUi(w); konviAddSubPage(interfaceGroup, w, i18n("Colors"), QLatin1String("preferences-desktop-color")); //Interface/Fonts Ui::FontAppearance_Config confFontAppearance; w = new QWidget(); confFontAppearance.setupUi(w); konviAddSubPage(interfaceGroup, w, i18n("Fonts"), QLatin1String("preferences-desktop-font")); //Interface/Quick Buttons m_confQuickButtonsWdg = new QuickButtons_Config( this, "QuickButtons" ); konviAddSubPage(interfaceGroup, m_confQuickButtonsWdg, i18n("Quick Buttons"), QLatin1String("preferences-desktop-keyboard")); m_pages.append(m_confQuickButtonsWdg); connect(m_confQuickButtonsWdg, &QuickButtons_Config::modified, this, &KonviSettingsDialog::modifiedSlot); //Interface/Tabs m_confTabBarWdg = new Tabs_Config( this, "TabBar" ); konviAddSubPage(interfaceGroup, m_confTabBarWdg, i18n("Tabs"), QLatin1String("tab-new")); //Behavior/General Ui::GeneralBehavior_ConfigUI confGeneralBehavior; w = new QWidget(); confGeneralBehavior.setupUi(w); konviAddSubPage(behaviorGroup, w, i18n("General Behavior"), QLatin1String("configure")); //Behavior/Connection ConnectionBehavior_Config* confConnectionBehavior = new ConnectionBehavior_Config(this); confConnectionBehavior->setObjectName("ConnectionBehavior"); konviAddSubPage(behaviorGroup, confConnectionBehavior, i18n("Connection"), QLatin1String("network-connect")); m_pages.append(confConnectionBehavior); //Behaviour/Chat Window Ui::ChatwindowBehaviour_Config confChatwindowBehaviour; w = new QWidget(); confChatwindowBehaviour.setupUi(w); confChatwindowBehaviour.kcfg_ScrollbackMax->setSuffix(ki18np(" line", " lines")); confChatwindowBehaviour.kcfg_AutoWhoNicksLimit->setSuffix(ki18np(" nick", " nicks")); confChatwindowBehaviour.kcfg_AutoWhoContinuousInterval->setSuffix(ki18np(" second", " seconds")); konviAddSubPage(behaviorGroup, w, i18n("Chat Window"), QLatin1String("view-list-text")); //Behaviour/Nickname List m_confNicklistBehaviorWdg = new NicklistBehavior_Config( this, "NicklistBehavior" ); konviAddSubPage(behaviorGroup, m_confNicklistBehaviorWdg, i18n("Nickname List"), QLatin1String("preferences-contact-list")); connect(m_confNicklistBehaviorWdg, &NicklistBehavior_Config::modified, this, &KonviSettingsDialog::modifiedSlot); m_pages.append(m_confNicklistBehaviorWdg); //Behaviour/Command Aliases m_confAliasWdg = new Alias_Config( this, "Alias" ); konviAddSubPage(behaviorGroup, m_confAliasWdg, i18n("Command Aliases"), QLatin1String("edit-rename")); m_pages.append(m_confAliasWdg); connect(m_confAliasWdg, &Alias_Config::modified, this, &KonviSettingsDialog::modifiedSlot); //Behaviour/Auto Replace m_confAutoreplaceWdg = new Autoreplace_Config( this, "Autoreplace" ); konviAddSubPage(behaviorGroup, m_confAutoreplaceWdg, i18n("Auto Replace"), QLatin1String("edit-rename")); m_pages.append(m_confAutoreplaceWdg); connect(m_confAutoreplaceWdg, &Autoreplace_Config::modified, this, &KonviSettingsDialog::modifiedSlot); //Behaviour/Ignore m_confIgnoreWdg = new Ignore_Config(this, "Ignore"); konviAddSubPage(behaviorGroup, m_confIgnoreWdg, i18nc("@title:tab", "Ignore"), QLatin1String("process-stop")); connect(m_confIgnoreWdg, &Ignore_Config::modified, this, &KonviSettingsDialog::modifiedSlot); m_pages.append(m_confIgnoreWdg); //Behaviour/Logging Ui::Log_Config confLog; w = new QWidget(); confLog.setupUi(w); confLog.kcfg_LogfilePath->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); konviAddSubPage(behaviorGroup, w, i18n("Logging"), QLatin1String("text-plain")); //DCC m_confDCCWdg = new DCC_Config(this, "DCC"); konviAddSubPage(behaviorGroup, m_confDCCWdg, i18nc("@title:tab", "DCC"), QLatin1String("arrow-right-double")); //Notifications/Tab Bar Ui::TabNotifications_Config confTabNotifications; w = new QWidget(); confTabNotifications.setupUi(w); konviAddSubPage(notificationGroup, w, i18n("Tabs"), QLatin1String("tab-new")); //Notification/Highlighting m_confHighlightWdg = new Highlight_Config( this, "Highlight" ); konviAddSubPage(notificationGroup, m_confHighlightWdg, i18n("Highlight"), QLatin1String("flag-red")); connect(m_confHighlightWdg, &Highlight_Config::modified, this, &KonviSettingsDialog::modifiedSlot); m_pages.append(m_confHighlightWdg); //Notification/Watched Nicknames Ui::WatchedNicknames_ConfigUI confWatchedNicks; w = new QWidget(); confWatchedNicks.setupUi(w); konviAddSubPage(notificationGroup, w, i18n("Watched Nicknames"), QLatin1String("edit-find-user")); //Notification/On Screen Display m_confOSDWdg = new OSD_Config( this, "OSD" ); konviAddSubPage(notificationGroup, m_confOSDWdg, i18n("On Screen Display"), QLatin1String("video-display")); //no modified connection needed - it's all kcfg widgets m_pages.append(m_confOSDWdg); //Notification/Warning Dialogs m_confWarningsWdg = new Warnings_Config( this, "Warnings" ); konviAddSubPage(notificationGroup, m_confWarningsWdg, i18n("Warning Dialogs"), QLatin1String("dialog-warning")); m_pages.append(m_confWarningsWdg); connect(m_confWarningsWdg, &Warnings_Config::modified, this, &KonviSettingsDialog::modifiedSlot); } void KonviSettingsDialog::modifiedSlot() { // this is for the non KConfigXT parts to tell us, if the user actually changed // something or went back to the old settings // qDebug(); m_modified = false; foreach (KonviSettingsPage *page, m_pages) { if (page->hasChanged()) { m_modified = true; // qDebug() << "modified!"; break; } } updateButtons(); } KonviSettingsDialog::~KonviSettingsDialog() { } void KonviSettingsDialog::updateSettings() { foreach (KonviSettingsPage *page, m_pages) { // this is for the non KConfigXT parts to update the UI (like quick buttons) page->saveSettings(); } m_modified = false; emit settingsChanged(QLatin1String("settings")); } void KonviSettingsDialog::updateWidgets() { foreach (KonviSettingsPage *page, m_pages) { page->loadSettings(); } m_modified = false; } void KonviSettingsDialog::updateWidgetsDefault() { foreach (KonviSettingsPage *page, m_pages) { page->restorePageToDefaults(); } m_modified = true; } // accessor method - will be used by KonviConfigDialog::updateButtons() bool KonviSettingsDialog::hasChanged() { return m_modified; } // accessor method - will be used by KonviConfigDialog::updateButtons() bool KonviSettingsDialog::isDefault() { return true; } diff --git a/src/config/theme_config.cpp b/src/config/theme_config.cpp index b58021fc..4aad2cc1 100644 --- a/src/config/theme_config.cpp +++ b/src/config/theme_config.cpp @@ -1,358 +1,358 @@ /* 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) any later version. */ /* Copyright (C) 2005 Ismail Donmez Copyright (C) 2006 Dario Abatianni Copyright (C) 2006 John Tapsell Copyright (C) 2007 Eike Hein */ #include "theme_config.h" #include "preferences_base.h" #include "images.h" #include "common.h" #include "application.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef Q_OS_WIN #include // unlink() #endif using namespace Konversation; Theme_Config::Theme_Config(QWidget* parent, const char* name) : QWidget(parent) { setObjectName(QString::fromLatin1(name)); setupUi(this); m_defaultThemeIndex = -1; // load the current settings loadSettings(); connect(iconThemeIndex, &QListWidget::currentRowChanged, this, &Theme_Config::updatePreview); connect(iconThemeIndex, &QListWidget::itemSelectionChanged, this, &Theme_Config::updateButtons); connect(iconThemeIndex, &QListWidget::itemSelectionChanged, this, &Theme_Config::modified); connect(installButton, &QPushButton::clicked, this, &Theme_Config::installTheme); connect(removeButton, &QPushButton::clicked, this, &Theme_Config::removeTheme); } Theme_Config::~Theme_Config() { } void Theme_Config::loadSettings() { // get list of theme dirs QStringList paths = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "konversation/themes/", QStandardPaths::LocateDirectory); m_dirs.clear(); foreach(const QString& path, paths) { QDir dir(path); foreach(const QString& themedir, dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { QFileInfo file(path + themedir + "/index.desktop"); if(file.exists()) { m_dirs.append(file.absoluteFilePath()); } } } // if we have any themes if (m_dirs.count() > 0) { m_dirs.sort(); QString themeName, themeComment, themeDir; QString currentTheme = Preferences::self()->iconTheme(); int currentThemeIndex = 0; // clear listview iconThemeIndex->clear(); // initialize index counter int i = 0; // iterate through all found theme directories for(QStringList::ConstIterator it = m_dirs.constBegin(); it != m_dirs.constEnd(); ++it) { KDesktopFile themeRC(*it); // get the name and comment from the theme themeName = themeRC.readName(); themeComment = themeRC.readComment(); // extract folder name themeDir=(*it).section('/',-2,-2); // is this our currently used theme? if (themeDir==currentTheme) { // remember for hasChanged() m_oldTheme=themeDir; // remember for updatePreview() currentThemeIndex = i; } if (themeDir=="oxygen") m_defaultThemeIndex= i; // if there was a comment to the theme, add it to the listview entry string if(!themeComment.isEmpty()) themeName = themeName+" ("+themeComment+')'; // insert entry into the listview iconThemeIndex->addItem(themeName); // increment index counter ++i; } // highlight currently active theme and update preview box iconThemeIndex->setCurrentRow(currentThemeIndex); } // if there was no currently used theme found, use the default theme // If anyone knows how to get the default value from this, please change this! if(m_oldTheme.isEmpty()) m_oldTheme = "oxygen"; // update enabled/disabled state of buttons updateButtons(); } bool Theme_Config::hasChanged() { // return true if the theme selected is different from the saved theme return ( m_oldTheme != m_currentTheme ); } void Theme_Config::saveSettings() { // if there are any themes in the listview ... if(iconThemeIndex->count()) { // and if anything has changed ... if(hasChanged()) { // save icon theme name KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup grp = config->group("Themes"); grp.writeEntry("IconTheme",m_currentTheme); // set in-memory theme to the saved theme Preferences::self()->setIconTheme(m_currentTheme); // update theme on runtime Application::instance()->images()->initializeNickIcons(); // remember current theme for hasChanged() m_oldTheme = m_currentTheme; } } } void Theme_Config::restorePageToDefaults() { if (m_defaultThemeIndex != -1) iconThemeIndex->setCurrentRow(m_defaultThemeIndex); } void Theme_Config::installTheme() { QUrl themeURL = QFileDialog::getOpenFileUrl(this, i18n("Select Theme Package"), QUrl (), i18n("Konversation Themes (*.tar.gz *.tar.bz2 *.tar *.zip)") ); if(themeURL.isEmpty()) return; QString themesDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "konversation/themes/"); QString tmpThemeFile; QTemporaryFile tmpFile; // file automatically deleted when object is destroyed if (!themeURL.isLocalFile()) { tmpFile.open(); // create the file, and thus create tmpFile.fileName tmpFile.close(); // no need to keep the file open, it isn't deleted until the destructor is called QUrl tmpUrl = QUrl::fromLocalFile(tmpFile.fileName()); KIO::FileCopyJob *fileCopyJob = KIO::file_copy(themeURL, tmpUrl, -1, KIO::Overwrite); if (!fileCopyJob->exec()) { int errorCode = fileCopyJob->error(); QString errorString; if (errorCode != 0) errorString = fileCopyJob->errorString(); else errorString = i18n("Unknown error (0)"); - KMessageBox::error(0L, + KMessageBox::error(nullptr, errorString, i18n("Failed to Download Theme"), KMessageBox::Notify ); return; } tmpThemeFile = tmpUrl.toLocalFile(); } else { tmpThemeFile = themeURL.toLocalFile(); } QDir themeInstallDir(tmpThemeFile); if(themeInstallDir.exists()) // We got a directory not a file { if(themeInstallDir.exists("index.desktop")) { KIO::CopyJob* job = KIO::copy(QUrl(tmpThemeFile), QUrl(themesDir)); job->exec(); //FIXME error handling } else { - KMessageBox::error(0L, + KMessageBox::error(nullptr, i18n("Theme archive is invalid."), i18n("Cannot Install Theme"), KMessageBox::Notify ); } } else // we got a file { QMimeDatabase db; QMimeType mimeType = db.mimeTypeForFile(tmpThemeFile); KArchive *themeArchive; if (mimeType.inherits(QStringLiteral("application/zip"))) { themeArchive = new KZip(tmpThemeFile); } else { themeArchive = new KTar(tmpThemeFile); } themeArchive->open(QIODevice::ReadOnly); qApp->processEvents(); const KArchiveDirectory* themeDir = themeArchive->directory(); QStringList allEntries = themeDir->entries(); for(QStringList::ConstIterator it=allEntries.constBegin(); it != allEntries.constEnd(); ++it) { - if(themeDir->entry(*it+"/index.desktop") == NULL) + if(themeDir->entry(*it+"/index.desktop") == nullptr) { - KMessageBox::error(0L, + KMessageBox::error(nullptr, i18n("Theme archive is invalid."), i18n("Cannot Install Theme"), KMessageBox::Notify ); break; } else themeDir->copyTo(themesDir); } themeArchive->close(); delete themeArchive; } loadSettings(); } void Theme_Config::removeTheme() { QString dir; QString themeName = iconThemeIndex->currentItem() ? iconThemeIndex->currentItem()->text() : QString(); dir = m_dirs[iconThemeIndex->currentRow()]; - int remove = KMessageBox::warningContinueCancel(0L, + int remove = KMessageBox::warningContinueCancel(nullptr, i18n("Do you want to remove %1?", themeName), i18n("Remove Theme"), KStandardGuiItem::del(),KStandardGuiItem::cancel(), "warningRemoveTheme" ); if(remove == KMessageBox::Continue) { QByteArray encoded = QFile::encodeName(dir); unlink(encoded.data()); KIO::DeleteJob* job = KIO::del(QUrl(dir.remove("index.desktop"))); connect(job, &KIO::DeleteJob::result, this, &Theme_Config::postRemoveTheme); } } void Theme_Config::postRemoveTheme(KJob* /* delete_job */) { loadSettings(); } void Theme_Config::updatePreview(int id) { if (id < 0) return; QString dir; dir = m_dirs[id]; dir.remove("/index.desktop"); QPixmap normal(dir+"/irc_normal.png"); previewLabel1->setPixmap(normal); previewLabel2->setPixmap(overlayPixmaps(normal,QPixmap(dir+"/irc_away.png"))); previewLabel3->setPixmap(overlayPixmaps(normal,QPixmap(dir+"/irc_voice.png"))); previewLabel4->setPixmap(overlayPixmaps(normal,QPixmap(dir+"/irc_halfop.png"))); previewLabel5->setPixmap(overlayPixmaps(normal,QPixmap(dir+"/irc_op.png"))); previewLabel6->setPixmap(overlayPixmaps(normal,QPixmap(dir+"/irc_admin.png"))); previewLabel7->setPixmap(overlayPixmaps(normal,QPixmap(dir+"/irc_owner.png"))); } void Theme_Config::updateButtons() { // don't allow clicking "remove" if there is only one or even no theme installed if(iconThemeIndex->count() < 2 || m_dirs.count() - 1 < iconThemeIndex->currentRow()) { removeButton->setEnabled(false); return; } // get directory of current theme QString dir = m_dirs[iconThemeIndex->currentRow()]; QFile themeRC(dir); // get name for directory m_currentTheme = dir.section('/',-2,-2); // allow delete action only for themes that have been installed by the user if(!themeRC.open(QIODevice::ReadOnly | QIODevice::WriteOnly)) removeButton->setEnabled(false); else removeButton->setEnabled(true); themeRC.close(); } diff --git a/src/connectionmanager.cpp b/src/connectionmanager.cpp index bf470774..438d74fb 100644 --- a/src/connectionmanager.cpp +++ b/src/connectionmanager.cpp @@ -1,712 +1,712 @@ /* 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) any later version. */ /* Copyright (C) 2008 Eike Hein */ #include "connectionmanager.h" #include "connectionsettings.h" #include "serversettings.h" #include "servergroupsettings.h" #include "config/preferences.h" #include "application.h" #include "mainwindow.h" #include "statuspanel.h" #include #include #include #include ConnectionManager::ConnectionManager(QObject* parent) : QObject(parent), m_overrideAutoReconnect (false) { // Reenable the check again when it works reliably for all backends // if (Solid::Networking::status() != Solid::Networking::Connected) // m_overrideAutoReconnect = true; connect(this, &ConnectionManager::requestReconnect, this, &ConnectionManager::handleReconnect); } ConnectionManager::~ConnectionManager() { } void ConnectionManager::connectTo(Konversation::ConnectionFlag flag, const QString& target, const QString& port, const QString& password, const QString& nick, const QString& channel, bool useSSL) { ConnectionSettings settings; if (target.startsWith(QLatin1String("irc://")) || target.startsWith(QLatin1String("ircs://"))) decodeIrcUrl(target, settings); else { decodeAddress(target, settings); Konversation::ServerSettings server = settings.server(); if (!port.isEmpty()) server.setPort(port.toInt()); if (!password.isEmpty()) server.setPassword(password); if (useSSL) server.setSSLEnabled(true); settings.setServer(server); if (!nick.isEmpty()) settings.setInitialNick(nick); if (!channel.isEmpty()) { Konversation::ChannelSettings channelSettings(channel); Konversation::ChannelList cl; cl << channelSettings; settings.setOneShotChannelList(cl); } } connectTo(flag, settings); } void ConnectionManager::connectTo(Konversation::ConnectionFlag flag, int serverGroupId) { ConnectionSettings settings; Konversation::ServerGroupSettingsPtr serverGroup; serverGroup = Preferences::serverGroupById(serverGroupId); if (serverGroup) { settings.setServerGroup(serverGroup); if (serverGroup->serverList().size() > 0) settings.setServer(serverGroup->serverList()[0]); } connectTo(flag, settings); } void ConnectionManager::connectTo(Konversation::ConnectionFlag flag, const QList& list) { QMap serverChannels; QMap serverConnections; QList::ConstIterator it = list.constBegin(); QList::ConstIterator end = list.constEnd(); for (; it != end; ++it) { ConnectionSettings settings; decodeIrcUrl(it->url(), settings); qDebug() << settings.name() << " - " << settings.server().host() << settings.server().port() << settings.server().password() << " - " << (settings.serverGroup()?settings.serverGroup()->name():QString()); QString sname = (settings.serverGroup() ? settings.serverGroup()->name() : (settings.server().host() + QLatin1Char(':') + QString::number(settings.server().port()))); if (!serverChannels.contains(sname)) serverConnections[sname] = settings; serverChannels[sname] += settings.oneShotChannelList(); } // Perform the connection. QMap::ConstIterator s_i = serverChannels.constBegin(); for (; s_i != serverChannels.constEnd(); ++s_i) { serverConnections[s_i.key()].setOneShotChannelList(s_i.value()); connectTo(flag, serverConnections[s_i.key()]); } } void ConnectionManager::connectTo(Konversation::ConnectionFlag flag, ConnectionSettings settings) { if (!settings.isValid()) return; emit closeServerList(); if (flag != Konversation::CreateNewConnection && reuseExistingConnection(settings, (flag == Konversation::PromptToReuseConnection))) { return; } IdentityPtr identity = settings.identity(); if (!identity || !validateIdentity(identity)) return; Application* konvApp = Application::instance(); MainWindow* mainWindow = konvApp->getMainWindow(); Server* server = new Server(this, settings); enlistConnection(server->connectionId(), server); connect(server, &Server::destroyed, this, &ConnectionManager::delistConnection); connect(server, SIGNAL(connectionStateChanged(Server*,Konversation::ConnectionState)), this, SLOT(handleConnectionStateChange(Server*,Konversation::ConnectionState))); connect(server, &Server::awayState, this, &ConnectionManager::connectionChangedAwayState); connect(server, SIGNAL(nicksNowOnline(Server*,QStringList,bool)), mainWindow, SLOT(setOnlineList(Server*,QStringList,bool))); connect(server, SIGNAL(awayInsertRememberLine(Server*)), mainWindow, SIGNAL(triggerRememberLines(Server*))); connect(server, SIGNAL(multiServerCommand(QString,QString)), konvApp, SLOT(sendMultiServerCommand(QString,QString))); } void ConnectionManager::enlistConnection(int connectionId, Server* server) { m_connectionList.insert(connectionId, server); emit connectionListChanged(); } void ConnectionManager::delistConnection(int connectionId) { m_connectionList.remove(connectionId); emit connectionListChanged(); } void ConnectionManager::handleConnectionStateChange(Server* server, Konversation::ConnectionState state) { emit connectionChangedState(server, state); int identityId = server->getIdentity()->id(); if (state == Konversation::SSConnected) { m_overrideAutoReconnect = false; if (!m_activeIdentities.contains(identityId)) { m_activeIdentities.insert(identityId); emit identityOnline(identityId); } } else if (state != Konversation::SSConnecting) { if (m_activeIdentities.contains(identityId)) { m_activeIdentities.remove(identityId); emit identityOffline(identityId); } } if (state == Konversation::SSInvoluntarilyDisconnected && !m_overrideAutoReconnect) { // The asynchronous invocation of handleReconnect() makes sure that // connectionChangedState() is emitted and delivered before it runs // (and causes the next connection state change to occur). emit requestReconnect(server); } else if (state == Konversation::SSInvoluntarilyDisconnected && m_overrideAutoReconnect) { server->getStatusView()->appendServerMessage(i18n("Info"), i18n ("Network is down, will reconnect automatically when it is back up.")); } } void ConnectionManager::handleReconnect(Server* server) { if (!Preferences::self()->autoReconnect() || m_overrideAutoReconnect) return; ConnectionSettings settings = server->getConnectionSettings(); uint reconnectCount = Preferences::self()->reconnectCount(); // For server groups, one iteration over their server list shall count as one // connection attempt. if (settings.serverGroup()) reconnectCount = reconnectCount * settings.serverGroup()->serverList().size(); if (reconnectCount == 0 || settings.reconnectCount() < reconnectCount) { if (settings.serverGroup() && settings.serverGroup()->serverList().size() > 1) { Konversation::ServerList serverList = settings.serverGroup()->serverList(); int index = serverList.indexOf(settings.server()); int size = serverList.size(); if (index == size - 1 || index == -1) settings.setServer(serverList[0]); else if (index < size - 1) settings.setServer(serverList[index+1]); server->setConnectionSettings(settings); server->getStatusView()->appendServerMessage(i18n("Info"), i18np( "Trying to connect to %2 (port %3) in 1 second.", "Trying to connect to %2 (port %3) in %1 seconds.", Preferences::self()->reconnectDelay(), settings.server().host(), QString::number(settings.server().port()))); } else { server->getStatusView()->appendServerMessage(i18n("Info"), i18np( "Trying to reconnect to %2 (port %3) in 1 second.", "Trying to reconnect to %2 (port %3) in %1 seconds.", Preferences::self()->reconnectDelay(), settings.server().host(), QString::number(settings.server().port()))); } server->getConnectionSettings().incrementReconnectCount(); server->connectToIRCServerIn(Preferences::self()->reconnectDelay()); } else { server->getConnectionSettings().setReconnectCount(0); server->getStatusView()->appendServerMessage(i18n("Error"), i18n("Reconnection attempts exceeded.")); } } void ConnectionManager::quitServers() { QMap::ConstIterator it; for (it = m_connectionList.constBegin(); it != m_connectionList.constEnd(); ++it) it.value()->quitServer(); } void ConnectionManager::reconnectServers() { QMap::ConstIterator it; for (it = m_connectionList.constBegin(); it != m_connectionList.constEnd(); ++it) it.value()->reconnectServer(); } void ConnectionManager::decodeIrcUrl(const QString& url, ConnectionSettings& settings) { if (!url.startsWith(QLatin1String("irc://")) && !url.startsWith(QLatin1String("ircs://"))) return; QString mangledUrl = url; mangledUrl.remove(QRegExp(QStringLiteral("^ircs?:/+"))); if (mangledUrl.isEmpty()) return; // Parsing address and channel. QStringList mangledUrlSegments; mangledUrlSegments = mangledUrl.split(QLatin1Char('/'), QString::KeepEmptyParts); // Check for ",isserver". if (mangledUrlSegments[0].contains(QLatin1Char(','))) { QStringList addressSegments; bool checkIfServerGroup = true; addressSegments = mangledUrlSegments[0].split(QLatin1Char(','), QString::KeepEmptyParts); if (addressSegments.filter(QStringLiteral("isserver")).size() > 0) checkIfServerGroup = false; decodeAddress(addressSegments[0], settings, checkIfServerGroup); } else decodeAddress(mangledUrlSegments[0], settings); QString channel; Konversation::ChannelSettings channelSettings; // Grabbing channel from in front of potential ?key=value parameters. if (mangledUrlSegments.size() > 1) channel = mangledUrlSegments[1].section(QLatin1Char('?'), 0, 0); if (!channel.isEmpty()) { // Add default prefix to channel if necessary. if (!channel.contains(QRegExp(QStringLiteral("^[#+&]{1}")))) channel = QLatin1Char('#') + channel; // Qt already encoded |, we've forced # as well channel = QUrl::fromPercentEncoding(channel.toUtf8()); channelSettings.setName(channel); } // Parsing ?key=value parameters. QString parameterString; if (mangledUrlSegments.size() > 1) parameterString = mangledUrlSegments[1].section(QLatin1Char('?'), 1); if (parameterString.isEmpty() && mangledUrlSegments.size() > 2) parameterString = mangledUrlSegments[2]; if (!parameterString.isEmpty()) { QRegExp parameterCatcher; parameterCatcher.setPattern(QStringLiteral("pass=([^&]+)")); if (parameterCatcher.indexIn(parameterString) != -1) { Konversation::ServerSettings server = settings.server(); server.setPassword(parameterCatcher.cap(1)); settings.setServer(server); } parameterCatcher.setPattern(QStringLiteral("key=([^&]+)")); if (parameterCatcher.indexIn(parameterString) != -1) channelSettings.setPassword(parameterCatcher.cap(1)); } // Assigning channel. if (!channelSettings.name().isEmpty()) { Konversation::ChannelList cl; cl << channelSettings; settings.setOneShotChannelList(cl); } // Override SSL setting state with directive from URL. if (url.startsWith(QLatin1String("ircs://"))) { Konversation::ServerSettings server = settings.server(); server.setSSLEnabled(true); settings.setServer(server); } } void ConnectionManager::decodeAddress(const QString& address, ConnectionSettings& settings, bool checkIfServerGroup) { QString host; QString port = QStringLiteral("6667"); // Full-length IPv6 address with port // Example: RFC 2732 notation: [2001:0DB8:0000:0000:0000:0000:1428:57ab]:6666 // Example: Non-RFC 2732 notation: 2001:0DB8:0000:0000:0000:0000:1428:57ab:6666 if (address.count(QLatin1Char(':'))==8) { host = address.section(QLatin1Char(':'),0,-2).remove(QLatin1Char('[')).remove(QLatin1Char(']')); port = address.section(QLatin1Char(':'),-1); } // Full-length IPv6 address without port or not-full-length IPv6 address with port // Example: Without port, RFC 2732 notation: [2001:0DB8:0000:0000:0000:0000:1428:57ab] // Example: Without port, Non-RFC 2732 notation: 2001:0DB8:0000:0000:0000:0000:1428:57ab // Example: With port, RFC 2732 notation: [2001:0DB8::1428:57ab]:6666 else if (address.count(QLatin1Char(':'))>=4) { // Last segment does not end with ], but the next to last does; // Assume not-full-length IPv6 address with port // Example: [2001:0DB8::1428:57ab]:6666 if (address.section(QLatin1Char(':'),0,-2).endsWith(QLatin1Char(']')) && !address.section(QLatin1Char(':'),-1).endsWith(QLatin1Char(']'))) { host = address.section(QLatin1Char(':'),0,-2).remove(QLatin1Char('[')).remove(QLatin1Char(']')); port = address.section(QLatin1Char(':'),-1); } else { QString addressCopy = address; host = addressCopy.remove(QLatin1Char('[')).remove(QLatin1Char(']')); } } // IPv4 address or ordinary hostname with port // Example: IPv4 address with port: 123.123.123.123:6666 // Example: Hostname with port: irc.bla.org:6666 else if (address.count(QLatin1Char(':'))==1) { host = address.section(QLatin1Char(':'),0,-2); port = address.section(QLatin1Char(':'),-1); } else host = address; // Try to assign server group. if (checkIfServerGroup && Preferences::isServerGroup(host)) { // If host is found to be the name of a server group. int serverGroupId = Preferences::serverGroupIdsByName(host).first(); Konversation::ServerGroupSettingsPtr serverGroup; serverGroup = Preferences::serverGroupById(serverGroupId); settings.setServerGroup(serverGroup); if (serverGroup->serverList().size() > 0) settings.setServer(serverGroup->serverList()[0]); } else { QList groups = Preferences::serverGroupsByServer(host); if (!groups.isEmpty()) { // If the host is found to be part of a server group's server list. Konversation::ServerGroupSettingsPtr serverGroup = groups.first(); settings.setServerGroup(serverGroup); } Konversation::ServerSettings server; server.setHost(host); server.setPort(port.toInt()); settings.setServer(server); } } bool ConnectionManager::reuseExistingConnection(ConnectionSettings& settings, bool interactive) { - Server* dupe = 0; + Server* dupe = nullptr; ConnectionDupe dupeType; bool doReuse = true; Application* konvApp = Application::instance(); MainWindow* mainWindow = konvApp->getMainWindow(); QMap::ConstIterator it; for (it = m_connectionList.constBegin(); it != m_connectionList.constEnd(); ++it) { if (it.value()->getServerGroup() && settings.serverGroup() && it.value()->getServerGroup() == settings.serverGroup()) { dupe = it.value(); dupeType = SameServerGroup; break; } } if (!dupe) { for (it = m_connectionList.constBegin(); it != m_connectionList.constEnd(); ++it) { if (it.value()->getConnectionSettings().server() == settings.server()) { dupe = it.value(); dupeType = SameServer; break; } } } if (dupe && interactive) { int result = KMessageBox::warningContinueCancel( mainWindow, i18n("You are already connected to %1. Do you want to open another connection?", dupe->getDisplayName()), i18n("Already connected to %1", dupe->getDisplayName()), KGuiItem(i18n("Create connection")), KStandardGuiItem::cancel(), QStringLiteral("ReuseExistingConnection")); if (result == KMessageBox::Continue) doReuse = false; } if (dupe && doReuse) { if (interactive && dupeType == SameServerGroup && !(dupe->getConnectionSettings().server() == settings.server())) { int result = KMessageBox::warningContinueCancel( mainWindow, i18n("You are presently connected to %1 via '%2' (port %3). Do you want to switch to '%4' (port %5) instead?", dupe->getDisplayName(), dupe->getServerName(), QString::number(dupe->getPort()), settings.server().host(), QString::number(settings.server().port())), i18n("Already connected to %1", dupe->getDisplayName()), KGuiItem(i18n("Switch Server")), KStandardGuiItem::cancel(), QStringLiteral("ReconnectWithDifferentServer")); if (result == KMessageBox::Continue) { dupe->disconnectServer(); dupe->setConnectionSettings(settings); } } if (!dupe->isConnected()) { if (!settings.oneShotChannelList().isEmpty()) dupe->updateAutoJoin(settings.oneShotChannelList()); if (!dupe->isConnecting()) dupe->reconnectServer(); } else { if (!settings.oneShotChannelList().isEmpty()) { Konversation::ChannelList::ConstIterator it = settings.oneShotChannelList().constBegin(); Konversation::ChannelList::ConstIterator itend = settings.oneShotChannelList().constEnd(); for ( ; it != itend; ++it ) { dupe->sendJoinCommand((*it).name(), (*it).password()); } settings.clearOneShotChannelList(); } } } return (dupe && doReuse); } bool ConnectionManager::validateIdentity(IdentityPtr identity, bool interactive) { Application* konvApp = Application::instance(); MainWindow* mainWindow = konvApp->getMainWindow(); QString errors; if (identity->getIdent().isEmpty()) errors+=i18n("Please fill in your Ident.
"); if (identity->getRealName().isEmpty()) errors+=i18n("Please fill in your Real name.
"); if (identity->getNickname(0).isEmpty()) errors+=i18n("Please provide at least one Nickname.
"); if (!errors.isEmpty()) { if (interactive) { int result = KMessageBox::warningContinueCancel( mainWindow, i18n("Your identity \"%1\" is not set up correctly:
%2
", identity->getName(), errors), i18n("Identity Settings"), KGuiItem(i18n("Edit Identity...")), KStandardGuiItem::cancel()); if (result == KMessageBox::Continue) { identity = mainWindow->editIdentity(identity); if (identity && validateIdentity(identity, false)) return true; else return false; } else return false; } return false; } return true; } QList ConnectionManager::getServerList() const { QList serverList; QMap::ConstIterator it; for (it = m_connectionList.constBegin(); it != m_connectionList.constEnd(); ++it) serverList.append(it.value()); return serverList; } Server* ConnectionManager::getServerByConnectionId(int connectionId) { if (m_connectionList.contains(connectionId)) return m_connectionList[connectionId]; else - return 0; + return nullptr; } Server* ConnectionManager::getServerByName(const QString& name, NameMatchFlags flags) { if (flags == MatchByIdThenName) { bool conversion = false; const int connectionId = name.toInt(&conversion); if (conversion) { Server* const server = this->getServerByConnectionId(connectionId); if (server) return server; } } QMap::ConstIterator it; for (it = m_connectionList.constBegin(); it != m_connectionList.constEnd(); ++it) { if (it.value()->getDisplayName() == name || it.value()->getServerName() == name) return it.value(); } - return 0; + return nullptr; } void ConnectionManager::involuntaryQuitServers() { m_overrideAutoReconnect = true; QMap::ConstIterator it; for (it = m_connectionList.constBegin(); it != m_connectionList.constEnd(); ++it) it.value()->involuntaryQuit(); } void ConnectionManager::reconnectInvoluntary() { m_overrideAutoReconnect = false; QMap::ConstIterator it; for (it = m_connectionList.constBegin(); it != m_connectionList.constEnd(); ++it) it.value()->reconnectInvoluntary(); } void ConnectionManager::onOnlineStateChanged(bool isOnline) { if (isOnline) { reconnectInvoluntary(); } else { involuntaryQuitServers(); } } diff --git a/src/dcc/chat.cpp b/src/dcc/chat.cpp index 02eecd83..3e86eb50 100644 --- a/src/dcc/chat.cpp +++ b/src/dcc/chat.cpp @@ -1,620 +1,620 @@ /* 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) any later version. */ /* Copyright (C) 2002 Dario Abatianni Copyright (C) 2006 Eike Hein Copyright (C) 2004,2007 Shintaro Matsuoka Copyright (C) 2009 Michael Kreitzer Copyright (C) 2009 Bernd Buschinski */ #include "chat.h" #include "dcccommon.h" #include "application.h" #include "irccharsets.h" #include "server.h" #include "upnprouter.h" #include "transfermanager.h" #include "connectionmanager.h" #include #include #include #include #include #include #include using namespace Konversation::UPnP; namespace Konversation { namespace DCC { Chat::Chat(QObject *parent) : QObject(parent), m_selfOpened(true), - m_dccSocket(0), - m_dccServer(0), + m_dccSocket(nullptr), + m_dccServer(nullptr), m_chatStatus(Configuring), m_chatExtension(Unknown) { qDebug(); // set default values m_reverse = Preferences::self()->dccPassiveSend(); setEncoding(Konversation::IRCCharsets::self()->encodingForLocale()); } Chat::~Chat() { close(); } void Chat::close() { if (!m_dccSocket && !m_dccServer) { return; //already closed } emit aboutToClose(); - m_textStream.setDevice(0); + m_textStream.setDevice(nullptr); if (m_dccServer) { - disconnect(m_dccServer, 0, 0, 0); + disconnect(m_dccServer, nullptr, nullptr, nullptr); m_dccServer->close(); if (Preferences::self()->dccUPnP()) { UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router) { router->undoForward(m_ownPort, QAbstractSocket::TcpSocket); } } - m_dccServer = 0; + m_dccServer = nullptr; } if (m_dccSocket) { - disconnect(m_dccSocket, 0, 0, 0); + disconnect(m_dccSocket, nullptr, nullptr, nullptr); m_dccSocket->close(); - m_dccSocket = 0; + m_dccSocket = nullptr; } } void Chat::start() { Server *server = serverByConnectionId(); if (!server) { qDebug() << "could not retrieve the instance of Server. Connection id: " << m_connectionId; failed(i18nc("%1=dcc extension like Chat or Whiteboard", "Could not send a DCC %1 request to the partner via the IRC server.", localizedExtensionString())); return; } if (m_ownIp.isEmpty()) { m_ownIp = DccCommon::getOwnIp(server); } qDebug() << "ownip: " << m_ownIp; if (m_selfOpened) { //we started the dcc chat if (m_reverse) { qDebug() << "passive dcc chat"; int token = Application::instance()->getDccTransferManager()->generateReverseTokenNumber(); m_token = QString::number(token); qDebug() << "token:" << m_token; server->dccPassiveChatRequest(m_partnerNick, extensionString(), DccCommon::textIpToNumericalIp(m_ownIp), m_token); setStatus(WaitingRemote, i18n("Awaiting acceptance by remote user...")); } else { listenForPartner(); if (Preferences::self()->dccUPnP()) { UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router && router->forward(QHostAddress(server->getOwnIpByNetworkInterface()), m_ownPort, QAbstractSocket::TcpSocket)) { connect(router, &UPnPRouter::forwardComplete, this, &Chat::sendRequest); } else { sendRequest(true, 0); // On error try anyways } } else { sendRequest(false, 0); } } } else { if (!Preferences::self()->dccChatAutoAccept()) { - int ret = KMessageBox::questionYesNo(0, + int ret = KMessageBox::questionYesNo(nullptr, i18nc("%1=partnerNick, %2=Servername, %3=dcc extension as chat or wboard", "%1 (on %2) offers to DCC %3 with you", m_partnerNick, server->getServerName(), localizedExtensionString()), i18nc("%1=dcc extension as Chat or Whiteboard, %2=partnerNick", "DCC %1 offer from %2", localizedExtensionString(), m_partnerNick), KGuiItem(i18n("Accept")), KGuiItem(i18n("Reject")) ); if (ret == KMessageBox::No) { setStatus(Aborted, i18nc("%1=dcc extension like Chat or Whiteboard", "You rejected the DCC %1 offer.", localizedExtensionString())); server->dccRejectChat(m_partnerNick, extensionString()); return; } } if (m_reverse) { qDebug() << "partner1: passive:1"; listenForPartner(); if (Preferences::self()->dccUPnP()) { UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router && router->forward(QHostAddress(server->getOwnIpByNetworkInterface()), m_ownPort, QAbstractSocket::TcpSocket)) { connect(router, &UPnPRouter::forwardComplete, this, &Chat::sendReverseAck); } else { sendReverseAck(true, 0); // On error try anyways } } else { sendReverseAck(false, 0); } } else { connectToPartner(); } } } void Chat::reject() { failed(i18nc("%1=dcc extension as Chat or Whiteboard", "DCC %1 request was rejected", localizedExtensionString())); } void Chat::removedFromView() { emit removed(this); } void Chat::setConnectionId(int connectionId) { m_connectionId = connectionId; } void Chat::setSelfOpened(bool opened) { m_selfOpened = opened; } bool Chat::selfOpened() const { return m_selfOpened; } void Chat::setPartnerNick(const QString &partnerNick) { m_partnerNick = partnerNick; } void Chat::setOwnNick(const QString &ownNick) { m_ownNick = ownNick; } void Chat::setReverse(bool reverse, const QString &token) { m_reverse = reverse; m_token = token; } void Chat::setPartnerIp(const QString &partnerIP) { m_partnerIp = partnerIP; } void Chat::setPartnerPort(quint16 partnerPort) { m_partnerPort = partnerPort; } QString Chat::getEncoding() const { return m_encoding; } bool Chat::setEncoding(const QString &encoding) { if (m_encoding != encoding && !m_encoding.isEmpty()) { m_encoding = encoding; QTextCodec *codec = QTextCodec::codecForName(m_encoding.toLatin1()); if (codec) { m_textStream.setCodec(codec); return true; } } return false; } void Chat::setStatus(Chat::Status status, const QString &detailMessage) { qDebug() << "old: " << m_chatStatus << " != " << status << " :new"; if (m_chatStatus != status) { m_chatDetailedStatus = detailMessage; emit statusChanged(this, status, m_chatStatus); m_chatStatus = status; } } void Chat::sendRequest(bool error, quint16 port) { Server *server = serverByConnectionId(); if (!server) { failed(i18nc("%1=dcc extension like Chat or Whiteboard", "Could not send Reverse DCC %1 acknowledgement to the partner via the IRC server.", localizedExtensionString())); return; } if (Preferences::self()->dccUPnP() && this->sender()) { // Somebody elses forward succeeded if (port != m_ownPort) { return; } disconnect(this->sender(), SIGNAL(forwardComplete(bool,quint16)), this, SLOT(sendRequest(bool,quint16))); if (error) { failedUPnP(i18n("Failed to forward port %1. Sending DCC request to remote user regardless.", QString::number(m_ownPort))); } } QString ownNumericalIp = DccCommon::textIpToNumericalIp(DccCommon::getOwnIp(server)); server->requestDccChat(m_partnerNick, extensionString(), ownNumericalIp, m_ownPort); } void Chat::sendReverseAck(bool error, quint16 port) { Server *server = serverByConnectionId(); if (!server) { failed(i18nc("%1=extension like Chat or Whiteboard", "Could not send Reverse DCC %1 acknowledgement to the partner via the IRC server.", localizedExtensionString())); return; } if (Preferences::self()->dccUPnP() && this->sender()) { // Somebody elses forward succeeded if (port != m_ownPort) { return; } disconnect(this->sender(), SIGNAL(forwardComplete(bool,quint16)), this, SLOT(sendRequest(bool,quint16))); if (error) { failedUPnP(i18n("Failed to forward port %1. Sending DCC request to remote user regardless.", QString::number(m_ownPort))); } } server->dccReverseChatAck(m_partnerNick, extensionString(), DccCommon::textIpToNumericalIp(m_ownIp), m_ownPort, m_token); } void Chat::listenForPartner() { qDebug() << "[BEGIN]"; // Set up server socket QString failedReason; if (Preferences::self()->dccSpecificChatPorts()) { m_dccServer = DccCommon::createServerSocketAndListen(this, &failedReason, Preferences::self()->dccChatPortsFirst(), Preferences::self()->dccChatPortsLast()); } else { m_dccServer = DccCommon::createServerSocketAndListen(this, &failedReason); } if (!m_dccServer) { failed(i18n("Could not open a socket for listening: %1", failedReason)); return; } connect(m_dccServer, &QTcpServer::newConnection, this, &Chat::heardPartner); // Get our own port number m_ownPort = m_dccServer->serverPort(); qDebug() << "using port: " << m_ownPort ; setStatus(Chat::WaitingRemote, i18nc("%1=dcc extension like Chat or Whiteboard,%2=partnerNick, %3=port", "Offering DCC %1 connection to %2 on port %3...", localizedExtensionString(), m_partnerNick, QString::number(m_ownPort))); qDebug() << "[END]"; } Chat::Status Chat::status() const { return m_chatStatus; } QString Chat::statusDetails() const { return m_chatDetailedStatus; } void Chat::setExtension(const QString& extension) { QString ext = extension.toLower(); if (ext == "chat") { m_chatExtension = SimpleChat; return; } else if (ext == "wboard") { m_chatExtension = Whiteboard; return; } qDebug() << "unknown chat extension:" << extension; m_chatExtension = Unknown; return; } void Chat::setExtension(Extension extension) { m_chatExtension = extension; } Chat::Extension Chat::extension() const { return m_chatExtension; } QString Chat::extensionString() const { switch (extension()) { case Whiteboard: return "wboard"; case SimpleChat: default: return "chat"; } } QString Chat::localizedExtensionString() const { switch (extension()) { case Whiteboard: return i18nc("DCC extension", "Whiteboard"); case SimpleChat: default: return i18nc("DCC extension", "Chat"); } } int Chat::connectionId() const { return m_connectionId; } QString Chat::partnerIp() const { return m_partnerIp; } QString Chat::reverseToken() const { return m_token; } quint16 Chat::partnerPort() const { return m_partnerPort; } void Chat::connectToPartner() { //qDebug() << "num: " << m_partnerIp; //m_partnerIp = DccCommon::numericalIpToTextIp(m_partnerIp); qDebug() << "partnerIP: " << m_partnerIp << " partnerport: " << m_partnerPort << " nick: " << m_partnerNick; setStatus(Chat::Connecting, i18nc("%1=extension like Chat or Whiteboard ,%2 = nickname, %3 = IP, %4 = port", "Establishing DCC %1 connection to %2 (%3:%4)...", localizedExtensionString(), m_partnerNick, m_partnerIp, QString::number(m_partnerPort))); m_dccSocket = new QTcpSocket(this); //connect(m_dccSocket, SIGNAL(hostFound()), this, SLOT(lookupFinished())); connect(m_dccSocket, &QTcpSocket::connected, this, &Chat::connectionEstablished); connect(m_dccSocket, static_cast(&QTcpSocket::error), this, &Chat::connectionFailed); connect(m_dccSocket, &QTcpSocket::readyRead, this, &Chat::readData); connect(m_dccSocket, &QTcpSocket::disconnected, this, &Chat::socketClosed); m_dccSocket->connectToHost(m_partnerIp, m_partnerPort); } void Chat::connectionEstablished() { m_textStream.setDevice(m_dccSocket); setStatus(Chat::Chatting, i18nc("%1=extension like Chat or Whiteboard, %2 = partnerNick", "Established DCC %1 connection to %2.", localizedExtensionString(), m_partnerNick)); emit connected(); } void Chat::connectionFailed(QAbstractSocket::SocketError/* error*/) { setStatus(Chat::Failed, i18n("Socket error: %1", m_dccSocket->errorString())); close(); } void Chat::readData() { - char *buffer = 0; + char *buffer = nullptr; QString line; QTextCodec *codec = m_textStream.codec(); qint64 available = m_dccSocket->bytesAvailable(); if (available < 0) { failed(m_dccSocket->errorString()); return; } while (available > 1 && m_dccSocket->canReadLine()) { buffer = new char[available + 1]; qint64 actual = m_dccSocket->readLine(buffer, available); buffer[actual] = 0; line = codec->toUnicode(buffer); delete[] buffer; const QStringList &lines = line.split('\n', QString::SkipEmptyParts); foreach (const QString &lin, lines) { emit receivedRawLine(lin); } available = m_dccSocket->bytesAvailable(); } } void Chat::sendAction(const QString &action) { QString line(action); OutputFilter::replaceAliases(line); static const QString actionText("\x01""ACTION %2\x01"); sendRawLine(actionText.arg(line)); } void Chat::sendRawLine(const QString &text) { if (m_dccSocket && m_textStream.device()) { m_textStream << text << endl; } } void Chat::sendText(const QString &text) { QString line(text); OutputFilter::replaceAliases(line); sendRawLine(line); } void Chat::heardPartner() { m_dccSocket = m_dccServer->nextPendingConnection(); if(!m_dccSocket) { failed(i18n("Could not accept the client.")); return; } connect(m_dccSocket, &QTcpSocket::readyRead, this, &Chat::readData); connect(m_dccSocket, &QTcpSocket::disconnected, this, &Chat::socketClosed); connect(m_dccSocket, static_cast(&QTcpSocket::error), this, &Chat::connectionFailed); // the listen socket isn't needed anymore - disconnect(m_dccServer, 0, 0, 0); + disconnect(m_dccServer, nullptr, nullptr, nullptr); m_dccServer->close(); - m_dccServer = 0; + m_dccServer = nullptr; if (Preferences::self()->dccUPnP()) { UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router) { router->undoForward(m_ownPort, QAbstractSocket::TcpSocket); } } m_textStream.setDevice(m_dccSocket); setStatus(Chat::Chatting, i18nc("%1=dcc extension as Chat or Whiteboard, %2=partnerNick", "Established DCC %1 connection to %2.", localizedExtensionString(), m_partnerNick)); } void Chat::socketClosed() { setStatus(Chat::Closed, i18n("Connection closed.")); close(); } quint16 Chat::ownPort() const { return m_ownPort; } QString Chat::ownNick() const { return m_ownNick; } QString Chat::partnerNick() const { return m_partnerNick; } Server *Chat::serverByConnectionId() { return Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId); } void Chat::failed(const QString &description) { setStatus(Chat::Failed, description); close(); } void Chat::failedUPnP(const QString &description) { emit upnpError(description); } } } diff --git a/src/dcc/chatcontainer.cpp b/src/dcc/chatcontainer.cpp index 678246bb..431abc19 100644 --- a/src/dcc/chatcontainer.cpp +++ b/src/dcc/chatcontainer.cpp @@ -1,345 +1,345 @@ /* 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) any later version. */ /* Copyright (C) 2009 Bernd Buschinski */ #include "chatcontainer.h" #include #include #include #include #include #include #include #include #include "whiteboard.h" #include #include #include #include #include namespace Konversation { namespace DCC { ChatContainer::ChatContainer(QWidget *parent, Chat *chat) : ChatWindow(parent), m_chat(chat), - m_whiteBoard(0) + m_whiteBoard(nullptr) { setType(ChatWindow::DccChat); //dcc chat, not used here - setServer(0); + setServer(nullptr); setChannelEncodingSupported(true); setPartnerNick(m_chat->partnerNick()); m_headerSplitter = new QSplitter(Qt::Vertical, this); m_topicLabel = new Konversation::TopicLabel(m_headerSplitter); m_headerSplitter->setStretchFactor(m_headerSplitter->indexOf(m_topicLabel), 0); // setup layout if (m_chat->extension() == Chat::Whiteboard) { QSplitter* chatSplitter = new QSplitter(Qt::Vertical); m_whiteBoard = new WhiteBoard(chatSplitter); connect(m_whiteBoard, &WhiteBoard::rawWhiteBoardCommand, m_chat, &Chat::sendRawLine); connect(m_chat, &Chat::connected, m_whiteBoard, &WhiteBoard::connected); //chatSplitter->setStretchFactor(chatSplitter->indexOf(paintLabel), 1); IRCViewBox *ircViewBox = new IRCViewBox(chatSplitter); //chatSplitter->setStretchFactor(chatSplitter->indexOf(ircViewBox), 1); setTextView(ircViewBox->ircView()); m_headerSplitter->addWidget(chatSplitter); } else //(m_chat->extension() == Chat::SimpleChat || m_chat->extension() == Chat::Unknown) { IRCViewBox *ircViewBox = new IRCViewBox(m_headerSplitter); m_headerSplitter->setStretchFactor(m_headerSplitter->indexOf(ircViewBox), 1); setTextView(ircViewBox->ircView()); } m_inputBar = new IRCInput(this); getTextView()->installEventFilter(m_inputBar); m_inputBar->setReadOnly(true); connect(m_chat, &Chat::receivedRawLine, this, &ChatContainer::receivedLine); connect(m_chat, &Chat::statusChanged, this, &ChatContainer::chatStatusChanged); connect(m_chat, &Chat::upnpError, this, &ChatContainer::upnpError); connect(m_inputBar, &IRCInput::submit, this, &ChatContainer::textEntered); connect(m_inputBar, &IRCInput::textPasted, this, &ChatContainer::textPasted); connect(getTextView(), SIGNAL(textPasted(bool)), m_inputBar, SLOT(paste(bool))); connect(getTextView(), SIGNAL(gotFocus()), m_inputBar, SLOT(setFocus())); connect(getTextView(), SIGNAL(autoText(QString)), this, SLOT(textPasted(QString))); updateAppearance(); } ChatContainer::~ChatContainer() { if (m_chat) { //Do not delete the chat, its the transfermanagers job - disconnect(m_chat, 0, 0, 0); + disconnect(m_chat, nullptr, nullptr, nullptr); m_chat->close(); m_chat->removedFromView(); - m_chat = 0; + m_chat = nullptr; } } void ChatContainer::chatStatusChanged(Chat *chat, Konversation::DCC::Chat::Status newstatus, Konversation::DCC::Chat::Status oldstatus) { Q_UNUSED(oldstatus); Q_UNUSED(chat); Q_ASSERT(newstatus != oldstatus); Q_ASSERT(m_chat == chat); switch (newstatus) { case Chat::WaitingRemote: m_topicLabel->setText(i18nc("%1=extension like Chat or Whiteboard, %2=partnerNick, %3=port", "DCC %1 with %2 on port %3.", m_chat->localizedExtensionString(), m_chat->partnerNick(), QString::number(m_chat->ownPort()))); getTextView()->appendServerMessage(i18n("DCC"), m_chat->statusDetails()); break; case Chat::Connecting: m_topicLabel->setText(i18nc("%1=extension like Chat or Whiteboard, %2 = nickname, %3 = IP, %4 = port", "DCC %1 with %2 on %3:%4.", m_chat->localizedExtensionString(), m_chat->partnerNick(), m_chat->partnerIp(), QString::number(m_chat->partnerPort()))); getTextView()->appendServerMessage(i18n("DCC"), m_chat->statusDetails()); break; case Chat::Chatting: getTextView()->appendServerMessage(i18n("DCC"), m_chat->statusDetails()); m_inputBar->setReadOnly(false); // KTextEdit::setReadOnly(true) from the ChatContainer constructor fucked up the palette. m_inputBar->updateAppearance(); break; case Chat::Failed: default: getTextView()->appendServerMessage(i18n("DCC"), m_chat->statusDetails()); m_inputBar->setReadOnly(true); break; } } void ChatContainer::upnpError(const QString &errorMessage) { getTextView()->appendServerMessage(i18nc("Universal Plug and Play", "UPnP"), errorMessage); } void ChatContainer::setPartnerNick (const QString &nick) { ChatWindow::setName('-' + nick + '-'); ChatWindow::setLogfileName('-' + nick + '-'); m_chat->setPartnerNick(nick); } QString ChatContainer::ownNick() const { return m_chat->ownNick(); } bool ChatContainer::canBeFrontView() { return true; } void ChatContainer::childAdjustFocus() { m_inputBar->setFocus(); } bool ChatContainer::closeYourself(bool askForConfirmation) { // if chat is already closed don't ask if (m_chat->status() >= Chat::Closed) { deleteLater(); return true; } int result = KMessageBox::Continue; if (askForConfirmation) { result = KMessageBox::warningContinueCancel(this, i18nc("%1=extension like Chat or Whiteboard, %2=partnerNick", "Do you want to close your DCC %1 with %2?", m_chat->localizedExtensionString(), m_chat->partnerNick()), i18nc("%1=extension like Chat or Whiteboard", "Close DCC %1", m_chat->localizedExtensionString()), KStandardGuiItem::close(), KStandardGuiItem::cancel(), "QuitDCCChatTab"); if (result == KMessageBox::Continue) { deleteLater(); return true; } } return false; } void ChatContainer::emitUpdateInfo() { //qDebug(); QString info; if (m_chat && m_chat->partnerNick() == m_chat->ownNick()) info = i18n("Talking to yourself"); else if (m_chat) info = m_chat->ownNick(); else info = getName(); emit updateInfo(info); } QString ChatContainer::getChannelEncoding() { return m_chat->getEncoding(); } QString ChatContainer::getChannelEncodingDefaultDesc() { return i18nc("%1=Encoding","Default ( %1 )", Konversation::IRCCharsets::self()->encodingForLocale()); } bool ChatContainer::searchView() { return true; } void ChatContainer::setChannelEncoding(const QString &encoding) { m_chat->setEncoding(encoding); } void ChatContainer::textEntered() { const QString &line = sterilizeUnicode(m_inputBar->toPlainText()); if (line.isEmpty()) { return; } const QString &cc = Preferences::self()->commandChar(); if (line.startsWith(cc)) { QString cmd = line.section(' ', 0, 0).toLower(); qDebug() << "cmd" << cmd; if (cmd == cc + "clear") { textView->clear(); } else if (cmd == cc + "me") { QString toSend = line.section(' ', 1); //qDebug() << "toSend" << toSend; if (toSend.isEmpty()) { getTextView()->appendServerMessage(i18n("Usage"), i18n("Usage: %1ME text", Preferences::self()->commandChar())); } else { m_chat->sendAction(toSend); appendAction(m_chat->ownNick(), toSend); } } else if (cmd == cc + "close") { closeYourself(false); } else { getTextView()->appendServerMessage(i18n("Error"), i18n("Unknown command.")); } } else { textPasted(line); } m_inputBar->clear(); } void ChatContainer::textPasted(const QString &text) { if (text.isEmpty()) { return; } const QStringList lines = sterilizeUnicode(text.split('\n', QString::SkipEmptyParts)); foreach (const QString &line, lines) { m_chat->sendText(line); getTextView()->append(m_chat->ownNick(), line); } } void ChatContainer::receivedLine(const QString &_line) { const QString line(sterilizeUnicode(_line)); if (line.startsWith('\x01')) { // cut out the CTCP command const QString ctcp = line.mid(1, line.indexOf(1, 1) - 1); const QString ctcpCommand = ctcp.section(' ', 0, 0); QString ctcpArgument(ctcp.section(' ', 1)); OutputFilter::replaceAliases(ctcpArgument); if (ctcpCommand.toLower() == "action") { appendAction(m_chat->partnerNick(), ctcpArgument); } else if (m_chat->extension() == Chat::Whiteboard && m_whiteBoard && WhiteBoard::whiteboardCommands().contains(ctcpCommand.toUpper())) { m_whiteBoard->receivedWhiteBoardLine(ctcp); } else { appendServerMessage(i18n("CTCP"), i18n("Received unknown CTCP-%1 request from %2", ctcp, m_chat->partnerNick())); } } else { getTextView()->append(m_chat->partnerNick(), line); QRegExp regexp(QStringLiteral("(^|[^\\d\\w])") + QRegExp::escape(m_chat->ownNick()) + QStringLiteral("([^\\d\\w]|$)")); regexp.setCaseSensitivity(Qt::CaseInsensitive); if(line.contains(regexp)) { Application::instance()->notificationHandler()->nick(this, m_chat->partnerNick(), line); } else { Application::instance()->notificationHandler()->message(this, m_chat->partnerNick(), line); } } } } } diff --git a/src/dcc/dcccommon.cpp b/src/dcc/dcccommon.cpp index 244deb4d..e87be9d0 100644 --- a/src/dcc/dcccommon.cpp +++ b/src/dcc/dcccommon.cpp @@ -1,175 +1,175 @@ /* 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) any later version. */ /* Copyright (C) 2007 Shintaro Matsuoka Copyright (C) 2009 Bernd Buschinski */ #include "dcccommon.h" #include "preferences.h" #include "server.h" #include #include #include #ifndef Q_OS_WIN # include # include # include # include # ifdef HAVE_STROPTS_H # include # endif # include #endif #include #include namespace Konversation { namespace DCC { //TODO: IPv6 support, CHECK ME QString DccCommon::textIpToNumericalIp( const QString& ipString ) { QHostAddress ip; ip.setAddress( ipString ); switch (ip.protocol()) { case QAbstractSocket::IPv4Protocol: return QString::number( ip.toIPv4Address() ); case QAbstractSocket::IPv6Protocol: //ipv6 is not numerical, it is just normal text, "0:c00:0:0:1f::" for example return ip.toString(); default: qDebug() << "unsupported protocol: " << ipString; return QString(); } } QString DccCommon::numericalIpToTextIp( const QString& numericalIp ) { QHostAddress ip; //Only IPV6 can contain ':' if (numericalIp.contains(':')) { return numericalIp; } //ipv4 comes as numericalip else { ip.setAddress( numericalIp.toULong() ); } return ip.toString(); } QString DccCommon::getOwnIp( Server* server ) { QString ownIp; int methodId = Preferences::self()->dccMethodToGetOwnIp(); if ( methodId == 1 && server ) { // by the WELCOME message or the USERHOST message from the server ownIp = server->getOwnIpByServerMessage(); } else if ( methodId == 2 && !Preferences::self()->dccSpecificOwnIp().isEmpty() ) { // manual QHostInfo res = QHostInfo::fromName(Preferences::self()->dccSpecificOwnIp()); if(res.error() == QHostInfo::NoError && !res.addresses().isEmpty()) { ownIp = res.addresses().first().toString(); } } // fallback or methodId == 0 (network interface) if ( ownIp.isEmpty() && server ) { ownIp = server->getOwnIpByNetworkInterface(); } qDebug() << ownIp; return ownIp; } QString DccCommon::ipv6FallbackAddress(const QString& address) { QString fallbackIp = address; QHostAddress ip(address); if (ip.protocol() == QAbstractSocket::IPv6Protocol) { #ifndef Q_OS_WIN /* This is fucking ugly but there is no KDE way to do this yet :| -cartman */ struct ifreq ifr; const QByteArray addressBa = Preferences::self()->dccIPv4FallbackIface().toLatin1(); const char* address = addressBa.constData(); int sock = socket(AF_INET, SOCK_DGRAM, 0); strncpy(ifr.ifr_name, address, IF_NAMESIZE); ifr.ifr_addr.sa_family = AF_INET; if (ioctl( sock, SIOCGIFADDR, &ifr ) >= 0) { struct sockaddr_in sock; memcpy(&sock, &ifr.ifr_addr, sizeof(ifr.ifr_addr)); fallbackIp = inet_ntoa(sock.sin_addr); } qDebug() << "Falling back to IPv4 address " << fallbackIp; #else qDebug() << "TODO: implement ipv6 fallback"; #endif } return fallbackIp; } QTcpServer* DccCommon::createServerSocketAndListen( QObject* parent, QString* failedReason, int minPort, int maxPort ) { QTcpServer* socket = new QTcpServer( parent ); if ( minPort > 0 && maxPort >= minPort ) // ports are configured manually { // set port bool found = false; // whether succeeded to set port for ( int port = minPort; port <= maxPort ; ++port ) { bool success = socket->listen( QHostAddress::Any, port ); if ( ( found = ( success && socket->isListening() ) ) ) break; socket->close(); } if ( !found ) { if ( failedReason ) *failedReason = i18n( "No vacant port" ); delete socket; - return 0; + return nullptr; } } else { // Let the operating system choose a port if ( !socket->listen() ) { if ( failedReason ) *failedReason = i18n( "Could not open a socket" ); delete socket; - return 0; + return nullptr; } } return socket; } } } diff --git a/src/dcc/resumedialog.cpp b/src/dcc/resumedialog.cpp index d229c0c3..c6e83d73 100644 --- a/src/dcc/resumedialog.cpp +++ b/src/dcc/resumedialog.cpp @@ -1,240 +1,240 @@ /* 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) any later version. */ /* Copyright (C) 2004 Dario Abatianni Copyright (C) 2004 Shintaro Matsuoka Copyright (C) 2009 Bernd Buschinski */ #include "resumedialog.h" #include "transferrecv.h" #include #include #include #include #include #include #include #include #include #include namespace Konversation { namespace DCC { ResumeDialog::ReceiveAction ResumeDialog::ask(TransferRecv* item, const QString& message, int enabledActions, ReceiveAction defaultAction) { QFlags enabledButtonCodes = QDialogButtonBox::NoButton; QDialogButtonBox::StandardButton defaultButtonCode = QDialogButtonBox::Ok; if(enabledActions & RA_Rename || enabledActions & RA_Overwrite) enabledButtonCodes |= QDialogButtonBox::Ok; if(enabledActions & RA_Resume) enabledButtonCodes |= QDialogButtonBox::Retry; if(enabledActions & RA_Cancel) enabledButtonCodes |= QDialogButtonBox::Cancel; if(defaultAction == RA_Rename || defaultAction == RA_Overwrite) defaultButtonCode = QDialogButtonBox::Ok; else if(defaultAction == RA_Resume) defaultButtonCode = QDialogButtonBox::Retry; else if(defaultAction == RA_Cancel) defaultButtonCode = QDialogButtonBox::Cancel; QPointer dlg = new ResumeDialog(item, i18n("DCC Receive Question"), message, enabledActions, enabledButtonCodes, defaultButtonCode); dlg->exec(); ReceiveAction ra = dlg->m_selectedAction; if (ra == RA_Rename) { item->setFileURL( dlg->m_urlreqFileURL->url() ); if ((enabledActions & RA_OverwriteDefaultPath) && dlg->m_overwriteDefaultPathCheckBox->isChecked()) { Preferences::self()->setDccPath(KIO::upUrl(dlg->m_urlreqFileURL->url())); } } delete dlg; return ra; } ResumeDialog::ResumeDialog(TransferRecv* item, const QString& caption, const QString& message, int enabledActions, QFlags enabledButtonCodes, QDialogButtonBox::StandardButton defaultButtonCode) - : QDialog(0) - , m_overwriteDefaultPathCheckBox(0) + : QDialog(nullptr) + , m_overwriteDefaultPathCheckBox(nullptr) , m_item(item) , m_enabledActions(enabledActions) , m_selectedAction(RA_Cancel) { setWindowTitle(caption); setModal(true); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QLabel* labelMessage = new QLabel(this); labelMessage->setText(message); m_urlreqFileURL = new KUrlRequester(m_item->getFileURL(), this); m_urlreqFileURL->setMode(KFile::File | KFile::LocalOnly); connect(m_urlreqFileURL, &KUrlRequester::textChanged, this, &ResumeDialog::updateDialogButtons); mainLayout->addWidget(labelMessage); mainLayout->addWidget(m_urlreqFileURL); if (m_enabledActions & RA_Rename) { QFrame* filePathToolsFrame = new QFrame(this); QHBoxLayout* filePathToolsLayout = new QHBoxLayout(filePathToolsFrame); QPushButton* btnDefaultName = new QPushButton(i18n("O&riginal Filename"),filePathToolsFrame); QPushButton* btnSuggestNewName = new QPushButton(i18n("Suggest &New Filename"),filePathToolsFrame); filePathToolsLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); filePathToolsLayout->addWidget(btnDefaultName); filePathToolsLayout->addWidget(btnSuggestNewName); connect(btnSuggestNewName, &QPushButton::clicked, this, &ResumeDialog::suggestNewName); connect(btnDefaultName, &QPushButton::clicked, this, &ResumeDialog::setDefaultName); mainLayout->addWidget(filePathToolsFrame); } if (m_enabledActions & RA_OverwriteDefaultPath) { QFrame* settingsFrame = new QFrame(this); QVBoxLayout* settingsLayout = new QVBoxLayout(settingsFrame); m_overwriteDefaultPathCheckBox = new QCheckBox(i18n("Use as new default download folder"), settingsFrame); settingsLayout->addWidget(m_overwriteDefaultPathCheckBox); mainLayout->addWidget(settingsFrame); } m_buttonBox = new QDialogButtonBox(enabledButtonCodes); QPushButton *defaultButton = m_buttonBox->button(defaultButtonCode); defaultButton->setDefault(true); if (enabledButtonCodes & QDialogButtonBox::Retry) m_buttonBox->button(QDialogButtonBox::Retry)->setText(i18n("&Resume")); mainLayout->addWidget(m_buttonBox); connect(m_buttonBox, &QDialogButtonBox::clicked, this, &ResumeDialog::buttonClicked); updateDialogButtons(); } ResumeDialog::~ResumeDialog() { } void ResumeDialog::buttonClicked(QAbstractButton* button) { if (m_buttonBox->button(QDialogButtonBox::Ok) == button) { if (m_item->getFileURL() == m_urlreqFileURL->url()) m_selectedAction = RA_Overwrite; else m_selectedAction = RA_Rename; accept(); } else if (m_buttonBox->button(QDialogButtonBox::Cancel) == button) { m_selectedAction = RA_Cancel; reject(); } else if (m_buttonBox->button(QDialogButtonBox::Retry) == button) { m_selectedAction = RA_Resume; accept(); } } void ResumeDialog::updateDialogButtons() // slot { if(m_item->getFileURL() == m_urlreqFileURL->url()) { m_buttonBox->button(QDialogButtonBox::Ok)->setText(KStandardGuiItem::overwrite().text()); m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(m_enabledActions & RA_Overwrite); if(m_buttonBox->standardButtons() & QDialogButtonBox::Retry) m_buttonBox->button(QDialogButtonBox::Retry)->setEnabled(true); if (m_enabledActions & RA_OverwriteDefaultPath) m_overwriteDefaultPathCheckBox->setEnabled(false); } else { m_buttonBox->button(QDialogButtonBox::Ok)->setText(i18n("R&ename")); m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(m_enabledActions & RA_Rename); if(m_buttonBox->standardButtons() & QDialogButtonBox::Retry) m_buttonBox->button(QDialogButtonBox::Retry)->setEnabled(false); if (m_enabledActions & RA_OverwriteDefaultPath) m_overwriteDefaultPathCheckBox->setEnabled(true); } } // FIXME: kio-fy me! // taken and adapted from kio::renamedlg.cpp void ResumeDialog::suggestNewName() // slot { QString dotSuffix, suggestedName; QString basename = m_urlreqFileURL->url().url().section('/', -1); QUrl baseURL(m_urlreqFileURL->url().url().section('/', 0, -2)); int index = basename.indexOf( '.' ); if ( index != -1 ) { dotSuffix = basename.mid( index ); basename.truncate( index ); } int pos = basename.lastIndexOf( '_' ); if(pos != -1 ) { QString tmp = basename.mid( pos+1 ); bool ok; int number = tmp.toInt( &ok ); if ( !ok ) // ok there is no number { suggestedName = basename + '1' + dotSuffix; } else { // yes there's already a number behind the _ so increment it by one basename.replace( pos+1, tmp.length(), QString::number(number+1) ); suggestedName = basename + dotSuffix; } } else // no underscore yet suggestedName = basename + "_1" + dotSuffix; // Check if suggested name already exists bool exists = false; // TODO: network transparency. However, using NetAccess from a modal dialog // could be a problem, no? (given that it uses a modal widget itself....) if ( baseURL.isLocalFile() ) exists = QFileInfo(baseURL.adjusted(QUrl::StripTrailingSlash).toLocalFile() + QDir::separator() + suggestedName).exists(); m_urlreqFileURL->setUrl(QUrl::fromLocalFile(baseURL.adjusted(QUrl::StripTrailingSlash).toLocalFile() + QDir::separator() + suggestedName)); if ( exists ) // already exists -> recurse suggestNewName(); } void ResumeDialog::setDefaultName() // slot { m_urlreqFileURL->setUrl(m_item->getFileURL()); } } } diff --git a/src/dcc/transfer.cpp b/src/dcc/transfer.cpp index 00ec8c4d..04ea219a 100644 --- a/src/dcc/transfer.cpp +++ b/src/dcc/transfer.cpp @@ -1,423 +1,423 @@ /* 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) any later version. */ /* Copyright (C) 2002-2004 Dario Abatianni Copyright (C) 2004-2007 Shintaro Matsuoka Copyright (C) 2004,2005 John Tapsell Copyright (C) 2009 Bernd Buschinski */ #include "transfer.h" #include "application.h" #include "connectionmanager.h" #include "notificationhandler.h" #include "preferences.h" #include #include #include namespace Konversation { namespace DCC { Transfer::Transfer(Type dccType, QObject *parent) : QObject(parent) { qDebug(); m_type = dccType; m_status = Configuring; m_ownPort = 0; m_fileSize = 0; m_resumed = false; m_reverse = false; m_connectionId = -1; // Not configured m_timeLeft = Transfer::NotInTransfer; m_transferringPosition = 0; m_transferStartPosition = 0; m_averageSpeed = 0.0; m_currentSpeed = 0.0; m_bufferSize = Preferences::self()->dccBufferSize(); m_buffer = new char[m_bufferSize]; connect(&m_loggerTimer, &QTimer::timeout, this, &Transfer::logTransfer); m_timeOffer = QDateTime::currentDateTime(); } Transfer::~Transfer() { qDebug(); } void Transfer::setConnectionId(int id) { if (getStatus() == Configuring || getStatus() == Queued) { m_connectionId = id; } } void Transfer::setPartnerNick(const QString &nick) { if (getStatus() == Configuring || getStatus() == Queued) { m_partnerNick = nick; } } bool Transfer::queue() { qDebug(); if (getStatus() != Configuring) { return false; } if (m_fileName.isEmpty()) { return false; } if (m_connectionId == -1 || m_partnerNick.isEmpty()) { return false; } setStatus(Queued); return true; } void Transfer::runFile() { if (getType() == Transfer::Send || getStatus() == Transfer::Done) { - new KRun(getFileURL(), 0); + new KRun(getFileURL(), nullptr); } } void Transfer::startTransferLogger() { m_timeTransferStarted = QDateTime::currentDateTime(); m_loggerBaseTime.start(); m_loggerTimer.start(100); } void Transfer::finishTransferLogger() { if (m_timeTransferFinished.isNull()) { m_timeTransferFinished = QDateTime::currentDateTime(); } m_loggerTimer.stop(); updateTransferMeters(); } // called by m_loggerTimer void Transfer::logTransfer() { m_transferLogTime.append(m_loggerBaseTime.elapsed()); m_transferLogPosition.append(m_transferringPosition); updateTransferMeters(); } void Transfer::cleanUp() { qDebug(); delete[] m_buffer; - m_buffer = 0; + m_buffer = nullptr; m_loggerTimer.stop(); } void Transfer::removedFromView() { emit removed(this); } // just for convenience void Transfer::failed(const QString &errorMessage) { cleanUp(); Application *konv_app = Application::instance(); Server *server = konv_app->getConnectionManager()->getServerByConnectionId(m_connectionId); if (server) { qDebug() << "notification:" << errorMessage; konv_app->notificationHandler()->dccError(server->getStatusView(), errorMessage); } setStatus(Failed, errorMessage); emit done(this); } void Transfer::setStatus(Status status, const QString &statusDetail) { bool changed = (status != m_status); Status oldStatus = m_status; m_status = status; m_statusDetail = statusDetail; if (changed) { emit statusChanged(this, m_status, oldStatus); } if (m_status == Done) { Application *konv_app = Application::instance(); Server *server = konv_app->getConnectionManager()->getServerByConnectionId(m_connectionId); if (server) { qDebug() << "notification:" << m_fileName; konv_app->notificationHandler()->dccTransferDone(server->getStatusView(), m_fileName, this); } } } void Transfer::updateTransferMeters() { const int timeToCalc = 5; if (getStatus() == Transferring) { // update CurrentSpeed // remove too old data QList::iterator itTime = m_transferLogTime.begin(); QList::iterator itPos = m_transferLogPosition.begin(); while (itTime != m_transferLogTime.end() && (m_transferLogTime.last() - (*itTime) > timeToCalc * 1000)) { itTime = m_transferLogTime.erase(itTime); itPos = m_transferLogPosition.erase(itPos); } // shift the base of the time (m_transferLoggerBaseTime) // reason: QTime can't handle a time longer than 24 hours int shiftOffset = m_loggerBaseTime.restart(); itTime = m_transferLogTime.begin(); for (; itTime != m_transferLogTime.end(); ++itTime) { (*itTime) = (*itTime) - shiftOffset; } // The logTimer is 100ms, as 200ms is below 1sec we get "undefined" speed if (m_transferLogTime.count() >= 2 && m_timeTransferStarted.secsTo(QDateTime::currentDateTime()) > 0) { // FIXME: precision of average speed is too bad m_averageSpeed = (double)(m_transferringPosition - m_transferStartPosition) / (double)m_timeTransferStarted.secsTo(QDateTime::currentDateTime()); m_currentSpeed = (double)(m_transferLogPosition.last() - m_transferLogPosition.front()) / (double)(m_transferLogTime.last() - m_transferLogTime.front()) * 1000; } else // avoid zero devision { m_averageSpeed = Transfer::Calculating; m_currentSpeed = Transfer::Calculating; } // update the remaining time if (m_transferringPosition == (KIO::fileoffset_t)m_fileSize) { m_timeLeft = 0; } else if (m_currentSpeed <= 0) { m_timeLeft = Transfer::InfiniteValue; } else { m_timeLeft = (int)((double)(m_fileSize - m_transferringPosition) / m_currentSpeed); } } else if (m_status >= Done) { if (m_timeTransferStarted.secsTo(m_timeTransferFinished) > 1) { m_averageSpeed = (double)(m_transferringPosition - m_transferStartPosition) / (double)m_timeTransferStarted.secsTo(m_timeTransferFinished); } else { m_averageSpeed = Transfer::InfiniteValue; } m_currentSpeed = 0; if (m_status == Done) { m_timeLeft = 0; } else { m_timeLeft = Transfer::NotInTransfer; } } else { m_averageSpeed = 0; m_currentSpeed = 0; m_timeLeft = Transfer::NotInTransfer; } } QString Transfer::sanitizeFileName(const QString &fileName) { QString fileNameTmp = QFileInfo(fileName).fileName(); if (fileNameTmp.startsWith('.')) { fileNameTmp.replace(0, 1, '_'); // Don't create hidden files } if (fileNameTmp.isEmpty()) { fileNameTmp = "unnamed"; } return fileNameTmp; } Transfer::Type Transfer::getType() const { return m_type; } Transfer::Status Transfer::getStatus() const { return m_status; } const QString &Transfer::getStatusDetail() const { return m_statusDetail; } QDateTime Transfer::getTimeOffer() const { return m_timeOffer; } int Transfer::getConnectionId() const { return m_connectionId; } QString Transfer::getOwnIp() const { return m_ownIp; } quint16 Transfer::getOwnPort() const { return m_ownPort; } QString Transfer::getPartnerNick() const { return m_partnerNick; } QString Transfer::getPartnerIp() const { return m_partnerIp; } quint16 Transfer::getPartnerPort() const { return m_partnerPort; } QString Transfer::getFileName() const { return m_fileName; } KIO::filesize_t Transfer::getFileSize() const { return m_fileSize; } KIO::fileoffset_t Transfer::getTransferringPosition() const { return m_transferringPosition; } KIO::fileoffset_t Transfer::getTransferStartPosition() const { return m_transferStartPosition; } QUrl Transfer::getFileURL() const { return m_fileURL; } bool Transfer::isResumed() const { return m_resumed; } bool Transfer::isReverse() const { return m_reverse; } QString Transfer::getReverseToken() const { return m_reverseToken; } transferspeed_t Transfer::getAverageSpeed() const { return m_averageSpeed; } transferspeed_t Transfer::getCurrentSpeed() const { return m_currentSpeed; } int Transfer::getTimeLeft() const { return m_timeLeft; } int Transfer::getProgress() const { if (getFileSize() == 0) { return 0; } else { return (int)(((double)getTransferringPosition() / (double)getFileSize()) * 100.0); } } QDateTime Transfer::getTimeTransferStarted() const { return m_timeTransferStarted; } QDateTime Transfer::getTimeTransferFinished() const { return m_timeTransferFinished; } QString Transfer::transferFileName(const QString & fileName) { if (fileName.contains(' ') && !(fileName.startsWith('\"') && fileName.endsWith('\"'))) { return '\"'+fileName+'\"'; } return fileName; } } } diff --git a/src/dcc/transferdetailedinfopanel.cpp b/src/dcc/transferdetailedinfopanel.cpp index 7400d2ec..3ef9d63a 100644 --- a/src/dcc/transferdetailedinfopanel.cpp +++ b/src/dcc/transferdetailedinfopanel.cpp @@ -1,285 +1,285 @@ /* 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) any later version. */ /* Copyright (C) 2007 Shintaro Matsuoka Copyright (C) 2009 Bernd Buschinski */ #include "transferdetailedinfopanel.h" #include "transfer.h" #include "transferrecv.h" #include "transfermanager.h" #include "application.h" #include "connectionmanager.h" #include "server.h" #include "transferlistmodel.h" #include "dcccommon.h" #include #include namespace Konversation { namespace DCC { TransferDetailedInfoPanel::TransferDetailedInfoPanel(QWidget * parent) : QTabWidget(parent) { QWidget *tab = new QWidget(this); m_locationInfo.setupUi(tab); addTab(tab, i18n("Status") ); tab = new QWidget(this); m_timeInfo.setupUi(tab); addTab(tab, i18n("Details")); - m_transfer = 0; + m_transfer = nullptr; m_autoViewUpdateTimer = new QTimer(this); m_autoViewUpdateTimer->setInterval(1000); connect(m_locationInfo.m_urlreqLocation, &KUrlRequester::textChanged, this, &TransferDetailedInfoPanel::slotLocationChanged); connect(Application::instance()->getDccTransferManager(), SIGNAL(fileURLChanged(Konversation::DCC::TransferRecv*)), this, SLOT(updateView())); // it's a little rough.. //only enable when needed m_locationInfo.m_urlreqLocation->lineEdit()->setReadOnly(true); m_locationInfo.m_urlreqLocation->button()->setEnabled(false); m_locationInfo.m_urlreqLocation->setMode(KFile::File | KFile::LocalOnly); } TransferDetailedInfoPanel::~TransferDetailedInfoPanel() { } void TransferDetailedInfoPanel::setTransfer(Transfer *item) { if (item == m_transfer) { return; } clear(); m_transfer = item; // set up the auto view-update timer connect(m_autoViewUpdateTimer, &QTimer::timeout, this, &TransferDetailedInfoPanel::updateChangeableView); // If the file is already being transferred, the timer must be started here, // otherwise the information will not be updated every 0.5sec if (m_transfer->getStatus() == Transfer::Transferring) m_autoViewUpdateTimer->start(500); connect(item, SIGNAL(statusChanged(Konversation::DCC::Transfer*,int,int)), this, SLOT(slotTransferStatusChanged(Konversation::DCC::Transfer*,int,int))); updateView(); } Transfer *TransferDetailedInfoPanel::transfer() const { return m_transfer; } void TransferDetailedInfoPanel::clear() { m_autoViewUpdateTimer->stop(); // disconnect all slots once disconnect(this); // we can't do disconnect( m_item->transfer(), 0, this, 0 ) here // because m_item can have been deleted already. - m_transfer = 0; + m_transfer = nullptr; m_locationInfo.m_urlreqLocation->lineEdit()->setReadOnly(true); m_locationInfo.m_urlreqLocation->lineEdit()->setFrame(false); m_locationInfo.m_urlreqLocation->button()->setEnabled(false); m_locationInfo.m_labelDccType->clear(); m_locationInfo.m_labelFilename->clear(); m_locationInfo.m_urlreqLocation->clear(); m_locationInfo.m_labelPartner->clear(); m_locationInfo.m_labelSelf->clear(); m_timeInfo.m_labelFileSize->clear(); m_timeInfo.m_labelIsResumed->clear(); m_timeInfo.m_labelTimeOffered->clear(); m_timeInfo.m_labelTimeStarted->clear(); m_timeInfo.m_labelTimeFinished->clear(); m_locationInfo.m_labelStatus->clear(); m_locationInfo.m_progress->setValue(0); m_timeInfo.m_labelCurrentPosition->clear(); m_timeInfo.m_labelCurrentSpeed->clear(); m_timeInfo.m_labelAverageSpeed->clear(); m_timeInfo.m_labelTransferringTime->clear(); m_timeInfo.m_labelTimeLeft->clear(); } void TransferDetailedInfoPanel::updateView() { if (!m_transfer) { return; } // Type: QString type(m_transfer->getType() == Transfer::Send ? i18n("DCC Send") : i18n("DCC Receive")); if (m_transfer->isReverse()) { type += i18n(" (Reverse DCC)"); } m_locationInfo.m_labelDccType->setText(type); // Filename: m_locationInfo.m_labelFilename->setText(m_transfer->getFileName()); // Location: m_locationInfo.m_urlreqLocation->setUrl(m_transfer->getFileURL()); //m_urlreqLocation->lineEdit()->setFocusPolicy( m_item->getStatus() == Transfer::Queued ? Qt::StrongFocus : ClickFocus ); m_locationInfo.m_urlreqLocation->lineEdit()->setReadOnly(m_transfer->getStatus() != Transfer::Queued); m_locationInfo.m_urlreqLocation->lineEdit()->setFrame(m_transfer->getStatus() == Transfer::Queued); m_locationInfo.m_urlreqLocation->button()->setEnabled(m_transfer->getStatus() == Transfer::Queued); // Partner: QString partnerInfoServerName; if (m_transfer->getConnectionId() == -1) partnerInfoServerName = i18n("Unknown server"); else if (Application::instance()->getConnectionManager()->getServerByConnectionId(m_transfer->getConnectionId())) partnerInfoServerName = Application::instance()->getConnectionManager()->getServerByConnectionId(m_transfer->getConnectionId())->getServerName(); else partnerInfoServerName = i18n("Unknown server"); if (!m_transfer->getPartnerIp().isEmpty()) { m_locationInfo.m_labelPartner->setText(i18nc("%1=partnerNick, %2=IRC Servername, %3=partnerIP, %4=partnerPort", "%1 on %2, %3 (port %4)", m_transfer->getPartnerNick().isEmpty() ? "?" : m_transfer->getPartnerNick(), partnerInfoServerName, m_transfer->getPartnerIp(), QString::number(m_transfer->getPartnerPort()))); } else { m_locationInfo.m_labelPartner->setText(i18nc("%1 = PartnerNick, %2 = Partner IRC Servername","%1 on %2", m_transfer->getPartnerNick().isEmpty() ? "?" : m_transfer->getPartnerNick(), partnerInfoServerName)); } // Self: if (!m_transfer->getOwnIp().isEmpty()) m_locationInfo.m_labelSelf->setText(i18nc("%1=ownIP, %2=ownPort", "%1 (port %2)", m_transfer->getOwnIp(), QString::number(m_transfer->getOwnPort()))); // File Size: m_timeInfo.m_labelFileSize->setText(KFormat().formatByteSize(m_transfer->getFileSize())); // Resumed: if (m_transfer->isResumed()) m_timeInfo.m_labelIsResumed->setText(ki18nc("%1=Transferstartposition","Yes, %1").subs(m_transfer->getTransferStartPosition()).toString()); else m_timeInfo.m_labelIsResumed->setText(i18nc("no - not a resumed transfer","No")); // Offered at: m_timeInfo.m_labelTimeOffered->setText(m_transfer->getTimeOffer().toString("hh:mm:ss")); // Started at: if (!m_transfer->getTimeTransferStarted().isNull()) m_timeInfo.m_labelTimeStarted->setText(m_transfer->getTimeTransferStarted().toString("hh:mm:ss")); // Finished at: if (!m_transfer->getTimeTransferFinished().isNull()) { m_timeInfo.m_labelTimeFinished->setText(m_transfer->getTimeTransferFinished().toString("hh:mm:ss")); } updateChangeableView(); } void TransferDetailedInfoPanel::updateChangeableView() { if (!m_transfer) { return; } // Status: if (m_transfer->getStatus() == Transfer::Transferring) { m_locationInfo.m_labelStatus->setText(TransferListModel::getStatusText(m_transfer->getStatus(), m_transfer->getType()) + " ( " + TransferListModel::getSpeedPrettyText(m_transfer->getCurrentSpeed()) + " )"); } else { m_locationInfo.m_labelStatus->setText(m_transfer->getStatusDetail().isEmpty() ? TransferListModel::getStatusText(m_transfer->getStatus(), m_transfer->getType()) : TransferListModel::getStatusText(m_transfer->getStatus(), m_transfer->getType()) + " (" + m_transfer->getStatusDetail() + ')'); } // Progress: m_locationInfo.m_progress->setValue(m_transfer->getProgress()); // Current Position: m_timeInfo.m_labelCurrentPosition->setText(KFormat().formatByteSize(m_transfer->getTransferringPosition())); // Current Speed: m_timeInfo.m_labelCurrentSpeed->setText(TransferListModel::getSpeedPrettyText(m_transfer->getCurrentSpeed())); // Average Speed: m_timeInfo.m_labelAverageSpeed->setText(TransferListModel::getSpeedPrettyText(m_transfer->getAverageSpeed())); // Transferring Time: if (!m_transfer->getTimeTransferStarted().isNull()) { int m_itemringTime; // The m_item is still in progress if (m_transfer->getTimeTransferFinished().isNull()) m_itemringTime = m_transfer->getTimeTransferStarted().secsTo(QDateTime::currentDateTime()); // The m_item has finished else m_itemringTime = m_transfer->getTimeTransferStarted().secsTo(m_transfer->getTimeTransferFinished()); if (m_itemringTime >= 1) m_timeInfo.m_labelTransferringTime->setText(TransferListModel::secToHMS(m_itemringTime)); else m_timeInfo.m_labelTransferringTime->setText(i18nc("less than 1 sec","< 1sec")); } // Estimated Time Left: m_timeInfo.m_labelTimeLeft->setText(TransferListModel::getTimeLeftPrettyText(m_transfer->getTimeLeft())); } void TransferDetailedInfoPanel::slotTransferStatusChanged(Transfer *transfer, int newStatus, int oldStatus) { Q_UNUSED(transfer); updateView(); if (newStatus == Transfer::Transferring) { // start auto view-update timer m_autoViewUpdateTimer->start(); } else if (oldStatus == Transfer::Transferring) { // stop auto view-update timer m_autoViewUpdateTimer->stop(); } } void TransferDetailedInfoPanel::slotLocationChanged(const QString &url) { if (m_transfer && m_transfer->getType() == Transfer::Receive) { TransferRecv *transfer = static_cast(m_transfer); transfer->setFileURL(QUrl::fromLocalFile(url)); updateView(); } } } } diff --git a/src/dcc/transferlistmodel.cpp b/src/dcc/transferlistmodel.cpp index cc1763f9..2c44a472 100644 --- a/src/dcc/transferlistmodel.cpp +++ b/src/dcc/transferlistmodel.cpp @@ -1,671 +1,671 @@ /* This class represents a DCC transferlist model. */ /* 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) any later version. */ /* Copyright (C) 2009,2010 Bernd Buschinski */ #include "transferlistmodel.h" #include #include #include #include #include #include namespace Konversation { namespace DCC { QString TransferHeaderData::typeToName(int headertype) { switch (headertype) { case FileName: return i18n("File"); case PartnerNick: return i18n("Partner"); case OfferDate: return i18n("Started At"); case Progress: return i18n("Progress"); case Position: return i18n("Position"); case CurrentSpeed: return i18n("Speed"); case SenderAdress: return i18n("Sender Address"); case Status: return i18n("Status"); case TimeLeft: return i18n("Remaining"); case TypeIcon: return i18n("Type"); default: return QString(); }; } TransferSizeDelegate::TransferSizeDelegate(KCategoryDrawer* categoryDrawer, QObject* parent) : QStyledItemDelegate(parent) { m_categoryDrawer = categoryDrawer; } QSize TransferSizeDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { const int itemType = index.data(TransferListModel::TransferDisplayType).toInt(); int height; QFontMetrics metrics(option.font); int width = metrics.width(index.data(Qt::DisplayRole).toString()); if (itemType == TransferItemData::SendCategory || itemType == TransferItemData::ReceiveCategory) { height = m_categoryDrawer->categoryHeight(index, QStyleOption()); } else { height = metrics.height(); QVariant pixmapVariant = index.data(Qt::DecorationRole); if (!pixmapVariant.isNull()) { QPixmap tPix = pixmapVariant.value(); height = qMax(height, tPix.height()); width = qMax(width, tPix.width()); } } return QSize(width, height); } void TransferSizeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.isValid()) { int type = index.data(TransferListModel::TransferDisplayType).toInt(); if (type == TransferItemData::SpaceRow) return; QStyledItemDelegate::paint(painter, option, index); } } TransferProgressBarDelegate::TransferProgressBarDelegate(QObject *parent) : QStyledItemDelegate(parent) { } void TransferProgressBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.isValid()) { int type = index.data(TransferListModel::TransferDisplayType).toInt(); if (type == TransferItemData::SendCategory || type == TransferItemData::ReceiveCategory || type == TransferItemData::SpaceRow) { return; } } QStyle* style = option.widget ? option.widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, option.widget); QStyleOptionProgressBar progressBarOption; progressBarOption.rect = option.rect; progressBarOption.minimum = 0; progressBarOption.maximum = 100; progressBarOption.progress = index.data().toInt(); progressBarOption.text = QString::number(progressBarOption.progress) + '%'; progressBarOption.textVisible = true; progressBarOption.state = option.state; style->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter); } TransferListProxyModel::TransferListProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { } bool TransferListProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { int leftType = left.data(TransferListModel::TransferDisplayType).toInt(); int rightType = right.data(TransferListModel::TransferDisplayType).toInt(); if (leftType == rightType) { const int headerType = left.data(TransferListModel::HeaderType).toInt(); if (headerType == TransferHeaderData::Position) { return (left.data(TransferListModel::TransferProgress).toInt() < right.data(TransferListModel::TransferProgress).toInt()); } else if (headerType == TransferHeaderData::OfferDate) { return (left.data(TransferListModel::TransferOfferDate).toDateTime() < right.data(TransferListModel::TransferOfferDate).toDateTime()); } return QSortFilterProxyModel::lessThan(left, right); } //visible order should always be //DCCReceiveCategory > DCCReceiveItem > SpacerRow > DCCSendCategory > DCCSendItem if (sortOrder() == Qt::AscendingOrder) { return !(leftType < rightType); } return (leftType < rightType); } TransferListModel::TransferListModel(QObject *parent) : QAbstractListModel(parent) { } void TransferListModel::append(const TransferItemData &item) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); m_transferList.append(item); endInsertRows(); if (item.transfer) { connect (item.transfer, SIGNAL(statusChanged(Konversation::DCC::Transfer*,int,int)), this, SLOT(transferStatusChanged(Konversation::DCC::Transfer*,int,int))); } } Qt::ItemFlags TransferListModel::flags (const QModelIndex &index) const { if (index.isValid()) { int type = index.data(TransferDisplayType).toInt(); if (type == TransferItemData::ReceiveCategory || type == TransferItemData::SendCategory || type == TransferItemData::SpaceRow) { return Qt::NoItemFlags; } } return QAbstractItemModel::flags(index); } void TransferListModel::transferStatusChanged (Transfer *transfer, int/* oldstatus*/, int/* newstatus*/) { for (int i = 0; i < m_transferList.count(); ++i) { const TransferItemData &item = m_transferList.at(i); if (transfer == item.transfer) { emit dataChanged(createIndex(i, 0), createIndex(i, columnCount() - 1)); return; } } } void TransferListModel::appendHeader(TransferHeaderData data) { data.name = TransferHeaderData::typeToName(data.type); m_headerList.append(data); } QVariant TransferListModel::data (const QModelIndex &index, int role) const { if(!index.isValid() || index.row() >= m_transferList.count()) { return QVariant(); } if (role == TransferDisplayType) { return m_transferList[index.row()].displayType; } else if (role == KCategorizedSortFilterProxyModel::CategoryDisplayRole) { return displayTypeToString(m_transferList[index.row()].displayType); } else if (role == HeaderType) { return headerData(index.column(), Qt::Horizontal, role); } - Transfer *transfer = 0; + Transfer *transfer = nullptr; if (m_transferList[index.row()].displayType == TransferItemData::ReceiveItem || m_transferList[index.row()].displayType == TransferItemData::SendItem) { transfer = m_transferList[index.row()].transfer; } else { //category item return QVariant(); } switch (role) { case Qt::DisplayRole: { int type = columnToHeaderType(index.column()); switch (type) { case TransferHeaderData::FileName: return transfer->getFileName(); case TransferHeaderData::PartnerNick: return transfer->getPartnerNick(); case TransferHeaderData::Progress: return transfer->getProgress(); case TransferHeaderData::OfferDate: return transfer->getTimeOffer().toString("hh:mm:ss"); case TransferHeaderData::Position: return getPositionPrettyText(transfer->getTransferringPosition(), transfer->getFileSize()); case TransferHeaderData::CurrentSpeed: return getSpeedPrettyText(transfer->getCurrentSpeed()); case TransferHeaderData::SenderAdress: return getSenderAddressPrettyText(transfer); case TransferHeaderData::Status: return getStatusText(transfer->getStatus(), transfer->getType()); case TransferHeaderData::TimeLeft: return getTimeLeftPrettyText(transfer->getTimeLeft()); default: qDebug() << "unknown columntype: " << type; case TransferHeaderData::TypeIcon: return QVariant(); }; break; } case TransferType: return transfer->getType(); case TransferStatus: return transfer->getStatus(); case TransferPointer: return qVariantFromValue(transfer); case TransferProgress: return transfer->getProgress(); case TransferOfferDate: return transfer->getTimeOffer(); case Qt::DecorationRole: { int type = columnToHeaderType(index.column()); switch (type) { case TransferHeaderData::Status: { QVariant decoration(QVariant::Pixmap); decoration.setValue(getStatusIcon(transfer->getStatus())); return decoration; } case TransferHeaderData::TypeIcon: { QVariant decoration(QVariant::Pixmap); decoration.setValue(getTypeIcon(transfer->getType())); return decoration; } default: return QVariant(); } break; } case Qt::ToolTipRole: { int type = columnToHeaderType(index.column()); QString tooltip; switch (type) { case TransferHeaderData::FileName: tooltip = transfer->getFileName(); break; case TransferHeaderData::PartnerNick: tooltip = transfer->getPartnerNick(); break; case TransferHeaderData::Progress: tooltip = QString::number(transfer->getProgress()) + '%'; break; case TransferHeaderData::OfferDate: tooltip = transfer->getTimeOffer().toString("hh:mm:ss"); break; case TransferHeaderData::Position: tooltip = getPositionPrettyText(transfer->getTransferringPosition(), transfer->getFileSize()); break; case TransferHeaderData::CurrentSpeed: tooltip = getSpeedPrettyText(transfer->getCurrentSpeed()); break; case TransferHeaderData::SenderAdress: tooltip = getSenderAddressPrettyText(transfer); break; case TransferHeaderData::Status: tooltip = getStatusDescription(transfer->getStatus(), transfer->getType(), transfer->getStatusDetail()); break; case TransferHeaderData::TimeLeft: tooltip = getTimeLeftPrettyText(transfer->getTimeLeft()); break; case TransferHeaderData::TypeIcon: tooltip = displayTypeToString(m_transferList[index.row()].displayType); break; default: qDebug() << "unknown columntype: " << type; break; }; if (tooltip.isEmpty()) { return QVariant(); } else { return QString(QLatin1String("") + tooltip + QLatin1String("")); } } default: return QVariant(); } } QPixmap TransferListModel::getTypeIcon(Transfer::Type type) const { if (type == Transfer::Send) { return KIconLoader::global()->loadIcon("arrow-up", KIconLoader::Small); } else { return KIconLoader::global()->loadIcon("arrow-down", KIconLoader::Small); } } QPixmap TransferListModel::getStatusIcon(Transfer::Status status) const { QString icon; switch (status) { case Transfer::Queued: icon = "media-playback-stop"; break; case Transfer::Preparing: case Transfer::WaitingRemote: case Transfer::Connecting: icon = "network-disconnect"; break; case Transfer::Transferring: icon = "media-playback-start"; break; case Transfer::Done: icon = "dialog-ok"; break; case Transfer::Aborted: case Transfer::Failed: icon = "process-stop"; break; default: break; } return KIconLoader::global()->loadIcon(icon, KIconLoader::Small); } QString TransferListModel::getSpeedPrettyText (transferspeed_t speed) { if (speed == Transfer::Calculating || speed == Transfer::InfiniteValue) { return QString("?"); } else if (speed == Transfer::NotInTransfer) { return QString(); } else { return i18n("%1/sec", KIO::convertSize((KIO::fileoffset_t)speed)); } } QString TransferListModel::getPositionPrettyText(KIO::fileoffset_t position, KIO::filesize_t filesize) const { return KIO::convertSize(position) + " / " + KIO::convertSize(filesize); } QString TransferListModel::getSenderAddressPrettyText(Transfer *transfer) const { if (transfer->getType() == Transfer::Send) { return QString("%1:%2").arg(transfer->getOwnIp()).arg(transfer->getOwnPort()); } else { return QString("%1:%2").arg(transfer->getPartnerIp()).arg(transfer->getPartnerPort()); } } QString TransferListModel::getTimeLeftPrettyText(int timeleft) { switch (timeleft) { case Transfer::InfiniteValue: return QString('?'); default: return secToHMS(timeleft); case Transfer::NotInTransfer: return QString(); } } QString TransferListModel::secToHMS(long sec) { int remSec = sec; int remHour = remSec / 3600; remSec -= remHour * 3600; int remMin = remSec / 60; remSec -= remMin * 60; // remHour can be more than 25, so we can't use QTime here. return QString("%1:%2:%3") .arg(QString::number(remHour).rightJustified(2, '0', false)) .arg(QString::number(remMin).rightJustified(2, '0')) .arg(QString::number(remSec).rightJustified(2, '0')); } QString TransferListModel::getStatusText(Transfer::Status status, Transfer::Type type) { switch (status) { case Transfer::Queued: return i18n("Queued"); case Transfer::Preparing: return i18n("Preparing"); case Transfer::WaitingRemote: return i18nc("Transfer is waiting for the partner to accept or reject it", "Pending"); case Transfer::Connecting: return i18nc("Transfer is connecting to the partner", "Connecting"); case Transfer::Transferring: switch (type) { case Transfer::Receive: return i18n("Receiving"); case Transfer::Send: return i18n("Sending"); default: qDebug() << "unknown type: " << type; return QString(); } case Transfer::Done: return i18nc("Transfer has completed successfully", "Done"); case Transfer::Failed: return i18nc("Transfer failed", "Failed"); case Transfer::Aborted: return i18n("Aborted"); default: qDebug() << "unknown status: " << status; return QString(); } } QString TransferListModel::getStatusDescription(Transfer::Status status, Transfer::Type type, const QString& errorMessage) const { switch (status) { case Transfer::Queued: return i18n("Queued - Transfer is waiting for you to accept or reject it"); case Transfer::Preparing: switch (type) { case Transfer::Receive: return i18n("Preparing - Transfer is checking for resumable files"); case Transfer::Send: return i18n("Preparing - Transfer is acquiring the data to send"); default: qDebug() << "unknown type: " << type; return QString(); } case Transfer::WaitingRemote: return i18n("Pending - Transfer is waiting for the partner to accept or reject it"); case Transfer::Connecting: return i18n("Connecting - Transfer is connecting to the partner"); case Transfer::Transferring: switch (type) { case Transfer::Receive: return i18n("Receiving - Transfer is receiving data from the partner"); case Transfer::Send: return i18n("Sending - Transfer is sending data to partner"); default: qDebug() << "unknown type: " << type; return QString(); } case Transfer::Done: return i18n("Done - Transfer has completed successfully"); case Transfer::Failed: if (errorMessage.isEmpty()) { return i18n("Failed - Transfer failed with 'unknown reason'"); } else { return i18n("Failed - Transfer failed with reason '%1'", errorMessage); } case Transfer::Aborted: return i18n("Aborted - Transfer was aborted by the User"); default: qDebug() << "unknown status: " << status; return QString(); } } int TransferListModel::columnToHeaderType (int column) const { return m_headerList.at(column).type; } int TransferListModel::columnCount (const QModelIndex &/*parent*/) const { return m_headerList.count(); } QVariant TransferListModel::headerData (int section, Qt::Orientation orientation, int role) const { if(orientation == Qt::Vertical || section >= columnCount() || section < 0) { return QVariant(); } switch (role) { case Qt::DisplayRole: return m_headerList.at(section).name; case HeaderType: return m_headerList.at(section).type; default: return QVariant(); } } int TransferListModel::rowCount (const QModelIndex &/*parent*/) const { return m_transferList.count(); } bool TransferListModel::removeRow (int row, const QModelIndex &parent) { return removeRows(row, 1, parent); } bool TransferListModel::removeRows (int row, int count, const QModelIndex &parent) { if (count < 1 || row < 0 || (row + count) > rowCount(parent)) { return false; } beginRemoveRows(parent, row, row + count - 1); bool blockSignal = signalsBlocked(); blockSignals(true); for (int i = row + count - 1; i >= row; --i) { TransferItemData item = m_transferList.takeAt(i); //Category items don't have a transfer if (item.transfer) { item.transfer->removedFromView(); } } blockSignals(blockSignal); endRemoveRows(); emit rowsPermanentlyRemoved (row, row + count - 1); return true; } int TransferListModel::itemCount (TransferItemData::ItemDisplayType displaytype) const { int count = 0; foreach (const TransferItemData &item, m_transferList) { if (item.displayType == displaytype) { ++count; } } return count; } QString TransferListModel::displayTypeToString (int type) const { if (type == TransferItemData::ReceiveCategory) { return i18n("Incoming Transfers"); } else { return i18n("Outgoing Transfers"); } } } } diff --git a/src/dcc/transfermanager.cpp b/src/dcc/transfermanager.cpp index 3aa3d2ae..589496aa 100644 --- a/src/dcc/transfermanager.cpp +++ b/src/dcc/transfermanager.cpp @@ -1,428 +1,428 @@ /* 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) any later version. */ /* Copyright (C) 2007 Shintaro Matsuoka Copyright (C) 2009 Michael Kreitzer Copyright (C) 2009 Bernd Buschinski */ #include #include "transfermanager.h" #include "transferrecv.h" #include "transfersend.h" #include "application.h" #include "preferences.h" #include "upnpmcastsocket.h" #include "upnprouter.h" #include "transfer.h" #include "chat.h" using namespace Konversation::UPnP; namespace Konversation { namespace DCC { TransferManager::TransferManager( QObject* parent ) : QObject( parent ) { // initial number m_nextReverseTokenNumber = 1001; m_defaultIncomingFolder = Preferences::self()->dccPath(); connect( Application::instance(), SIGNAL(appearanceChanged()), this, SLOT(slotSettingsChanged()) ); - m_upnpRouter = 0; - m_upnpSocket = 0; + m_upnpRouter = nullptr; + m_upnpSocket = nullptr; if (Preferences::self()->dccUPnP()) startupUPnP(); } TransferManager::~TransferManager() { qDebug(); foreach (TransferSend* sendItem, m_sendItems) { sendItem->abort(); } foreach (TransferRecv* recvItem, m_recvItems) { recvItem->abort(); } foreach (Chat* chatItem, m_chatItems) { chatItem->close(); } // give the sockets a minor break to close everything 100% // yes I am aware of the fact that this is not really needed, theoretical while (!m_sendItems.isEmpty()) { delete m_sendItems.takeFirst(); } while (!m_recvItems.isEmpty()) { delete m_recvItems.takeFirst(); } while (!m_chatItems.isEmpty()) { delete m_chatItems.takeFirst(); } shutdownUPnP(); } void TransferManager::startupUPnP(void) { m_upnpSocket = new UPnPMCastSocket(); connect(m_upnpSocket, &UPnP::UPnPMCastSocket::discovered, this, &TransferManager::upnpRouterDiscovered); m_upnpSocket->discover(); } void TransferManager::shutdownUPnP(void) { // This deletes the router too. delete m_upnpSocket; - m_upnpSocket = 0; - m_upnpRouter = 0; + m_upnpSocket = nullptr; + m_upnpRouter = nullptr; } TransferRecv* TransferManager::newDownload() { TransferRecv* transfer = new TransferRecv(this); m_recvItems.push_back( transfer ); connect(transfer, &TransferRecv::removed, this, &TransferManager::removeRecvItem); initTransfer( transfer ); return transfer; } TransferSend* TransferManager::newUpload() { TransferSend* transfer = new TransferSend(this); m_sendItems.push_back( transfer ); connect(transfer, &TransferSend::removed, this, &TransferManager::removeSendItem); initTransfer( transfer ); return transfer; } Chat* TransferManager::newChat() { Chat* chat = new Chat(this); m_chatItems.append(chat); connect(chat, &Chat::removed, this, &TransferManager::removeChatItem); return chat; } TransferSend* TransferManager::rejectSend(int connectionId, const QString& partnerNick, const QString& fileName) { - TransferSend* transfer = 0; + TransferSend* transfer = nullptr; // find applicable one foreach (TransferSend* it, m_sendItems ) { if ( ( it->getStatus() == Transfer::Queued || it->getStatus() == Transfer::WaitingRemote ) && it->getConnectionId() == connectionId && it->getPartnerNick() == partnerNick && it->getFileName() == fileName ) { transfer = it; qDebug() << "Filename match: " << fileName; break; } } if ( transfer ) transfer->reject(); return transfer; } Chat* TransferManager::rejectChat(int connectionId, const QString& partnerNick) { - Chat* chat = 0; + Chat* chat = nullptr; // find applicable one foreach (Chat* it, m_chatItems) { if (it->status() == Chat::WaitingRemote && it->connectionId() == connectionId && it->partnerNick() == partnerNick) { chat = it; break; } } if (chat) chat->reject(); return chat; } TransferRecv* TransferManager::resumeDownload( int connectionId, const QString& partnerNick, const QString& fileName, quint16 ownPort, quint64 position ) { - TransferRecv* transfer = 0; + TransferRecv* transfer = nullptr; // find applicable one foreach (TransferRecv* it, m_recvItems ) { if ( ( it->getStatus() == Transfer::Queued || it->getStatus() == Transfer::WaitingRemote ) && it->getConnectionId() == connectionId && it->getPartnerNick() == partnerNick && it->getFileName() == fileName && it->isResumed() ) { transfer = it; qDebug() << "Filename match: " << fileName << ", claimed port: " << ownPort << ", item port: " << transfer->getOwnPort(); // the port number can be changed behind NAT, so we pick an item which only the filename is correspondent in that case. if ( transfer->getOwnPort() == ownPort ) { break; } } } if ( transfer ) transfer->startResume( position ); return transfer; } TransferSend* TransferManager::resumeUpload( int connectionId, const QString& partnerNick, const QString& fileName, quint16 ownPort, quint64 position ) { - TransferSend* transfer = 0; + TransferSend* transfer = nullptr; // find applicable one foreach ( TransferSend* it, m_sendItems ) { if ( ( it->getStatus() == Transfer::Queued || it->getStatus() == Transfer::WaitingRemote ) && it->getConnectionId() == connectionId && it->getPartnerNick() == partnerNick && it->getFileName() == fileName && !it->isResumed() ) { transfer = it; qDebug() << "Filename match: " << fileName << ", claimed port: " << ownPort << ", item port: " << transfer->getOwnPort(); // the port number can be changed behind NAT, so we pick an item which only the filename is correspondent in that case. if ( transfer->getOwnPort() == ownPort ) { break; } } } if ( transfer ) transfer->setResume( position ); return transfer; } TransferSend* TransferManager::startReverseSending( int connectionId, const QString& partnerNick, const QString& fileName, const QString& partnerHost, quint16 partnerPort, quint64 fileSize, const QString& token ) { qDebug() << "Server group ID: " << connectionId << ", partner: " << partnerNick << ", filename: " << fileName << ", partner IP: " << partnerHost << ", parnter port: " << partnerPort << ", filesize: " << fileSize << ", token: " << token; - TransferSend* transfer = 0; + TransferSend* transfer = nullptr; // find applicable one foreach ( TransferSend* it, m_sendItems ) { if ( it->getStatus() == Transfer::WaitingRemote && it->getConnectionId() == connectionId && it->getPartnerNick() == partnerNick && it->getFileName() == fileName && it->getFileSize() == fileSize && it->getReverseToken() == token ) { transfer = it; break; } } if ( transfer ) transfer->connectToReceiver( partnerHost, partnerPort ); return transfer; } Chat* TransferManager::startReverseChat(int connectionId, const QString& partnerNick, const QString& partnerHost, quint16 partnerPort, const QString& token) { qDebug() << "Server group ID: " << connectionId << ", partner: " << partnerNick << ", partner IP: " << partnerHost << ", parnter port: " << partnerPort << ", token: " << token; - Chat* chat = 0; + Chat* chat = nullptr; // find applicable one foreach (Chat* it, m_chatItems) { if ( it->status() == Chat::WaitingRemote && it->connectionId() == connectionId && it->partnerNick() == partnerNick && it->reverseToken() == token ) { chat = it; break; } } if (chat) { chat->setPartnerIp(partnerHost); chat->setPartnerPort(partnerPort); chat->connectToPartner(); } return chat; } void TransferManager::acceptDccGet(int connectionId, const QString& partnerNick, const QString& fileName) { qDebug() << "Server group ID: " << connectionId << ", partner: " << partnerNick << ", filename: " << fileName; bool nickEmpty = partnerNick.isEmpty(); bool fileEmpty = fileName.isEmpty(); foreach ( TransferRecv* it, m_recvItems ) { if ( it->getStatus() == Transfer::Queued && it->getConnectionId() == connectionId && (nickEmpty || it->getPartnerNick() == partnerNick) && (fileEmpty || it->getFileName() == fileName) ) { it->start(); } } } void TransferManager::initTransfer( Transfer* transfer ) { connect(transfer, &TransferSend::statusChanged, this, &TransferManager::slotTransferStatusChanged); emit newTransferAdded( transfer ); } bool TransferManager::isLocalFileInWritingProcess( const QUrl &url ) const { foreach ( TransferRecv* it, m_recvItems ) { if ( ( it->getStatus() == Transfer::Connecting || it->getStatus() == Transfer::Transferring ) && it->getFileURL() == url ) { return true; } } return false; } int TransferManager::generateReverseTokenNumber() { return m_nextReverseTokenNumber++; } bool TransferManager::hasActiveTransfers() { foreach ( TransferSend* it, m_sendItems ) { if (it->getStatus() == Transfer::Transferring) return true; } foreach ( TransferRecv* it, m_recvItems ) { if (it->getStatus() == Transfer::Transferring) return true; } return false; } bool TransferManager::hasActiveChats() { foreach (Chat* chat, m_chatItems) { if (chat->status() == Chat::Chatting) return true; } return false; } void TransferManager::slotTransferStatusChanged( Transfer* item, int newStatus, int oldStatus ) { qDebug() << oldStatus << " -> " << newStatus << " " << item->getFileName() << " (" << item->getType() << ")"; if ( newStatus == Transfer::Queued ) emit newDccTransferQueued( item ); } void TransferManager::slotSettingsChanged() { // update the default incoming directory for already existed DCCRECV items if ( Preferences::self()->dccPath() != m_defaultIncomingFolder ) { foreach ( TransferRecv* it, m_recvItems ) { if ( it->getStatus() == Transfer::Queued && it->getFileURL().adjusted(QUrl::RemoveFilename) == m_defaultIncomingFolder.adjusted(QUrl::RemoveFilename)) { QUrl url = QUrl::fromLocalFile(Preferences::self()->dccPath().adjusted(QUrl::StripTrailingSlash).toString() + QDir::separator() + it->getFileURL().fileName()); it->setFileURL(url); emit fileURLChanged( it ); } } m_defaultIncomingFolder = Preferences::self()->dccPath(); } } void TransferManager::removeSendItem( Transfer* item ) { TransferSend* transfer = static_cast< TransferSend* > ( item ); m_sendItems.removeOne( transfer ); item->deleteLater(); } void TransferManager::removeRecvItem( Transfer* item ) { TransferRecv* transfer = static_cast< TransferRecv* > ( item ); m_recvItems.removeOne( transfer ); item->deleteLater(); } void TransferManager::removeChatItem(Konversation::DCC::Chat* chat) { m_chatItems.removeOne(chat); chat->deleteLater(); } void TransferManager::upnpRouterDiscovered(UPnPRouter *router) { qDebug() << "Router discovered!"; // Assuming only 1 router for now m_upnpRouter = router; } UPnPRouter* TransferManager::getUPnPRouter() { return m_upnpRouter; } } } diff --git a/src/dcc/transferpanel.cpp b/src/dcc/transferpanel.cpp index a81835aa..acf3ec32 100644 --- a/src/dcc/transferpanel.cpp +++ b/src/dcc/transferpanel.cpp @@ -1,533 +1,533 @@ /* 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) any later version. */ /* begin: Mit Aug 7 2002 copyright: (C) 2002 by Dario Abatianni email: eisfuchs@tigress.com */ /* Copyright (C) 2004-2008 Shintaro Matsuoka Copyright (C) 2009,2010 Bernd Buschinski */ #include "transferpanel.h" #include "application.h" #include "transferdetailedinfopanel.h" #include "transfermanager.h" #include "transfersend.h" #include "preferences.h" #include "transferview.h" #include "transferlistmodel.h" #include #include #include #include #include #include #include namespace Konversation { namespace DCC { TransferPanel::TransferPanel(QWidget *parent) : ChatWindow(parent) { setType(ChatWindow::DccTransferPanel); setName(i18n("DCC Status")); initGUI(); connect(Application::instance()->getDccTransferManager(), SIGNAL(newTransferAdded(Konversation::DCC::Transfer*)), this, SLOT(slotNewTransferAdded(Konversation::DCC::Transfer*))); } TransferPanel::~TransferPanel() { KConfigGroup config(KSharedConfig::openConfig(), "DCC Settings"); const QByteArray state = m_splitter->saveState(); config.writeEntry(QString("PanelSplitter"), state.toBase64()); } void TransferPanel::initGUI() { setSpacing(0); m_toolBar = new KToolBar(this, true, true); m_toolBar->setObjectName("dccstatus_toolbar"); m_splitter = new QSplitter(this); m_splitter->setOrientation(Qt::Vertical); m_transferView = new TransferView(m_splitter); connect(m_transferView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(updateButton())); connect(m_transferView, &TransferView::runSelectedTransfers, this, &TransferPanel::runDcc); // detailed info panel m_detailPanel = new TransferDetailedInfoPanel(m_splitter); m_splitter->setStretchFactor(0, QSizePolicy::Expanding); // popup menu m_popup = new QMenu(this); m_selectAll = m_popup->addAction(i18n("&Select All Items"), this, SLOT(selectAll())); m_selectAllCompleted = m_popup->addAction(i18n("S&elect All Completed Items"), this, SLOT(selectAllCompleted())); m_popup->addSeparator(); // ----- m_accept = m_popup->addAction(QIcon::fromTheme("media-playback-start"), i18n("&Accept"), this, SLOT(acceptDcc())); m_accept->setStatusTip(i18n("Start receiving")); m_abort = m_popup->addAction(QIcon::fromTheme("process-stop"),i18n("A&bort"), this, SLOT(abortDcc())); m_abort->setStatusTip(i18n("Abort the transfer(s)")); m_popup->addSeparator(); // ----- m_resend = m_popup->addAction(QIcon::fromTheme("edit-redo"),i18n("Resend"), this, SLOT(resendFile())); m_clear = m_popup->addAction(QIcon::fromTheme("edit-delete"),i18nc("clear selected dcctransfer","&Clear"), this, SLOT(clearDcc())); m_clear->setStatusTip(i18n("Clear all selected Items")); m_clearCompleted = m_popup->addAction(QIcon::fromTheme("edit-clear-list"),i18n("Clear Completed"), this, SLOT(clearCompletedDcc())); m_clearCompleted->setStatusTip(i18n("Clear Completed Items")); m_popup->addSeparator(); // ----- m_open = m_popup->addAction(QIcon::fromTheme("system-run"), i18n("&Open File"), this, SLOT(runDcc())); m_open->setStatusTip(i18n("Run the file")); m_openLocation = m_popup->addAction(QIcon::fromTheme("document-open-folder"), i18n("Open Location"), this, SLOT(openLocation())); m_openLocation->setStatusTip(i18n("Open the file location")); m_transferView->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_transferView, &TransferView::customContextMenuRequested, this, &TransferPanel::popupRequested); // misc. connect(m_transferView, &TransferView::doubleClicked, this, &TransferPanel::doubleClicked); connect(m_transferView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(setDetailPanelItem(QItemSelection,QItemSelection))); m_toolBar->addAction(m_accept); m_toolBar->addAction(m_abort); m_toolBar->addAction(m_clear); m_toolBar->addAction(m_clearCompleted); m_toolBar->addAction(m_open); m_toolBar->addAction(m_openLocation); KConfigGroup config(KSharedConfig::openConfig(), "DCC Settings"); QByteArray state; if (config.hasKey("PanelSplitter")) { state = config.readEntry("PanelSplitter", state); state = QByteArray::fromBase64(state); m_splitter->restoreState(state); } updateButton(); } void TransferPanel::slotNewTransferAdded(Transfer *transfer) { connect(transfer, SIGNAL(statusChanged(Konversation::DCC::Transfer*,int,int)), this, SLOT(slotTransferStatusChanged())); m_transferView->addTransfer(transfer); if (m_transferView->itemCount() == 1) { m_transferView->selectAll(); m_detailPanel->setTransfer(transfer); updateButton(); } } void TransferPanel::slotTransferStatusChanged() { updateButton(); activateTabNotification(Konversation::tnfSystem); } void TransferPanel::updateButton() { bool accept = false, abort = false, clear = false, info = false, open = false, openLocation = false, resend = false, selectAll = false, selectAllCompleted = false; QItemSelectionModel *selectionModel = m_transferView->selectionModel(); foreach (const QModelIndex &index, m_transferView->rowIndexes()) { Transfer::Type type = (Transfer::Type)index.data(TransferListModel::TransferType).toInt(); Transfer::Status status = (Transfer::Status)index.data(TransferListModel::TransferStatus).toInt(); selectAll = true; selectAllCompleted |= (status >= Transfer::Done); if (selectionModel->isRowSelected(index.row(), QModelIndex())) { accept |= (status == Transfer::Queued); abort |= (status < Transfer::Done); clear |= (status >= Transfer::Done); info |= (type == Transfer::Send || status == Transfer::Done); open |= (type == Transfer::Send || status == Transfer::Done); openLocation = true; resend |= (type == Transfer::Send && status >= Transfer::Done); } } if (!KAuthorized::authorizeAction("allow_downloading")) { accept = false; } m_selectAll->setEnabled(selectAll); m_selectAllCompleted->setEnabled(selectAllCompleted); m_accept->setEnabled(accept); m_abort->setEnabled(abort); m_clear->setEnabled(clear); m_clearCompleted->setEnabled(selectAllCompleted); m_open->setEnabled(open); m_openLocation->setEnabled(openLocation); m_resend->setEnabled(resend); } void TransferPanel::setDetailPanelItem (const QItemSelection &/*newindex*/, const QItemSelection &/*oldindex*/) { QModelIndex index; if (m_transferView->selectionModel()->selectedRows().contains(m_transferView->selectionModel()->currentIndex())) { index = m_transferView->selectionModel()->currentIndex(); } else if (!m_transferView->selectionModel()->selectedRows().isEmpty()) { index = m_transferView->selectionModel()->selectedRows().first(); } if (index.isValid()) { Transfer *transfer = static_cast(index.data(TransferListModel::TransferPointer).value()); if (transfer) { m_detailPanel->setTransfer(transfer); } } } void TransferPanel::acceptDcc() { foreach (const QModelIndex &index, m_transferView->selectedRows()) { if (index.data(TransferListModel::TransferType).toInt() == Transfer::Receive && index.data(TransferListModel::TransferStatus).toInt() == Transfer::Queued) { Transfer *transfer = static_cast(index.data(TransferListModel::TransferPointer).value()); if (transfer) { transfer->start(); } } } updateButton(); } void TransferPanel::abortDcc() { foreach (const QModelIndex &index, m_transferView->selectedRows()) { if (index.data(TransferListModel::TransferStatus).toInt() < Transfer::Done) { Transfer *transfer = static_cast(index.data(TransferListModel::TransferPointer).value()); if (transfer) { transfer->abort(); } } } updateButton(); } void TransferPanel::resendFile() { QList transferList; foreach (const QModelIndex &index, m_transferView->selectedRows()) { if (index.data(TransferListModel::TransferType).toInt() == Transfer::Send && index.data(TransferListModel::TransferStatus).toInt() >= Transfer::Done) { Transfer *transfer = static_cast(index.data(TransferListModel::TransferPointer).value()); if (!transfer) { continue; } transferList.append(transfer); } } foreach (Transfer* transfer, transferList) { TransferSend *newTransfer = Application::instance()->getDccTransferManager()->newUpload(); newTransfer->setConnectionId(transfer->getConnectionId()); newTransfer->setPartnerNick(transfer->getPartnerNick()); newTransfer->setFileURL(transfer->getFileURL()); newTransfer->setFileName(transfer->getFileName()); newTransfer->setReverse(transfer->isReverse()); if (newTransfer->queue()) { newTransfer->start(); } } } //sort QModelIndexList descending bool rowGreaterThan(const QModelIndex &index1, const QModelIndex &index2) { return index1.row() >= index2.row(); } void TransferPanel::clearDcc() { //selected item Transfer *transfer = m_detailPanel->transfer(); if (transfer && transfer->getStatus() >= Transfer::Done) { //item will be gone - transfer = 0; + transfer = nullptr; } QModelIndexList indexes = m_transferView->selectedRows(); QModelIndexList indexesToRemove; foreach (const QModelIndex &index, indexes) { if (index.data(TransferListModel::TransferStatus).toInt() >= Transfer::Done) { indexesToRemove.append(index); } } //sort QModelIndexList descending //NOTE: selectedRows() returned an unsorted list qSort(indexesToRemove.begin(), indexesToRemove.end(), rowGreaterThan); //remove from last to first item, to keep a valid row foreach (const QModelIndex &index, indexesToRemove) { m_transferView->model()->removeRow(index.row(), QModelIndex()); //needed, otherwise valid rows "can be treated" as invalid, //proxymodel does not keep up with changes m_transferView->updateModel(); } //remove all gone items foreach (const QModelIndex &index, indexesToRemove) { indexes.removeOne(index); } m_transferView->clearSelection(); QList toSelectList; //select everything that got not removed foreach (const QModelIndex &index, indexes) { int offset = 0; foreach (const QModelIndex &removedIndex, indexesToRemove) { if (removedIndex.row() < index.row()) { ++offset; } } toSelectList.append(index.row() - offset); } m_transferView->selectRows(toSelectList); if (transfer) { m_detailPanel->setTransfer(transfer); } else if (!transfer || m_transferView->itemCount() == 0 || m_transferView->selectedIndexes().count() == 0) { m_detailPanel->clear(); } updateButton(); } void TransferPanel::clearCompletedDcc() { //save selected item Transfer *transfer = m_detailPanel->transfer(); if (transfer && transfer->getStatus() >= Transfer::Done) { //item will be gone - transfer = 0; + transfer = nullptr; } QModelIndexList indexesToRemove; QModelIndexList selectedIndexes = m_transferView->selectedRows(); foreach (const QModelIndex &index, m_transferView->rowIndexes()) { if (index.data(TransferListModel::TransferStatus).toInt() >= Transfer::Done) { indexesToRemove.append(index); } } //sort QModelIndexList descending //NOTE: selectedRows() returned an unsorted list qSort(indexesToRemove.begin(), indexesToRemove.end(), rowGreaterThan); //remove from last to first item, to keep a valid row foreach (const QModelIndex &index, indexesToRemove) { m_transferView->model()->removeRow(index.row(), QModelIndex()); //needed, otherwise valid rows "can be treated" as invalid, //proxymodel does not keep up with changes m_transferView->updateModel(); } //remove all gone items foreach (const QModelIndex &index, indexesToRemove) { selectedIndexes.removeOne(index); } m_transferView->clearSelection(); QList toSelectList; //select everything that got not removed foreach (const QModelIndex &index, selectedIndexes) { int offset = 0; foreach (const QModelIndex &removedIndex, indexesToRemove) { if (removedIndex.row() < index.row()) { ++offset; } } toSelectList.append(index.row() - offset); } m_transferView->selectRows(toSelectList); if (transfer) { m_detailPanel->setTransfer(transfer); } else if (m_transferView->itemCount() == 0 || m_transferView->selectedIndexes().count() == 0 || !transfer) { m_detailPanel->clear(); } updateButton(); } void TransferPanel::runDcc() { const int selectedRows = m_transferView->selectedRows().count(); if (selectedRows > 3) { int ret = KMessageBox::questionYesNo(this, i18np("You have selected %1 file to execute, are you sure you want to continue?", "You have selected %1 files to execute, are you sure you want to continue?", selectedRows), i18np("Execute %1 file", "Execute %1 files", selectedRows) ); if (ret == KMessageBox::No) { return; } } foreach (const QModelIndex &index, m_transferView->selectedRows()) { if (index.data(TransferListModel::TransferType).toInt() == Transfer::Send || index.data(TransferListModel::TransferStatus).toInt() == Transfer::Done) { Transfer *transfer = static_cast(index.data(TransferListModel::TransferPointer).value()); if (transfer) { transfer->runFile(); } } } } void TransferPanel::openLocation() { foreach (const QModelIndex &index, m_transferView->selectedRows()) { Transfer *transfer = static_cast(index.data(TransferListModel::TransferPointer).value()); if (transfer) { openLocation(transfer); } } } void TransferPanel::selectAll() { m_transferView->selectAll(); updateButton(); } void TransferPanel::selectAllCompleted() { m_transferView->selectAllCompleted(); updateButton(); } void TransferPanel::popupRequested(const QPoint &pos) { updateButton(); m_popup->popup(QWidget::mapToGlobal(m_transferView->viewport()->mapTo(this, pos))); } void TransferPanel::doubleClicked(const QModelIndex &index) { Transfer *transfer = static_cast(index.data(TransferListModel::TransferPointer).value()); if (transfer) { transfer->runFile(); } } // virtual void TransferPanel::childAdjustFocus() { } TransferView *TransferPanel::getTransferView() { return m_transferView; } void TransferPanel::openLocation(Transfer *transfer) { QString urlString = transfer->getFileURL().toString(QUrl::PreferLocalFile|QUrl::RemoveFilename|QUrl::StripTrailingSlash); if (!urlString.isEmpty()) { QUrl url(QUrl::fromLocalFile(urlString)); - new KRun(url, 0, true); + new KRun(url, nullptr, true); } } } } diff --git a/src/dcc/transferrecv.cpp b/src/dcc/transferrecv.cpp index 8f55a693..05db293e 100644 --- a/src/dcc/transferrecv.cpp +++ b/src/dcc/transferrecv.cpp @@ -1,966 +1,966 @@ /* receive a file on DCC protocol begin: Mit Aug 7 2002 copyright: (C) 2002 by Dario Abatianni email: eisfuchs@tigress.com */ /* Copyright (C) 2004-2007 Shintaro Matsuoka Copyright (C) 2004,2005 John Tapsell Copyright (C) 2009 Michael Kreitzer Copyright (C) 2009 Bernd Buschinski */ /* 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) any later version. */ #include #ifdef Q_OS_WIN // Prevent windows system header files from defining min/max as macros. #define NOMINMAX 1 #include #endif #include "transferrecv.h" #include "dcccommon.h" #include "transfermanager.h" #include "application.h" #include "connectionmanager.h" #include "server.h" #include "upnprouter.h" #include #include #include #include #include #include #include /* *flow chart* TransferRecv() start() : called from TransferPanel when user pushes the accept button | \ | requestResume() : called when user chooses to resume in ResumeDialog. it emits the signal ResumeRequest() | | startResume() : called by "Server" | | connectToSender() connectionSuccess() : called by recvSocket */ namespace Konversation { namespace DCC { TransferRecv::TransferRecv(QObject *parent) : Transfer(Transfer::Receive, parent) { qDebug(); - m_serverSocket = 0; - m_recvSocket = 0; - m_writeCacheHandler = 0; + m_serverSocket = nullptr; + m_recvSocket = nullptr; + m_writeCacheHandler = nullptr; m_connectionTimer = new QTimer(this); m_connectionTimer->setSingleShot(true); connect(m_connectionTimer, &QTimer::timeout, this, &TransferRecv::connectionTimeout); //timer hasn't started yet. qtimer will be deleted automatically when 'this' object is deleted } TransferRecv::~TransferRecv() { qDebug(); cleanUp(); } void TransferRecv::cleanUp() { qDebug(); stopConnectionTimer(); - disconnect(m_connectionTimer, 0, 0, 0); + disconnect(m_connectionTimer, nullptr, nullptr, nullptr); finishTransferLogger(); if (m_serverSocket) { m_serverSocket->close(); - m_serverSocket = 0; + m_serverSocket = nullptr; if (m_reverse && Preferences::self()->dccUPnP()) { UPnP::UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router) { router->undoForward(m_ownPort, QAbstractSocket::TcpSocket); } } } if (m_recvSocket) { - disconnect(m_recvSocket, 0, 0, 0); + disconnect(m_recvSocket, nullptr, nullptr, nullptr); m_recvSocket->close(); - m_recvSocket = 0; // the instance will be deleted automatically by its parent + m_recvSocket = nullptr; // the instance will be deleted automatically by its parent } if (m_writeCacheHandler) { m_writeCacheHandler->closeNow(); m_writeCacheHandler->deleteLater(); - m_writeCacheHandler = 0; + m_writeCacheHandler = nullptr; } Transfer::cleanUp(); } void TransferRecv::setPartnerIp(const QString &ip) { if (getStatus() == Configuring) { m_partnerIp = ip; } } void TransferRecv::setPartnerPort(quint16 port) { if (getStatus() == Configuring) { m_partnerPort = port; } } void TransferRecv::setFileSize(quint64 fileSize) { if (getStatus() == Configuring) { m_fileSize = fileSize; } } void TransferRecv::setFileName(const QString &fileName) { if (getStatus() == Configuring) { m_fileName = fileName; m_saveFileName = m_fileName; } } void TransferRecv::setFileURL(const QUrl &url) { if (getStatus() == Preparing || getStatus() == Configuring || getStatus() == Queued) { m_fileURL = url; m_saveFileName = url.fileName(); } } void TransferRecv::setReverse(bool reverse, const QString &reverseToken) { if (getStatus() == Configuring) { m_reverse = reverse; if (reverse) { m_partnerPort = 0; m_reverseToken = reverseToken; } } } bool TransferRecv::queue() { qDebug(); if (getStatus() != Configuring) { return false; } if (m_partnerIp.isEmpty()) { return false; } if (m_ownIp.isEmpty()) { m_ownIp = DccCommon::getOwnIp(Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId)); } if (!KAuthorized::authorizeAction("allow_downloading")) { //note we have this after the initialisations so that item looks okay //Do not have the rights to send the file. Shouldn't have gotten this far anyway failed(i18n("The admin has restricted the right to receive files")); return false; } // check if the sender IP is valid if (m_partnerIp == "0.0.0.0") { failed(i18n("Invalid sender address (%1)", m_partnerIp)); return false; } // TODO: should we support it? if (m_fileSize == 0) { failed(i18n("Unsupported negotiation (filesize=0)")); return false; } if (m_fileName.isEmpty()) { m_fileName = "unnamed_file_" + QDateTime::currentDateTime().toString(Qt::ISODate).remove(':'); m_saveFileName = m_fileName; } if (m_fileURL.isEmpty()) { // determine default incoming file URL // set default folder if (!Preferences::self()->dccPath().isEmpty()) { m_fileURL = Preferences::self()->dccPath(); } else { m_fileURL.setPath(KUser(KUser::UseRealUserID).homeDir()); // default folder is *not* specified } //buschinski TODO CHECK ME // add a slash if there is none //m_fileURL.adjustPath(KUrl::AddTrailingSlash); // Append folder with partner's name if wanted if (Preferences::self()->dccCreateFolder()) { m_fileURL = m_fileURL.adjusted(QUrl::StripTrailingSlash); m_fileURL.setPath(m_fileURL.path() + QDir::separator() + m_partnerNick); } // Just incase anyone tries to do anything nasty QString fileNameSanitized = sanitizeFileName(m_saveFileName); // Append partner's name to file name if wanted if (Preferences::self()->dccAddPartner()) { m_fileURL = m_fileURL.adjusted(QUrl::StripTrailingSlash); m_fileURL.setPath(m_fileURL.path() + QDir::separator() + m_partnerNick + '.' + fileNameSanitized); } else { m_fileURL = m_fileURL.adjusted(QUrl::StripTrailingSlash); m_fileURL.setPath(m_fileURL.path() + QDir::separator() + fileNameSanitized); } } return Transfer::queue(); } void TransferRecv::abort() // public slot { qDebug(); if (getStatus() == Transfer::Queued) { Server *server = Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId); if (server) { server->dccRejectSend(m_partnerNick, transferFileName(m_fileName)); } } if(m_writeCacheHandler) { m_writeCacheHandler->write(true); // flush } cleanUp(); setStatus(Aborted); emit done(this); } void TransferRecv::start() // public slot { qDebug() << "[BEGIN]"; if (getStatus() != Queued) { return; } setStatus(Preparing); prepareLocalKio(false, false); qDebug() << "[END]"; } void TransferRecv::prepareLocalKio(bool overwrite, bool resume, KIO::fileoffset_t startPosition) { qDebug() << "URL: " << m_fileURL << endl << "Overwrite: " << overwrite << endl << "Resume: " << resume << " (Position: " << startPosition << ")"; m_resumed = resume; m_transferringPosition = startPosition; if (!createDirs(KIO::upUrl(m_fileURL))) { askAndPrepareLocalKio(i18n("Cannot create the folder or destination is not writable.
" "Folder: %1
", KIO::upUrl(m_fileURL).toString()), ResumeDialog::RA_Rename | ResumeDialog::RA_Cancel | ResumeDialog::RA_OverwriteDefaultPath, ResumeDialog::RA_Rename); return; } if (Application::instance()->getDccTransferManager()->isLocalFileInWritingProcess(m_fileURL)) { askAndPrepareLocalKio(i18n("The file is used by another transfer.
" "%1
", m_fileURL.toString()), ResumeDialog::RA_Rename | ResumeDialog::RA_Cancel, ResumeDialog::RA_Rename); return; } KIO::JobFlags flags; if(overwrite) { flags |= KIO::Overwrite; } if(m_resumed) { flags |= KIO::Resume; } //for now, maybe later flags |= KIO::HideProgressInfo; KIO::TransferJob *transferJob = KIO::put(m_fileURL, -1, flags); if (!transferJob) { qDebug() << "KIO::put() returned NULL. what happened?"; failed(i18n("Could not create a KIO instance")); return; } transferJob->setAutoDelete(true); connect(transferJob, &KIO::TransferJob::canResume, this, &TransferRecv::slotLocalCanResume); connect(transferJob, &KIO::TransferJob::result, this, &TransferRecv::slotLocalGotResult); connect(transferJob, &KIO::TransferJob::dataReq, this, &TransferRecv::slotLocalReady); } void TransferRecv::askAndPrepareLocalKio(const QString &message, int enabledActions, ResumeDialog::ReceiveAction defaultAction, KIO::fileoffset_t startPosition) { switch (ResumeDialog::ask(this, message, enabledActions, defaultAction)) { case ResumeDialog::RA_Resume: prepareLocalKio(false, true, startPosition); break; case ResumeDialog::RA_Overwrite: prepareLocalKio(true, false); break; case ResumeDialog::RA_Rename: prepareLocalKio(false, false); break; case ResumeDialog::RA_Cancel: default: setStatus(Queued); } } bool TransferRecv::createDirs(const QUrl &dirURL) const { QUrl kurl(dirURL); //First we split directories until we reach to the top, //since we need to create directories one by one QList dirList; while (kurl != KIO::upUrl(kurl)) { dirList.prepend(kurl); kurl = KIO::upUrl(kurl); } //Now we create the directories QList::ConstIterator it; for (it=dirList.constBegin(); it != dirList.constEnd(); ++it) { KIO::StatJob* statJob = KIO::stat(*it, KIO::StatJob::SourceSide, 0); statJob->exec(); if (statJob->error()) { KIO::MkdirJob* job = KIO::mkdir(*it, -1); if (!job->exec()) { return false; } } } #ifndef Q_OS_WIN QFileInfo dirInfo(dirURL.toLocalFile()); if (!dirInfo.isWritable()) { return false; } #else //!TODO find equivalent windows solution //from 4.7 QFile Doc: // File permissions are handled differently on Linux/Mac OS X and Windows. // In a non writable directory on Linux, files cannot be created. // This is not always the case on Windows, where, for instance, // the 'My Documents' directory usually is not writable, but it is still // possible to create files in it. #endif return true; } void TransferRecv::slotLocalCanResume(KIO::Job *job, KIO::filesize_t size) { qDebug() << "[BEGIN]" << endl << "size: " << size; KIO::TransferJob* transferJob = dynamic_cast(job); if (!transferJob) { qDebug() << "not a TransferJob? returning"; return; } if (size != 0) { - disconnect(transferJob, 0, 0, 0); + disconnect(transferJob, nullptr, nullptr, nullptr); if (Preferences::self()->dccAutoResume()) { prepareLocalKio(false, true, size); } else { askAndPrepareLocalKio(i18np( "A partial file exists:
" "%2
" "Size of the partial file: 1 byte.
", "A partial file exists:
" "%2
" "Size of the partial file: %1 bytes.
", size, m_fileURL.toString()), ResumeDialog::RA_Resume | ResumeDialog::RA_Overwrite | ResumeDialog::RA_Rename | ResumeDialog::RA_Cancel, ResumeDialog::RA_Resume, size); } transferJob->putOnHold(); } qDebug() << "[END]"; } void TransferRecv::slotLocalGotResult(KJob *job) { qDebug() << "[BEGIN]"; KIO::TransferJob* transferJob = static_cast(job); - disconnect(transferJob, 0, 0, 0); + disconnect(transferJob, nullptr, nullptr, nullptr); switch (transferJob->error()) { case 0: // no error qDebug() << "job->error() returned 0." << endl << "Why was I called in spite of no error?"; break; case KIO::ERR_FILE_ALREADY_EXIST: askAndPrepareLocalKio(i18nc("%1=fileName, %2=local filesize, %3=sender filesize", "The file already exists.
" "%1 (%2)
" "Sender reports file size of %3
", m_fileURL.toString(), KIO::convertSize(QFileInfo(m_fileURL.path()).size()), KIO::convertSize(m_fileSize)), ResumeDialog::RA_Overwrite | ResumeDialog::RA_Rename | ResumeDialog::RA_Cancel, ResumeDialog::RA_Overwrite); break; default: askAndPrepareLocalKio(i18n("Could not open the file.
" "Error: %1

" "%2
", transferJob->error(), m_fileURL.toString()), ResumeDialog::RA_Rename | ResumeDialog::RA_Cancel, ResumeDialog::RA_Rename); } qDebug() << "[END]"; } void TransferRecv::slotLocalReady(KIO::Job *job) { qDebug(); KIO::TransferJob* transferJob = static_cast(job); - disconnect(transferJob, 0, 0, 0); // WriteCacheHandler will control the job after this + disconnect(transferJob, nullptr, nullptr, nullptr); // WriteCacheHandler will control the job after this m_writeCacheHandler = new TransferRecvWriteCacheHandler(transferJob); connect(m_writeCacheHandler, &TransferRecvWriteCacheHandler::done, this, &TransferRecv::slotLocalWriteDone); connect(m_writeCacheHandler, &TransferRecvWriteCacheHandler::gotError, this, &TransferRecv::slotLocalGotWriteError); if (!m_resumed) { connectWithSender(); } else { requestResume(); } } void TransferRecv::connectWithSender() { if (m_reverse) { if (!startListeningForSender()) { return; } Server *server = Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId); if (!server) { failed(i18n("Could not send Reverse DCC SEND acknowledgement to the partner via the IRC server.")); return; } m_ownIp = DccCommon::getOwnIp(server); m_ownPort = m_serverSocket->serverPort(); if (Preferences::self()->dccUPnP()) { UPnP::UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router && router->forward(QHostAddress(server->getOwnIpByNetworkInterface()), m_ownPort, QAbstractSocket::TcpSocket)) { connect(router, &UPnP::UPnPRouter::forwardComplete, this, &TransferRecv::sendReverseAck); } else { sendReverseAck(true, 0); // Try anyways on error } } else { sendReverseAck(false, 0); } } else { connectToSendServer(); } } void TransferRecv::sendReverseAck(bool error, quint16 port) { Server *server = Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId); if (!server) { failed(i18n("Could not send Reverse DCC SEND acknowledgement to the partner via the IRC server.")); return; } qDebug(); if (Preferences::self()->dccUPnP() && this->sender()) { if (port != m_ownPort) return; // Somebody elses forward succeeded disconnect (this->sender(), SIGNAL(forwardComplete(bool,quint16)), this, SLOT(sendRequest(bool,quint16))); if (error) { server->appendMessageToFrontmost(i18nc("Universal Plug and Play", "UPnP"), i18n("Failed to forward port %1. Sending DCC request to remote user regardless.", QString::number(m_ownPort)), QHash(), false); } } setStatus(WaitingRemote, i18n("Waiting for connection")); server->dccReverseSendAck(m_partnerNick, transferFileName(m_fileName), DccCommon::textIpToNumericalIp(m_ownIp), m_ownPort, m_fileSize, m_reverseToken); } void TransferRecv::requestResume() { qDebug(); setStatus(WaitingRemote, i18n("Waiting for remote host's acceptance")); startConnectionTimer(30); qDebug() << "Requesting resume for " << m_partnerNick << " file " << m_fileName << " partner " << m_partnerPort; Server *server = Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId); if (!server) { qDebug() << "Could not retrieve the instance of Server. Connection id: " << m_connectionId; failed(i18n("Could not send DCC RECV resume request to the partner via the IRC server.")); return; } if (m_reverse) { server->dccPassiveResumeGetRequest(m_partnerNick, transferFileName(m_fileName), m_partnerPort, m_transferringPosition, m_reverseToken); } else { server->dccResumeGetRequest(m_partnerNick, transferFileName(m_fileName), m_partnerPort, m_transferringPosition); } } // public slot void TransferRecv::startResume(quint64 position) { qDebug() << "Position:" << position; stopConnectionTimer(); if ((quint64)m_transferringPosition != position) { qDebug() << "remote responded with an unexpected position"<< endl << "expected: " << m_transferringPosition << endl << "remote response: " << position; failed(i18n("Unexpected response from remote host")); return; } connectWithSender(); } void TransferRecv::connectToSendServer() { qDebug(); // connect to sender setStatus(Connecting); startConnectionTimer(30); m_recvSocket = new QTcpSocket(this); connect(m_recvSocket, &QTcpSocket::connected, this, &TransferRecv::startReceiving); connect(m_recvSocket, static_cast(&QTcpSocket::error), this, &TransferRecv::connectionFailed); qDebug() << "Attempting to connect to " << m_partnerIp << ":" << m_partnerPort; m_recvSocket->connectToHost(m_partnerIp, m_partnerPort); } bool TransferRecv::startListeningForSender() { // Set up server socket QString failedReason; if (Preferences::self()->dccSpecificSendPorts()) { m_serverSocket = DccCommon::createServerSocketAndListen(this, &failedReason, Preferences::self()->dccSendPortsFirst(), Preferences::self()->dccSendPortsLast()); } else { m_serverSocket = DccCommon::createServerSocketAndListen(this, &failedReason); } if (!m_serverSocket) { failed(failedReason); return false; } connect(m_serverSocket, &QTcpServer::newConnection, this, &TransferRecv::slotServerSocketReadyAccept); startConnectionTimer(30); return true; } void TransferRecv::slotServerSocketReadyAccept() { //reverse dcc m_recvSocket = m_serverSocket->nextPendingConnection(); if (!m_recvSocket) { failed(i18n("Could not accept the connection (socket error).")); return; } connect(m_recvSocket, static_cast(&QTcpSocket::error), this, &TransferRecv::connectionFailed); // we don't need ServerSocket anymore m_serverSocket->close(); - m_serverSocket = 0; // Will be deleted by parent + m_serverSocket = nullptr; // Will be deleted by parent if (Preferences::self()->dccUPnP()) { UPnP::UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router) { router->undoForward(m_ownPort, QAbstractSocket::TcpSocket); } } startReceiving(); } void TransferRecv::startReceiving() { qDebug(); stopConnectionTimer(); connect(m_recvSocket, &QTcpSocket::readyRead, this, &TransferRecv::readData); m_transferStartPosition = m_transferringPosition; //we don't need the original filename anymore, overwrite it to display the correct one in transfermanager/panel m_fileName = m_saveFileName; m_ownPort = m_recvSocket->localPort(); startTransferLogger(); // initialize CPS counter, ETA counter, etc... setStatus(Transferring); } // slot void TransferRecv::connectionFailed(QAbstractSocket::SocketError errorCode) { qDebug() << "Code = " << errorCode << ", string = " << m_recvSocket->errorString(); failed(m_recvSocket->errorString()); } void TransferRecv::readData() // slot { //qDebug(); qint64 actual = m_recvSocket->read(m_buffer, m_bufferSize); if (actual > 0) { //actual is the size we read in, and is guaranteed to be less than m_bufferSize m_transferringPosition += actual; m_writeCacheHandler->append(m_buffer, actual); m_writeCacheHandler->write(false); //in case we could not read all the data, leftover data could get lost if (m_recvSocket->bytesAvailable() > 0) { readData(); } else { sendAck(); } } } void TransferRecv::sendAck() // slot { //qDebug() << m_transferringPosition << "/" << (KIO::fileoffset_t)m_fileSize; //It is bound to be 32bit according to dcc specs, -> 4GB limit. //But luckily no client ever reads this value, //except for old mIRC versions, but they couldn't send or receive files over 4GB anyway. //Note: The resume and filesize are set via dcc send command and can be over 4GB quint32 pos = htonl((quint32)m_transferringPosition); m_recvSocket->write((char*)&pos, 4); if (m_transferringPosition == (KIO::fileoffset_t)m_fileSize) { qDebug() << "Sent final ACK."; - disconnect(m_recvSocket, 0, 0, 0); + disconnect(m_recvSocket, nullptr, nullptr, nullptr); m_writeCacheHandler->close(); // WriteCacheHandler will send the signal done() } else if (m_transferringPosition > (KIO::fileoffset_t)m_fileSize) { qDebug() << "The remote host sent larger data than expected: " << m_transferringPosition; failed(i18n("Transfer error")); } } void TransferRecv::slotLocalWriteDone() // <-WriteCacheHandler::done() { qDebug(); cleanUp(); setStatus(Done); emit done(this); } // <- WriteCacheHandler::gotError() void TransferRecv::slotLocalGotWriteError(const QString &errorString) { qDebug(); failed(i18n("KIO error: %1", errorString)); } void TransferRecv::startConnectionTimer(int secs) { qDebug(); m_connectionTimer->start(secs * 1000); } void TransferRecv::stopConnectionTimer() { if (m_connectionTimer->isActive()) { m_connectionTimer->stop(); qDebug(); } } void TransferRecv::connectionTimeout() // slot { qDebug(); failed(i18n("Timed out")); } // WriteCacheHandler TransferRecvWriteCacheHandler::TransferRecvWriteCacheHandler(KIO::TransferJob *transferJob) : m_transferJob(transferJob) { m_writeReady = true; - m_cacheStream = 0; + m_cacheStream = nullptr; connect(m_transferJob, &KIO::TransferJob::dataReq, this, &TransferRecvWriteCacheHandler::slotKIODataReq); connect(m_transferJob, &KIO::TransferJob::result, this, &TransferRecvWriteCacheHandler::slotKIOResult); m_transferJob->setAsyncDataEnabled(m_writeAsyncMode = true); } TransferRecvWriteCacheHandler::~TransferRecvWriteCacheHandler() { closeNow(); } // public void TransferRecvWriteCacheHandler::append(char *data, int size) { // sendAsyncData() and dataReq() cost a lot of time, so we should pack some caches. static const int maxWritePacketSize = 1 * 1024 * 1024; // 1meg if (m_cacheList.isEmpty() || m_cacheList.back().size() + size > maxWritePacketSize) { m_cacheList.append(QByteArray()); delete m_cacheStream; m_cacheStream = new QDataStream(&m_cacheList.back(), QIODevice::WriteOnly); } m_cacheStream->writeRawData(data, size); } // public bool TransferRecvWriteCacheHandler::write(bool force) { // force == false: return without doing anything when the whole cache size is smaller than maxWritePacketSize if (m_cacheList.isEmpty() || !m_transferJob || !m_writeReady || !m_writeAsyncMode) { return false; } if (!force && m_cacheList.count() < 2) { return false; } // do write m_writeReady = false; m_transferJob->sendAsyncData(m_cacheList.front()); //qDebug() << "wrote " << m_cacheList.front().size() << " bytes."; m_cacheList.pop_front(); return true; } void TransferRecvWriteCacheHandler::close() // public { qDebug(); write(true); // write once if kio is ready to write m_transferJob->setAsyncDataEnabled(m_writeAsyncMode = false); qDebug() << "switched to synchronized mode."; qDebug() << "flushing... (remaining caches: " << m_cacheList.count() << ")"; } void TransferRecvWriteCacheHandler::closeNow() // public { write(true); // flush if (m_transferJob) { m_transferJob->kill(); - m_transferJob = 0; + m_transferJob = nullptr; } m_cacheList.clear(); delete m_cacheStream; - m_cacheStream = 0; + m_cacheStream = nullptr; } void TransferRecvWriteCacheHandler::slotKIODataReq(KIO::Job *job, QByteArray &data) { Q_UNUSED(job); // We are in writeAsyncMode if there is more data to be read in from dcc if (m_writeAsyncMode) { m_writeReady = true; } else { // No more data left to read from incoming dcctransfer if (!m_cacheList.isEmpty()) { // once we write everything in cache, the file is complete. // This function will be called once more after this last data is written. data = m_cacheList.front(); qDebug() << "will write " << m_cacheList.front().size() << " bytes."; m_cacheList.pop_front(); } else { // finally, no data left to write or read. qDebug() << "flushing done."; - m_transferJob = 0; + m_transferJob = nullptr; emit done(); // -> TransferRecv::slotLocalWriteDone() } } } void TransferRecvWriteCacheHandler::slotKIOResult(KJob *job) { Q_ASSERT(m_transferJob); - disconnect(m_transferJob, 0, 0, 0); - m_transferJob = 0; + disconnect(m_transferJob, nullptr, nullptr, nullptr); + m_transferJob = nullptr; if (job->error()) { QString errorString = job->errorString(); closeNow(); emit gotError(errorString); // -> TransferRecv::slotLocalGotWriteError() } } } } diff --git a/src/dcc/transfersend.cpp b/src/dcc/transfersend.cpp index 181e2929..83ee708e 100644 --- a/src/dcc/transfersend.cpp +++ b/src/dcc/transfersend.cpp @@ -1,648 +1,648 @@ /* send a file on DCC protocol begin: Mit Aug 7 2002 copyright: (C) 2002 by Dario Abatianni email: eisfuchs@tigress.com */ /* Copyright (C) 2004-2007 Shintaro Matsuoka Copyright (C) 2004,2005 John Tapsell Copyright (C) 2009 Michael Kreitzer Copyright (C) 2009 Bernd Buschinski */ /* 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) any later version. */ #include #ifdef Q_OS_WIN // Prevent windows system header files from defining min/max as macros. #define NOMINMAX 1 #include #endif #include "transfersend.h" #include "dcccommon.h" #include "transfermanager.h" #include "application.h" #include "connectionmanager.h" #include "server.h" #include "upnprouter.h" #include #include #include #include #include #include // TODO: remove the dependence #include #include using namespace Konversation::UPnP; namespace Konversation { namespace DCC { TransferSend::TransferSend(QObject *parent) : Transfer(Transfer::Send, parent) { qDebug(); - m_serverSocket = 0; - m_sendSocket = 0; - m_tmpFile = 0; + m_serverSocket = nullptr; + m_sendSocket = nullptr; + m_tmpFile = nullptr; m_connectionTimer = new QTimer(this); m_connectionTimer->setSingleShot(true); connect(m_connectionTimer, &QTimer::timeout, this, &TransferSend::slotConnectionTimeout); // set defualt values m_reverse = Preferences::self()->dccPassiveSend(); } TransferSend::~TransferSend() { cleanUp(); } void TransferSend::cleanUp() { qDebug(); stopConnectionTimer(); - disconnect(m_connectionTimer, 0, 0, 0); + disconnect(m_connectionTimer, nullptr, nullptr, nullptr); finishTransferLogger(); if (m_tmpFile) { delete m_tmpFile; - m_tmpFile = 0; + m_tmpFile = nullptr; } m_file.close(); if (m_sendSocket) { - disconnect(m_sendSocket, 0, 0, 0); + disconnect(m_sendSocket, nullptr, nullptr, nullptr); m_sendSocket->close(); - m_sendSocket = 0; // the instance will be deleted automatically by its parent + m_sendSocket = nullptr; // the instance will be deleted automatically by its parent } if (m_serverSocket) { m_serverSocket->close(); - m_serverSocket = 0; // the instance will be deleted automatically by its parent + m_serverSocket = nullptr; // the instance will be deleted automatically by its parent if (Preferences::self()->dccUPnP()) { UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router) { router->undoForward(m_ownPort, QAbstractSocket::TcpSocket); } } } Transfer::cleanUp(); } void TransferSend::setFileURL(const QUrl &url) { if (getStatus() == Configuring) { m_fileURL = url; } } void TransferSend::setFileName(const QString &fileName) { if (getStatus() == Configuring) { m_fileName = fileName; } } void TransferSend::setOwnIp(const QString &ownIp) { if (getStatus() == Configuring) { m_ownIp = ownIp; } } void TransferSend::setFileSize(KIO::filesize_t fileSize) { if (getStatus() == Configuring) { m_fileSize = fileSize; } } void TransferSend::setReverse(bool reverse) { if (getStatus() == Configuring) { m_reverse = reverse; } } bool TransferSend::queue() { qDebug(); if (getStatus() != Configuring) { return false; } if (m_ownIp.isEmpty()) { m_ownIp = DccCommon::getOwnIp(Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId)); } if (!KAuthorized::authorizeAction("allow_downloading")) { //Do not have the rights to send the file. Shouldn't have gotten this far anyway //Note this is after the initialisation so the view looks correct still failed(i18n("The admin has restricted the right to send files")); return false; } if (m_fileName.isEmpty()) { m_fileName = sanitizeFileName(m_fileURL.fileName()); } if (Preferences::self()->dccIPv4Fallback()) { m_ownIp = DCC::DccCommon::ipv6FallbackAddress(m_ownIp); } m_fastSend = Preferences::self()->dccFastSend(); qDebug() << "Fast DCC send: " << m_fastSend; //Check the file exists KIO::StatJob* statJob = KIO::stat(m_fileURL, KIO::StatJob::SourceSide, 0); statJob->exec(); if (statJob->error()) { failed(i18n("The url \"%1\" does not exist", m_fileURL.toString())); return false; } //Some protocols, like http, maybe not return a filename, and altFileName may be empty, So prompt the user for one. if (m_fileName.isEmpty()) { bool pressedOk; - m_fileName = QInputDialog::getText(0, i18n("Enter Filename"), + m_fileName = QInputDialog::getText(nullptr, i18n("Enter Filename"), i18n("The file that you are sending to %1 does not have a filename.
Please enter a filename to be presented to the receiver, or cancel the dcc transfer
", getPartnerNick()), QLineEdit::EchoMode::Normal, i18n("unknown"), &pressedOk); if (!pressedOk) { failed(i18n("No filename was given")); return false; } } //FIXME: if "\\\"" works well on other IRC clients, replace "\"" with "\\\"" m_fileName.replace('\"', '_'); if (Preferences::self()->dccSpaceToUnderscore()) { m_fileName.replace(' ', '_'); } if (!m_fileURL.isLocalFile()) { m_tmpFile = new QTemporaryFile(); m_tmpFile->open(); // create the file, and thus create m_tmpFile.fileName m_tmpFile->close(); // no need to keep the file open, it isn't deleted until the destructor is called QUrl tmpUrl = QUrl::fromLocalFile(m_tmpFile->fileName()); KIO::FileCopyJob *fileCopyJob = KIO::file_copy(m_fileURL, tmpUrl, -1, KIO::Overwrite); connect(fileCopyJob, &KIO::FileCopyJob::result, this, &TransferSend::slotLocalCopyReady); fileCopyJob->start(); setStatus(Preparing); return false; // not ready to send yet } - slotLocalCopyReady(0); + slotLocalCopyReady(nullptr); return true; } void TransferSend::slotLocalCopyReady(KJob *job) { QString fn = m_fileURL.toDisplayString(); - bool remoteFile = job != 0; + bool remoteFile = job != nullptr; int error = job ? job->error() : 0; qDebug() << "m_tmpFile: " << fn << "error: " << error << "remote file: " << remoteFile; if (error) { failed(i18n("Could not retrieve \"%1\"", fn)); return; } if (remoteFile) { m_file.setFileName(m_tmpFile->fileName()); } else { m_file.setFileName(m_fileURL.toLocalFile()); } if (m_fileSize == 0) { m_fileSize = m_file.size(); qDebug() << "filesize 0, new filesize: " << m_fileSize; if (m_fileSize == 0) { failed(i18n("Unable to send a 0 byte file.")); return; } } setStatus(Queued); if (remoteFile) { start(); // addDccSend would have done it for us if this was a local file } } void TransferSend::reject() { qDebug(); failed(i18n("DCC SEND request was rejected")); } void TransferSend::abort() // public slot { qDebug(); cleanUp(); setStatus(Aborted); emit done(this); } void TransferSend::start() // public slot { qDebug(); if (getStatus() != Queued) { return; } // common procedure Server *server = Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId); if (!server) { qDebug() << "could not retrieve the instance of Server. Connection id: " << m_connectionId; failed(i18n("Could not send a DCC SEND request to the partner via the IRC server.")); return; } if (!m_reverse) { // Normal DCC SEND qDebug() << "normal DCC SEND"; // Set up server socket QString failedReason; if (Preferences::self()->dccSpecificSendPorts()) { m_serverSocket = DccCommon::createServerSocketAndListen(this, &failedReason, Preferences::self()->dccSendPortsFirst(), Preferences::self()->dccSendPortsLast()); } else { m_serverSocket = DccCommon::createServerSocketAndListen(this, &failedReason); } if (!m_serverSocket) { failed(failedReason); return; } connect(m_serverSocket, &QTcpServer::newConnection, this, &TransferSend::acceptClient); // Get own port number m_ownPort = m_serverSocket->serverPort(); qDebug() << "Own Address=" << m_ownIp << ":" << m_ownPort; if (Preferences::self()->dccUPnP()) { UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router && router->forward(QHostAddress(server->getOwnIpByNetworkInterface()), m_ownPort, QAbstractSocket::TcpSocket)) { connect(router, &UPnPRouter::forwardComplete, this, &TransferSend::sendRequest); } else { sendRequest(true, 0); // Just try w/o UPnP on failure } } else { sendRequest(false, 0); } } else { // Passive DCC SEND qDebug() << "Passive DCC SEND"; int tokenNumber = Application::instance()->getDccTransferManager()->generateReverseTokenNumber(); // TODO: should we append a letter "T" to this token? m_reverseToken = QString::number(tokenNumber); qDebug() << "Passive DCC key(token): " << m_reverseToken; startConnectionTimer(Preferences::self()->dccSendTimeout()); server->dccPassiveSendRequest(m_partnerNick, transferFileName(m_fileName), DccCommon::textIpToNumericalIp(m_ownIp), m_fileSize, m_reverseToken); } setStatus(WaitingRemote, i18n("Awaiting acceptance by remote user...")); } void TransferSend::sendRequest(bool error, quint16 port) { Server *server = Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId); if (!server) { qDebug() << "could not retrieve the instance of Server. Connection id: " << m_connectionId; failed(i18n("Could not send a DCC SEND request to the partner via the IRC server.")); return; } if (Preferences::self()->dccUPnP() && this->sender()) { if (port != m_ownPort) return; // Somebody elses forward succeeded disconnect(this->sender(), SIGNAL(forwardComplete(bool,quint16)), this, SLOT(sendRequest(bool,quint16))); if (error) { server->appendMessageToFrontmost(i18nc("Universal Plug and Play", "UPnP"), i18n("Failed to forward port %1. Sending DCC request to remote user regardless.", QString::number(m_ownPort)), QHash(), false); } } startConnectionTimer(Preferences::self()->dccSendTimeout()); server->dccSendRequest(m_partnerNick, transferFileName(m_fileName), DccCommon::textIpToNumericalIp(m_ownIp), m_ownPort, m_fileSize); } void TransferSend::connectToReceiver(const QString &partnerHost, quint16 partnerPort) { qDebug() << "host:" << partnerHost << "port:" << partnerPort; // Reverse DCC startConnectionTimer(Preferences::self()->dccSendTimeout()); m_partnerIp = partnerHost; m_partnerPort = partnerPort; m_sendSocket = new QTcpSocket(this); connect(m_sendSocket, &QTcpSocket::connected, this, &TransferSend::startSending); connect(m_sendSocket, static_cast(&QTcpSocket::error), this, &TransferSend::slotGotSocketError); setStatus(Connecting); m_sendSocket->connectToHost(partnerHost, partnerPort); } // public bool TransferSend::setResume(quint64 position) { qDebug() << "Position=" << position; if (getStatus() > WaitingRemote) { return false; } if (position >= m_fileSize) { return false; } m_resumed = true; m_transferringPosition = position; return true; } void TransferSend::acceptClient() // slot { // Normal DCC qDebug(); stopConnectionTimer(); m_sendSocket = m_serverSocket->nextPendingConnection(); if (!m_sendSocket) { failed(i18n("Could not accept the connection (socket error).")); return; } connect(m_sendSocket, static_cast(&QTcpSocket::error), this, &TransferSend::slotGotSocketError); // we don't need ServerSocket anymore m_serverSocket->close(); - m_serverSocket = 0; // the instance will be deleted automatically by its parent + m_serverSocket = nullptr; // the instance will be deleted automatically by its parent if (Preferences::self()->dccUPnP()) { UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router) { router->undoForward(m_ownPort, QAbstractSocket::TcpSocket); } } startSending(); } void TransferSend::startSending() { stopConnectionTimer(); connect(m_sendSocket, &QTcpSocket::bytesWritten, this, &TransferSend::bytesWritten); connect(m_sendSocket, &QTcpSocket::readyRead, this, &TransferSend::getAck); m_partnerIp = m_sendSocket->peerAddress().toString(); m_partnerPort = m_sendSocket->peerPort(); m_ownPort = m_sendSocket->localPort(); if (m_file.open(QIODevice::ReadOnly)) { // seek to file position to make resume work m_file.seek(m_transferringPosition); m_transferStartPosition = m_transferringPosition; writeData(); startTransferLogger(); // initialize CPS counter, ETA counter, etc... setStatus(Transferring); } else { failed(getQFileErrorString(m_file.error())); } } void TransferSend::bytesWritten(qint64 bytes) { if (bytes > 0) { m_transferringPosition += bytes; if ((KIO::fileoffset_t)m_fileSize <= m_transferringPosition) { Q_ASSERT((KIO::fileoffset_t)m_fileSize == m_transferringPosition); qDebug() << "Done."; } } if (m_sendSocket) { if (m_fastSend && m_sendSocket->bytesToWrite() <= (qint64)m_bufferSize) { writeData(); } else if (!m_fastSend && bytes == 0) { writeData(); } } } void TransferSend::writeData() // slot { //qDebug(); qint64 actual = m_file.read(m_buffer, m_bufferSize); if (actual > 0) { m_sendSocket->write(m_buffer, actual); } } void TransferSend::getAck() // slot { //qDebug(); if (m_transferringPosition < (KIO::fileoffset_t)m_fileSize) { //don't write data directly, in case we get spammed with ACK we try so send too fast bytesWritten(0); } quint32 pos; while (m_sendSocket->bytesAvailable() >= 4) { m_sendSocket->read((char*)&pos, 4); pos = ntohl(pos); //qDebug() << pos << "/" << m_fileSize; if (pos == m_fileSize) { qDebug() << "Received final ACK."; cleanUp(); setStatus(Done); emit done(this); break; // for safe } } } void TransferSend::slotGotSocketError(QAbstractSocket::SocketError errorCode) { stopConnectionTimer(); qDebug() << "code = " << errorCode << " string = " << m_sendSocket->errorString(); failed(i18n("Socket error: %1", m_sendSocket->errorString())); } void TransferSend::startConnectionTimer(int secs) { qDebug(); //start also restarts, no need for us to double check it m_connectionTimer->start(secs * 1000); } void TransferSend::stopConnectionTimer() { if (m_connectionTimer->isActive()) { qDebug() << "stop"; m_connectionTimer->stop(); } } void TransferSend::slotConnectionTimeout() // slot { qDebug(); failed(i18n("Timed out")); } // protected, static QString TransferSend::getQFileErrorString(int code) { QString errorString; switch(code) { case QFile::NoError: errorString = i18n("The operation was successful. Should never happen in an error dialog."); break; case QFile::ReadError: errorString = i18n("Could not read from file \"%1\".", m_fileName); break; case QFile::WriteError: errorString = i18n("Could not write to file \"%1\".", m_fileName); break; case QFile::FatalError: errorString = i18n("A fatal unrecoverable error occurred."); break; case QFile::OpenError: errorString = i18n("Could not open file \"%1\".", m_fileName); break; // Same case value? Damn! // case IO_ConnectError: // errorString="Could not connect to the device."; // break; case QFile::AbortError: errorString = i18n("The operation was unexpectedly aborted."); break; case QFile::TimeOutError: errorString = i18n("The operation timed out."); break; case QFile::UnspecifiedError: errorString = i18n("An unspecified error happened on close."); break; default: errorString = i18n("Unknown error. Code %1",code); break; } return errorString; } } } diff --git a/src/dcc/transferview.cpp b/src/dcc/transferview.cpp index b567cdaa..58eac581 100644 --- a/src/dcc/transferview.cpp +++ b/src/dcc/transferview.cpp @@ -1,657 +1,657 @@ /* This class represents a DCC transferview for transfermodel. */ /* 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) any later version. */ /* Copyright (C) 2009,2010 Bernd Buschinski */ #include "transferview.h" #include #include #include #include #include #include #include "dcccommon.h" namespace Konversation { namespace DCC { TransferView::TransferView(QWidget *parent) : QTreeView(parent) { m_categorieFlags = None; m_dccModel = new TransferListModel(this); m_proxyModel = new TransferListProxyModel(this); m_proxyModel->setDynamicSortFilter(true); m_proxyModel->setSourceModel(m_dccModel); setModel(m_proxyModel); // doc says it improves performance // but brings problems with KCategoryDrawer starting with kde4.4 setUniformRowHeights(false); setSortingEnabled(true); setRootIsDecorated(false); //not implemented for special items setSelectionMode(QAbstractItemView::ExtendedSelection); - m_categoryDrawer = new KCategoryDrawer(0); + m_categoryDrawer = new KCategoryDrawer(nullptr); setItemDelegate(new TransferSizeDelegate(m_categoryDrawer, this)); //only after model was set restoreColumns(); //only after model and columns were set setProgressBarDeletegate(); header()->setContextMenuPolicy(Qt::CustomContextMenu); connect(header(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(headerCustomContextMenuRequested(QPoint))); m_activeTransfers = 0; m_itemCategoryToRemove = 0; m_updateTimer = new QTimer(this); m_updateTimer->setInterval(1000); connect(m_updateTimer, &QTimer::timeout, this, &TransferView::update); connect(model(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemovedFromModel(QModelIndex,int,int))); //we can't use rowsRemoved here, it seems when rowsRemoved is emitted //the rows are not permanently removed from model, //so if we trigger a new removeRows in our slot, //the new remove happens before the old pending connect(m_dccModel, &TransferListModel::rowsPermanentlyRemoved, this, &TransferView::rowsRemovedFromModel); } TransferView::~TransferView() { - disconnect(m_updateTimer, 0, 0, 0); + disconnect(m_updateTimer, nullptr, nullptr, nullptr); saveColumns(); clear(); delete m_categoryDrawer; } void TransferView::clear() { if (rowCount() > 0) { removeItems(TransferItemData::SendItem); removeItems(TransferItemData::ReceiveItem); } } void TransferView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { int type = index.data(TransferListModel::TransferDisplayType).toInt(); if (type == TransferItemData::SendCategory || type == TransferItemData::ReceiveCategory) { QStyleOptionViewItem _option(option); _option.rect.adjust(1, 1, -1, 0); m_categoryDrawer->drawCategory(index, 0, //ignored anyway _option, painter); } else { QTreeView::drawRow(painter, option, index); } } void TransferView::addTransfer(Transfer *transfer) { //save selected rows QModelIndexList selectedIndexes = selectedRows(); QList selectedItems; foreach (const QModelIndex &index, selectedIndexes) { selectedItems.append(index.data(TransferListModel::TransferPointer)); } if (transfer->getType() == Transfer::Send) { if (!(m_categorieFlags & SendCategory)) { - addItem(0, TransferItemData::SendCategory); + addItem(nullptr, TransferItemData::SendCategory); m_categorieFlags |= SendCategory; } if ((m_categorieFlags & ReceiveCategory) && !(m_categorieFlags & SpacerRow)) { - addItem(0, TransferItemData::SpaceRow); + addItem(nullptr, TransferItemData::SpaceRow); m_categorieFlags |= SpacerRow; } addItem(transfer, TransferItemData::SendItem); } else if (transfer->getType() == Transfer::Receive) { if (!(m_categorieFlags & ReceiveCategory)) { - addItem(0, TransferItemData::ReceiveCategory); + addItem(nullptr, TransferItemData::ReceiveCategory); m_categorieFlags |= ReceiveCategory; } if ((m_categorieFlags & SendCategory) && !(m_categorieFlags & SpacerRow)) { - addItem(0, TransferItemData::SpaceRow); + addItem(nullptr, TransferItemData::SpaceRow); m_categorieFlags |= SpacerRow; } addItem(transfer, TransferItemData::ReceiveItem); } //catch already running transfers if (transfer->getStatus() == Transfer::Transferring) { ++m_activeTransfers; if (m_activeTransfers > 0 && !m_updateTimer->isActive()) { m_updateTimer->start(); qDebug() << "timer start"; } } connect (transfer, SIGNAL(statusChanged(Konversation::DCC::Transfer*,int,int)), this, SLOT(transferStatusChanged(Konversation::DCC::Transfer*,int,int))); clearSelection(); //restore selected QList rows; foreach (const QModelIndex &index, rowIndexes()) { QVariant pointer = index.data(TransferListModel::TransferPointer); if (selectedItems.contains(pointer)) { selectedItems.removeOne(pointer); rows.append(index.row()); if (selectedItems.isEmpty()) { break; } } } selectRows(rows); } void TransferView::addItem(Transfer *transfer, TransferItemData::ItemDisplayType type) { TransferItemData tD; tD.displayType = type; tD.transfer = transfer; m_dccModel->append(tD); } void TransferView::transferStatusChanged(Transfer *transfer, int newStatus, int oldStatus) { Q_ASSERT(newStatus != oldStatus); QModelIndex rowIndex = index(transfer); if (rowIndex.isValid()) { dataChanged(rowIndex, index(rowIndex.row(), model()->columnCount()-1)); } if (newStatus == Transfer::Transferring) { ++m_activeTransfers; if (m_activeTransfers > 0 && !m_updateTimer->isActive()) { m_updateTimer->start(); } } if (oldStatus == Transfer::Transferring) { --m_activeTransfers; if (m_activeTransfers <= 0 && m_updateTimer->isActive()) { m_updateTimer->stop(); } } update(); } int TransferView::itemCount() const { int offset = 0; if (m_categorieFlags & SendCategory) { ++offset; } if (m_categorieFlags & ReceiveCategory) { ++offset; } if (m_categorieFlags & SpacerRow) { ++offset; } return m_dccModel->rowCount() - offset; } int TransferView::rowCount() const { return m_dccModel->rowCount(); } QList TransferView::rowIndexes(int column) const { QList list; if (column >= m_dccModel->columnCount()) { return list; } for (int i = 0; i < m_dccModel->rowCount(); ++i) { QModelIndex index = m_proxyModel->index(i, column); int displaytype = index.data(TransferListModel::TransferDisplayType).toInt(); if (displaytype == TransferItemData::ReceiveItem || displaytype == TransferItemData::SendItem) { list.append(index); } } return list; } QList TransferView::selectedIndexes() const { return selectionModel()->selectedIndexes(); } QList TransferView::selectedRows(int column) const { return selectionModel()->selectedRows(column); } QModelIndex TransferView::index(int row, int column) const { return model()->index(row, column); } QModelIndex TransferView::index(Transfer *transfer) const { if (!transfer) { return QModelIndex(); } foreach (const QModelIndex &rowIndex, rowIndexes()) { Transfer *rowTransfer = static_cast(rowIndex.data(TransferListModel::TransferPointer).value()); if (rowTransfer == transfer) { return rowIndex; } } return QModelIndex(); } void TransferView::headerCustomContextMenuRequested(const QPoint &pos) { QMenu menu(this); menu.addSection(i18n("Columns")); for (int i = 0; i < m_dccModel->columnCount(); ++i) { QAction *tAction = new QAction(m_dccModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(), &menu); tAction->setCheckable(true); int headerType = m_dccModel->headerData(i, Qt::Horizontal, TransferListModel::HeaderType).toInt(); //there must be at least one column that is not hideable if (headerType == TransferHeaderData::FileName) { delete tAction; continue; } switch (headerType) { // case DccHeaderData::FileName: // connect (tAction, SIGNAL(toggled(bool)), this, SLOT(toggleFilenameColumn(bool))); // break; case TransferHeaderData::PartnerNick: connect(tAction, &QAction::toggled, this, &TransferView::togglePartnerNickColumn); break; case TransferHeaderData::Progress: connect(tAction, &QAction::toggled, this, &TransferView::toggleProgressColumn); break; case TransferHeaderData::OfferDate: connect(tAction, &QAction::toggled, this, &TransferView::toggleStartedAtColumn); break; case TransferHeaderData::Position: connect(tAction, &QAction::toggled, this, &TransferView::togglePositionColumn); break; case TransferHeaderData::CurrentSpeed: connect(tAction, &QAction::toggled, this, &TransferView::toggleCurrentSpeedColumn); break; case TransferHeaderData::SenderAdress: connect(tAction, &QAction::toggled, this, &TransferView::toggleSenderAdressColumn); break; case TransferHeaderData::Status: connect(tAction, &QAction::toggled, this, &TransferView::toggleStatusColumn); break; case TransferHeaderData::TimeLeft: connect(tAction, &QAction::toggled, this, &TransferView::toggleTimeLeftColumn); break; case TransferHeaderData::TypeIcon: connect(tAction, &QAction::toggled, this, &TransferView::toogleTypeIconColumn); break; } tAction->setChecked(!isColumnHidden(i)); menu.addAction(tAction); } menu.exec(QWidget::mapToGlobal(pos)); } void TransferView::toggleFilenameColumn(bool visible) { setColumnHidden(headerTypeToColumn(TransferHeaderData::FileName), !visible); } void TransferView::togglePartnerNickColumn(bool visible) { setColumnHidden(headerTypeToColumn(TransferHeaderData::PartnerNick), !visible); } void TransferView::toggleProgressColumn(bool visible) { setColumnHidden(headerTypeToColumn(TransferHeaderData::Progress), !visible); } void TransferView::toggleStartedAtColumn(bool visible) { setColumnHidden(headerTypeToColumn(TransferHeaderData::OfferDate), !visible); } void TransferView::toggleCurrentSpeedColumn(bool visible) { setColumnHidden(headerTypeToColumn(TransferHeaderData::CurrentSpeed), !visible); } void TransferView::togglePositionColumn(bool visible) { setColumnHidden(headerTypeToColumn(TransferHeaderData::Position), !visible); } void TransferView::toggleSenderAdressColumn(bool visible) { setColumnHidden(headerTypeToColumn(TransferHeaderData::SenderAdress), !visible); } void TransferView::toggleStatusColumn(bool visible) { setColumnHidden(headerTypeToColumn(TransferHeaderData::Status), !visible); } void TransferView::toggleTimeLeftColumn(bool visible) { setColumnHidden(headerTypeToColumn(TransferHeaderData::TimeLeft), !visible); } void TransferView::toogleTypeIconColumn(bool visible) { setColumnHidden(headerTypeToColumn(TransferHeaderData::TypeIcon), !visible); } int TransferView::headerTypeToColumn(int headerType) const { for (int i = 0; i < m_dccModel->columnCount(); ++i) { if (m_dccModel->headerData(i, Qt::Horizontal, TransferListModel::HeaderType).toInt() == headerType) { return i; } } qDebug() << "unknown headerType: " << headerType; return -1; } void TransferView::setProgressBarDeletegate() { for (int i = 0; i < m_dccModel->columnCount(); ++i) { int headerType = m_dccModel->headerData(i, Qt::Horizontal, TransferListModel::HeaderType).toInt(); if (headerType == TransferHeaderData::Progress) { setItemDelegateForColumn (i, new TransferProgressBarDelegate(this)); return; } } } void TransferView::saveColumns() { QList columnWidths; QList columnOrder; QList columnVisible; for (int i = 0; i < header()->count(); ++i) { int index = header()->logicalIndex(i); columnWidths.append(columnWidth(index)); columnOrder.append(m_dccModel->headerData(index, Qt::Horizontal, TransferListModel::HeaderType).toInt()); columnVisible.append(!isColumnHidden(index)); } Preferences::self()->setDccColumnWidths(columnWidths); Preferences::self()->setDccColumnOrders(columnOrder); Preferences::self()->setDccColumnVisibles(columnVisible); Preferences::self()->setDccColumnSorted(m_proxyModel->sortColumn()); Preferences::self()->setDccColumnSortDescending(m_proxyModel->sortOrder() == Qt::DescendingOrder ? true : false); } void TransferView::restoreColumns() { QList columnWidths = Preferences::self()->dccColumnWidths(); QList columnOrder = Preferences::self()->dccColumnOrders(); QList columnVisible = Preferences::self()->dccColumnVisibles(); //fallback, columnOrder is empty for me after crash //rather restore default than show an empty TransferView if (columnOrder.count() == TransferHeaderData::COUNT && columnWidths.count() == TransferHeaderData::COUNT && columnVisible.count() == TransferHeaderData::COUNT) { for (int i = 0; i < columnOrder.count(); ++i) { TransferHeaderData data; data.type = columnOrder.at(i); m_dccModel->appendHeader(data); } //update model, otherwise new column are unknown updateModel(); for (int i = 0; i < columnOrder.count(); ++i) { int column = headerTypeToColumn(columnOrder.at(i)); setColumnWidth(column, columnWidths.at(i) == 0 ? 100 : columnWidths.at(i)); setColumnHidden(column, (columnVisible.at(i) > 0) ? false : true); } Qt::SortOrder order; if (Preferences::self()->dccColumnSortDescending()) { order = Qt::DescendingOrder; } else { order = Qt::AscendingOrder; } sortByColumn(Preferences::self()->dccColumnSorted(), order); } else { qDebug() << "transferview fallback, did we crash last time?\n" << " columnOrder.count():"<< columnOrder.count() << " columnWidths.count():"<< columnWidths.count() << " columnVisible.count():"<< columnVisible.count() << ", expected: " << TransferHeaderData::COUNT; for (int i = 0; i < TransferHeaderData::COUNT; ++i) { TransferHeaderData data; data.type = i; m_dccModel->appendHeader(data); } updateModel(); } } void TransferView::updateModel() { m_proxyModel->setSourceModel(m_dccModel); m_proxyModel->invalidate(); } void TransferView::scrollContentsBy(int dx, int dy) { if (dx) //KCategoryDrawer is a bit slow when it comes to horiz redraws, force it { update(); } QTreeView::scrollContentsBy(dx, dy); } void TransferView::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { emit runSelectedTransfers(); } QTreeView::keyPressEvent(event); } void TransferView::selectAllCompleted() { QItemSelection selection; foreach (const QModelIndex &index, rowIndexes()) { if (index.data(TransferListModel::TransferStatus).toInt() >= Transfer::Done) { selection.append(QItemSelectionRange(index)); } } selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } void TransferView::selectRow(int row) { if (row >= rowCount()) { return; } selectionModel()->select(m_proxyModel->index(row, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } void TransferView::selectRows(QList rows) { QItemSelection selection; foreach (const QModelIndex &index, rowIndexes()) { foreach (int row, rows) { if (row == index.row()) { selection.append(QItemSelectionRange(index)); break; } } } selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } void TransferView::update() { const int columnCount = model()->columnCount()-1; foreach (const QModelIndex &rowIndex, rowIndexes(0)) { int status = rowIndex.data(TransferListModel::TransferStatus).toInt(); if (status == Transfer::Transferring) { dataChanged(rowIndex, index(rowIndex.row(), columnCount)); } } } void TransferView::rowsAboutToBeRemovedFromModel(const QModelIndex &/*parent*/, int start, int end) { // The items that will be removed are those between start and end inclusive for (int i = start; i < end+1; ++i) { m_itemCategoryToRemove |= model()->index(i, 0).data(TransferListModel::TransferType).toInt(); } } void TransferView::rowsRemovedFromModel(int start, int end) { if (m_itemCategoryToRemove & Transfer::Send) { if (m_dccModel->itemCount(TransferItemData::SendItem) == (start - end)) { m_itemCategoryToRemove &= ~Transfer::Send; m_categorieFlags &= ~TransferView::SendCategory; int removed = removeItems(TransferItemData::SendCategory); //qDebug() << "Sendremoved:" << removed; if (removed > 0 && (m_categorieFlags & SpacerRow)) { removeItems(TransferItemData::SpaceRow); m_categorieFlags &= ~TransferView::SpacerRow; } } } if (m_itemCategoryToRemove & Transfer::Receive) { if (m_dccModel->itemCount(TransferItemData::ReceiveItem) == (start - end)) { m_itemCategoryToRemove &= ~Transfer::Receive; m_categorieFlags &= ~TransferView::ReceiveCategory; int removed = removeItems(TransferItemData::ReceiveCategory); //qDebug() << "Receiveremoved:" << removed; if (removed > 0 && (m_categorieFlags & SpacerRow)) { removeItems(TransferItemData::SpaceRow); m_categorieFlags &= ~TransferView::SpacerRow; } } } } int TransferView::removeItems(TransferItemData::ItemDisplayType displaytype) { int removed = 0; for (int i = model()->rowCount()-1; i >= 0; --i) { QModelIndex index = m_proxyModel->index(i, 0); if (index.data(TransferListModel::TransferDisplayType).toInt() == displaytype) { model()->removeRow(index.row()); ++removed; } } return removed; } } } diff --git a/src/dcc/whiteboardcolorchooser.cpp b/src/dcc/whiteboardcolorchooser.cpp index 38936d6d..5b90c63c 100644 --- a/src/dcc/whiteboardcolorchooser.cpp +++ b/src/dcc/whiteboardcolorchooser.cpp @@ -1,240 +1,240 @@ /* 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) any later version. */ /* Copyright (C) 2009-2010 Bernd Buschinski */ #include "whiteboardcolorchooser.h" #include #include #include #include #include #include namespace Konversation { namespace DCC { WhiteBoardColorChooser::WhiteBoardColorChooser(QWidget* parent) : QFrame(parent), m_foregroundColor(Qt::black), m_backgroundColor(Qt::white), m_swapPixmap(16,16) { setFrameStyle(QFrame::NoFrame | QFrame::Plain); setMinimumSize(40,40); drawSwapPixmap(); } WhiteBoardColorChooser::~WhiteBoardColorChooser() { } QColor WhiteBoardColorChooser::color(const ColorLayer& layer) const { switch (layer) { case BackgroundColor: return m_backgroundColor; case ForegroundColor: return m_foregroundColor; default: Q_ASSERT(false); return QColor(Qt::transparent); } } QColor WhiteBoardColorChooser::foregroundColor() const { return color(ForegroundColor); } QColor WhiteBoardColorChooser::backgroundColor() const { return color(BackgroundColor); } void WhiteBoardColorChooser::setColor(ColorLayer layer, const QColor& color) { switch (layer) { case ForegroundColor: setForegroundColor(color); emit foregroundColorChanged(color); break; case BackgroundColor: setBackgroundColor(color); emit backgroundColorChanged(color); break; default: Q_ASSERT(false); return; } } void WhiteBoardColorChooser::setForegroundColor (const QColor& color) { m_foregroundColor = color; update(); } void WhiteBoardColorChooser::setBackgroundColor (const QColor& color) { m_backgroundColor = color; update(); } void WhiteBoardColorChooser::mouseReleaseEvent(QMouseEvent *e) { // qDebug() << "epos:"<< e->pos(); // qDebug() << "foregroundrect" << foregroundRect(); // qDebug() << "backgroundrect" << backgroundRect(); // qDebug() << "swap" << swapPixmapRect(); ColorLayer whichColor = None; if (foregroundRect().contains(e->pos())) { whichColor = ForegroundColor; qDebug() << "> in foreground"; } else if (backgroundRect().contains(e->pos())) { whichColor = BackgroundColor; qDebug() << "> in background"; } else if (swapPixmapRect().contains(e->pos())) { qDebug() << "> in swap"; QColor oldFore = m_foregroundColor; m_foregroundColor = m_backgroundColor; m_backgroundColor = oldFore; emit colorsSwapped(m_foregroundColor, m_backgroundColor); update(); return; } if (whichColor == ForegroundColor || whichColor == BackgroundColor) { QColor col = color(whichColor); if (QColorDialog::getColor(col, this) == QColorDialog::Accepted) { setColor(whichColor, col); update(); } } } void WhiteBoardColorChooser::paintEvent(QPaintEvent *e) { QFrame::paintEvent(e); QPainter tPaint; tPaint.begin(this); tPaint.drawPixmap(swapPixmapRect().topLeft(), m_swapPixmap); QRect bgRect = backgroundRect(); QRect bgRectInside = QRect(bgRect.x () + 2, bgRect.y () + 2, bgRect.width () - 4, bgRect.height () - 4); tPaint.fillRect(bgRectInside, m_backgroundColor); qDrawShadePanel(&tPaint, bgRect, palette(), false/*not sunken*/, 2/*lineWidth*/, - 0/*never fill*/); + nullptr/*never fill*/); QRect fgRect = foregroundRect(); QRect fgRectInside = QRect(fgRect.x () + 2, fgRect.y () + 2, fgRect.width () - 4, fgRect.height () - 4); tPaint.fillRect(fgRectInside, m_foregroundColor); qDrawShadePanel(&tPaint, fgRect, palette (), false/*not sunken*/, 2/*lineWidth*/, - 0/*never fill*/); + nullptr/*never fill*/); tPaint.end(); } void WhiteBoardColorChooser::resizeEvent(QResizeEvent *e) { Q_UNUSED(e); const int minWidthHeight = qMin(width(),height()); const int swapImageSize = minWidthHeight/3; m_swapPixmap = QPixmap(swapImageSize, swapImageSize); drawSwapPixmap(); } QRect WhiteBoardColorChooser::swapPixmapRect() const { return QRect(contentsRect().width() - m_swapPixmap.width(), 0, m_swapPixmap.width(), m_swapPixmap.height()); } QRect WhiteBoardColorChooser::foregroundBackgroundRect() const { QRect cr(contentsRect()); return QRect(cr.width() / 8.0f, cr.height() / 8.0f, cr.width() * 6.0f / 8.0f, cr.height() * 6.0f / 8.0f); } QRect WhiteBoardColorChooser::foregroundRect() const { QRect fbr(foregroundBackgroundRect()); return QRect(fbr.x(), fbr.y(), fbr.width() * 3.0f / 4.0f, fbr.height() * 3.0f / 4.0f); } QRect WhiteBoardColorChooser::backgroundRect() const { QRect fbr(foregroundBackgroundRect()); return QRect(fbr.x() + fbr.width() / 4.0f, fbr.y() + fbr.height() / 4.0f, fbr.width() * 3.0f / 4.0f, fbr.height() * 3.0f / 4.0f); } void WhiteBoardColorChooser::drawSwapPixmap() { const int arrowHeight = m_swapPixmap.height() / 4.0f; const int arrowWidth = m_swapPixmap.width() / 2.0f; const int imageHeight = m_swapPixmap.height(); const int imageWidth = m_swapPixmap.width(); const QPointF arrowLeftPolyGon[3] = { QPointF(0, arrowHeight), QPointF(arrowHeight, 0), QPointF(arrowHeight, arrowHeight*2) }; const QPointF arrowDownPolyGon[3] = { QPointF(imageWidth-arrowWidth, imageHeight-arrowHeight-1), QPointF(imageWidth, imageHeight-arrowHeight-1), QPointF(imageWidth-arrowHeight, imageHeight-1) }; m_swapPixmap.fill(Qt::transparent); QPainter tPainter(&m_swapPixmap); tPainter.setBrush(Qt::black); tPainter.drawPolygon(arrowLeftPolyGon, 3); tPainter.drawPolygon(arrowDownPolyGon, 3); QPoint tCenterLine(imageWidth-arrowHeight, arrowHeight); tPainter.drawLine(tCenterLine,QPoint(arrowHeight, arrowHeight)); tPainter.drawLine(tCenterLine,QPoint(imageWidth-arrowHeight, imageWidth-arrowHeight)); tPainter.end(); } } } diff --git a/src/dcc/whiteboardtoolbar.cpp b/src/dcc/whiteboardtoolbar.cpp index 5e088f4b..95e27823 100644 --- a/src/dcc/whiteboardtoolbar.cpp +++ b/src/dcc/whiteboardtoolbar.cpp @@ -1,514 +1,514 @@ /* 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) any later version. */ /* Copyright (C) 2009 Bernd Buschinski */ #include "whiteboardtoolbar.h" #include #include #include #include #include #include #include #include #include #include #include #include "whiteboardfontchooser.h" namespace Konversation { namespace DCC { WhiteBoardToolBar::WhiteBoardToolBar(QWidget* parent) : QWidget(parent), m_lineWidthPixmap(20, 20), m_textType(SimpleText), - m_fontDialog(0) + m_fontDialog(nullptr) { setupUi(this); m_clearPushButton->setIcon(QIcon::fromTheme("document-edit")); m_clearPushButton->setToolTip(i18n("Clear Image")); m_savePushButton->setIcon(QIcon::fromTheme("document-save")); m_savePushButton->setToolTip(i18n("Save As...")); m_pencilPushButton->setIcon(QIcon::fromTheme("draw-freehand")); m_pencilPushButton->setToolTip(i18n("Freehand Drawing")); m_pencilPushButton->setFlat(true); m_toggleButtonHash.insert(WhiteBoardGlobals::Pencil, m_pencilPushButton); m_pencilPushButton->setChecked(true); m_linePushButton->setIcon(QIcon::fromTheme("draw-line")); m_linePushButton->setToolTip(i18n("Draw a straight line")); m_linePushButton->setFlat(true); m_toggleButtonHash.insert(WhiteBoardGlobals::Line, m_linePushButton); m_rectanglePushButton->setIcon(QIcon::fromTheme("draw-rectangle")); m_rectanglePushButton->setToolTip(i18n("Draw a rectangle")); m_rectanglePushButton->setFlat(true); m_toggleButtonHash.insert(WhiteBoardGlobals::Rectangle, m_rectanglePushButton); m_toggleButtonHash.insert(WhiteBoardGlobals::FilledRectangle, m_rectanglePushButton); m_ellipsePushButton->setIcon(QIcon::fromTheme("draw-circle")); m_ellipsePushButton->setToolTip(i18n("Draw an ellipse")); m_ellipsePushButton->setFlat(true); m_toggleButtonHash.insert(WhiteBoardGlobals::Ellipse, m_ellipsePushButton); m_toggleButtonHash.insert(WhiteBoardGlobals::FilledEllipse, m_ellipsePushButton); m_textPushButton->setIcon(QIcon::fromTheme("draw-text")); m_textPushButton->setToolTip(i18n("Draw text")); m_textPushButton->setFlat(true); m_toggleButtonHash.insert(WhiteBoardGlobals::Text, m_textPushButton); m_selectionPushButton->setEnabled(false); // it has no function in current whiteboard m_selectionPushButton->setIcon(QIcon::fromTheme("select-rectangular")); m_selectionPushButton->setToolTip(i18nc("dcc whiteboard selection tool", "Selection")); m_selectionPushButton->setFlat(true); m_toggleButtonHash.insert(WhiteBoardGlobals::Selection, m_selectionPushButton); m_eraserPushButton->setIcon(QIcon::fromTheme("draw-eraser")); m_eraserPushButton->setToolTip(i18n("Eraser")); m_eraserPushButton->setFlat(true); m_toggleButtonHash.insert(WhiteBoardGlobals::Eraser, m_eraserPushButton); m_fillPushButton->setIcon(QIcon::fromTheme("fill-color")); m_fillPushButton->setToolTip(i18n("Fill a contiguous area with the foreground color")); m_fillPushButton->setFlat(true); m_toggleButtonHash.insert(WhiteBoardGlobals::FloodFill, m_fillPushButton); m_arrowPushButton->setIcon(QIcon::fromTheme("draw-arrow-forward")); m_arrowPushButton->setToolTip(i18n("Draw an arrow")); m_arrowPushButton->setFlat(true); m_toggleButtonHash.insert(WhiteBoardGlobals::Arrow, m_arrowPushButton); m_colorPickerPushButton->setIcon(QIcon::fromTheme("color-picker")); m_colorPickerPushButton->setToolTip(i18n("Select a color from the image")); m_colorPickerPushButton->setFlat(true); m_toggleButtonHash.insert(WhiteBoardGlobals::ColorPicker, m_colorPickerPushButton); m_lineWidthSlider->setMaximum(WhiteBoardGlobals::MaxPenWidth); connectToggleButtons(); //foreward colorchooser signals connect(m_colorChooser, &Konversation::DCC::WhiteBoardColorChooser::colorsSwapped, this, &WhiteBoardToolBar::colorsSwapped); connect(m_colorChooser, &Konversation::DCC::WhiteBoardColorChooser::foregroundColorChanged, this, &WhiteBoardToolBar::foregroundColorChanged); connect(m_colorChooser, &Konversation::DCC::WhiteBoardColorChooser::backgroundColorChanged, this, &WhiteBoardToolBar::backgroundColorChanged); connect(m_lineWidthSlider, &QSlider::valueChanged, this, &WhiteBoardToolBar::lineWidthChanged); connect(m_lineWidthSlider, &QSlider::valueChanged, this, &WhiteBoardToolBar::updateLineWidthPixmap); connect(m_clearPushButton, &QPushButton::clicked, this, &WhiteBoardToolBar::clearClicked); connect(m_savePushButton, &QPushButton::clicked, this, &WhiteBoardToolBar::saveClicked); setFormOptionVisible(false); setLineWidthVisible(true); updateLineWidthPixmap(1); } WhiteBoardToolBar::~WhiteBoardToolBar() { delete m_fontDialog; } QColor WhiteBoardToolBar::foregroundColor() const { return m_colorChooser->foregroundColor(); } void WhiteBoardToolBar::setForegroundColor(const QColor& foregroundColor) { m_colorChooser->setForegroundColor(foregroundColor); } QColor WhiteBoardToolBar::backgroundColor() const { return m_colorChooser->backgroundColor(); } void WhiteBoardToolBar::setBackgroundColor(const QColor& backgroundColor) { m_colorChooser->setBackgroundColor(backgroundColor); } void WhiteBoardToolBar::disableTool(WhiteBoardGlobals::WhiteBoardTool tool) { QPushButton* button = m_toggleButtonHash.value(tool); if (button) { button->setEnabled(false); } else { qDebug() << "unhandled tool:" << tool; } } void WhiteBoardToolBar::enableTool(WhiteBoardGlobals::WhiteBoardTool tool) { QPushButton* button = m_toggleButtonHash.value(tool); if (button) { button->setEnabled(true); } else { qDebug() << "unhandled tool:" << tool; } } void WhiteBoardToolBar::setSupportedTextType(WhiteBoardToolBar::TextType textType) { m_textType = textType; if (m_textType == WhiteBoardToolBar::ExtentedText) { if (m_fontDialog) { return; } m_fontDialog = new WhiteBoardFontChooser(this); connect(m_fontDialog, &WhiteBoardFontChooser::fontChanged, this, &WhiteBoardToolBar::fontChanged); } else { if (m_fontDialog) { - disconnect(m_fontDialog, 0, 0, 0); + disconnect(m_fontDialog, nullptr, nullptr, nullptr); delete m_fontDialog; - m_fontDialog = 0; + m_fontDialog = nullptr; } } } WhiteBoardToolBar::TextType WhiteBoardToolBar::textType() const { return m_textType; } void WhiteBoardToolBar::connectToggleButtons() { qDebug(); connect(m_pencilPushButton, &QPushButton::toggled, this, &WhiteBoardToolBar::pencilToggled); connect(m_linePushButton, &QPushButton::toggled, this, &WhiteBoardToolBar::lineToggled); connect(m_rectanglePushButton, &QPushButton::toggled, this, &WhiteBoardToolBar::rectangleToggled); connect(m_ellipsePushButton, &QPushButton::toggled, this, &WhiteBoardToolBar::ellipseToggled); connect(m_textPushButton, &QPushButton::toggled, this, &WhiteBoardToolBar::textToggled); connect(m_selectionPushButton, &QPushButton::toggled, this, &WhiteBoardToolBar::selectionToggled); connect(m_eraserPushButton, &QPushButton::toggled, this, &WhiteBoardToolBar::eraseToggled); connect(m_fillPushButton, &QPushButton::toggled, this, &WhiteBoardToolBar::fillToggled); connect(m_arrowPushButton, &QPushButton::toggled, this, &WhiteBoardToolBar::arrowToggled); connect(m_colorPickerPushButton, &QPushButton::toggled, this, &WhiteBoardToolBar::colorPickerToggled); } void WhiteBoardToolBar::disconnectToggleButtons() { qDebug(); - disconnect(m_pencilPushButton, 0, 0, 0); - disconnect(m_linePushButton, 0, 0, 0); - disconnect(m_rectanglePushButton, 0, 0, 0); - disconnect(m_ellipsePushButton, 0, 0, 0); - disconnect(m_textPushButton, 0, 0, 0); - disconnect(m_selectionPushButton, 0, 0, 0); - disconnect(m_eraserPushButton, 0, 0, 0); - disconnect(m_fillPushButton, 0, 0, 0); - disconnect(m_arrowPushButton, 0, 0, 0); - disconnect(m_colorPickerPushButton, 0, 0, 0); + disconnect(m_pencilPushButton, nullptr, nullptr, nullptr); + disconnect(m_linePushButton, nullptr, nullptr, nullptr); + disconnect(m_rectanglePushButton, nullptr, nullptr, nullptr); + disconnect(m_ellipsePushButton, nullptr, nullptr, nullptr); + disconnect(m_textPushButton, nullptr, nullptr, nullptr); + disconnect(m_selectionPushButton, nullptr, nullptr, nullptr); + disconnect(m_eraserPushButton, nullptr, nullptr, nullptr); + disconnect(m_fillPushButton, nullptr, nullptr, nullptr); + disconnect(m_arrowPushButton, nullptr, nullptr, nullptr); + disconnect(m_colorPickerPushButton, nullptr, nullptr, nullptr); } void WhiteBoardToolBar::clearClicked() { //TODO ask for confirm emit clear(); } void WhiteBoardToolBar::saveClicked() { QPointer fileDialog = new QFileDialog(this, i18n("Save Image"), QDir::homePath(), i18n("Images (*.png *.xpm *.jpg)")); fileDialog->setAcceptMode(QFileDialog::AcceptSave); fileDialog->setFileMode(QFileDialog::AnyFile); int ret = fileDialog->exec(); if (ret == QDialog::Accepted && fileDialog) { QStringList saveList = fileDialog->selectedFiles(); qDebug() << saveList; if (saveList.count() > 0) emit save(saveList.at(0)); } delete fileDialog; } void WhiteBoardToolBar::colorPickerToggled(bool checked) { handleToggleButton(m_colorPickerPushButton, checked, WhiteBoardGlobals::ColorPicker); setLineWidthVisible(false); setFormOptionVisible(false); setFontDialogVisible(false); } void WhiteBoardToolBar::arrowToggled (bool checked) { handleToggleButton(m_arrowPushButton, checked, WhiteBoardGlobals::Arrow); setLineWidthVisible(true); setFormOptionVisible(false); setFontDialogVisible(false); } void WhiteBoardToolBar::ellipseToggled(bool checked) { handleToggleButton(m_ellipsePushButton, checked, WhiteBoardGlobals::Ellipse); setLineWidthVisible(true); setFormOptionVisible(true); fillFormOptionList(Ellipse); setFontDialogVisible(false); } void WhiteBoardToolBar::eraseToggled(bool checked) { handleToggleButton(m_eraserPushButton, checked, WhiteBoardGlobals::Eraser); setLineWidthVisible(true); setFormOptionVisible(false); setFontDialogVisible(false); } void WhiteBoardToolBar::fillToggled(bool checked) { handleToggleButton(m_fillPushButton, checked, WhiteBoardGlobals::FloodFill); setLineWidthVisible(false); setFormOptionVisible(false); setFontDialogVisible(false); } void WhiteBoardToolBar::lineToggled(bool checked) { handleToggleButton(m_linePushButton, checked, WhiteBoardGlobals::Line); setLineWidthVisible(true); setFormOptionVisible(false); setFontDialogVisible(false); } void WhiteBoardToolBar::pencilToggled(bool checked) { handleToggleButton(m_pencilPushButton, checked, WhiteBoardGlobals::Pencil); setLineWidthVisible(true); setFormOptionVisible(false); setFontDialogVisible(false); } void WhiteBoardToolBar::rectangleToggled(bool checked) { handleToggleButton(m_rectanglePushButton, checked, WhiteBoardGlobals::Rectangle); setLineWidthVisible(true); setFormOptionVisible(true); fillFormOptionList(Rectangle); setFontDialogVisible(false); } void WhiteBoardToolBar::selectionToggled(bool checked) { handleToggleButton(m_selectionPushButton, checked, WhiteBoardGlobals::Selection); setLineWidthVisible(false); setFormOptionVisible(false); setFontDialogVisible(false); } void WhiteBoardToolBar::textToggled(bool checked) { if (textType() == WhiteBoardToolBar::SimpleText) { handleToggleButton(m_textPushButton, checked, WhiteBoardGlobals::Text); } else { handleToggleButton(m_textPushButton, checked, WhiteBoardGlobals::TextExtended); } setLineWidthVisible(false); setFormOptionVisible(false); setFontDialogVisible(true); } void WhiteBoardToolBar::handleToggleButton(QPushButton* button, bool checked, Konversation::DCC::WhiteBoardGlobals::WhiteBoardTool tool) { disconnectToggleButtons(); qDebug() << "tool:" << tool << "checked:" << checked; if (checked) { unCheckOtherButtons(button); emit toolChanged(tool); } else { // don't uncheck the button button->setChecked(true); } connectToggleButtons(); } void WhiteBoardToolBar::unCheckOtherButtons(QPushButton* button) { foreach(QPushButton* pushButton, m_toggleButtonHash) { if (pushButton != button && pushButton->isChecked()) { pushButton->setChecked(false); return; } } } void WhiteBoardToolBar::updateLineWidthPixmap(int lineWidth) { if (m_lineWidthLabel->width() != m_lineWidthPixmap.width()-2 || m_lineWidthLabel->height() != m_lineWidthPixmap.height()) { m_lineWidthPixmap = QPixmap(m_lineWidthLabel->width()-2, m_lineWidthLabel->height()); } //hm.. really white? or is transparent better? m_lineWidthPixmap.fill(Qt::white); QPainter tPaint(&m_lineWidthPixmap); tPaint.setPen(QPen(Qt::black, lineWidth)); tPaint.drawLine(0, m_lineWidthPixmap.height()/2, m_lineWidthPixmap.width(), m_lineWidthPixmap.height()/2); tPaint.end(); m_lineWidthLabel->setPixmap(m_lineWidthPixmap); } void WhiteBoardToolBar::formSelectionChanged() { // qDebug(); QList selectList = m_formOptionListWidget->selectedItems(); const int selectedRow = m_formOptionListWidget->row(selectList.first()); if (selectedRow == 0) { if (m_rectanglePushButton->isChecked()) { qDebug() << "emit rectangle"; emit toolChanged(WhiteBoardGlobals::Rectangle); } else if (m_ellipsePushButton->isChecked()) { qDebug() << "emit ellipse"; emit toolChanged(WhiteBoardGlobals::Ellipse); } } else if (selectedRow == 1) { if (m_rectanglePushButton->isChecked()) { qDebug() << "emit filledrectangle"; emit toolChanged(WhiteBoardGlobals::FilledRectangle); } else if (m_ellipsePushButton->isChecked()) { qDebug() << "emit filledellipse"; emit toolChanged(WhiteBoardGlobals::FilledEllipse); } } } void WhiteBoardToolBar::setLineWidthVisible(bool visible) { m_lineWidthFrame->setVisible(visible); } void WhiteBoardToolBar::setFormOptionVisible(bool visible) { m_formOptionListWidget->setVisible(visible); } void WhiteBoardToolBar::setFontDialogVisible(bool visible) { if (m_textType == WhiteBoardToolBar::ExtentedText && m_fontDialog) { if (visible) m_fontDialog->show(); else m_fontDialog->hide(); } } void WhiteBoardToolBar::fillFormOptionList(FormOption form) { - disconnect(m_formOptionListWidget, 0, 0, 0); + disconnect(m_formOptionListWidget, nullptr, nullptr, nullptr); m_formOptionListWidget->clear(); const int width = m_formOptionListWidget->contentsRect().width() - m_formOptionListWidget->lineWidth()*4 - 3; const int drawHeight = 20 - 2; const int widthLayoutOffset = 2; const QSize sizeHint(width, 20); // qDebug() << "wanted width" << width; // qDebug() << "actual width" << m_formOptionListWidget->contentsRect().width(); switch (form) { case Rectangle: { if (m_rectanglePixmap.width() != width || m_rectanglePixmap.height() != 20) { m_rectanglePixmap = QPixmap(width, 20); m_rectanglePixmap.fill(Qt::transparent); m_filledRectanglePixmap = QPixmap(width, 20); m_filledRectanglePixmap.fill(Qt::transparent); QPainter tPaint(&m_rectanglePixmap); tPaint.drawRect(0, 0, width-widthLayoutOffset, drawHeight); tPaint.end(); tPaint.begin(&m_filledRectanglePixmap); tPaint.setBrush(Qt::black); tPaint.drawRect(0, 0, width-widthLayoutOffset, drawHeight); tPaint.end(); } QListWidgetItem *tRectangle = new QListWidgetItem(QString(), m_formOptionListWidget, QListWidgetItem::UserType +1); tRectangle->setData(Qt::DecorationRole, QVariant(m_rectanglePixmap)); tRectangle->setSizeHint(sizeHint); QListWidgetItem *tFilledRectangle = new QListWidgetItem(QString(), m_formOptionListWidget, QListWidgetItem::UserType +1); tFilledRectangle->setData(Qt::DecorationRole, QVariant(m_filledRectanglePixmap)); tFilledRectangle->setSizeHint(sizeHint); tRectangle->setSelected(true); break; } case Ellipse: { if (m_ellipsePixmap.width() != width || m_ellipsePixmap.height() != 20) { m_ellipsePixmap = QPixmap(width, 20); m_ellipsePixmap.fill(Qt::transparent); m_filledEllipsePixmap = QPixmap(width, 20); m_filledEllipsePixmap.fill(Qt::transparent); QPainter tPaint(&m_ellipsePixmap); tPaint.drawEllipse(0, 0, width-widthLayoutOffset, drawHeight); tPaint.end(); tPaint.begin(&m_filledEllipsePixmap); tPaint.setBrush(Qt::black); tPaint.drawEllipse(0, 0, width-widthLayoutOffset, drawHeight); tPaint.end(); } QListWidgetItem *tEllipse = new QListWidgetItem(QString(), m_formOptionListWidget, QListWidgetItem::UserType +1); tEllipse->setData(Qt::DecorationRole, QVariant(m_ellipsePixmap)); tEllipse->setSizeHint(sizeHint); QListWidgetItem *tFilledEllipse = new QListWidgetItem(QString(), m_formOptionListWidget, QListWidgetItem::UserType +1); tFilledEllipse->setData(Qt::DecorationRole, QVariant(m_filledEllipsePixmap)); tFilledEllipse->setSizeHint(sizeHint); tEllipse->setSelected(true); break; } } connect(m_formOptionListWidget, &QListWidget::itemSelectionChanged, this, &WhiteBoardToolBar::formSelectionChanged); } } } diff --git a/src/identitydialog.cpp b/src/identitydialog.cpp index f26be60c..18dc730d 100644 --- a/src/identitydialog.cpp +++ b/src/identitydialog.cpp @@ -1,428 +1,428 @@ /* 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) any later version. */ /* copyright: (C) 2004, 2009 by Peter Simonsson email: peter.simonsson@gmail.com Copyright (C) 2012 Eike Hein */ #include "identitydialog.h" #include "application.h" #include "awaymanager.h" #include "irccharsets.h" #include #include #include #include #include #include #include #include #include #include namespace Konversation { IdentityDialog::IdentityDialog(QWidget *parent) : QDialog(parent) { setWindowTitle( i18n("Identities") ); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &IdentityDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &IdentityDialog::reject); okButton->setDefault(true); // Initialize the dialog widget QWidget* w = new QWidget(this); setupUi(w); mainLayout->addWidget(w); mainLayout->addWidget(buttonBox); QGroupBox* nickGroupBox = new QGroupBox(i18n("Nickname")); verticalLayout->insertWidget(1, nickGroupBox); QVBoxLayout* nickGroupBoxLayout = new QVBoxLayout(nickGroupBox); nickGroupBoxLayout->setContentsMargins(0, 0, 0, 0); m_nicknameLBox = new KEditListWidget(nickGroupBox); nickGroupBoxLayout->addWidget(m_nicknameLBox); m_nicknameLBox->setWhatsThis(i18n("This is your list of nicknames. A nickname is the name that other users will " "know you by. You may use any name you desire. The first character must be a letter.\n\n" "Since nicknames must be unique across an entire IRC network, your desired name may be " "rejected by the server because someone else is already using that nickname. Enter " "alternate nicknames for yourself. If your first choice is rejected by the server, " "Konversation will try the alternate nicknames.")); newBtn->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); connect(newBtn, &QPushButton::clicked, this, &IdentityDialog::newIdentity); copyBtn->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); connect(copyBtn, &QPushButton::clicked, this, &IdentityDialog::copyIdentity); m_editBtn->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); connect(m_editBtn, &QPushButton::clicked, this, &IdentityDialog::renameIdentity); m_delBtn->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); connect(m_delBtn, &QPushButton::clicked, this, &IdentityDialog::deleteIdentity); foreach(const IdentityPtr &id, Preferences::identityList()) { m_identityCBox->addItem(id->getName()); m_identityList.append( IdentityPtr( id ) ); } m_additionalAuthInfo = new KMessageWidget(generalWidget); m_additionalAuthInfo->setWordWrap(true); m_additionalAuthInfo->setCloseButtonVisible(false); m_additionalAuthInfo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); connect(m_authTypeCombo, static_cast(&KComboBox::currentIndexChanged), this, &IdentityDialog::authTypeChanged); m_authTypeCombo->addItem(i18n("Standard NickServ"), QStringLiteral("nickserv")); m_authTypeCombo->addItem(i18n("Server Password"), QStringLiteral("serverpw")); m_authTypeCombo->addItem(i18n("SASL PLAIN"), QStringLiteral("saslplain")); m_authTypeCombo->addItem(i18nc("Cert = Certificate", "SASL EXTERNAL (Cert)"), QStringLiteral("saslexternal")); m_authTypeCombo->addItem(i18n("SSL Client Certificate"), QStringLiteral("pemclientcert")); // add encodings to combo box m_codecCBox->addItems(Konversation::IRCCharsets::self()->availableEncodingDescriptiveNames()); // set the suffix for the inactivity time spinbox m_awayInactivitySpin->setSuffix(ki18np(" minute", " minutes")); // set values for the widgets updateIdentity(0); // Set up signals / slots for identity page //connect(m_identityCBox, SIGNAL(activated(int)), this, SLOT(updateIdentity(int))); okButton->setToolTip(i18n("Change identity information")); buttonBox->button(QDialogButtonBox::Cancel)->setToolTip(i18n("Discards all changes made")); AwayManager* awayManager = Application::instance()->getAwayManager(); connect(m_identityCBox, static_cast(&KComboBox::currentIndexChanged), this, &IdentityDialog::updateIdentity); connect(this, &IdentityDialog::identitiesChanged, awayManager, &AwayManager::identitiesChanged); } void IdentityDialog::updateIdentity(int index) { if (m_currentIdentity && !checkCurrentIdentity()) { return; } refreshCurrentIdentity(); m_currentIdentity = m_identityList[index]; m_realNameEdit->setText(m_currentIdentity->getRealName()); m_nicknameLBox->clear(); m_nicknameLBox->insertStringList(m_currentIdentity->getNicknameList()); m_authTypeCombo->setCurrentIndex(m_authTypeCombo->findData(m_currentIdentity->getAuthType())); m_authPasswordEdit->setText(m_currentIdentity->getAuthPassword()); m_nickservNicknameEdit->setText(m_currentIdentity->getNickservNickname()); m_nickservCommandEdit->setText(m_currentIdentity->getNickservCommand()); m_saslAccountEdit->setText(m_currentIdentity->getSaslAccount()); m_pemClientCertFile->setUrl(m_currentIdentity->getPemClientCertFile()); m_insertRememberLineOnAwayChBox->setChecked(m_currentIdentity->getInsertRememberLineOnAway()); m_awayMessageEdit->setText(m_currentIdentity->getAwayMessage()); m_awayNickEdit->setText(m_currentIdentity->getAwayNickname()); awayCommandsGroup->setChecked(m_currentIdentity->getRunAwayCommands()); m_awayEdit->setText(m_currentIdentity->getAwayCommand()); m_unAwayEdit->setText(m_currentIdentity->getReturnCommand()); automaticAwayGroup->setChecked(m_currentIdentity->getAutomaticAway()); m_awayInactivitySpin->setValue(m_currentIdentity->getAwayInactivity()); m_automaticUnawayChBox->setChecked(m_currentIdentity->getAutomaticUnaway()); m_sCommandEdit->setText(m_currentIdentity->getShellCommand()); m_codecCBox->setCurrentIndex(Konversation::IRCCharsets::self()->shortNameToIndex(m_currentIdentity->getCodecName())); m_loginEdit->setText(m_currentIdentity->getIdent()); m_quitEdit->setText(m_currentIdentity->getQuitReason()); m_partEdit->setText(m_currentIdentity->getPartReason()); m_kickEdit->setText(m_currentIdentity->getKickReason()); if(index == 0) { m_editBtn->setEnabled(false); m_delBtn->setEnabled(false); } else { m_editBtn->setEnabled(true); m_delBtn->setEnabled(true); } } void IdentityDialog::refreshCurrentIdentity() { if(!m_currentIdentity) { return; } m_currentIdentity->setRealName(m_realNameEdit->text()); const QStringList nicks = m_nicknameLBox->items(); m_currentIdentity->setNicknameList(nicks); m_currentIdentity->setAuthType(m_authTypeCombo->itemData(m_authTypeCombo->currentIndex()).toString()); m_currentIdentity->setAuthPassword(m_authPasswordEdit->text()); m_currentIdentity->setNickservNickname(m_nickservNicknameEdit->text()); m_currentIdentity->setNickservCommand(m_nickservCommandEdit->text()); m_currentIdentity->setSaslAccount(m_saslAccountEdit->text()); m_currentIdentity->setPemClientCertFile(m_pemClientCertFile->url()); m_currentIdentity->setInsertRememberLineOnAway(m_insertRememberLineOnAwayChBox->isChecked()); m_currentIdentity->setAwayMessage(m_awayMessageEdit->text()); m_currentIdentity->setAwayNickname(m_awayNickEdit->text()); m_currentIdentity->setRunAwayCommands(awayCommandsGroup->isChecked()); m_currentIdentity->setAwayCommand(m_awayEdit->text()); m_currentIdentity->setReturnCommand(m_unAwayEdit->text()); m_currentIdentity->setAutomaticAway(automaticAwayGroup->isChecked()); m_currentIdentity->setAwayInactivity(m_awayInactivitySpin->value()); m_currentIdentity->setAutomaticUnaway(m_automaticUnawayChBox->isChecked()); m_currentIdentity->setShellCommand(m_sCommandEdit->text()); if(m_codecCBox->currentIndex() >= 0 && m_codecCBox->currentIndex() < Konversation::IRCCharsets::self()->availableEncodingShortNames().count()) m_currentIdentity->setCodecName(Konversation::IRCCharsets::self()->availableEncodingShortNames()[m_codecCBox->currentIndex()]); m_currentIdentity->setIdent(m_loginEdit->text()); m_currentIdentity->setQuitReason(m_quitEdit->text()); m_currentIdentity->setPartReason(m_partEdit->text()); m_currentIdentity->setKickReason(m_kickEdit->text()); } void IdentityDialog::accept() { if (!checkCurrentIdentity()) { return; } refreshCurrentIdentity(); Preferences::setIdentityList(m_identityList); Application::instance()->saveOptions(true); emit identitiesChanged(); QDialog::accept(); } void IdentityDialog::newIdentity() { bool ok = false; QString txt = QInputDialog::getText(this, i18n("Add Identity"), i18n("Identity name:"), QLineEdit::Normal, QString(), &ok); if(ok && !txt.isEmpty()) { KUser user(KUser::UseRealUserID); IdentityPtr identity=IdentityPtr(new Identity); identity->setName(txt); identity->setIdent(user.loginName()); m_identityList.append(identity); m_identityCBox->addItem(txt); m_identityCBox->setCurrentIndex(m_identityCBox->count() - 1); } else if(ok && txt.isEmpty()) { KMessageBox::error(this, i18n("You need to give the identity a name.")); newIdentity(); } } void IdentityDialog::renameIdentity() { bool ok = false; QString currentTxt = m_identityCBox->currentText(); QString txt = QInputDialog::getText(this, i18n("Rename Identity"), i18n("Identity name:"), QLineEdit::Normal, currentTxt, &ok); if(ok && !txt.isEmpty()) { m_currentIdentity->setName(txt); m_identityCBox->setItemText(m_identityCBox->currentIndex(), txt); } else if(ok && txt.isEmpty()) { KMessageBox::error(this, i18n("You need to give the identity a name.")); renameIdentity(); } } void IdentityDialog::deleteIdentity() { int current = m_identityCBox->currentIndex(); if(current <= 0) { return; } ServerGroupHash serverGroups = Preferences::serverGroupHash(); QHashIterator it(serverGroups); bool found = false; while (it.hasNext() && !found) if (it.next().value()->identityId() == m_currentIdentity->id()) found = true; QString warningTxt; if(found) { warningTxt = i18n("This identity is in use, if you remove it the network settings using it will" " fall back to the default identity. Should it be deleted anyway?"); } else { warningTxt = i18n("Are you sure you want to delete all information for this identity?"); } if(KMessageBox::warningContinueCancel(this, warningTxt, i18n("Delete Identity"), KStandardGuiItem::del()) == KMessageBox::Continue) { m_identityList.removeOne(m_currentIdentity); - m_currentIdentity = 0; + m_currentIdentity = nullptr; m_identityCBox->removeItem(current); } } void IdentityDialog::copyIdentity() { bool ok = false; QString currentTxt = m_identityCBox->currentText(); QString txt = QInputDialog::getText(this, i18n("Duplicate Identity"), i18n("Identity name:"), QLineEdit::Normal, currentTxt, &ok); if(ok && !txt.isEmpty()) { IdentityPtr identity(new Identity); identity->copy(*m_currentIdentity); identity->setName(txt); m_identityList.append(identity); m_identityCBox->addItem(txt); m_identityCBox->setCurrentIndex(m_identityCBox->count() - 1); } else if(ok && txt.isEmpty()) { KMessageBox::error(this, i18n("You need to give the identity a name.")); renameIdentity(); } } void IdentityDialog::setCurrentIdentity(int index) { if (index >= m_identityCBox->count()) index = 0; m_identityCBox->setCurrentIndex(index); } IdentityPtr IdentityDialog::setCurrentIdentity(IdentityPtr identity) { int index = Preferences::identityList().indexOf(identity); setCurrentIdentity(index); return m_currentIdentity; } IdentityPtr IdentityDialog::currentIdentity() const { return m_currentIdentity; } bool IdentityDialog::checkCurrentIdentity() { if(m_nicknameLBox->count() == 0) { KMessageBox::error(this, i18n("You must add at least one nick to the identity.")); bool block = m_identityCBox->blockSignals(true); m_identityCBox->setCurrentIndex(m_identityCBox->findText(m_currentIdentity->getName())); m_identityCBox->blockSignals(block); tabWidget->setCurrentIndex(0); m_nicknameLBox->lineEdit()->setFocus(); return false; } if(m_realNameEdit->text().isEmpty()) { KMessageBox::error(this, i18n("Please enter a real name.")); bool block = m_identityCBox->blockSignals(true); m_identityCBox->setCurrentIndex(m_identityCBox->findText(m_currentIdentity->getName())); m_identityCBox->blockSignals(block); tabWidget->setCurrentIndex(0); m_realNameEdit->setFocus(); return false; } return true; } void IdentityDialog::authTypeChanged(int index) { QString authType = m_authTypeCombo->itemData(index).toString(); bool isNickServ = (authType == QStringLiteral("nickserv")); bool isSaslPlain = (authType == QStringLiteral("saslplain")); bool isSaslExernal = (authType == QStringLiteral("saslexternal")); bool isServerPw = (authType == QStringLiteral("serverpw")); bool isPemClientCert = (isSaslExernal || (authType == QStringLiteral("pemclientcert"))); nickservNicknameLabel->setVisible(isNickServ); m_nickservNicknameEdit->setVisible(isNickServ); nickservCommandLabel->setVisible(isNickServ); m_nickservCommandEdit->setVisible(isNickServ); saslAccountLabel->setVisible(isSaslPlain || isSaslExernal); m_saslAccountEdit->setVisible(isSaslPlain || isSaslExernal); authPasswordLabel->setVisible(!isPemClientCert); m_authPasswordEdit->setVisible(!isPemClientCert); pemClientCertFileLabel->setVisible(isPemClientCert); m_pemClientCertFile->setVisible(isPemClientCert); m_additionalAuthInfo->setVisible(isServerPw || isPemClientCert); // Clear. m_saslAccountEdit->setPlaceholderText(QString()); for (int i = 0; i < autoIdentifyLayout->count(); ++i) autoIdentifyLayout->removeItem(autoIdentifyLayout->itemAt(0)); autoIdentifyLayout->addRow(authTypeLabel, m_authTypeCombo); if (isNickServ) { autoIdentifyLayout->addRow(nickservNicknameLabel, m_nickservNicknameEdit); autoIdentifyLayout->addRow(nickservCommandLabel, m_nickservCommandEdit); autoIdentifyLayout->addRow(authPasswordLabel, m_authPasswordEdit); } else if (isServerPw) { autoIdentifyLayout->addRow(authPasswordLabel, m_authPasswordEdit); m_additionalAuthInfo->setText(i18n("The password entered here will override the one set in the server settings, if any.")); - autoIdentifyLayout->addRow(0, m_additionalAuthInfo); + autoIdentifyLayout->addRow(nullptr, m_additionalAuthInfo); } else if (isSaslPlain) { autoIdentifyLayout->addRow(saslAccountLabel, m_saslAccountEdit); autoIdentifyLayout->addRow(authPasswordLabel, m_authPasswordEdit); } else if (isSaslExernal) { autoIdentifyLayout->addRow(saslAccountLabel, m_saslAccountEdit); m_saslAccountEdit->setPlaceholderText(i18nc("Shown in unfilled line edit", "(optional)")); } if (isPemClientCert) { autoIdentifyLayout->addRow(pemClientCertFileLabel, m_pemClientCertFile); m_additionalAuthInfo->setText(i18n("Certificate-based authentication forces SSL to be enabled for a connection, overriding any server settings.")); - autoIdentifyLayout->addRow(0, m_additionalAuthInfo); + autoIdentifyLayout->addRow(nullptr, m_additionalAuthInfo); } } } diff --git a/src/irc/channel.cpp b/src/irc/channel.cpp index fa6234c8..dbe86c59 100644 --- a/src/irc/channel.cpp +++ b/src/irc/channel.cpp @@ -1,2906 +1,2906 @@ /* 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) any later version. */ /* Copyright (C) 2002 Dario Abatianni Copyright (C) 2004-2016 Peter Simonsson Copyright (C) 2006-2008 Eike Hein */ #include "channel.h" #include "channeloptionsdialog.h" #include "application.h" #include "server.h" #include "nick.h" #include "nicklistview.h" #include "quickbutton.h" #include "modebutton.h" #include "ircinput.h" #include "ircviewbox.h" #include "ircview.h" #include "awaylabel.h" #include "topiclabel.h" #include "topichistorymodel.h" #include "notificationhandler.h" #include "viewcontainer.h" #include #include #include #include #include #include #include #include #include #include #include #define DELAYED_SORT_TRIGGER 10 using namespace Konversation; bool nickTimestampLessThan(const Nick* nick1, const Nick* nick2) { int returnValue = nick2->getChannelNick()->timeStamp() - nick1->getChannelNick()->timeStamp(); if( returnValue == 0) { returnValue = QString::compare(nick1->getChannelNick()->loweredNickname(), nick2->getChannelNick()->loweredNickname()); } return (returnValue < 0); } bool nickLessThan(const Nick* nick1, const Nick* nick2) { return nick1->getChannelNick()->loweredNickname() < nick2->getChannelNick()->loweredNickname(); } using Konversation::ChannelOptionsDialog; Channel::Channel(QWidget* parent, const QString& _name) : ChatWindow(parent) { // init variables //HACK I needed the channel name at time of setServer, but setName needs m_server.. // This effectively assigns the name twice, but none of the other logic has been moved or updated. name=_name; - m_ownChannelNick = 0; - m_optionsDialog = NULL; - m_delayedSortTimer = 0; + m_ownChannelNick = nullptr; + m_optionsDialog = nullptr; + m_delayedSortTimer = nullptr; m_delayedSortTrigger = 0; m_processedNicksCount = 0; m_processedOpsCount = 0; m_initialNamesReceived = false; nicks = 0; ops = 0; completionPosition = 0; m_nicknameListViewTextChanged = 0; m_joined = false; quickButtonsChanged = false; quickButtonsState = false; modeButtonsChanged = false; modeButtonsState = false; awayChanged = false; awayState = false; splittersInitialized = false; topicSplitterHidden = false; channelSplitterHidden = false; setType(ChatWindow::Channel); m_isTopLevelView = false; setChannelEncodingSupported(true); // Build some size policies for the widgets QSizePolicy hfixed = QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); QSizePolicy hmodest = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); QSizePolicy vfixed = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); QSizePolicy greedy = QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); m_vertSplitter = new QSplitter(Qt::Vertical, this); QWidget* topicWidget = new QWidget(m_vertSplitter); m_vertSplitter->setStretchFactor(m_vertSplitter->indexOf(topicWidget), 0); QGridLayout* topicLayout = new QGridLayout(topicWidget); topicLayout->setMargin(0); topicLayout->setSpacing(0); m_topicButton = new QToolButton(topicWidget); m_topicButton->setIcon(QIcon::fromTheme(QStringLiteral("document-edit"))); m_topicButton->setToolTip(i18n("Edit Channel Settings")); m_topicButton->setAutoRaise(true); connect(m_topicButton, SIGNAL(clicked()), this, SLOT(showOptionsDialog())); topicLine = new Konversation::TopicLabel(topicWidget); topicLine->setContextMenuOptions(IrcContextMenus::ShowChannelActions | IrcContextMenus::ShowLogAction, true); topicLine->setChannelName(getName()); topicLine->setWordWrap(true); topicLine->setWhatsThis(i18n("

Every channel on IRC has a topic associated with it. This is simply a message that everybody can see.

If you are an operator, or the channel mode 'T' has not been set, then you can change the topic by clicking the Edit Channel Properties button to the left of the topic. You can also view the history of topics there.

")); connect(topicLine, SIGNAL(setStatusBarTempText(QString)), this, SIGNAL(setStatusBarTempText(QString))); connect(topicLine, SIGNAL(clearStatusBarTempText()), this, SIGNAL(clearStatusBarTempText())); m_topicHistory = new TopicHistoryModel(this); connect(m_topicHistory, SIGNAL(currentTopicChanged(QString)), topicLine, SLOT(setText(QString))); topicLayout->addWidget(m_topicButton, 0, 0); topicLayout->addWidget(topicLine, 0, 1, -1, 1); // The box holding the channel modes modeBox = new QFrame(topicWidget); QHBoxLayout* modeBoxLayout = new QHBoxLayout(modeBox); modeBoxLayout->setMargin(0); modeBox->hide(); modeBox->setSizePolicy(hfixed); modeT = new ModeButton(QStringLiteral("T"),modeBox,0); modeBoxLayout->addWidget(modeT); modeN = new ModeButton(QStringLiteral("N"),modeBox,1); modeBoxLayout->addWidget(modeN); modeS = new ModeButton(QStringLiteral("S"),modeBox,2); modeBoxLayout->addWidget(modeS); modeI = new ModeButton(QStringLiteral("I"),modeBox,3); modeBoxLayout->addWidget(modeI); modeP = new ModeButton(QStringLiteral("P"),modeBox,4); modeBoxLayout->addWidget(modeP); modeM = new ModeButton(QStringLiteral("M"),modeBox,5); modeBoxLayout->addWidget(modeM); modeK = new ModeButton(QStringLiteral("K"),modeBox,6); modeBoxLayout->addWidget(modeK); modeL = new ModeButton(QStringLiteral("L"),modeBox,7); modeBoxLayout->addWidget(modeL); modeT->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('T')); modeN->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('N')); modeS->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('S')); modeI->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('I')); modeP->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('P')); modeM->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('M')); modeK->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('K')); modeL->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('L')); connect(modeT, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeN, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeS, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeI, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeP, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeM, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeK, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeL, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeT, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); limit=new KLineEdit(modeBox); modeBoxLayout->addWidget(limit); limit->setToolTip(i18n("Maximum users allowed in channel")); limit->setWhatsThis(i18n("

This is the channel user limit - the maximum number of users that can be in the channel at a time. If you are an operator, you can set this. The channel mode Topic (button to left) will automatically be set if set this.

")); connect(limit,SIGNAL (returnPressed()),this,SLOT (channelLimitChanged()) ); connect(limit,SIGNAL (editingFinished()), this, SLOT(channelLimitChanged()) ); topicLayout->addWidget(modeBox, 0, 2); topicLayout->setRowStretch(1, 10); topicLayout->setColumnStretch(1, 10); showTopic(Preferences::self()->showTopic()); showModeButtons(Preferences::self()->showModeButtons()); // (this) The main Box, holding the channel view/topic and the input line m_horizSplitter = new QSplitter(m_vertSplitter); m_vertSplitter->setStretchFactor(m_vertSplitter->indexOf(m_horizSplitter), 1); // Server will be set later in setServer() IRCViewBox* ircViewBox = new IRCViewBox(m_horizSplitter); m_horizSplitter->setStretchFactor(m_horizSplitter->indexOf(ircViewBox), 1); setTextView(ircViewBox->ircView()); ircViewBox->ircView()->setContextMenuOptions(IrcContextMenus::ShowChannelActions, true); // The box that holds the Nick List and the quick action buttons nickListButtons = new QFrame(m_horizSplitter); m_horizSplitter->setStretchFactor(m_horizSplitter->indexOf(nickListButtons), 0); QVBoxLayout* nickListButtonsLayout = new QVBoxLayout(nickListButtons); nickListButtonsLayout->setSpacing(0); nickListButtonsLayout->setMargin(0); nicknameListView=new NickListView(nickListButtons, this); nickListButtons->layout()->addWidget(nicknameListView); nicknameListView->installEventFilter(this); // initialize buttons grid, will be set up in updateQuickButtons - m_buttonsGrid = 0; + m_buttonsGrid = nullptr; // The box holding the Nickname button and Channel input commandLineBox = new QFrame(this); QHBoxLayout* commandLineLayout = new QHBoxLayout(commandLineBox); commandLineBox->setLayout(commandLineLayout); commandLineLayout->setMargin(0); commandLineLayout->setSpacing(spacing()); nicknameCombobox = new KComboBox(commandLineBox); nicknameCombobox->setEditable(true); nicknameCombobox->setSizeAdjustPolicy(KComboBox::AdjustToContents); KLineEdit* nicknameComboboxLineEdit = qobject_cast(nicknameCombobox->lineEdit()); if (nicknameComboboxLineEdit) nicknameComboboxLineEdit->setClearButtonEnabled(false); nicknameCombobox->setWhatsThis(i18n("

This shows your current nick, and any alternatives you have set up. If you select or type in a different nickname, then a request will be sent to the IRC server to change your nick. If the server allows it, the new nickname will be selected. If you type in a new nickname, you need to press 'Enter' at the end.

You can edit the alternative nicknames from the Identities option in the Settings menu.

")); awayLabel = new AwayLabel(commandLineBox); awayLabel->hide(); cipherLabel = new QLabel(commandLineBox); cipherLabel->hide(); cipherLabel->setPixmap(KIconLoader::global()->loadIcon(QStringLiteral("document-encrypt"), KIconLoader::Toolbar)); m_inputBar = new IRCInput(commandLineBox); commandLineLayout->addWidget(nicknameCombobox); commandLineLayout->addWidget(awayLabel); commandLineLayout->addWidget(cipherLabel); commandLineLayout->addWidget(m_inputBar); getTextView()->installEventFilter(m_inputBar); topicLine->installEventFilter(m_inputBar); m_inputBar->installEventFilter(this); // Set the widgets size policies m_topicButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); topicLine->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum)); commandLineBox->setSizePolicy(vfixed); limit->setMaximumSize(40,100); limit->setSizePolicy(hfixed); modeT->setMaximumSize(20,100); modeT->setSizePolicy(hfixed); modeN->setMaximumSize(20,100); modeN->setSizePolicy(hfixed); modeS->setMaximumSize(20,100); modeS->setSizePolicy(hfixed); modeI->setMaximumSize(20,100); modeI->setSizePolicy(hfixed); modeP->setMaximumSize(20,100); modeP->setSizePolicy(hfixed); modeM->setMaximumSize(20,100); modeM->setSizePolicy(hfixed); modeK->setMaximumSize(20,100); modeK->setSizePolicy(hfixed); modeL->setMaximumSize(20,100); modeL->setSizePolicy(hfixed); getTextView()->setSizePolicy(greedy); nicknameListView->setSizePolicy(hmodest); connect(m_inputBar,SIGNAL (submit()),this,SLOT (channelTextEntered()) ); connect(m_inputBar,SIGNAL (envelopeCommand()),this,SLOT (channelPassthroughCommand()) ); connect(m_inputBar,SIGNAL (nickCompletion()),this,SLOT (completeNick()) ); connect(m_inputBar,SIGNAL (endCompletion()),this,SLOT (endCompleteNick()) ); connect(m_inputBar,SIGNAL (textPasted(QString)),this,SLOT (textPasted(QString)) ); connect(getTextView(), SIGNAL(textPasted(bool)), m_inputBar, SLOT(paste(bool))); connect(getTextView(),SIGNAL (gotFocus()),m_inputBar,SLOT (setFocus()) ); connect(getTextView(),SIGNAL (sendFile()),this,SLOT (sendFileMenu()) ); connect(getTextView(),SIGNAL (autoText(QString)),this,SLOT (sendText(QString)) ); connect(nicknameListView,SIGNAL (itemDoubleClicked(QTreeWidgetItem*,int)),this,SLOT (doubleClickCommand(QTreeWidgetItem*,int)) ); connect(nicknameCombobox,SIGNAL (activated(int)),this,SLOT(nicknameComboboxChanged())); if(nicknameCombobox->lineEdit()) connect(nicknameCombobox->lineEdit(), SIGNAL (returnPressed()),this,SLOT(nicknameComboboxChanged())); connect(&userhostTimer,SIGNAL (timeout()),this,SLOT (autoUserhost())); m_whoTimer.setSingleShot(true); connect(&m_whoTimer, SIGNAL(timeout()), this, SLOT(autoWho())); connect(Application::instance(), SIGNAL(appearanceChanged()), this, SLOT(updateAutoWho())); // every 5 minutes decrease everyone's activity by 1 unit m_fadeActivityTimer.start(5*60*1000); connect(&m_fadeActivityTimer, SIGNAL(timeout()), this, SLOT(fadeActivity())); updateAppearance(); #ifdef HAVE_QCA2 - m_cipher = 0; + m_cipher = nullptr; #endif // Setup delayed sort timer m_delayedSortTimer = new QTimer(this); m_delayedSortTimer->setSingleShot(true); connect(m_delayedSortTimer, SIGNAL(timeout()), this, SLOT(delayedSortNickList())); } //FIXME there is some logic in setLogfileName that needs to be split out and called here if the server display name gets changed void Channel::setServer(Server* server) { if (m_server != server) { connect(server, SIGNAL(connectionStateChanged(Server*,Konversation::ConnectionState)), SLOT(connectionStateChanged(Server*,Konversation::ConnectionState))); connect(server, SIGNAL(nickInfoChanged()), this, SLOT(updateNickInfos())); connect(server, SIGNAL(channelNickChanged(QString)), this, SLOT(updateChannelNicks(QString))); } ChatWindow::setServer(server); if (!server->getKeyForRecipient(getName()).isEmpty()) cipherLabel->show(); topicLine->setServer(server); refreshModeButtons(); nicknameCombobox->setModel(m_server->nickListModel()); connect(awayLabel, SIGNAL(unaway()), m_server, SLOT(requestUnaway())); connect(awayLabel, SIGNAL(awayMessageChanged(QString)), m_server, SLOT(requestAway(QString))); } void Channel::connectionStateChanged(Server* server, Konversation::ConnectionState state) { if (server == m_server) { if (state != Konversation::SSConnected) { m_joined = false; ViewContainer* viewContainer = Application::instance()->getMainWindow()->getViewContainer(); //HACK the way the notification priorities work sucks, this forces the tab text color to gray right now. if (viewContainer->getFrontView() == this || m_currentTabNotify == Konversation::tnfNone || (!Preferences::self()->tabNotificationsEvents() && m_currentTabNotify == Konversation::tnfControl)) { viewContainer->unsetViewNotification(this); } } } } void Channel::setEncryptedOutput(bool e) { #ifdef HAVE_QCA2 if (e) { cipherLabel->show(); if (!getCipher()->setKey(m_server->getKeyForRecipient(getName()))) return; m_topicHistory->setCipher(getCipher()); topicLine->setText(m_topicHistory->currentTopic()); } else { cipherLabel->hide(); m_topicHistory->clearCipher(); topicLine->setText(m_topicHistory->currentTopic()); } #else Q_UNUSED(e) #endif } Channel::~Channel() { qDebug() << "(" << getName() << ")"; // Purge nickname list purgeNicks(); qDebug() << "Nicks purged."; // Unlink this channel from channel list m_server->removeChannel(this); qDebug() << "Channel removed."; if (m_recreationScheduled) { QMetaObject::invokeMethod(m_server, "sendJoinCommand", Qt::QueuedConnection, Q_ARG(QString, getName()), Q_ARG(QString, getPassword())); } } bool Channel::rejoinable() { if (getServer() && getServer()->isConnected()) return !m_joined; return false; } void Channel::rejoin() { if (rejoinable()) m_server->sendJoinCommand(getName(), getPassword()); } bool Channel::log() { return ChatWindow::log() && !Preferences::self()->privateOnly(); } ChannelNickPtr Channel::getOwnChannelNick() const { return m_ownChannelNick; } ChannelNickPtr Channel::getChannelNick(const QString &ircnick) const { return m_server->getChannelNick(getName(), ircnick); } void Channel::purgeNicks() { - m_ownChannelNick = 0; + m_ownChannelNick = nullptr; // Purge nickname list qDeleteAll(nicknameList); nicknameList.clear(); m_nicknameNickHash.clear(); // Execute this otherwise it may crash trying to access // deleted nicks nicknameListView->executeDelayedItemsLayout(); // clear stats counter nicks=0; ops=0; } void Channel::showOptionsDialog() { if (!m_optionsDialog) m_optionsDialog = new Konversation::ChannelOptionsDialog(this); m_optionsDialog->show(); } void Channel::textPasted(const QString& text) { if(m_server) { QStringList multiline = text.split(QLatin1Char('\n'), QString::SkipEmptyParts); for(int index=0;indexcommandChar()); // make sure that lines starting with command char get escaped if(line.startsWith(cChar)) line=cChar+line; sendText(line); } } } // Will be connected to NickListView::doubleClicked() void Channel::doubleClickCommand(QTreeWidgetItem *item, int column) { Q_UNUSED(column) if(item) { nicknameListView->clearSelection(); item->setSelected(true); // TODO: put the quick button code in another function to make reusal more legitimate quickButtonClicked(Preferences::self()->channelDoubleClickAction()); } } void Channel::completeNick() { int pos, oldPos; QTextCursor cursor = m_inputBar->textCursor(); pos = cursor.position(); oldPos = m_inputBar->getOldCursorPosition(); QString line=m_inputBar->toPlainText(); QString newLine; // Check if completion position is out of range if(completionPosition >= nicknameList.count()) completionPosition = 0; // Check, which completion mode is active char mode = m_inputBar->getCompletionMode(); if(mode == 'c') { line.remove(oldPos, pos - oldPos); pos = oldPos; } // If the cursor is at beginning of line, insert last completion if the nick is still around if(pos == 0 && !m_inputBar->lastCompletion().isEmpty() && nicknameList.containsNick(m_inputBar->lastCompletion())) { QString addStart(Preferences::self()->nickCompleteSuffixStart()); newLine = m_inputBar->lastCompletion() + addStart; // New cursor position is behind nickname pos = newLine.length(); // Add rest of the line newLine += line; } else { // remember old cursor position in input field m_inputBar->setOldCursorPosition(pos); // remember old cursor position locally oldPos = pos; // step back to []{}-_^`\| or start of line QString regexpStr("[^A-Z0-9a-z\\_\\[\\]\\{\\}\\-\\^\\`\\\\\\|"); if(!Preferences::self()->prefixCharacter().isEmpty()) regexpStr += "\\" + Preferences::self()->prefixCharacter(); regexpStr += QLatin1Char(']'); QRegExp tmp(regexpStr); pos = tmp.lastIndexIn(line, pos - 1); if (pos < 0) pos = 0; else pos++; // copy search pattern (lowercase) QString pattern = line.mid(pos, oldPos - pos); // copy line to newLine-buffer newLine = line; // did we find any pattern? if(!pattern.isEmpty()) { bool complete = false; QString foundNick; // try to find matching nickname in list of names if(Preferences::self()->nickCompletionMode() == 1 || Preferences::self()->nickCompletionMode() == 2) { // Shell like completion QStringList found; foundNick = nicknameList.completeNick(pattern, complete, found, (Preferences::self()->nickCompletionMode() == 2), Preferences::self()->nickCompletionCaseSensitive()); if(!complete && !found.isEmpty()) { if(Preferences::self()->nickCompletionMode() == 1) { QString nicksFound = found.join(QStringLiteral(" ")); appendServerMessage(i18n("Completion"), i18n("Possible completions: %1.", nicksFound)); } else { m_inputBar->showCompletionList(found); } } } // Cycle completion else if(Preferences::self()->nickCompletionMode() == 0 && !nicknameList.isEmpty()) { if(mode == '\0') { uint timeStamp = 0; int listPosition = 0; foreach (Nick* nick, nicknameList) { if(nick->getChannelNick()->getNickname().startsWith(pattern, Preferences::self()->nickCompletionCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive) && (nick->getChannelNick()->timeStamp() > timeStamp)) { timeStamp = nick->getChannelNick()->timeStamp(); completionPosition = listPosition; } ++listPosition; } } // remember old nick completion position int oldCompletionPosition = completionPosition; complete = true; QString prefixCharacter = Preferences::self()->prefixCharacter(); do { QString lookNick = nicknameList.at(completionPosition)->getChannelNick()->getNickname(); if(!prefixCharacter.isEmpty() && lookNick.contains(prefixCharacter)) { lookNick = lookNick.section( prefixCharacter,1 ); } if(lookNick.startsWith(pattern, Preferences::self()->nickCompletionCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive)) { foundNick = lookNick; } // increment search position completionPosition++; // wrap around if(completionPosition == nicknameList.count()) { completionPosition = 0; } // the search ends when we either find a suitable nick or we end up at the // first search position } while((completionPosition != oldCompletionPosition) && foundNick.isEmpty()); } // did we find a suitable nick? if(!foundNick.isEmpty()) { // set channel nicks completion mode m_inputBar->setCompletionMode('c'); // remove pattern from line newLine.remove(pos, pattern.length()); // did we find the nick in the middle of the line? if(pos && complete) { m_inputBar->setLastCompletion(foundNick); QString addMiddle = Preferences::self()->nickCompleteSuffixMiddle(); newLine.insert(pos, foundNick + addMiddle); pos = pos + foundNick.length() + addMiddle.length(); } // no, it was at the beginning else if(complete) { m_inputBar->setLastCompletion(foundNick); QString addStart = Preferences::self()->nickCompleteSuffixStart(); newLine.insert(pos, foundNick + addStart); pos = pos + foundNick.length() + addStart.length(); } // the nick wasn't complete else { newLine.insert(pos, foundNick); pos = pos + foundNick.length(); } } // no pattern found, so restore old cursor position else pos = oldPos; } } // Set new text and cursor position m_inputBar->setText(newLine); cursor.setPosition(pos); m_inputBar->setTextCursor(cursor); } // make sure to step back one position when completion ends so the user starts // with the last complete they made void Channel::endCompleteNick() { if(completionPosition) completionPosition--; else completionPosition=nicknameList.count()-1; } void Channel::setName(const QString& newName) { ChatWindow::setName(newName); setLogfileName(newName.toLower()); } bool Channel::autoJoin() { if (!m_server->getServerGroup()) return false; Konversation::ChannelList channelList = m_server->getServerGroup()->channelList(); return channelList.contains(channelSettings()); } void Channel::setAutoJoin(bool autojoin) { if (autojoin && !(autoJoin())) { Konversation::ChannelSettings before; QList channelList = m_server->getChannelList(); if (channelList.count() > 1) { QMap channelMap; int index = -1; int ownIndex = m_server->getViewContainer()->getViewIndex(this); foreach (Channel* channel, channelList) { index = m_server->getViewContainer()->getViewIndex(channel); if (index && index > ownIndex) channelMap.insert(index, channel); } if (channelMap.count()) { QMap::Iterator it2; Channel* channel; for (it2 = channelMap.begin(); it2 != channelMap.end(); ++it2) { channel = it2.value(); if (channel->autoJoin()) { before = channel->channelSettings(); break; } } } } if (m_server->getServerGroup()) m_server->getServerGroup()->addChannel(channelSettings(), before); } else { if (m_server->getServerGroup()) m_server->getServerGroup()->removeChannel(channelSettings()); } } QString Channel::getPassword() { QString password; for (QStringList::const_iterator it = m_modeList.constBegin(); it != m_modeList.constEnd(); ++it) { if ((*it)[0] == QLatin1Char('k')) password = (*it).mid(1); } if (password.isEmpty() && m_server->getServerGroup()) { Konversation::ChannelList channelSettingsList = m_server->getServerGroup()->channelList(); Konversation::ChannelSettings channelSettings(getName()); int index = channelSettingsList.indexOf(channelSettings); if(index >= 0) password = channelSettingsList.at(index).password(); } return password; } const Konversation::ChannelSettings Channel::channelSettings() { Konversation::ChannelSettings channel; channel.setName(getName()); channel.setPassword(getPassword()); channel.setNotificationsEnabled(notificationsEnabled()); return channel; } void Channel::sendFileMenu() { emit sendFile(); } void Channel::channelTextEntered() { QString line = m_inputBar->toPlainText(); m_inputBar->clear(); if (!line.isEmpty()) sendText(sterilizeUnicode(line)); } void Channel::channelPassthroughCommand() { QString commandChar = Preferences::self()->commandChar(); QString line = m_inputBar->toPlainText(); m_inputBar->clear(); if(!line.isEmpty()) { // Prepend commandChar on Ctrl+Enter to bypass outputfilter command recognition if (line.startsWith(commandChar)) { line = commandChar + line; } sendText(sterilizeUnicode(line)); } } void Channel::sendText(const QString& sendLine) { // create a work copy QString outputAll(sendLine); // replace aliases and wildcards m_server->getOutputFilter()->replaceAliases(outputAll, this); // Send all strings, one after another QStringList outList = outputAll.split(QRegExp(QStringLiteral("[\r\n]+")), QString::SkipEmptyParts); for(int index=0;indexgetOutputFilter()->parse(m_server->getNickname(), output, getName(), this); // Is there something we need to display for ourselves? if(!result.output.isEmpty()) { if(result.type == Konversation::Action) appendAction(m_server->getNickname(), result.output); else if(result.type == Konversation::Command) appendCommandMessage(result.typeString, result.output); else if(result.type == Konversation::Program) appendServerMessage(result.typeString, result.output); else if(result.type == Konversation::PrivateMessage) msgHelper(result.typeString, result.output); else append(m_server->getNickname(), result.output); } else if (result.outputList.count()) { if (result.type == Konversation::Message) { QStringListIterator it(result.outputList); while (it.hasNext()) append(m_server->getNickname(), it.next()); } else if (result.type == Konversation::Action) { for (int i = 0; i < result.outputList.count(); ++i) { if (i == 0) appendAction(m_server->getNickname(), result.outputList.at(i)); else append(m_server->getNickname(), result.outputList.at(i)); } } } // Send anything else to the server if (!result.toServerList.empty()) m_server->queueList(result.toServerList); else m_server->queue(result.toServer); } } void Channel::setNickname(const QString& newNickname) { nicknameCombobox->setCurrentIndex(nicknameCombobox->findText(newNickname)); } QStringList Channel::getSelectedNickList() { QStringList selectedNicks; foreach (Nick* nick, nicknameList) { if (nick->isSelected()) selectedNicks << nick->getChannelNick()->getNickname(); } return selectedNicks; } void Channel::channelLimitChanged() { unsigned int lim=limit->text().toUInt(); modeButtonClicked(7,lim>0); } void Channel::modeButtonClicked(int id, bool on) { char mode[]={'t','n','s','i','p','m','k','l'}; QString command(QStringLiteral("MODE %1 %2%3 %4")); QString args = getPassword(); if (mode[id] == 'k') { if (args.isEmpty()) { QPointer dlg = new KPasswordDialog(this); dlg->setPrompt(i18n("Channel Password")); if (dlg->exec() && !dlg->password().isEmpty()) { args = dlg->password(); } delete dlg; } } else if(mode[id]=='l') { if(limit->text().isEmpty() && on) { bool ok=false; // ask user how many nicks should be the limit args=QInputDialog::getText(this, i18n("Channel User Limit"), i18n("Enter the new user limit for the channel:"), QLineEdit::Normal, limit->text(), // will be always "" but what the hell ;) &ok); // leave this function if user cancels if(!ok) return; } else if(on) args=limit->text(); } // put together the mode command and send it to the server queue m_server->queue(command.arg(getName()).arg((on) ? QStringLiteral("+") : QStringLiteral("-")).arg(mode[id]).arg(args)); } void Channel::quickButtonClicked(const QString &buttonText) { // parse wildcards (toParse,nickname,channelName,nickList,queryName,parameter) QString out=m_server->parseWildcards(buttonText,m_server->getNickname(),getName(),getPassword(),getSelectedNickList(), m_inputBar->toPlainText()); // are there any newlines in the definition? if (out.contains(QLatin1Char('\n'))) sendText(out); // single line without newline needs to be copied into input line else m_inputBar->setText(out, true); } void Channel::addNickname(ChannelNickPtr channelnick) { QString nickname = channelnick->loweredNickname(); - Nick* nick=0; + Nick* nick=nullptr; foreach (Nick* lookNick, nicknameList) { if(lookNick->getChannelNick()->loweredNickname() == nickname) { nick = lookNick; break; } } - if (nick == 0) + if (nick == nullptr) { fastAddNickname(channelnick); if(channelnick->isAnyTypeOfOp()) { adjustOps(1); } adjustNicks(1); requestNickListSort(); } // TODO (re)-investigate why it was thought unusual to add an already added nick // -- see bug 333969 } // Use with caution! Does not check for duplicates or may not // sort if delayed sorting is in effect. void Channel::fastAddNickname(ChannelNickPtr channelnick, Nick *nick) { Q_ASSERT(channelnick); if(!channelnick) return; if (!nick || !nick->treeWidget()) { // Deal with nicknameListView now (creating nick if necessary) NickListView::NoSorting noSorting(nicknameListView); int index = nicknameListView->topLevelItemCount(); // Append nick to the lists if (nick) { nicknameListView->addTopLevelItem(nick); } else { nick = new Nick(nicknameListView, this, channelnick); m_nicknameListViewTextChanged |= 0xFF; // new nick, text changed. } if (!m_delayedSortTimer->isActive()) { // Find its right place and insert where it belongs int newindex = nicknameListView->findLowerBound(*nick); if (newindex != index) { if (newindex >= index) newindex--; nicknameListView->takeTopLevelItem(index); nicknameListView->insertTopLevelItem(newindex, nick); } } // Otherwise it will be sorted by delayed sort. } // Now deal with nicknameList if (m_delayedSortTimer->isActive()) { // nicks get sorted later nicknameList.append(nick); } else { NickList::iterator it = qLowerBound(nicknameList.begin(), nicknameList.end(), nick, nickLessThan); nicknameList.insert(it, nick); } m_nicknameNickHash.insert (channelnick->loweredNickname(), nick); } /* Determines whether Nick/Part/Join event should be shown or skipped based on user settings. */ bool Channel::shouldShowEvent(ChannelNickPtr channelNick) { if (Preferences::self()->hideUnimportantEvents()) { if (channelNick && Preferences::self()->hideUnimportantEventsExcludeActive()) { uint activityThreshold = 3600; if (Preferences::self()->hideUnimportantEventsExcludeActiveThreshold() == 0) // last 10 minutes activityThreshold = 600; if (Preferences::self()->hideUnimportantEventsExcludeActiveThreshold() == 1) // last hour activityThreshold = 3600; else if (Preferences::self()->hideUnimportantEventsExcludeActiveThreshold() == 2) // last day activityThreshold = 86400; else if (Preferences::self()->hideUnimportantEventsExcludeActiveThreshold() == 3) // last week activityThreshold = 604800; if (m_server->isWatchedNick(channelNick->getNickname())) return true; // nick is on our watched list, so we probably want to see the event else if (channelNick->timeStamp()+activityThreshold > QDateTime::currentDateTime().toTime_t()) return true; // the nick has spoken within activity threshold else return false; } else return false; // if hideUnimportantEventsExcludeActive is off, we hide all events } else return true; // if hideUnimportantEvents is off we don't care and just show the event } void Channel::nickRenamed(const QString &oldNick, const NickInfo& nickInfo, const QHash &messageTags) { QString newNick = nickInfo.getNickname(); Nick *nick = getNickByName(oldNick); bool displayCommandMessage; if (Preferences::self()->hideUnimportantEventsExcludeActive() && m_server->isWatchedNick(oldNick)) displayCommandMessage = true; // this is for displaying watched people NICK events both ways (watched->unwatched and unwatched->watched) else if (nick) displayCommandMessage = shouldShowEvent(nick->getChannelNick()); else displayCommandMessage = shouldShowEvent(ChannelNickPtr()); // passing null pointer /* Did we change our nick name? */ if(newNick == m_server->getNickname()) /* Check newNick because m_server->getNickname() is already updated to new nick */ { setNickname(newNick); if (displayCommandMessage) appendCommandMessage(i18n("Nick"),i18n("You are now known as %1.", newNick), messageTags, true, true); } else if (displayCommandMessage) { /* No, must've been someone else */ appendCommandMessage(i18n("Nick"),i18n("%1 is now known as %2.", oldNick, newNick), messageTags); } if (nick) { m_nicknameNickHash.remove(oldNick.toLower()); m_nicknameNickHash.insert(newNick.toLower(), nick); repositionNick(nick); } } void Channel::joinNickname(ChannelNickPtr channelNick, const QHash &messageTags) { bool displayCommandMessage = shouldShowEvent(channelNick); if(channelNick->getNickname() == m_server->getNickname()) { m_joined = true; emit joined(this); if (displayCommandMessage) appendCommandMessage(i18nc("Message type", "Join"), i18nc("%1 = our hostmask, %2 = channel", "You (%1) have joined the channel %2.", channelNick->getHostmask(), getName()), messageTags, false, true); // Prepare for impending NAMES. purgeNicks(); nicknameListView->setUpdatesEnabled(false); m_ownChannelNick = channelNick; refreshModeButtons(); setActive(true); ViewContainer* viewContainer = Application::instance()->getMainWindow()->getViewContainer(); //HACK the way the notification priorities work sucks, this forces the tab text color to ungray right now. if (viewContainer->getFrontView() == this || m_currentTabNotify == Konversation::tnfNone || (!Preferences::self()->tabNotificationsEvents() && m_currentTabNotify == Konversation::tnfControl)) { Application::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this); } Application::instance()->notificationHandler()->channelJoin(this,getName()); } else { QString nick = channelNick->getNickname(); QString hostname = channelNick->getHostmask(); if (displayCommandMessage) appendCommandMessage(i18nc("Message type", "Join"), i18nc("%1 is the nick joining and %2 the hostmask of that nick", "%1 (%2) has joined this channel.", nick, hostname), messageTags, false); addNickname(channelNick); } } void Channel::removeNick(ChannelNickPtr channelNick, const QString &reason, bool quit, const QHash &messageTags) { bool displayCommandMessage = shouldShowEvent(channelNick); QString displayReason = reason; if(!displayReason.isEmpty()) { // if the reason contains text markup characters, play it safe and reset all if (hasIRCMarkups(displayReason)) displayReason += QStringLiteral("\017"); } if(channelNick->getNickname() == m_server->getNickname()) { if (displayCommandMessage) { //If in the future we can leave a channel, but not close the window, refreshModeButtons() has to be called. if (quit) { if (displayReason.isEmpty()) appendCommandMessage(i18nc("Message type", "Quit"), i18n("You (%1) have left this server.", channelNick->getHostmask()), messageTags); else appendCommandMessage(i18nc("Message type", "Quit"), i18nc("%1 = our hostmask, %2 = reason", "You (%1) have left this server (%2).", channelNick->getHostmask(), displayReason), messageTags, false); } else { if (displayReason.isEmpty()) appendCommandMessage(i18nc("Message type", "Part"), i18n("You have left channel %1.", getName()), messageTags); else appendCommandMessage(i18nc("Message type", "Part"), i18nc("%1 = our hostmask, %2 = channel, %3 = reason", "You (%1) have left channel %2 (%3).", channelNick->getHostmask(), getName(), displayReason), messageTags, false); } } delete this; } else { if (displayCommandMessage) { if (quit) { if (displayReason.isEmpty()) appendCommandMessage(i18nc("Message type", "Quit"), i18n("%1 (%2) has left this server.", channelNick->getNickname(), channelNick->getHostmask()), messageTags, false); else appendCommandMessage(i18nc("Message type", "Quit"), i18nc("%1 = nick, %2 = hostname, %3 = reason", "%1 (%2) has left this server (%3).", channelNick->getNickname(), channelNick->getHostmask(), displayReason), messageTags, false); } else { if (displayReason.isEmpty()) appendCommandMessage(i18nc("Message type", "Part"), i18n("%1 (%2) has left this channel.", channelNick->getNickname(), channelNick->getHostmask()), messageTags, false); else appendCommandMessage(i18nc("Message type", "Part"), i18nc("%1 = nick, %2 = hostmask, %3 = reason", "%1 (%2) has left this channel (%3).", channelNick->getNickname(), channelNick->getHostmask(), displayReason), messageTags, false); } } if(channelNick->isAnyTypeOfOp()) { adjustOps(-1); } adjustNicks(-1); Nick* nick = getNickByName(channelNick->loweredNickname()); if(nick) { nicknameList.removeOne(nick); m_nicknameNickHash.remove(channelNick->loweredNickname()); delete nick; // Execute this otherwise it may crash trying to access deleted nick nicknameListView->executeDelayedItemsLayout(); } else { qWarning() << "Nickname " << channelNick->getNickname() << " not found!"<< endl; } } } void Channel::flushNickQueue() { processQueuedNicks(true); } void Channel::kickNick(ChannelNickPtr channelNick, const QString &kicker, const QString &reason, const QHash &messageTags) { QString displayReason = reason; if(!displayReason.isEmpty()) { // if the reason contains text markup characters, play it safe and reset all if (hasIRCMarkups(displayReason)) displayReason += QStringLiteral("\017"); } if(channelNick->getNickname() == m_server->getNickname()) { if(kicker == m_server->getNickname()) { if (displayReason.isEmpty()) appendCommandMessage(i18n("Kick"), i18n("You have kicked yourself from channel %1.", getName()), messageTags); else appendCommandMessage(i18n("Kick"), i18nc("%1 adds the channel and %2 the reason", "You have kicked yourself from channel %1 (%2).", getName(), displayReason), messageTags); } else { if (displayReason.isEmpty()) { appendCommandMessage(i18n("Kick"), i18nc("%1 adds the channel, %2 adds the kicker", "You have been kicked from channel %1 by %2.", getName(), kicker), messageTags); } else { appendCommandMessage(i18n("Kick"), i18nc("%1 adds the channel, %2 the kicker and %3 the reason", "You have been kicked from channel %1 by %2 (%3).", getName(), kicker, displayReason), messageTags); } Application::instance()->notificationHandler()->kick(this,getName(), kicker); } m_joined=false; setActive(false); //HACK the way the notification priorities work sucks, this forces the tab text color to gray right now. if (m_currentTabNotify == Konversation::tnfNone || (!Preferences::self()->tabNotificationsEvents() && m_currentTabNotify == Konversation::tnfControl)) Application::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this); return; } else { if(kicker == m_server->getNickname()) { if (displayReason.isEmpty()) appendCommandMessage(i18n("Kick"), i18n("You have kicked %1 from the channel.", channelNick->getNickname()), messageTags); else appendCommandMessage(i18n("Kick"), i18nc("%1 adds the kicked nick and %2 the reason", "You have kicked %1 from the channel (%2).", channelNick->getNickname(), displayReason), messageTags); } else { if (displayReason.isEmpty()) { appendCommandMessage(i18n("Kick"), i18nc("%1 adds the kicked nick, %2 adds the kicker", "%1 has been kicked from the channel by %2.", channelNick->getNickname(), kicker), messageTags); } else { appendCommandMessage(i18n("Kick"), i18nc("%1 adds the kicked nick, %2 the kicker and %3 the reason", "%1 has been kicked from the channel by %2 (%3).", channelNick->getNickname(), kicker, displayReason), messageTags); } } if(channelNick->isAnyTypeOfOp()) adjustOps(-1); adjustNicks(-1); Nick* nick = getNickByName(channelNick->loweredNickname()); - if(nick == 0) + if(nick == nullptr) { qWarning() << "Nickname " << channelNick->getNickname() << " not found!"<< endl; } else { nicknameList.removeOne(nick); m_nicknameNickHash.remove(channelNick->loweredNickname()); delete nick; } } } Nick* Channel::getNickByName(const QString &lookname) const { QString lcLookname(lookname.toLower()); return m_nicknameNickHash.value(lcLookname); } void Channel::adjustNicks(int value) { if((nicks == 0) && (value <= 0)) { return; } nicks += value; if(nicks < 0) { nicks = 0; } emitUpdateInfo(); } void Channel::adjustOps(int value) { if((ops == 0) && (value <= 0)) { return; } ops += value; if(ops < 0) { ops = 0; } emitUpdateInfo(); } void Channel::emitUpdateInfo() { QString info = getName() + QStringLiteral(" - "); info += i18np("%1 nick", "%1 nicks", numberOfNicks()); info += i18np(" (%1 op)", " (%1 ops)", numberOfOps()); emit updateInfo(info); } QString Channel::getTopic() { return m_topicHistory->currentTopic(); } void Channel::setTopic(const QString& text, const QHash &messageTags) { QString cleanTopic = text; // If the reason contains text markup characters, play it safe and reset all. if (!cleanTopic.isEmpty() && hasIRCMarkups(cleanTopic)) cleanTopic += QStringLiteral("\017"); appendCommandMessage(i18n("Topic"), i18n("The channel topic is \"%1\".", cleanTopic), messageTags); m_topicHistory->appendTopic(replaceIRCMarkups(Konversation::removeIrcMarkup(text))); } void Channel::setTopic(const QString& nickname, const QString& text, const QHash &messageTags) { QString cleanTopic = text; // If the reason contains text markup characters, play it safe and reset all. if (!cleanTopic.isEmpty() && hasIRCMarkups(cleanTopic)) cleanTopic += QStringLiteral("\017"); if (nickname == m_server->getNickname()) appendCommandMessage(i18n("Topic"), i18n("You set the channel topic to \"%1\".", cleanTopic), messageTags); else appendCommandMessage(i18n("Topic"), i18n("%1 sets the channel topic to \"%2\".", nickname, cleanTopic), messageTags); m_topicHistory->appendTopic(replaceIRCMarkups(Konversation::removeIrcMarkup(text)), nickname); } void Channel::setTopicAuthor(const QString& author, QDateTime time) { if (time.isNull() || !time.isValid()) time = QDateTime::currentDateTime(); m_topicHistory->setCurrentTopicMetadata(author, time); } void Channel::updateMode(const QString& sourceNick, char mode, bool plus, const QString ¶meter, const QHash &messageTags) { // Note for future expansion: // m_server->getChannelNick(getName(), sourceNick); // may not return a valid channelNickPtr if the mode is updated by // the server. // --johnflux, 9 September 2004 // Note: nick repositioning in the nicknameListView should be // triggered by nickinfo / channelnick signals QString message; ChannelNickPtr parameterChannelNick = m_server->getChannelNick(getName(), parameter); bool fromMe = false; bool toMe = false; // HACK right now Server only keeps type A modes bool banTypeThang = m_server->banAddressListModes().contains(QLatin1Char(mode)); // remember if this nick had any type of op. bool wasAnyOp = false; if (parameterChannelNick) { addNickname(parameterChannelNick); wasAnyOp = parameterChannelNick->isAnyTypeOfOp(); } if (sourceNick.toLower() == m_server->loweredNickname()) fromMe = true; if (parameter.toLower() == m_server->loweredNickname()) toMe = true; switch (mode) { case 'q': if (banTypeThang) { if (plus) { if (fromMe) message = i18n("You set a quiet on %1.", parameter); else message = i18n("%1 sets a quiet on %2.", sourceNick, parameter); } else { if (fromMe) message = i18n("You remove the quiet on %1.", parameter); else message = i18n("%1 removes the quiet on %2.", sourceNick, parameter); } } else { if (plus) { if (fromMe) { if (toMe) message = i18n("You give channel owner privileges to yourself."); else message = i18n("You give channel owner privileges to %1.", parameter); } else { if (toMe) message = i18n("%1 gives channel owner privileges to you.", sourceNick); else message = i18n("%1 gives channel owner privileges to %2.", sourceNick, parameter); } } else { if (fromMe) { if (toMe) message = i18n("You take channel owner privileges from yourself."); else message = i18n("You take channel owner privileges from %1.", parameter); } else { if (toMe) message = i18n("%1 takes channel owner privileges from you.", sourceNick); else message = i18n("%1 takes channel owner privileges from %2.", sourceNick, parameter); } } if (parameterChannelNick) { parameterChannelNick->setOwner(plus); emitUpdateInfo(); } } break; case 'a': if (plus) { if (fromMe) { if (toMe) message = i18n("You give channel admin privileges to yourself."); else message = i18n("You give channel admin privileges to %1.", parameter); } else { if (toMe) message = i18n("%1 gives channel admin privileges to you.", sourceNick); else message = i18n("%1 gives channel admin privileges to %2.", sourceNick, parameter); } } else { if (fromMe) { if (toMe) message = i18n("You take channel admin privileges from yourself."); else message = i18n("You take channel admin privileges from %1.", parameter); } else { if (toMe) message = i18n("%1 takes channel admin privileges from you.", sourceNick); else message = i18n("%1 takes channel admin privileges from %2.", sourceNick, parameter); } } if (parameterChannelNick) { parameterChannelNick->setAdmin(plus); emitUpdateInfo(); } break; case 'o': if (plus) { if (fromMe) { if (toMe) message = i18n("You give channel operator privileges to yourself."); else message = i18n("You give channel operator privileges to %1.", parameter); } else { if (toMe) message = i18n("%1 gives channel operator privileges to you.", sourceNick); else message = i18n("%1 gives channel operator privileges to %2.", sourceNick, parameter); } } else { if (fromMe) { if (toMe) message = i18n("You take channel operator privileges from yourself."); else message = i18n("You take channel operator privileges from %1.", parameter); } else { if (toMe) message = i18n("%1 takes channel operator privileges from you.", sourceNick); else message = i18n("%1 takes channel operator privileges from %2.", sourceNick, parameter); } } if (parameterChannelNick) { parameterChannelNick->setOp(plus); emitUpdateInfo(); } break; case 'h': if (plus) { if (fromMe) { if (toMe) message = i18n("You give channel halfop privileges to yourself."); else message = i18n("You give channel halfop privileges to %1.", parameter); } else { if (toMe) message = i18n("%1 gives channel halfop privileges to you.", sourceNick); else message = i18n("%1 gives channel halfop privileges to %2.", sourceNick, parameter); } } else { if (fromMe) { if (toMe) message = i18n("You take channel halfop privileges from yourself."); else message = i18n("You take channel halfop privileges from %1.", parameter); } else { if (toMe) message = i18n("%1 takes channel halfop privileges from you.", sourceNick); else message = i18n("%1 takes channel halfop privileges from %2.", sourceNick, parameter); } } if (parameterChannelNick) { parameterChannelNick->setHalfOp(plus); emitUpdateInfo(); } break; //case 'O': break; case 'v': if (plus) { if (fromMe) { if (toMe) message = i18n("You give yourself permission to talk."); else message = i18n("You give %1 permission to talk.", parameter); } else { if (toMe) message = i18n("%1 gives you permission to talk.", sourceNick); else message = i18n("%1 gives %2 permission to talk.", sourceNick, parameter); } } else { if (fromMe) { if (toMe) message = i18n("You take the permission to talk from yourself."); else message = i18n("You take the permission to talk from %1.", parameter); } else { if (toMe) message = i18n("%1 takes the permission to talk from you.", sourceNick); else message = i18n("%1 takes the permission to talk from %2.", sourceNick, parameter); } } if (parameterChannelNick) { parameterChannelNick->setVoice(plus); } break; case 'c': if (plus) { if (fromMe) message = i18n("You set the channel mode to 'no colors allowed'."); else message = i18n("%1 sets the channel mode to 'no colors allowed'.", sourceNick); } else { if (fromMe) message = i18n("You set the channel mode to 'allow color codes'."); else message = i18n("%1 sets the channel mode to 'allow color codes'.", sourceNick); } break; case 'i': if (plus) { if (fromMe) message = i18n("You set the channel mode to 'invite only'."); else message = i18n("%1 sets the channel mode to 'invite only'.", sourceNick); } else { if (fromMe) message = i18n("You remove the 'invite only' mode from the channel."); else message = i18n("%1 removes the 'invite only' mode from the channel.", sourceNick); } modeI->setDown(plus); break; case 'm': if (plus) { if (fromMe) message = i18n("You set the channel mode to 'moderated'."); else message = i18n("%1 sets the channel mode to 'moderated'.", sourceNick); } else { if (fromMe) message = i18n("You set the channel mode to 'unmoderated'."); else message = i18n("%1 sets the channel mode to 'unmoderated'.", sourceNick); } modeM->setDown(plus); break; case 'n': if (plus) { if (fromMe) message = i18n("You set the channel mode to 'no messages from outside'."); else message = i18n("%1 sets the channel mode to 'no messages from outside'.", sourceNick); } else { if (fromMe) message = i18n("You set the channel mode to 'allow messages from outside'."); else message = i18n("%1 sets the channel mode to 'allow messages from outside'.", sourceNick); } modeN->setDown(plus); break; case 'p': if (plus) { if (fromMe) message = i18n("You set the channel mode to 'private'."); else message = i18n("%1 sets the channel mode to 'private'.", sourceNick); } else { if (fromMe) message = i18n("You set the channel mode to 'public'."); else message = i18n("%1 sets the channel mode to 'public'.", sourceNick); } modeP->setDown(plus); if (plus) modeS->setDown(false); break; case 's': if (plus) { if (fromMe) message = i18n("You set the channel mode to 'secret'."); else message = i18n("%1 sets the channel mode to 'secret'.", sourceNick); } else { if (fromMe) message = i18n("You set the channel mode to 'visible'."); else message = i18n("%1 sets the channel mode to 'visible'.", sourceNick); } modeS->setDown(plus); if (plus) modeP->setDown(false); break; //case 'r': break; case 't': if (plus) { if (fromMe) message = i18n("You switch on 'topic protection'."); else message = i18n("%1 switches on 'topic protection'.", sourceNick); } else { if (fromMe) message = i18n("You switch off 'topic protection'."); else message = i18n("%1 switches off 'topic protection'.", sourceNick); } modeT->setDown(plus); break; case 'k': if (plus) { if (fromMe) message = i18n("You set the channel key to '%1'.", parameter); else message = i18n("%1 sets the channel key to '%2'.", sourceNick, parameter); } else { if (fromMe) message = i18n("You remove the channel key."); else message = i18n("%1 removes the channel key.", sourceNick); } modeK->setDown(plus); break; case 'l': if (plus) { if (fromMe) message = i18np("You set the channel limit to 1 nick.", "You set the channel limit to %1 nicks.", parameter); else message = i18np("%2 sets the channel limit to 1 nick.", "%2 sets the channel limit to %1 nicks.", parameter, sourceNick); } else { if (fromMe) message = i18n("You remove the channel limit."); else message = i18n("%1 removes the channel limit.", sourceNick); } modeL->setDown(plus); if (plus) limit->setText(parameter); else limit->clear(); break; case 'b': if (plus) { if (fromMe) message = i18n("You set a ban on %1.", parameter); else message = i18n("%1 sets a ban on %2.", sourceNick, parameter); } else { if (fromMe) message = i18n("You remove the ban on %1.", parameter); else message = i18n("%1 removes the ban on %2.", sourceNick, parameter); } break; case 'e': if (plus) { if (fromMe) message = i18n("You set a ban exception on %1.", parameter); else message = i18n("%1 sets a ban exception on %2.", sourceNick, parameter); } else { if (fromMe) message = i18n("You remove the ban exception on %1.", parameter); else message = i18n("%1 removes the ban exception on %2.", sourceNick, parameter); } break; case 'I': if (plus) { if (fromMe) message = i18n("You set invitation mask %1.", parameter); else message = i18n("%1 sets invitation mask %2.", sourceNick, parameter); } else { if (fromMe) message = i18n("You remove the invitation mask %1.", parameter); else message = i18n("%1 removes the invitation mask %2.", sourceNick, parameter); } break; default: if (plus) { if (Konversation::getChannelModesHash().contains(QLatin1Char(mode))) { if (fromMe) message = i18n("You set the channel mode '%1'.", Konversation::getChannelModesHash().value(QLatin1Char(mode))); else message= i18n("%1 sets the channel mode '%2'.", sourceNick, Konversation::getChannelModesHash().value(QLatin1Char(mode))); } else { if (fromMe) message = i18n("You set channel mode +%1", QLatin1Char(mode)); else message = i18n("%1 sets channel mode +%2", sourceNick, QLatin1Char(mode)); } } else { if (Konversation::getChannelModesHash().contains(QLatin1Char(mode))) { if (fromMe) message = i18n("You remove the channel mode '%1'.", Konversation::getChannelModesHash().value(QLatin1Char(mode))); else message= i18n("%1 removes the channel mode '%2'.", sourceNick, Konversation::getChannelModesHash().value(QLatin1Char(mode))); } else { if (fromMe) message = i18n("You set channel mode -%1", QLatin1Char(mode)); else message = i18n("%1 sets channel mode -%2", sourceNick, QLatin1Char(mode)); } } } // check if this nick's anyOp-status has changed and adjust ops accordingly if (parameterChannelNick) { if (wasAnyOp && (!parameterChannelNick->isAnyTypeOfOp())) adjustOps(-1); else if (!wasAnyOp && parameterChannelNick->isAnyTypeOfOp()) adjustOps(1); } if (!message.isEmpty() && !Preferences::self()->useLiteralModes()) { appendCommandMessage(i18n("Mode"), message, messageTags); } updateModeWidgets(mode, plus, parameter); } void Channel::clearModeList() { QString k; // Keep channel password in the backing store, for rejoins. for (QStringList::const_iterator it = m_modeList.constBegin(); it != m_modeList.constEnd(); ++it) { if ((*it)[0] == QLatin1Char('k')) k = (*it); } m_modeList.clear(); if (!k.isEmpty()) m_modeList << k; modeT->setOn(0); modeT->setDown(0); modeN->setOn(0); modeN->setDown(0); modeS->setOn(0); modeS->setDown(0); modeI->setOn(0); modeI->setDown(0); modeP->setOn(0); modeP->setDown(0); modeM->setOn(0); modeM->setDown(0); modeK->setOn(0); modeK->setDown(0); modeL->setOn(0); modeL->setDown(0); limit->clear(); emit modesChanged(); } void Channel::updateModeWidgets(char mode, bool plus, const QString ¶meter) { - ModeButton* widget=0; + ModeButton* widget=nullptr; if(mode=='t') widget=modeT; else if(mode=='n') widget=modeN; else if(mode=='s') widget=modeS; else if(mode=='i') widget=modeI; else if(mode=='p') widget=modeP; else if(mode=='m') widget=modeM; else if(mode=='k') widget=modeK; else if(mode=='l') { widget=modeL; if(plus) limit->setText(parameter); else limit->clear(); } if(widget) widget->setOn(plus); if(plus) { m_modeList.append(QString(QLatin1Char(mode) + parameter)); } else { QStringList removable = m_modeList.filter(QRegExp(QString(QStringLiteral("^%1.*")).arg(mode))); foreach(const QString &mode, removable) { m_modeList.removeOne(mode); } } emit modesChanged(); } void Channel::updateQuickButtons() { delete m_buttonsGrid; - m_buttonsGrid = 0; + m_buttonsGrid = nullptr; // the grid that holds the quick action buttons m_buttonsGrid = new QWidget (nickListButtons); //Q3Grid(2, nickListButtons); nickListButtons->layout()->addWidget(m_buttonsGrid); m_buttonsGrid->hide(); QGridLayout* layout = new QGridLayout (m_buttonsGrid); layout->setMargin(0); int col = 0; int row = 0; const QStringList &newButtonList = Preferences::quickButtonList(); // add new quick buttons for(int index=0;indexaddWidget (quickButton, row, col); row += col; connect(quickButton, SIGNAL(clicked(QString)), this, SLOT(quickButtonClicked(QString))); // Get the button definition QString buttonText=newButtonList[index]; // Extract button label QString buttonLabel=buttonText.section(QLatin1Char(','),0,0); // Extract button definition buttonText=buttonText.section(QLatin1Char(','),1); quickButton->setText(buttonLabel); quickButton->setDefinition(buttonText); // Add tool tips QString toolTip=buttonText.replace(QLatin1Char('&'),QStringLiteral("&")). replace(QLatin1Char('<'),QStringLiteral("<")). replace(QLatin1Char('>'),QStringLiteral(">")); quickButton->setToolTip(toolTip); quickButton->show(); } // for // set hide() or show() on grid showQuickButtons(Preferences::self()->showQuickButtons()); } void Channel::showQuickButtons(bool show) { // Qt does not redraw the buttons properly when they are not on screen // while getting hidden, so we remember the "soon to be" state here. if(isHidden() || !m_buttonsGrid) { quickButtonsChanged=true; quickButtonsState=show; } else { if(show) m_buttonsGrid->show(); else m_buttonsGrid->hide(); } } void Channel::showModeButtons(bool show) { // Qt does not redraw the buttons properly when they are not on screen // while getting hidden, so we remember the "soon to be" state here. if(isHidden()) { modeButtonsChanged=true; modeButtonsState=show; } else { if(show) { topicSplitterHidden = false; modeBox->show(); modeBox->parentWidget()->show(); } else { modeBox->hide(); if(topicLine->isHidden()) { topicSplitterHidden = true; modeBox->parentWidget()->hide(); } } } } void Channel::indicateAway(bool show) { // Qt does not redraw the label properly when they are not on screen // while getting hidden, so we remember the "soon to be" state here. if(isHidden()) { awayChanged=true; awayState=show; } else { if(show) awayLabel->show(); else awayLabel->hide(); } } void Channel::showEvent(QShowEvent*) { // If the show quick/mode button settings have changed, apply the changes now if(quickButtonsChanged) { quickButtonsChanged=false; showQuickButtons(quickButtonsState); } if(modeButtonsChanged) { modeButtonsChanged=false; showModeButtons(modeButtonsState); } if(awayChanged) { awayChanged=false; indicateAway(awayState); } syncSplitters(); } void Channel::syncSplitters() { QList vertSizes = Preferences::self()->topicSplitterSizes(); QList horizSizes = Preferences::self()->channelSplitterSizes(); if (vertSizes.isEmpty()) { vertSizes << m_topicButton->height() << (height() - m_topicButton->height()); Preferences::self()->setTopicSplitterSizes(vertSizes); } if (horizSizes.isEmpty()) { // An approximation of a common NICKLEN plus the width of the icon, // tested with 8pt and 10pt DejaVu Sans and Droid Sans. int listWidth = fontMetrics().averageCharWidth() * 17 + 20; horizSizes << (width() - listWidth) << listWidth; Preferences::self()->setChannelSplitterSizes(horizSizes); } m_vertSplitter->setSizes(vertSizes); m_horizSplitter->setSizes(horizSizes); splittersInitialized = true; } void Channel::updateAppearance() { QPalette palette; if (Preferences::self()->inputFieldsBackgroundColor()) { palette.setColor(QPalette::Text, Preferences::self()->color(Preferences::ChannelMessage)); palette.setColor(QPalette::Base, Preferences::self()->color(Preferences::TextViewBackground)); palette.setColor(QPalette::AlternateBase, Preferences::self()->color(Preferences::AlternateBackground)); } limit->setPalette(palette); topicLine->setPalette(QPalette()); if (Preferences::self()->customTextFont()) { topicLine->setFont(Preferences::self()->textFont()); m_inputBar->setFont(Preferences::self()->textFont()); nicknameCombobox->setFont(Preferences::self()->textFont()); limit->setFont(Preferences::self()->textFont()); } else { topicLine->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); m_inputBar->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); nicknameCombobox->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); limit->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); } nicknameListView->resort(); nicknameListView->setPalette(palette); nicknameListView->setAlternatingRowColors(Preferences::self()->inputFieldsBackgroundColor()); if (Preferences::self()->customListFont()) nicknameListView->setFont(Preferences::self()->listFont()); else nicknameListView->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); nicknameListView->refresh(); showModeButtons(Preferences::self()->showModeButtons()); showNicknameList(Preferences::self()->showNickList()); showNicknameBox(Preferences::self()->showNicknameBox()); showTopic(Preferences::self()->showTopic()); setAutoUserhost(Preferences::self()->autoUserhost()); QMetaObject::invokeMethod(this, "updateQuickButtons", Qt::QueuedConnection); // Nick sorting settings might have changed. Trigger timer if (m_delayedSortTimer) { m_delayedSortTrigger = DELAYED_SORT_TRIGGER + 1; m_delayedSortTimer->start(500 + qrand()/2000); } ChatWindow::updateAppearance(); } void Channel::nicknameComboboxChanged() { QString newNick=nicknameCombobox->currentText(); oldNick=m_server->getNickname(); if (oldNick != newNick) { nicknameCombobox->setCurrentIndex(nicknameCombobox->findText(oldNick)); changeNickname(newNick); // return focus to input line m_inputBar->setFocus(); } } void Channel::changeNickname(const QString& newNickname) { if (!newNickname.isEmpty()) m_server->queue(QStringLiteral("NICK ")+newNickname); } void Channel::queueNicks(const QStringList& nicknameList) { if (nicknameList.isEmpty()) return; m_nickQueue.append(nicknameList); processQueuedNicks(); } void Channel::endOfNames() { if (!m_initialNamesReceived) { m_initialNamesReceived = true; if (m_server->capabilities() & Server::AwayNotify && !Preferences::self()->autoWhoContinuousEnabled()) { // Do one who request to get the initial away state for the channel QMetaObject::invokeMethod(m_server, "requestWho", Qt::QueuedConnection, Q_ARG(QString, getName())); } scheduleAutoWho(); } } void Channel::childAdjustFocus() { m_inputBar->setFocus(); refreshModeButtons(); } void Channel::refreshModeButtons() { bool enable = true; if(getOwnChannelNick()) { enable=getOwnChannelNick()->isAnyTypeOfOp(); } // if not channel nick, then enable is true - fall back to assuming they are op //don't disable the mode buttons since you can't then tell if they are enabled or not. //needs to be fixed somehow /* modeT->setEnabled(enable); modeN->setEnabled(enable); modeS->setEnabled(enable); modeI->setEnabled(enable); modeP->setEnabled(enable); modeM->setEnabled(enable); modeK->setEnabled(enable); modeL->setEnabled(enable);*/ limit->setEnabled(enable); // Tooltips for the ModeButtons QString opOnly; if(!enable) opOnly = i18n("You have to be an operator to change this."); modeT->setToolTip(i18n("Topic can be changed by channel operator only. %1", opOnly)); modeN->setToolTip(i18n("No messages to channel from clients on the outside. %1", opOnly)); modeS->setToolTip(i18n("Secret channel. %1", opOnly)); modeI->setToolTip(i18n("Invite only channel. %1", opOnly)); modeP->setToolTip(i18n("Private channel. %1", opOnly)); modeM->setToolTip(i18n("Moderated channel. %1", opOnly)); modeK->setToolTip(i18n("Protect channel with a password.")); modeL->setToolTip(i18n("Set user limit to channel.")); } void Channel::nicknameListViewTextChanged(int textChangedFlags) { m_nicknameListViewTextChanged |= textChangedFlags; } void Channel::autoUserhost() { if(Preferences::self()->autoUserhost() && !Preferences::self()->autoWhoContinuousEnabled()) { int limit = 5; QString nickString; foreach (Nick* nick, getNickList()) { if(nick->getChannelNick()->getHostmask().isEmpty()) { if(limit--) nickString = nickString + nick->getChannelNick()->getNickname() + QLatin1Char(' '); else break; } } if(!nickString.isEmpty()) m_server->requestUserhost(nickString); } if(!nicknameList.isEmpty()) { resizeNicknameListViewColumns(); } } void Channel::setAutoUserhost(bool state) { nicknameListView->setColumnHidden(Nick::HostmaskColumn, !state); if (state) { nicknameListView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); // Cannot use QHeaderView::ResizeToContents here because it is slow // and it gets triggered by setSortingEnabled(). Using timed resize // instead, see Channel::autoUserhost() above. nicknameListView->header()->setSectionResizeMode(Nick::NicknameColumn, QHeaderView::Fixed); nicknameListView->header()->setSectionResizeMode(Nick::HostmaskColumn, QHeaderView::Fixed); userhostTimer.start(10000); m_nicknameListViewTextChanged |= 0xFF; // ResizeColumnsToContents QTimer::singleShot(0, this, SLOT(autoUserhost())); // resize columns ASAP } else { nicknameListView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); nicknameListView->header()->setSectionResizeMode(Nick::NicknameColumn, QHeaderView::Stretch); userhostTimer.stop(); } } void Channel::scheduleAutoWho(int msec) { // The first auto-who is scheduled by ENDOFNAMES in InputFilter, which means // the first auto-who occurs one interval after it. This has two desirable // consequences specifically related to the startup phase: auto-who dispatch // doesn't occur at the same time for all channels that are auto-joined, and // it gives some breathing room to process the NAMES replies for all channels // first before getting started on WHO. // Subsequent auto-whos are scheduled by ENDOFWHO in InputFilter. However, // autoWho() might refuse to actually do the request if the number of nicks // in the channel exceeds the threshold, and will instead schedule another // attempt later. Thus scheduling an auto-who does not guarantee it will be // performed. // If this is called mid-interval (e.g. due to the ENDOFWHO from a manual WHO) // it will reset the interval to avoid cutting it short. if (m_whoTimer.isActive()) m_whoTimer.stop(); if (Preferences::self()->autoWhoContinuousEnabled()) { if (msec > 0) m_whoTimer.start(msec); else m_whoTimer.start(Preferences::self()->autoWhoContinuousInterval() * 1000); } } void Channel::autoWho() { // Try again later if there are too many nicks or we're already processing a WHO request. if ((nicks > Preferences::self()->autoWhoNicksLimit()) || m_server->getInputFilter()->isWhoRequestUnderProcess(getName())) { scheduleAutoWho(); return; } m_server->requestWho(getName()); } void Channel::updateAutoWho() { if (!Preferences::self()->autoWhoContinuousEnabled()) m_whoTimer.stop(); else if (Preferences::self()->autoWhoContinuousEnabled() && !m_whoTimer.isActive()) autoWho(); else if (m_whoTimer.isActive()) { // The below tries to meet user expectations on an interval settings change, // making two assumptions: // - If the new interval is lower than the old one, the user may be impatient // and desires an information update. // - If the new interval is longer than the old one, the user may be trying to // avoid Konversation producing too much traffic in a given timeframe, and // wants it to stop doing so sooner rather than later. // Both require rescheduling the next auto-who request. int interval = Preferences::self()->autoWhoContinuousInterval() * 1000; if (interval != m_whoTimer.interval()) { if (m_whoTimerStarted.elapsed() >= interval) { // If the time since the last auto-who request is longer than (or // equal to) the new interval setting, it follows that the new // setting is lower than the old setting. In this case issue a new // request immediately, which is the closest we can come to acting // as if the new setting had been active all along, short of tra- // velling back in time to change history. This handles the impa- // tient user. // FIXME: Adjust algorithm when time machine becomes available. m_whoTimer.stop(); autoWho(); } else { // If on the other hand the elapsed time is shorter than the new // interval setting, the new setting could be either shorter or // _longer_ than the old setting. Happily, this time we can actually // behave as if the new setting had been active all along, by sched- // uling the next request to happen in the new interval time minus // the already elapsed time, meeting user expecations for both cases // originally laid out. scheduleAutoWho(interval - m_whoTimerStarted.elapsed()); } } } } void Channel::fadeActivity() { foreach (Nick *nick, nicknameList) { nick->getChannelNick()->lessActive(); } } bool Channel::canBeFrontView() { return true; } bool Channel::searchView() { return true; } bool Channel::closeYourself(bool confirm) { int result=KMessageBox::Continue; if (confirm) result = KMessageBox::warningContinueCancel(this, i18n("Do you want to leave %1?", getName()), i18n("Leave Channel"), KGuiItem(i18n("Leave")), KStandardGuiItem::cancel(), QStringLiteral("QuitChannelTab")); if (result==KMessageBox::Continue) { m_server->closeChannel(getName()); m_server->removeChannel(this); deleteLater(); return true; } else m_recreationScheduled = false; return false; } void Channel::serverOnline(bool online) { setActive(online); } //Used to disable functions when not connected, does not necessarily mean the server is offline void Channel::setActive(bool active) { if (active) nicknameCombobox->setEnabled(true); else { m_initialNamesReceived = false; purgeNicks(); nicknameCombobox->setEnabled(false); topicLine->clear(); clearModeList(); clearBanList(); m_whoTimer.stop(); } } void Channel::showTopic(bool show) { if(show) { topicSplitterHidden = false; topicLine->show(); m_topicButton->show(); topicLine->parentWidget()->show(); } else { topicLine->hide(); m_topicButton->hide(); if(modeBox->isHidden()) { topicSplitterHidden = true; topicLine->parentWidget()->hide(); } } } void Channel::processQueuedNicks(bool flush) { // This pops nicks from the front of a queue added to by incoming NAMES // messages and adds them to the channel nicklist, calling itself via // the event loop until the last invocation finds the queue empty and // adjusts the nicks/ops counters and requests a nicklist sort, but only // if previous invocations actually processed any nicks. The latter is // an optimization for the common case of processing being kicked off by // flushNickQueue(), which is done e.g. before a nick rename or part to // make sure the channel is up to date and will usually find an empty // queue. This is also the use case for the 'flush' parameter, which if // true causes the recursion to block in a tight loop instead of queueing // via the event loop. if (m_nickQueue.isEmpty()) { if (m_processedNicksCount) { adjustNicks(m_processedNicksCount); adjustOps(m_processedOpsCount); m_processedNicksCount = 0; m_processedOpsCount = 0; sortNickList(); nicknameListView->setUpdatesEnabled(true); if (Preferences::self()->autoUserhost()) resizeNicknameListViewColumns(); } } else { QString nickname; while (nickname.isEmpty() && !m_nickQueue.isEmpty()) nickname = m_nickQueue.takeFirst(); QString userHost; if(m_server->capabilities() & Server::UserHostInNames) { int index = nickname.indexOf(QLatin1Char('!')); if(index >= 0) { userHost = nickname.mid(index + 1); nickname = nickname.left(index); } } bool admin = false; bool owner = false; bool op = false; bool halfop = false; bool voice = false; // Remove possible mode characters from nickname and store the resulting mode. m_server->mangleNicknameWithModes(nickname, admin, owner, op, halfop, voice); // TODO: Make these an enumeration in KApplication or somewhere, we can use them as well. unsigned int mode = (admin ? 16 : 0) + (owner ? 8 : 0) + (op ? 4 : 0) + (halfop ? 2 : 0) + (voice ? 1 : 0); // Check if nick is already in the nicklist. if (!nickname.isEmpty() && !getNickByName(nickname)) { ChannelNickPtr nick = m_server->addNickToJoinedChannelsList(getName(), nickname); Q_ASSERT(nick); nick->setMode(mode); if(!userHost.isEmpty()) { nick->getNickInfo()->setHostmask(userHost); } fastAddNickname(nick); ++m_processedNicksCount; if (nick->isAdmin() || nick->isOwner() || nick->isOp() || nick->isHalfOp()) ++m_processedOpsCount; } QMetaObject::invokeMethod(this, "processQueuedNicks", flush ? Qt::DirectConnection : Qt::QueuedConnection, Q_ARG(bool, flush)); } } void Channel::setChannelEncoding(const QString& encoding) // virtual { if(m_server->getServerGroup()) Preferences::setChannelEncoding(m_server->getServerGroup()->id(), getName(), encoding); else Preferences::setChannelEncoding(m_server->getDisplayName(), getName(), encoding); } QString Channel::getChannelEncoding() // virtual { if(m_server->getServerGroup()) return Preferences::channelEncoding(m_server->getServerGroup()->id(), getName()); return Preferences::channelEncoding(m_server->getDisplayName(), getName()); } QString Channel::getChannelEncodingDefaultDesc() // virtual { return i18n("Identity Default ( %1 )", getServer()->getIdentity()->getCodecName()); } void Channel::showNicknameBox(bool show) { if(show) { nicknameCombobox->show(); } else { nicknameCombobox->hide(); } } void Channel::showNicknameList(bool show) { if (show) { channelSplitterHidden = false; nickListButtons->show(); } else { channelSplitterHidden = true; nickListButtons->hide(); } } void Channel::requestNickListSort() { m_delayedSortTrigger++; if (m_delayedSortTrigger == DELAYED_SORT_TRIGGER && !m_delayedSortTimer->isActive()) { nicknameListView->fastSetSortingEnabled(false); m_delayedSortTimer->start(1000); } } void Channel::delayedSortNickList() { sortNickList(true); } void Channel::sortNickList(bool delayed) { if (!delayed || m_delayedSortTrigger > DELAYED_SORT_TRIGGER) { qSort(nicknameList.begin(), nicknameList.end(), nickLessThan); nicknameListView->resort(); } if (!nicknameListView->isSortingEnabled()) nicknameListView->fastSetSortingEnabled(true); m_delayedSortTrigger = 0; m_delayedSortTimer->stop(); } void Channel::repositionNick(Nick *nick) { int index = nicknameList.indexOf(nick); if (index > -1) { // Trigger nick reposition in the nicklist including // field updates nick->refresh(); // Readd nick to the nicknameList nicknameList.removeAt(index); fastAddNickname(nick->getChannelNick(), nick); } else { qWarning() << "Nickname " << nick->getChannelNick()->getNickname() << " not found!"<< endl; } } bool Channel::eventFilter(QObject* watched, QEvent* e) { if((watched == nicknameListView) && (e->type() == QEvent::Resize) && splittersInitialized && isVisible()) { if (!topicSplitterHidden && !channelSplitterHidden) { Preferences::self()->setChannelSplitterSizes(m_horizSplitter->sizes()); Preferences::self()->setTopicSplitterSizes(m_vertSplitter->sizes()); } if (!topicSplitterHidden && channelSplitterHidden) { Preferences::self()->setTopicSplitterSizes(m_vertSplitter->sizes()); } if (!channelSplitterHidden && topicSplitterHidden) { Preferences::self()->setChannelSplitterSizes(m_horizSplitter->sizes()); } } return ChatWindow::eventFilter(watched, e); } void Channel::addBan(const QString& ban) { for ( QStringList::iterator it = m_BanList.begin(); it != m_BanList.end(); ++it ) { if ((*it).section(QLatin1Char(' '), 0, 0) == ban.section(QLatin1Char(' '), 0, 0)) { // Ban is already in list. it = m_BanList.erase(it); emit banRemoved(ban.section(QLatin1Char(' '), 0, 0)); if (it == m_BanList.end()) break; } } m_BanList.prepend(ban); emit banAdded(ban); } void Channel::removeBan(const QString& ban) { foreach(const QString &string, m_BanList) { if (string.section(QLatin1Char(' '), 0, 0) == ban) { m_BanList.removeOne(string); emit banRemoved(ban); } } } void Channel::clearBanList() { m_BanList.clear(); emit banListCleared(); } void Channel::append(const QString& nickname, const QString& message, const QHash &messageTags, const QString& label) { if(nickname != getServer()->getNickname()) { Nick* nick = getNickByName(nickname); if(nick) { nick->getChannelNick()->setTimeStamp(QDateTime::currentDateTime().toTime_t()); } } ChatWindow::append(nickname, message, messageTags, label); nickActive(nickname); } void Channel::appendAction(const QString& nickname, const QString& message, const QHash &messageTags) { if(nickname != getServer()->getNickname()) { Nick* nick = getNickByName(nickname); if(nick) { nick->getChannelNick()->setTimeStamp(QDateTime::currentDateTime().toTime_t()); } } ChatWindow::appendAction(nickname, message, messageTags); nickActive(nickname); } void Channel::nickActive(const QString& nickname) //FIXME reported to crash, can't reproduce { ChannelNickPtr channelnick=getChannelNick(nickname); //XXX Would be nice to know why it can be null here... if (channelnick) { channelnick->moreActive(); if (Preferences::self()->sortByActivity()) { Nick* nick = getNickByName(nickname); if (nick) { nick->repositionMe(); } } } } #ifdef HAVE_QCA2 Konversation::Cipher* Channel::getCipher() { if(!m_cipher) m_cipher = new Konversation::Cipher(); return m_cipher; } #endif void Channel::updateNickInfos() { foreach(Nick* nick, nicknameList) { if(nick->getChannelNick()->getNickInfo()->isChanged()) { nick->refresh(); } } } void Channel::updateChannelNicks(const QString& channel) { if(channel != name.toLower()) return; foreach(Nick* nick, nicknameList) { if(nick->getChannelNick()->isChanged()) { nick->refresh(); if(nick->getChannelNick() == m_ownChannelNick) { refreshModeButtons(); } } } } void Channel::resizeNicknameListViewColumns() { // Resize columns if needed (on regular basis) if (m_nicknameListViewTextChanged & (1 << Nick::NicknameColumn)) nicknameListView->resizeColumnToContents(Nick::NicknameColumn); if (m_nicknameListViewTextChanged & (1 << Nick::HostmaskColumn)) nicknameListView->resizeColumnToContents(Nick::HostmaskColumn); m_nicknameListViewTextChanged = 0; } // // NickList // NickList::NickList() : QList() { } QString NickList::completeNick(const QString& pattern, bool& complete, QStringList& found, bool skipNonAlfaNum, bool caseSensitive) { found.clear(); QString prefix(QLatin1Char('^')); QString newNick; QString prefixCharacter = Preferences::self()->prefixCharacter(); NickList foundNicks; if((pattern.contains(QRegExp(QStringLiteral("^(\\d|\\w)")))) && skipNonAlfaNum) { prefix = QStringLiteral("^([^\\d\\w]|[\\_]){0,}"); } QRegExp regexp(prefix + QRegExp::escape(pattern)); regexp.setCaseSensitivity(caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); foreach (Nick* nick, *this) { newNick = nick->getChannelNick()->getNickname(); if(!prefix.isEmpty() && newNick.contains(prefixCharacter)) { newNick = newNick.section( prefixCharacter,1 ); } if(newNick.contains(regexp)) { foundNicks.append(nick); } } qSort(foundNicks.begin(), foundNicks.end(), nickTimestampLessThan); foreach (Nick *nick, foundNicks) { found.append(nick->getChannelNick()->getNickname()); } if(found.count() > 1) { bool ok = true; int patternLength = pattern.length(); QString firstNick = found[0]; int firstNickLength = firstNick.length(); int foundCount = found.count(); while(ok && ((patternLength) < firstNickLength)) { ++patternLength; QStringList tmp = found.filter(firstNick.left(patternLength), caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); if(tmp.count() != foundCount) { ok = false; --patternLength; } } complete = false; return firstNick.left(patternLength); } else if(found.count() == 1) { complete = true; return found[0]; } return QString(); } bool NickList::containsNick(const QString& nickname) { foreach (Nick* nick, *this) { if (nick->getChannelNick()->getNickname()==nickname) return true; } return false; } // kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on; // vim: set et sw=4 ts=4 cino=l1,cs,U1: diff --git a/src/irc/inputfilter.cpp b/src/irc/inputfilter.cpp index 30bc9e65..42b1d6eb 100644 --- a/src/irc/inputfilter.cpp +++ b/src/irc/inputfilter.cpp @@ -1,2539 +1,2539 @@ /* 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) any later version. */ /* Copyright (C) 2002 Dario Abatianni Copyright (C) 2004, 2016 Peter Simonsson Copyright (C) 2006-2008 Eike Hein */ #include "inputfilter.h" #include "server.h" #include "replycodes.h" #include "application.h" #include "version.h" #include "commit.h" #include "query.h" #include "channel.h" #include "statuspanel.h" #include "common.h" #include "notificationhandler.h" #include #include #include #include #include InputFilter::InputFilter() - : m_server(0), + : m_server(nullptr), m_lagMeasuring(false) { m_connecting = false; } InputFilter::~InputFilter() { } void InputFilter::setServer(Server* newServer) { m_server = newServer; } template int posOrLen(T chr, const QString& str, int from=0) { int p=str.indexOf(QLatin1Char(chr), from); if (p<0) return str.size(); return p; } /// "[22:08] >> :thiago!n=thiago@kde/thiago QUIT :Read error: 110 (Connection timed out)" /// "[21:47] >> :Zarin!n=x365@kde/developer/lmurray PRIVMSG #plasma :If the decoration doesn't have paint( QPixmap ) it falls back to the old one" /// "[21:49] >> :niven.freenode.net 352 argonel #kde-forum i=beezle konversation/developer/argonel irc.freenode.net argonel H :0 Konversation User " void InputFilter::parseLine(const QString& line) { int start=0; QHash messageTags; if (line[0] == QLatin1Char('@')) { messageTags = parseMessageTags(line, &start); } QString prefix; int end(posOrLen(' ', line, start)); if (line[start]==QLatin1Char(':')) { prefix = line.mid(start + 1, end - start - 1); //skips the colon and does not include the trailing space start = end + 1; end = posOrLen(' ', line, start); } //even though the standard is UPPER CASE, someone when through a great deal of trouble to make this lower case... QString command = QString(line.mid(start, end-start)).toLower(); start=end+1; int trailing=line.indexOf(QStringLiteral(" :"), end); if (trailing >= 0) end=trailing; else end=line.size(); QStringList parameterList; while (start < end) { if (line[start]==QLatin1Char(' ')) start++; else { int p=line.indexOf(QLatin1Char(' '), start); //easier to have Qt loop for me :) if (p<0) p=end; parameterList << line.mid(start, p-start); start=p+1; } }; /* Quote: "The final colon is specified as a "last argument" designator, and * is always valid before the final argument." * Quote: "The last parameter may be an empty string." * Quote: "After extracting the parameter list, all parameters are equal * whether matched by or . is just a * syntactic trick to allow SPACE within the parameter." */ if (trailing >= 0 ) //<-- trailing == ":" - per above we need the empty string parameterList << line.mid( qMin(trailing+2, line.size()) ); Q_ASSERT(m_server); //how could we have gotten a line without a server? // Server command, if no "!" was found in prefix if ((!prefix.contains(QLatin1Char('!'))) && (prefix != m_server->getNickname())) { parseServerCommand(prefix, command, parameterList, messageTags); } else { parseClientCommand(prefix, command, parameterList, messageTags); } } #define trailing (parameterList.isEmpty() ? QString() : parameterList.last()) #define plHas(x) _plHas(parameterList.count(), (x)) bool _plHad=false; int _plWanted = 0; bool _plHas(int count, int x) { _plHad=(count >= x); _plWanted = x; if (!_plHad) qDebug() << "plhad" << count << "wanted" << x; return _plHad; } void InputFilter::parseClientCommand(const QString &prefix, const QString &command, QStringList ¶meterList, QHash messageTags) { Application* konv_app = Application::instance(); Q_ASSERT(konv_app); Q_ASSERT(m_server); // Extract nickname from prefix int pos = prefix.indexOf(QLatin1Char('!')); QString sourceNick = prefix.left(pos); QString sourceHostmask = prefix.mid(pos + 1); // remember hostmask for this nick, it could have changed m_server->addHostmaskToNick(sourceNick, sourceHostmask); bool isNumeric = false; int numeric = command.toInt(&isNumeric); if(isNumeric) { parseNumeric(prefix, numeric, parameterList, messageTags); } //PRIVMSG #channel :message else if (command == QStringLiteral("privmsg") && plHas(2)) { bool isChan = isAChannel(parameterList.value(0)); // CTCP message? if (m_server->identifyMsg() && (trailing.length() > 1 && (trailing.at(0) == QLatin1Char('+') || trailing.at(0) == QLatin1Char('-')))) { trailing = trailing.mid(1); } if (!trailing.isEmpty() && trailing.at(0)==QChar(0x01)) { // cut out the CTCP command QString ctcp = trailing.mid(1,trailing.indexOf(QChar(0x01),1)-1); //QString::left(-1) returns the entire string QString ctcpCommand = ctcp.left(ctcp.indexOf(QLatin1Char(' '))).toLower(); bool hasArg = ctcp.indexOf(QLatin1Char(' ')) > 0; //QString::mid(-1)+1 = 0, which returns the entire string if there is no space, resulting in command==arg QString ctcpArgument = hasArg ? ctcp.mid(ctcp.indexOf(QLatin1Char(' '))+1) : QString(); hasArg = !ctcpArgument.isEmpty(); if (hasArg) ctcpArgument = konv_app->doAutoreplace(ctcpArgument, false).first; // If it was a ctcp action, build an action string if (ctcpCommand == QStringLiteral("action") && isChan) { if (!isIgnore(prefix, Ignore::Channel)) { Channel* channel = m_server->getChannelByName( parameterList.value(0) ); if (!channel) { qCritical() << "Didn't find the channel " << parameterList.value(0) << endl; return; } channel->appendAction(sourceNick, ctcpArgument, messageTags); if (sourceNick != m_server->getNickname()) { if (hasArg && ctcpArgument.toLower().contains(QRegExp(QStringLiteral("(^|[^\\d\\w])") + QRegExp::escape(m_server->loweredNickname()) + QStringLiteral("([^\\d\\w]|$)")))) { konv_app->notificationHandler()->nick(channel, sourceNick, ctcpArgument); } else { konv_app->notificationHandler()->message(channel, sourceNick, ctcpArgument); } } } } // If it was a ctcp action, build an action string else if (ctcpCommand == QStringLiteral("action") && !isChan) { // Check if we ignore queries from this nick if (!isIgnore(prefix, Ignore::Query)) { NickInfoPtr nickinfo = m_server->obtainNickInfo(sourceNick); nickinfo->setHostmask(sourceHostmask); // create new query (server will check for dupes) Query* query = m_server->addQuery(nickinfo, false /* we didn't initiate this*/ ); // send action to query query->appendAction(sourceNick, ctcpArgument, messageTags); if (sourceNick != m_server->getNickname() && query) konv_app->notificationHandler()->queryMessage(query, sourceNick, ctcpArgument); } } // Answer ping requests else if (ctcpCommand == QStringLiteral("ping") && hasArg) { if (!isIgnore(prefix,Ignore::CTCP)) { if (isChan) { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-PING request from %1 to channel %2, sending answer.", sourceNick, parameterList.value(0)), messageTags ); } else { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-%1 request from %2, sending answer.", QStringLiteral("PING"), sourceNick), messageTags ); } m_server->ctcpReply(sourceNick, QString(QStringLiteral("PING %1")).arg(ctcpArgument)); } } // Maybe it was a version request, so act appropriately else if (ctcpCommand == QStringLiteral("version")) { if(!isIgnore(prefix,Ignore::CTCP)) { if (isChan) { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received Version request from %1 to channel %2.", sourceNick, parameterList.value(0)), messageTags ); } else { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received Version request from %1.", sourceNick), messageTags ); } QString reply; if (Preferences::self()->customVersionReplyEnabled()) { reply = Preferences::self()->customVersionReply().trimmed(); } else { // Do not internationalize the below version string reply = QString(QStringLiteral("Konversation %1 Build %2 (C) 2002-2018 by the Konversation team")) .arg(QStringLiteral(KONVI_VERSION)) .arg(QString::number(COMMIT)); } if (!reply.isEmpty()) m_server->ctcpReply(sourceNick,QStringLiteral("VERSION ")+reply); } } // DCC request? else if (ctcpCommand==QStringLiteral("dcc") && !isChan && hasArg) { if (!isIgnore(prefix,Ignore::DCC)) { // Extract DCC type and argument list QString dccType=ctcpArgument.toLower().section(QLatin1Char(' '),0,0); // Support file names with spaces QString dccArguments = ctcpArgument.mid(ctcpArgument.indexOf(QLatin1Char(' '))+1); QStringList dccArgumentList; if ((dccArguments.count(QLatin1Char('\"')) >= 2) && (dccArguments.startsWith(QLatin1Char('\"')))) { int lastQuotePos = dccArguments.lastIndexOf(QLatin1Char('\"')); if (dccArguments[lastQuotePos+1] == QLatin1Char(' ')) { QString fileName = dccArguments.mid(1, lastQuotePos-1); dccArguments = dccArguments.mid(lastQuotePos+2); dccArgumentList.append(fileName); } } dccArgumentList += dccArguments.split(QLatin1Char(' '), QString::SkipEmptyParts); if (dccType==QStringLiteral("send")) { if (dccArgumentList.count()==4) { // incoming file konv_app->notificationHandler()->dccIncoming(m_server->getStatusView(), sourceNick); emit addDccGet(sourceNick,dccArgumentList); } else if (dccArgumentList.count() >= 5) { if (dccArgumentList[dccArgumentList.size() - 3] == QStringLiteral("0")) { // incoming file (Reverse DCC) konv_app->notificationHandler()->dccIncoming(m_server->getStatusView(), sourceNick); emit addDccGet(sourceNick,dccArgumentList); } else { // the receiver accepted the offer for Reverse DCC emit startReverseDccSendTransfer(sourceNick,dccArgumentList); } } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Received invalid DCC SEND request from %1.", sourceNick), messageTags ); } } else if (dccType==QStringLiteral("accept")) { // resume request was accepted if (dccArgumentList.count() >= 3) { emit resumeDccGetTransfer(sourceNick,dccArgumentList); } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Received invalid DCC ACCEPT request from %1.", sourceNick), messageTags ); } } // Remote client wants our sent file resumed else if (dccType==QStringLiteral("resume")) { if (dccArgumentList.count() >= 3) { emit resumeDccSendTransfer(sourceNick,dccArgumentList); } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Received invalid DCC RESUME request from %1.", sourceNick), messageTags ); } } else if (dccType==QStringLiteral("chat")) { if (dccArgumentList.count() == 3) { // incoming chat emit addDccChat(sourceNick,dccArgumentList); } else if (dccArgumentList.count() == 4) { if (dccArgumentList[dccArgumentList.size() - 2] == QStringLiteral("0")) { // incoming chat (Reverse DCC) emit addDccChat(sourceNick,dccArgumentList); } else { // the receiver accepted the offer for Reverse DCC chat emit startReverseDccChat(sourceNick,dccArgumentList); } } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Received invalid DCC CHAT request from %1.", sourceNick), messageTags ); } } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Unknown DCC command %1 received from %2.", ctcpArgument, sourceNick), messageTags ); } } } else if (ctcpCommand==QStringLiteral("clientinfo") && !isChan) { if (!isIgnore(prefix, Ignore::CTCP)) { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-%1 request from %2, sending answer.", QString::fromLatin1("CLIENTINFO"), sourceNick), messageTags ); m_server->ctcpReply(sourceNick, QStringLiteral("CLIENTINFO ACTION CLIENTINFO DCC PING TIME VERSION")); } } else if (ctcpCommand==QStringLiteral("time") && !isChan) { if (!isIgnore(prefix, Ignore::CTCP)) { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-%1 request from %2, sending answer.", QString::fromLatin1("TIME"), sourceNick), messageTags ); m_server->ctcpReply(sourceNick, QStringLiteral("TIME ")+QDateTime::currentDateTime().toString()); } } // No known CTCP request, give a general message else { if (!isIgnore(prefix,Ignore::CTCP)) { if (isChan) m_server->appendServerMessageToChannel( parameterList.value(0), QStringLiteral("CTCP"), i18n("Received unknown CTCP-%1 request from %2 to Channel %3.", ctcp, sourceNick, parameterList.value(0)), messageTags ); else m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received unknown CTCP-%1 request from %2.", ctcp, sourceNick), messageTags ); } } } // No CTCP, so it's an ordinary channel or query message else { parsePrivMsg(prefix, parameterList, messageTags); } } else if (command==QStringLiteral("notice") && plHas(2)) { if (!isIgnore(prefix,Ignore::Notice)) { // Channel notice? if(isAChannel(parameterList.value(0))) { if (m_server->identifyMsg() && (trailing.length() > 1 && (trailing.at(0) == QLatin1Char('+') || trailing.at(0) == QLatin1Char('-')))) { trailing = trailing.mid(1); } m_server->appendServerMessageToChannel(parameterList.value(0), i18n("Notice"), i18n("-%1 to %2- %3", sourceNick, parameterList.value(0), trailing), messageTags ); } // Private notice else { // Was this a CTCP reply? if (!trailing.isEmpty() && trailing.at(0) == QChar(0x01)) { // cut 0x01 bytes from trailing string QString ctcp(trailing.mid(1,trailing.length()-2)); QString replyReason(ctcp.section(QLatin1Char(' '),0,0)); QString reply(ctcp.section(QLatin1Char(' '),1)); // pong reply, calculate turnaround time if (replyReason.toLower()==QStringLiteral("ping")) { int dateArrived=QDateTime::currentDateTime().toTime_t(); int dateSent=reply.toInt(); int time = dateArrived-dateSent; QString unit = i18np("second", "seconds", time); m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-PING reply from %1: %2 %3.", sourceNick, time, unit), messageTags ); } else if (replyReason.toLower() == QStringLiteral("dcc")) { qDebug() << reply; QStringList dccList = reply.split(QLatin1Char(' ')); //all dcc notices we receive are rejects if (dccList.count() >= 2 && dccList.first().toLower() == QStringLiteral("reject")) { dccList.removeFirst(); if (dccList.count() >= 2 && dccList.first().toLower() == QStringLiteral("send")) { dccList.removeFirst(); emit rejectDccSendTransfer(sourceNick,dccList); } else if (dccList.first().toLower() == QStringLiteral("chat")) { emit rejectDccChat(sourceNick); } } } // all other ctcp replies get a general message else { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-%1 reply from %2: %3.", replyReason, sourceNick, reply), messageTags ); } } // No, so it was a normal notice else { #ifdef HAVE_QCA2 //Key exchange if (trailing.startsWith(QLatin1String("DH1080_INIT "))) { m_server->appendMessageToFrontmost(i18n("Notice"), i18n("Received DH1080_INIT from %1", sourceNick), messageTags); m_server->parseInitKeyX(sourceNick, trailing.mid(12)); } else if (trailing.startsWith(QLatin1String("DH1080_FINISH "))) { m_server->appendMessageToFrontmost(i18n("Notice"), i18n("Received DH1080_FINISH from %1", sourceNick), messageTags); m_server->parseFinishKeyX(sourceNick, trailing.mid(14)); } else { #endif m_server->appendMessageToFrontmost(i18n("Notice"), i18n("-%1- %2", sourceNick, m_server->identifyMsg() ? trailing.mid(1) : trailing), messageTags); #ifdef HAVE_QCA2 } #endif } } } } else if (command==QStringLiteral("join") && plHas(1)) { QString channelName; QString account; QString realName; channelName = parameterList[0]; if (m_server->capabilities() & Server::ExtendedJoin && plHas(3)) { if (parameterList[1] != "*") account = parameterList[1]; realName = parameterList[2]; } // Did we join the channel, or was it someone else? if (m_server->isNickname(sourceNick)) { /* QString key; // TODO: Try to remember channel keys for autojoins and manual joins, so // we can get %k to work if(channelName.contains(' ')) { key=channelName.section(' ',1,1); channelName=channelName.section(' ',0,0); } */ // Join the channel Channel* channel = m_server->joinChannel(channelName, sourceHostmask, messageTags); // Upon JOIN we're going to receive some NAMES input from the server which // we need to be able to tell apart from manual invocations of /names setAutomaticRequest(QStringLiteral("NAMES"),channelName,true); channel->clearModeList(); // Request modes for the channel m_server->queue(QStringLiteral("MODE ")+channelName, Server::LowPriority); // Initiate channel ban list channel->clearBanList(); setAutomaticRequest(QStringLiteral("BANLIST"),channelName,true); m_server->queue(QStringLiteral("MODE ")+channelName+QStringLiteral(" +b"), Server::LowPriority); } else { Channel* channel = m_server->nickJoinsChannel(channelName, sourceNick, sourceHostmask, account, realName, messageTags); konv_app->notificationHandler()->join(channel, sourceNick); } } else if (command==QStringLiteral("kick") && plHas(2)) { m_server->nickWasKickedFromChannel(parameterList.value(0), parameterList.value(1), sourceNick, trailing, messageTags); } else if (command==QStringLiteral("part") && plHas(1)) { // A version of the PART line encountered on ircu: ":Nick!user@host PART :#channel" QString channel(parameterList.value(0)); QString reason(parameterList.value(1)); Channel* channelPtr = m_server->removeNickFromChannel(channel, sourceNick, reason, messageTags); if (sourceNick != m_server->getNickname()) { konv_app->notificationHandler()->part(channelPtr, sourceNick); } } else if (command==QStringLiteral("quit") && plHas(1)) { m_server->removeNickFromServer(sourceNick, trailing, messageTags); if (sourceNick != m_server->getNickname()) { konv_app->notificationHandler()->quit(m_server->getStatusView(), sourceNick); } } else if (command==QStringLiteral("nick") && plHas(1)) { QString newNick(parameterList.value(0)); // Message may not include ":" in front of the new nickname m_server->renameNick(sourceNick, newNick, messageTags); if (sourceNick != m_server->getNickname()) { konv_app->notificationHandler()->nickChange(m_server->getStatusView(), sourceNick, newNick); } } else if (command==QStringLiteral("topic") && plHas(2)) { m_server->setChannelTopic(sourceNick, parameterList.value(0), trailing, messageTags); } else if (command==QStringLiteral("mode") && plHas(2)) // mode #channel -/+ mmm params { parseModes(sourceNick, parameterList, messageTags); Channel* channel = m_server->getChannelByName(parameterList.value(0)); konv_app->notificationHandler()->mode(channel, sourceNick, parameterList.value(0), QStringList(parameterList.mid(1)).join (QStringLiteral(" "))); } else if (command==QStringLiteral("invite") && plHas(2)) //:ejm!i=beezle@bas5-oshawa95-1176455927.dsl.bell.ca INVITE argnl :#sug4 { if (!isIgnore(prefix, Ignore::Invite)) { QString channel(trailing); m_server->appendMessageToFrontmost(i18n("Invite"), i18n("%1 invited you to channel %2.", sourceNick, channel), messageTags ); emit invitation(sourceNick, channel); } } else if (command == QStringLiteral("away")) { NickInfoPtr nickInfo = m_server->getNickInfo(sourceNick); if (nickInfo) { if (!parameterList.isEmpty()) { nickInfo->setAway(true); nickInfo->setAwayMessage(parameterList.first()); } else { nickInfo->setAway(false); nickInfo->setAwayMessage(QString()); } } else { qDebug() << "Received away message for unknown nick," << sourceNick; } } else if (command == QStringLiteral("account") && plHas(1)) { NickInfoPtr nickInfo = m_server->getNickInfo(sourceNick); QString account = parameterList.first(); if (account == "*") { nickInfo->setAccount(QString()); } else { nickInfo->setAccount(account); } } else { qDebug() << "unknown client command" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); m_server->appendMessageToFrontmost(command, parameterList.join(QStringLiteral(" ")), messageTags); } } void InputFilter::parseServerCommand(const QString &prefix, const QString &command, QStringList ¶meterList, QHash messageTags) { bool isNumeric; int numeric = command.toInt(&isNumeric); Q_ASSERT(m_server); if (!m_server) return; if (!isNumeric) { if (command == QStringLiteral("ping")) { QString text; text = (!trailing.isEmpty()) ? trailing : parameterList.join(QStringLiteral(" ")); if (!trailing.isEmpty()) { text = prefix + QStringLiteral(" :") + text; } if (!text.startsWith(QLatin1Char(' '))) { text.prepend(QLatin1Char(' ')); } // queue the reply to send it as soon as possible m_server->queue(QStringLiteral("PONG")+text, Server::HighPriority); } else if (command == QStringLiteral("error :closing link:")) { qDebug() << "link closed"; } else if (command == QStringLiteral("pong")) { // double check if we are in lag measuring mode since some servers fail to send // the LAG cookie back in PONG if (trailing.startsWith(QLatin1String("LAG")) || getLagMeasuring()) { m_server->pongReceived(); } } else if (command == QStringLiteral("mode")) { parseModes(prefix, parameterList, messageTags); } else if (command == QStringLiteral("notice")) { m_server->appendStatusMessage(i18n("Notice"), i18n("-%1- %2", prefix, trailing), messageTags); } else if (command == QStringLiteral("kick") && plHas(3)) { m_server->nickWasKickedFromChannel(parameterList.value(1), parameterList.value(2), prefix, trailing, messageTags); } else if (command == QStringLiteral("privmsg")) { parsePrivMsg(prefix, parameterList, messageTags); } else if (command==QStringLiteral("cap") && plHas(3)) { QString command = parameterList.value(1).toLower(); if (command == QStringLiteral("ack") || command == QStringLiteral("nak")) { m_server->capReply(); QStringList capabilities = parameterList.value(2).split(QLatin1Char(' '), QString::SkipEmptyParts); foreach(const QString& capability, capabilities) { int nameStart = capability.indexOf(QRegExp(QStringLiteral("[a-z0-9]"), Qt::CaseInsensitive)); QString modifierString = capability.left(nameStart); QString name = capability.mid(nameStart); Server::CapModifiers modifiers = Server::NoModifiers; if (modifierString.contains(QLatin1Char('-'))) { modifiers = modifiers | Server::DisMod; modifiers = modifiers ^ Server::NoModifiers; } if (modifierString.contains(QLatin1Char('='))) { modifiers = modifiers | Server::StickyMod; modifiers = modifiers ^ Server::NoModifiers; } if (modifierString.contains(QLatin1Char('~'))) { modifiers = modifiers | Server::AckMod; modifiers = modifiers ^ Server::NoModifiers; } if (command == QStringLiteral("ack")) m_server->capAcknowledged(name, modifiers); else m_server->capDenied(name); } if(!m_server->capEndDelayed()) { m_server->capEndNegotiation(); } } else if (command == "ls" || command == "list") { m_server->appendStatusMessage(i18n("Capabilities"), trailing, messageTags); if (getAutomaticRequest(QStringLiteral("CAP LS"), QString()) != 0) { if (parameterList.count() == 3) setAutomaticRequest (QStringLiteral("CAP LS"), QString (), false); m_server->capInitiateNegotiation (trailing); } } } else if (command == QStringLiteral("authenticate") && plHas(1)) { if ((m_server->getLastAuthenticateCommand() == QStringLiteral("PLAIN") || m_server->getLastAuthenticateCommand() == QStringLiteral("EXTERNAL")) && parameterList.value(0) == QStringLiteral("+")) m_server->registerWithServices(); } // All yet unknown messages go into the frontmost window unaltered else { qDebug() << "unknown server command" << command; m_server->appendMessageToFrontmost(command, parameterList.join(QStringLiteral(" ")), messageTags); } } else if (plHas(2)) //[0]==ourNick, [1] needs to be *something* { parseNumeric(prefix, numeric, parameterList, messageTags); } // end of numeric elseif else { qDebug() << "unknown message format" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); } } // end of server void InputFilter::parseModes(const QString &sourceNick, const QStringList ¶meterList, const QHash &messageTags) { const QString modestring=parameterList.value(1); if (!isAChannel(parameterList.value(0))) { QString message; if (parameterList.value(0) == m_server->getNickname()) { if (sourceNick == m_server->getNickname()) { //XXX someone might care about the potentially unnecessary plural here message = i18n("You have set personal modes: %1", modestring); } else { //XXX someone might care about the potentially unnecessary plural here message = i18n("%1 has changed your personal modes: %2", sourceNick, modestring); } } if (!message.isEmpty()) m_server->appendStatusMessage(i18n("Mode"), message, messageTags); return; } bool plus=false; int parameterIndex=0; // List of modes that need a parameter (note exception with -k and -l) // Mode q is quiet on freenode and acts like b... if this is a channel mode on other // networks then more logic is needed here. --MrGrim //FIXME: The assumptions being made here about which modes have parameters and // which don't strike me as very wrong, as some of these mode chars are used // both as user and channel modes (e.g. A and O) and whether they use a para- // meter can vary between those cases. This would seem to lead to trouble with // the list indices down there. --hein, Thu Aug 6 19:48:02 CEST 2009 / r1008015 QString parameterModes = QStringLiteral("aAoOvhkbleIq"); QString message = i18n("%1 sets mode: %2", sourceNick, modestring); for (int index=0;indexupdateChannelMode(sourceNick, parameterList.value(0), mode, plus, parameter, messageTags); } } // endfor if (Preferences::self()->useLiteralModes()) { m_server->appendCommandMessageToChannel(parameterList.value(0), i18n("Mode"), message, messageTags); } } // # & + and ! are *often*, but not necessarily, Channel identifiers. + and ! are non-RFC, // so if a server doesn't offer 005 and supports + and ! channels, I think thats broken behaviour // on their part - not ours. --Argonel bool InputFilter::isAChannel(const QString &check) { if (check.isEmpty()) return false; Q_ASSERT(m_server); // if we ever see the assert, we need the ternary return m_server? m_server->isAChannel(check) : bool(QString(QStringLiteral("#&")).contains(check.at(0))); } bool InputFilter::isIgnore(const QString &sender, Ignore::Type type) { bool doIgnore = false; foreach (Ignore* item, Preferences::ignoreList()) { QRegExp ignoreItem(QRegExp::escape(item->getName()).replace(QStringLiteral("\\*"), QStringLiteral("(.*)")), Qt::CaseInsensitive); if (ignoreItem.exactMatch(sender) && (item->getFlags() & type)) doIgnore = true; if (ignoreItem.exactMatch(sender) && (item->getFlags() & Ignore::Exception)) return false; } return doIgnore; } void InputFilter::reset() { m_automaticRequest.clear(); m_whoRequestList.clear(); } void InputFilter::setAutomaticRequest(const QString& command, const QString& name, bool yes) { m_automaticRequest[command][name.toLower()] += (yes) ? 1 : -1; if(m_automaticRequest[command][name.toLower()]<0) { qDebug() << "( " << command << ", " << name << " ) was negative! Resetting!"; m_automaticRequest[command][name.toLower()]=0; } } int InputFilter::getAutomaticRequest(const QString& command, const QString& name) { return m_automaticRequest[command][name.toLower()]; } void InputFilter::addWhoRequest(const QString& name) { m_whoRequestList << name.toLower(); } bool InputFilter::isWhoRequestUnderProcess(const QString& name) { return (m_whoRequestList.contains(name.toLower())>0); } void InputFilter::setLagMeasuring(bool state) { m_lagMeasuring=state; } bool InputFilter::getLagMeasuring() { return m_lagMeasuring; } void InputFilter::parsePrivMsg(const QString& prefix, QStringList& parameterList, const QHash &messageTags) { int pos = prefix.indexOf(QLatin1Char('!')); QString source; QString sourceHostmask; QString message(trailing); if(pos > 0) { source = prefix.left(pos); sourceHostmask = prefix.mid(pos + 1); } else { source = prefix; } Application* konv_app = Application::instance(); message = konv_app->doAutoreplace(message, false).first; if(isAChannel(parameterList.value(0))) { if(!isIgnore(prefix, Ignore::Channel)) { Channel* channel = m_server->getChannelByName(parameterList.value(0)); if(channel) { QString label; if (m_server->getServerNickPrefixes().contains(parameterList.value(0).at(0))) { label = parameterList.value(0); } channel->append(source, message, messageTags, label); if(source != m_server->getNickname()) { QRegExp regexp(QStringLiteral("(^|[^\\d\\w])") + QRegExp::escape(m_server->loweredNickname()) + QStringLiteral("([^\\d\\w]|$)")); regexp.setCaseSensitivity(Qt::CaseInsensitive); if(message.contains(regexp)) { konv_app->notificationHandler()->nick(channel, source, message); } else { konv_app->notificationHandler()->message(channel, source, message); } } } } } else { if(!isIgnore(prefix,Ignore::Query)) { QString queryName = source; // Handle znc.in/self-message correctly if (source == m_server->getNickname()) queryName = parameterList[0]; NickInfoPtr nickinfo = m_server->obtainNickInfo(queryName); if (queryName == source) nickinfo->setHostmask(sourceHostmask); // Create a new query (server will check for dupes) Query* query = m_server->addQuery(nickinfo, false /*we didn't initiate this*/ ); // send action to query query->appendQuery(source, message, messageTags); if(source != m_server->getNickname() && query) { QRegExp regexp(QStringLiteral("(^|[^\\d\\w])") + QRegExp::escape(m_server->loweredNickname()) + QStringLiteral("([^\\d\\w]|$)")); regexp.setCaseSensitivity(Qt::CaseInsensitive); if(message.contains(regexp)) { konv_app->notificationHandler()->nick(query, source, message); } else { konv_app->notificationHandler()->queryMessage(query, source, message); } } } } } QHash InputFilter::parseMessageTags(const QString &line, int *startOfMessage) { int index = line.indexOf(QLatin1Char(' ')); *startOfMessage = index + 1; QStringList tags = line.mid(1, index - 1).split(QLatin1Char(';')); QHash tagHash; foreach(const QString &tag, tags) { QStringList tagList = tag.split(QLatin1Char('=')); tagHash.insert(tagList.first(), tagList.last()); } return tagHash; } void InputFilter::parseNumeric(const QString &prefix, int command, QStringList ¶meterList, const QHash &messageTags) { //:niven.freenode.net 353 argnl @ #konversation :@argonel psn @argnl bugbot pinotree CIA-13 //QString m_serverAssignedNick(parameterList.takeFirst()); QString m_serverAssignedNick(parameterList.first()); switch (command) { case RPL_WELCOME: case RPL_YOURHOST: case RPL_CREATED: { if (plHas(0)) //make the script happy { if (command == RPL_WELCOME) { QString host; if (trailing.contains(QStringLiteral("@"))) host = trailing.section(QLatin1Char('@'), 1); // re-set nickname, since the server may have truncated it if (m_serverAssignedNick != m_server->getNickname()) { m_server->renameNick(m_server->getNickname(), m_serverAssignedNick, messageTags); } // Send the welcome signal, so the server class knows we are connected properly emit welcome(host); m_connecting = true; } m_server->appendStatusMessage(i18n("Welcome"), trailing, messageTags); } break; } case RPL_MYINFO: { if (plHas(5)) { m_server->appendStatusMessage(i18n("Welcome"), i18n("Server %1 (Version %2), User modes: %3, Channel modes: %4", parameterList.value(1), parameterList.value(2), parameterList.value(3), parameterList.value(4)), messageTags ); QString allowed = m_server->allowedChannelModes(); QString newModes = parameterList.value(4); if(!allowed.isEmpty()) //attempt to merge the two { for(int i=0; i < allowed.length(); i++) { if(!newModes.contains(allowed.at(i))) newModes.append(allowed.at(i)); } } m_server->setAllowedChannelModes(newModes); } break; } //case RPL_BOUNCE: // RFC 1459 name, now seems to be obsoleted by ... case RPL_ISUPPORT: // ... DALnet RPL_ISUPPORT { if (plHas(0)) //make the script happy { m_server->appendStatusMessage(i18n("Support"), parameterList.join(QStringLiteral(" ")), messageTags); // The following behaviour is neither documented in RFC 1459 nor in 2810-2813 // Nowadays, most ircds send server capabilities out via 005 (BOUNCE). // refer to http://www.irc.org/tech_docs/005.html for a kind of documentation. // More on http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt QStringList::const_iterator it = parameterList.constBegin(); // don't want the user name ++it; for (; it != parameterList.constEnd(); ++it ) { QString property, value; int pos; if ((pos=(*it).indexOf( QLatin1Char('=') )) !=-1) { property = (*it).left(pos); value = (*it).mid(pos+1); } else { property = *it; } if (property==QStringLiteral("PREFIX")) { pos = value.indexOf(QLatin1Char(')'),1); if(pos==-1) { m_server->setPrefixes(QString(), value); // XXX if ) isn't in the string, NOTHING should be there. anyone got a server if (value.length() || property.length()) m_server->appendStatusMessage(QString(), QStringLiteral("XXX Server sent bad PREFIX in RPL_ISUPPORT, please report."), messageTags); } else { m_server->setPrefixes(value.mid(1, pos-1), value.mid(pos+1)); } } else if (property==QStringLiteral("CHANTYPES")) { m_server->setChannelTypes(value); } else if (property==QStringLiteral("MODES")) { if (!value.isEmpty()) { bool ok = false; // If a value is given, it must be numeric. int modesCount = value.toInt(&ok, 10); if(ok) m_server->setModesCount(modesCount); } } else if (property == QStringLiteral("CAPAB")) { // Disable as we don't use this for anything yet //server->queue("CAPAB IDENTIFY-MSG"); } else if (property == QStringLiteral("CHANMODES")) { if(!value.isEmpty()) { m_server->setChanModes(value); QString allowed = m_server->allowedChannelModes(); QString newModes = value.remove(QLatin1Char(',')); if(!allowed.isEmpty()) //attempt to merge the two { for(int i=0; i < allowed.length(); i++) { if(!newModes.contains(allowed.at(i))) newModes.append(allowed.at(i)); } } m_server->setAllowedChannelModes(newModes); } } else if (property == QStringLiteral("TOPICLEN")) { if (!value.isEmpty()) { bool ok = false; int topicLength = value.toInt(&ok); if (ok) m_server->setTopicLength(topicLength); } } else if (property == QStringLiteral("WHOX")) { m_server->setHasWHOX(true); } else { //qDebug() << "Ignored server-capability: " << property << " with value '" << value << "'"; } } // endfor } break; } case RPL_UMODEIS: { if (plHas(0)) { // TODO check this one... I amputated + ' '+trailing QString message=QString(QStringLiteral("%1 %2")).arg(i18n("Your personal modes are:")).arg(parameterList.join(QLatin1Char(' ')).section(QLatin1Char(' '),1)); m_server->appendMessageToFrontmost(QStringLiteral("Info"), message, messageTags); } break; } case RPL_CHANNELMODEIS: { if (plHas(2)) { const QString modeString=parameterList.value(2); // TEST this one was a 2 // This is the string the user will see QString modesAre; QString message = i18n("Channel modes: ") + modeString; int parameterCount=3; QHash channelModesHash = Konversation::getChannelModesHash(); for (int index=0;indexupdateChannelModeWidgets(parameterList.value(1), mode, parameter); } } // endfor if (!modesAre.isEmpty() && Preferences::self()->useLiteralModes()) { m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Mode"), message, messageTags); } else { m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Mode"), i18n("Channel modes: ") + modesAre, messageTags); } } break; } case RPL_CHANNELURLIS: {// :niven.freenode.net 328 argonel #channel :http://www.buggeroff.com/ if (plHas(3)) { m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("URL"), i18n("Channel URL: %1", trailing), messageTags); } break; } case RPL_CHANNELCREATED: { if (plHas(3)) { QDateTime when; when.setTime_t(parameterList.value(2).toUInt()); m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Created"), i18n("This channel was created on %1.", QLocale().toString(when, QLocale::ShortFormat)), messageTags ); } break; } case RPL_WHOISACCOUNT: { if (plHas(2)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); if (nickInfo) { nickInfo->setIdentified(true); } // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is logged in as %2.", parameterList.value(1), parameterList.value(2)), messageTags); } } break; } //:niven.freenode.net 353 argnl @ #konversation :@argonel psn @argnl bugbot pinotree CIA-13 case RPL_NAMREPLY: { if (plHas(4)) { QStringList nickList; if (!trailing.isEmpty()) { nickList = trailing.split(QLatin1Char(' '), QString::SkipEmptyParts); } else if (parameterList.count() > 3) { for(int i = 3; i < parameterList.count(); i++) { nickList.append(parameterList.value(i)); } } else { qDebug() << "Hmm seems something is broken... can't get to the names!"; } // send list to channel m_server->queueNicks(parameterList.value(2), nickList); // TEST this was a 2 // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("NAMES"), parameterList.value(2)) == 0) { m_server->appendMessageToFrontmost(i18n("Names"), trailing, messageTags); } } break; } case RPL_ENDOFNAMES: { if (plHas(2)) { if (getAutomaticRequest(QStringLiteral("NAMES"),parameterList.value(1)) != 0) { // This code path was taken for the automatic NAMES input on JOIN, upcoming // NAMES input for this channel will be manual invocations of /names setAutomaticRequest(QStringLiteral("NAMES"), parameterList.value(1), false); } else { m_server->appendMessageToFrontmost(i18n("Names"), i18n("End of NAMES list."), messageTags); } emit endOfNames(parameterList.value(1)); } break; } // Topic set messages case RPL_NOTOPIC: { if (plHas(2)) { //this really has 3, but [2] is "No topic has been set" m_server->appendMessageToFrontmost(i18n("TOPIC"), i18n("The channel %1 has no topic set.", parameterList.value(1)), messageTags); } break; } case RPL_TOPIC: { if (plHas(3)) { QString topic(trailing); // FIXME: This is an abuse of the automaticRequest system: We're // using it in an inverted manner, i.e. the automaticRequest is // set to true by a manual invocation of /topic. Bad bad bad - // needs rethinking of automaticRequest. if (getAutomaticRequest(QStringLiteral("TOPIC"), parameterList.value(1)) == 0) { // Update channel window m_server->setChannelTopic(parameterList.value(1), topic, messageTags); } else { m_server->appendMessageToFrontmost(i18n("Topic"), i18n("The channel topic for %1 is: \"%2\"", parameterList.value(1), topic), messageTags); } } break; } case RPL_TOPICSETBY: { if (plHas(4)) { // Inform user who set the topic and when QDateTime when; when.setTime_t(parameterList.value(3).toUInt()); // See FIXME in RPL_TOPIC if (getAutomaticRequest(QStringLiteral("TOPIC"), parameterList.value(1)) == 0) { m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Topic"), i18n("The topic was set by %1 on %2.", parameterList.value(2), QLocale().toString(when, QLocale::ShortFormat)), messageTags, false, false); } else { m_server->appendMessageToFrontmost(i18n("Topic"), i18n("The topic for %1 was set by %2 on %3.", parameterList.value(1), parameterList.value(2), QLocale().toString(when, QLocale::ShortFormat)), messageTags, false); setAutomaticRequest(QStringLiteral("TOPIC"),parameterList.value(1), false); } emit topicAuthor(parameterList.value(1), parameterList.value(2), when); } break; } case RPL_WHOISACTUALLY: { if (plHas(3)) { // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"),parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is actually using the host %2.", parameterList.value(1), parameterList.value(2)), messageTags); } } break; } case ERR_NOSUCHNICK: { if (plHas(2)) { // Display slightly different error message in case we performed a WHOIS for // IP resolve purposes, and clear it from the automaticRequest list if (getAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: No such nick/channel.", parameterList.value(1)), messageTags); } else if(getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) //Display message only if this was not an automatic request. { m_server->appendMessageToFrontmost(i18n("Error"), i18n("No such nick: %1.", parameterList.value(1)), messageTags); setAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1), false); } } break; } case ERR_NOSUCHCHANNEL: { if (plHas(2)) { // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: No such channel.", parameterList.value(1)), messageTags); } } break; } // Nick already on the server, so try another one case ERR_NICKNAMEINUSE: { if (plHas(1)) { // if we are already connected, don't try tro find another nick ourselves if (m_server->isConnected()) // Show message { m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Nickname already in use, try a different one."), messageTags); } else // not connected yet, so try to find a nick that's not in use { // Get the next nick from the list or ask for a new one QString newNick = m_server->getNextNickname(); // The user chose to disconnect... if (newNick.isNull()) { if (m_server->isConnecting()) // ... or did they? m_server->disconnectServer(); else // No they didn't! m_server->appendMessageToFrontmost(i18n("Info"), i18n("The nickname %1 was already in use, but the connection failed before you responded.", m_server->getNickname()), messageTags); } else { // Update Server window m_server->obtainNickInfo(m_server->getNickname()) ; m_server->renameNick(m_server->getNickname(), newNick, messageTags); // Show message m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Nickname already in use. Trying %1.", newNick), messageTags); // Send nickchange request to the server m_server->queue(QStringLiteral("NICK ")+newNick); } } } break; } case ERR_ERRONEUSNICKNAME: { if (plHas(1)) { if (m_server->isConnected()) { // We are already connected. Just print the error message m_server->appendMessageToFrontmost(i18n("Nick"), trailing, messageTags); } else // Find a new nick as in ERR_NICKNAMEINUSE { QString newNick = m_server->getNextNickname(); // The user chose to disconnect if (newNick.isNull()) { m_server->disconnectServer(); } else { m_server->obtainNickInfo(m_server->getNickname()) ; m_server->renameNick(m_server->getNickname(), newNick, messageTags); m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Erroneous nickname. Changing nick to %1.", newNick), messageTags); m_server->queue(QStringLiteral("NICK ")+newNick); } } } break; } case ERR_NOTONCHANNEL: { if (plHas(2)) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("You are not on %1.", parameterList.value(1)), messageTags); setAutomaticRequest(QStringLiteral("TOPIC"),parameterList.value(1), false); } break; } case RPL_MOTDSTART: { if (plHas(1)) { if (!m_connecting || !Preferences::self()->skipMOTD()) m_server->appendStatusMessage(i18n("MOTD"), i18n("Message of the day:"), messageTags); } break; } case RPL_MOTD: { if (plHas(2)) { if (!m_connecting || !Preferences::self()->skipMOTD()) m_server->appendStatusMessage(i18n("MOTD"), trailing, messageTags); } break; } case RPL_ENDOFMOTD: { if (plHas(1)) { if (!m_connecting || !Preferences::self()->skipMOTD()) m_server->appendStatusMessage(i18n("MOTD"), i18n("End of message of the day"), messageTags); if (m_connecting) m_server->autoCommandsAndChannels(); m_connecting = false; } break; } case ERR_NOMOTD: { if (plHas(1)) { if (m_connecting) m_server->autoCommandsAndChannels(); m_connecting = false; } break; } case ERR_CHANOPRIVSNEEDED: { if (plHas(2)) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("You need to be a channel operator in %1 to do that.", parameterList.value(1)), messageTags); } break; } case RPL_YOUREOPER: { if (plHas(1)) { m_server->appendMessageToFrontmost(i18n("Notice"), i18n("You are now an IRC operator on this server."), messageTags); } break; } case RPL_HOSTHIDDEN: { if (plHas(2)) { m_server->appendStatusMessage(i18n("Info"), i18n("'%1' is now your hidden host (set by services).", parameterList.value(1)), messageTags); } break; } case RPL_GLOBALUSERS: // Current global users: 589 Max: 845 { if (plHas(2)) { QString current(trailing.section(QLatin1Char(' '),3)); //QString max(trailing.section(' ',5,5)); m_server->appendStatusMessage(i18n("Users"), i18n("Current users on the network: %1", current), messageTags); } break; } case RPL_LOCALUSERS: // Current local users: 589 Max: 845 { if (plHas(2)) { QString current(trailing.section(QLatin1Char(' '), 3)); //QString max(trailing.section(' ',5,5)); m_server->appendStatusMessage(i18n("Users"), i18n("Current users on %1: %2.", prefix, current), messageTags); } break; } case RPL_ISON: { if (plHas(2)) { // Tell server to start the next notify timer round emit notifyResponse(trailing); } break; } case RPL_AWAY: { if (plHas(3)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); if (nickInfo) { nickInfo->setAway(true); if (nickInfo->getAwayMessage() != trailing) // FIXME i think this check should be in the setAwayMessage method { nickInfo->setAwayMessage(trailing); // TEST this used to skip the automatic request handler below } } if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Away"), i18n("%1 is away: %2", parameterList.value(1), trailing), messageTags ); } } break; } case RPL_INVITING: { if (plHas(3)) { m_server->appendMessageToFrontmost(i18n("Invite"), i18n("You invited %1 to channel %2.", parameterList.value(1), parameterList.value(2)), messageTags ); } break; } //Sample WHOIS response //"/WHOIS psn" //[19:11] :zahn.freenode.net 311 PhantomsDad psn ~psn h106n2fls23o1068.bredband.comhem.se * :Peter Simonsson //[19:11] :zahn.freenode.net 319 PhantomsDad psn :#kde-devel #koffice //[19:11] :zahn.freenode.net 312 PhantomsDad psn irc.freenode.net :http://freenode.net/ //[19:11] :zahn.freenode.net 301 PhantomsDad psn :away //[19:11] :zahn.freenode.net 320 PhantomsDad psn :is an identified user //[19:11] :zahn.freenode.net 317 PhantomsDad psn 4921 1074973024 :seconds idle, signon time //[19:11] :zahn.freenode.net 318 PhantomsDad psn :End of /WHOIS list. case RPL_WHOISUSER: { if (plHas(4)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); if (nickInfo) { nickInfo->setHostmask(i18n("%1@%2", parameterList.value(2), parameterList.value(3))); nickInfo->setRealName(trailing); } // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { // escape html tags QString escapedRealName(trailing); m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is %2@%3 (%4)", parameterList.value(1), parameterList.value(2), parameterList.value(3), escapedRealName), messageTags, false); // Don't parse any urls } else { // This WHOIS was requested by Server for DNS resolve purposes; try to resolve the host if (getAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1)) != 0) { QHostInfo resolved = QHostInfo::fromName(parameterList.value(3)); if (resolved.error() == QHostInfo::NoError && !resolved.addresses().isEmpty()) { QString ip = resolved.addresses().first().toString(); m_server->appendMessageToFrontmost(i18n("DNS"), i18n("Resolved %1 (%2) to address: %3", parameterList.value(1), parameterList.value(3), ip), messageTags ); } else { m_server->appendMessageToFrontmost(i18n("Error"), i18n("Unable to resolve address for %1 (%2)", parameterList.value(1), parameterList.value(3)), messageTags ); } // Clear this from the automaticRequest list so it works repeatedly setAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1), false); } } } break; } // From a WHOIS. //[19:11] :zahn.freenode.net 320 PhantomsDad psn :is an identified user case RPL_WHOISIDENTIFY: case RPL_IDENTIFIED: { if (plHas(2)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); if (nickInfo) { nickInfo->setIdentified(true); } if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { // Prints "psn is an identified user" //server->appendStatusMessage(i18n("Whois"),parameterList.join(" ").section(' ',1)+' '+trailing); // The above line works fine, but can't be i18n'ised. So use the below instead.. I hope this is okay. m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is an identified user.", parameterList.value(1)), messageTags); } } break; } case RPL_WHOISSECURE: { if (plHas(2)) { if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is using a secure connection.", parameterList.value(1)), messageTags); } break; } // Sample WHO response //"/WHO #lounge" //[21:39] [352] #lounge jasmine bots.worldforge.org irc.worldforge.org jasmine H 0 jasmine //[21:39] [352] #lounge ~Nottingha worldforge.org irc.worldforge.org SherwoodSpirit H 0 Arboreal Entity case RPL_WHOSPCRPL: case RPL_WHOREPLY: { if (plHas(6)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(5)); // G=away G@=away,op G+=away,voice bool bAway = parameterList.value(6).toUpper().startsWith(QLatin1Char('G')); QString realName = trailing; if (realName.indexOf (QRegExp(QStringLiteral("\\d\\s"))) == 0) realName = realName.mid (2); if (nickInfo) { nickInfo->setHostmask(i18n("%1@%2", parameterList.value(2), parameterList.value(3))); nickInfo->setRealName(realName); nickInfo->setAway(bAway); if(!bAway) { nickInfo->setAwayMessage(QString()); } if(m_server->capabilities() & Server::WHOX && m_server->capabilities() & Server::ExtendedJoin) { nickInfo->setAccount(parameterList.value(8)); } } // Display message only if this was not an automatic request. if (!m_whoRequestList.isEmpty()) // for safe { if (getAutomaticRequest(QStringLiteral("WHO"),m_whoRequestList.front())==0) { m_server->appendMessageToFrontmost(i18n("Who"), i18n("%1 is %2@%3 (%4)%5", parameterList.value(5), parameterList.value(2), parameterList.value(3), realName, bAway?i18n(" (Away)"):QString()), messageTags, false); // Don't parse as url } } } break; } case RPL_ENDOFWHO: { if (plHas(2)) { if (!m_whoRequestList.isEmpty()) { const QString param = parameterList.value(1).toLower(); // for safety const int idx = m_whoRequestList.indexOf(param); if (idx > -1) { if (getAutomaticRequest(QStringLiteral("WHO"), param) == 0) { m_server->appendMessageToFrontmost(i18n("Who"), i18n("End of /WHO list for %1", parameterList.value(1)), messageTags); } else { setAutomaticRequest(QStringLiteral("WHO"), param, false); } m_whoRequestList.removeAt(idx); } else { // whoRequestList seems to be broken. qDebug() << "RPL_ENDOFWHO: malformed ENDOFWHO. retrieved: " << parameterList.value(1) << " expected: " << m_whoRequestList.front(); m_whoRequestList.clear(); } } else { qDebug() << "RPL_ENDOFWHO: unexpected ENDOFWHO. retrieved: " << parameterList.value(1); } emit endOfWho(parameterList.value(1)); } break; } case RPL_WHOISCHANNELS: { if (plHas(3)) { QStringList userChannels,voiceChannels,opChannels,halfopChannels,ownerChannels,adminChannels; // get a list of all channels the user is in QStringList channelList=trailing.split(QLatin1Char(' '), QString::SkipEmptyParts); channelList.sort(); // split up the list in channels where they are operator / user / voice for (int index=0; index < channelList.count(); index++) { QString lookChannel=channelList[index]; if (lookChannel.startsWith(QLatin1Char('*')) || lookChannel.startsWith(QLatin1Char('&'))) { adminChannels.append(lookChannel.mid(1)); m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 16); } // See bug #97354 part 2 else if((lookChannel.startsWith(QLatin1Char('!')) || lookChannel.startsWith(QLatin1Char('~'))) && m_server->isAChannel(lookChannel.mid(1))) { ownerChannels.append(lookChannel.mid(1)); m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 8); } // See bug #97354 part 1 else if (lookChannel.startsWith(QStringLiteral("@+"))) { opChannels.append(lookChannel.mid(2)); m_server->setChannelNick(lookChannel.mid(2), parameterList.value(1), 4); } else if (lookChannel.startsWith(QLatin1Char('@'))) { opChannels.append(lookChannel.mid(1)); m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 4); } else if (lookChannel.startsWith(QLatin1Char('%'))) { halfopChannels.append(lookChannel.mid(1)); m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 2); } else if (lookChannel.startsWith(QLatin1Char('+'))) { voiceChannels.append(lookChannel.mid(1)); m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 1); } else { userChannels.append(lookChannel); m_server->setChannelNick(lookChannel, parameterList.value(1), 0); } } // endfor // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { if (userChannels.count()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is a user on channels: %2", parameterList.value(1), userChannels.join(QStringLiteral(" "))), messageTags ); } if (voiceChannels.count()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 has voice on channels: %2", parameterList.value(1), voiceChannels.join(QStringLiteral(" "))), messageTags ); } if (halfopChannels.count()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is a halfop on channels: %2", parameterList.value(1), halfopChannels.join(QStringLiteral(" "))), messageTags ); } if (opChannels.count()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is an operator on channels: %2", parameterList.value(1), opChannels.join(QStringLiteral(" "))), messageTags ); } if (ownerChannels.count()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is owner of channels: %2", parameterList.value(1), ownerChannels.join(QStringLiteral(" "))), messageTags ); } if (adminChannels.count()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is admin on channels: %2", parameterList.value(1), adminChannels.join(QStringLiteral(" "))), messageTags ); } } } break; } case RPL_WHOISSERVER: { if (plHas(4)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); if (nickInfo) { nickInfo->setNetServer(parameterList.value(2)); nickInfo->setNetServerInfo(trailing); // Clear the away state on assumption that if nick is away, this message will be followed // by a 301 RPL_AWAY message. Not necessary a invalid assumption, but what can we do? nickInfo->setAway(false); nickInfo->setAwayMessage(QString()); } // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is online via %2 (%3).", parameterList.value(1), parameterList.value(2), trailing), messageTags ); } } break; } case RPL_WHOISHELPER: { if (plHas(2)) { // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is available for help.", parameterList.value(1)), messageTags ); } } break; } case RPL_WHOISOPERATOR: { if (plHas(2)) { // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { if (trailing.toLower().simplified().startsWith(QLatin1String("is an irc operator"))) m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is an IRC Operator.", parameterList.value(1)), messageTags); else m_server->appendMessageToFrontmost(i18n("Whois"), QString(QStringLiteral("%1 %2")).arg(parameterList.value(1)).arg(trailing), messageTags); } } break; } case RPL_WHOISIDLE: { if (plHas(3)) { // get idle time in seconds bool ok = false; long seconds = parameterList.value(2).toLong(&ok); if (!ok) break; long minutes = seconds/60; long hours = minutes/60; long days = hours/24; QDateTime signonTime; uint signonTimestamp = parameterList.value(3).toUInt(&ok); if (ok && parameterList.count() == 5) signonTime.setTime_t(signonTimestamp); if (!signonTime.isNull()) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); if (nickInfo) nickInfo->setOnlineSince(signonTime); } // if idle time is longer than a day // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { if (days) { const QString daysString = i18np("1 day", "%1 days", days); const QString hoursString = i18np("1 hour", "%1 hours", (hours % 24)); const QString minutesString = i18np("1 minute", "%1 minutes", (minutes % 60)); const QString secondsString = i18np("1 second", "%1 seconds", (seconds % 60)); m_server->appendMessageToFrontmost(i18n("Whois"), i18nc("%1 = name of person, %2 = (x days), %3 = (x hours), %4 = (x minutes), %5 = (x seconds)", "%1 has been idle for %2, %3, %4, and %5.", parameterList.value(1), daysString, hoursString, minutesString, secondsString), messageTags); // or longer than an hour } else if (hours) { const QString hoursString = i18np("1 hour", "%1 hours", hours); const QString minutesString = i18np("1 minute", "%1 minutes", (minutes % 60)); const QString secondsString = i18np("1 second", "%1 seconds", (seconds % 60)); m_server->appendMessageToFrontmost(i18n("Whois"), i18nc("%1 = name of person, %2 = (x hours), %3 = (x minutes), %4 = (x seconds)", "%1 has been idle for %2, %3, and %4.", parameterList.value(1), hoursString, minutesString, secondsString), messageTags); // or longer than a minute } else if (minutes) { const QString minutesString = i18np("1 minute", "%1 minutes", minutes); const QString secondsString = i18np("1 second", "%1 seconds", (seconds % 60)); m_server->appendMessageToFrontmost(i18n("Whois"), i18nc("%1 = name of person, %2 = (x minutes), %3 = (x seconds)", "%1 has been idle for %2 and %3.", parameterList.value(1), minutesString, secondsString), messageTags); // or just some seconds } else { m_server->appendMessageToFrontmost(i18n("Whois"), i18np("%2 has been idle for 1 second.", "%2 has been idle for %1 seconds.", seconds, parameterList.value(1)), messageTags); } if (!signonTime.isNull()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 has been online since %2.", parameterList.value(1), QLocale().toString(signonTime, QLocale::ShortFormat)), messageTags); } } } break; } case RPL_ENDOFWHOIS: { if (plHas(2)) { /*FIXME why is the nickinfo line below commented out? //NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); */ // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("End of WHOIS list."), messageTags); } // was this an automatic request? if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) != 0) { setAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1), false); } } break; } case RPL_USERHOST: { if (plHas(2)) { // iterate over all nick/masks in reply QStringList uhosts=trailing.split(QLatin1Char(' '), QString::SkipEmptyParts); for (int index=0;indexappendMessageToFrontmost(i18n("Userhost"), i18nc("%1 = nick, %2 = shows if nick is op, %3 = hostmask, %4 = shows away", "%1%2 is %3%4.", nick, (ircOp) ? i18n(" (IRC Operator)") : QString() ,mask, (away) ? i18n(" (away)") : QString()), messageTags); } // was this an automatic request? if (getAutomaticRequest(QStringLiteral("USERHOST"),nick)!=0) { setAutomaticRequest(QStringLiteral("USERHOST"),nick,false); } } // for } break; } case RPL_LISTSTART: //FIXME This reply is obsolete!!! { if (plHas(0)) { if (getAutomaticRequest(QStringLiteral("LIST"),QString())==0) { m_server->appendMessageToFrontmost(i18n("List"), i18n("List of channels:"), messageTags); } } break; } case RPL_LIST: { if (plHas(3)) { if (getAutomaticRequest(QStringLiteral("LIST"),QString())==0) { QString message; message=i18np("%2 (%1 user): %3", "%2 (%1 users): %3", parameterList.value(2).toInt(), parameterList.value(1), trailing); m_server->appendMessageToFrontmost(i18n("List"), message, messageTags); } else // send them to /LIST window { emit addToChannelList(parameterList.value(1), parameterList.value(2).toInt(), trailing); } } break; } case RPL_LISTEND: { if (plHas(0)) { // was this an automatic request? if (getAutomaticRequest(QStringLiteral("LIST"),QString())==0) { m_server->appendMessageToFrontmost(i18n("List"), i18n("End of channel list."), messageTags); } else { emit endOfChannelList(); setAutomaticRequest(QStringLiteral("LIST"),QString(),false); } } break; } case RPL_NOWAWAY: { if (plHas(1)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(0)); if (nickInfo) { nickInfo->setAway(true); } m_server->setAway(true, messageTags); } break; } case RPL_UNAWAY: { if (plHas(1)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(0)); if (nickInfo) { nickInfo->setAway(false); nickInfo->setAwayMessage(QString()); } m_server->setAway(false, messageTags); } break; } case RPL_BANLIST: { //:calvino.freenode.net 367 argonel #konversation fooish!~a@example.com argonel!argkde4@konversation/developer/argonel 1269464382 if (plHas(3)) { if (getAutomaticRequest(QStringLiteral("BANLIST"), parameterList.value(1))) { m_server->addBan(parameterList.value(1), parameterList.join(QStringLiteral(" ")).section(QLatin1Char(' '), 2, 4)); //<-- QString::Section handles out of bounds end parameter } else { QDateTime when; if (plHas(5)) when.setTime_t(parameterList.value(4).toUInt()); else when = QDateTime::currentDateTime(); //use todays date instead of Jan 1 1970 QString setter(parameterList.value(3, i18nc("The server didn't respond with the identity of the ban creator, so we say unknown (in brackets to avoid confusion with a real nickname)", "(unknown)")).section(QLatin1Char('!'), 0, 0)); m_server->appendMessageToFrontmost(i18n("BanList:%1", parameterList.value(1)), i18nc("BanList message: e.g. *!*@aol.com set by MrGrim on ", "%1 set by %2 on %3", parameterList.value(2), setter, QLocale().toString(when, QLocale::ShortFormat)), messageTags ); } } break; } case RPL_ENDOFBANLIST: { if (plHas(2)) { if (getAutomaticRequest(QStringLiteral("BANLIST"), parameterList.value(1))) { setAutomaticRequest(QStringLiteral("BANLIST"), parameterList.value(1), false); } else { m_server->appendMessageToFrontmost(i18n("BanList:%1", parameterList.value(1)), i18n("End of Ban List."), messageTags); } } break; } case ERR_NOCHANMODES: { if (plHas(3)) { ChatWindow *chatwindow = m_server->getChannelByName(parameterList.value(1)); if (chatwindow) { chatwindow->appendServerMessage(i18n("Channel"), trailing, messageTags); } else // We couldn't join the channel , so print the error. with [#channel] : { m_server->appendMessageToFrontmost(i18n("Channel"), trailing, messageTags); } } break; } case ERR_NOSUCHSERVER: { if (plHas(2)) { //Some servers don't know their name, so they return an error instead of the PING data if (getLagMeasuring() && trailing.startsWith(prefix)) { m_server->pongReceived(); } else if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) != 0) //Inhibit message if this was an automatic request { setAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1), false); } else { m_server->appendMessageToFrontmost(i18n("Error"), i18n("No such server: %1.", parameterList.value(1)), messageTags); } } break; } case ERR_UNAVAILRESOURCE: { if (plHas(2)) { if (m_server->isConnected()) m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1 is currently unavailable.", parameterList.value(1)), messageTags); else { QString newNick = m_server->getNextNickname(); // The user chose to disconnect if (newNick.isNull()) m_server->disconnectServer(); else { m_server->obtainNickInfo(m_server->getNickname()) ; m_server->renameNick(m_server->getNickname(), newNick, messageTags); m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Nickname %1 is unavailable. Trying %2.", parameterList.value(1), newNick), messageTags); m_server->queue(QStringLiteral("NICK ")+newNick); } } } break; } case RPL_HIGHCONNECTCOUNT: case RPL_LUSERCLIENT: case RPL_LUSEROP: case RPL_LUSERUNKNOWN: case RPL_LUSERCHANNELS: case RPL_LUSERME: { // TODO make sure this works, i amputated the "+ ' '+trailing" if (plHas(0)) { m_server->appendStatusMessage(i18n("Users"), parameterList.join(QStringLiteral(" ")).section(QLatin1Char(' '),1), messageTags); } break; } case ERR_UNKNOWNCOMMAND: { if (plHas(2)) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: Unknown command.", parameterList.value(1)), messageTags); } break; } case ERR_NOTREGISTERED: { if (plHas(0)) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("Not registered."), messageTags); } break; } case ERR_NEEDMOREPARAMS: { if (plHas(2)) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: This command requires more parameters.", parameterList.value(1)), messageTags); } break; } case RPL_CAPAB: // Special freenode reply afaik { if (plHas(2)) { // Disable as we don't use this for anything yet if (trailing.contains(QStringLiteral("IDENTIFY-MSG"))) { m_server->enableIdentifyMsg(true); } else // TEST is this right? split the logic up in prep for slotization { m_server->appendMessageToFrontmost(QString::number(command), parameterList.join(QStringLiteral(" ")).section(QLatin1Char(' '),1) + QLatin1Char(' ')+trailing, messageTags); } } break; } case ERR_BADCHANNELKEY: { if (plHas(2)) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("Cannot join %1: The channel is password-protected and either a wrong or no password was given.", parameterList.value(1)), messageTags); } break; } case RPL_LOGGEDIN: { if (plHas(3)) m_server->appendStatusMessage(i18n("Info"), i18n("You are now logged in as %1.", parameterList.value(2)), messageTags); break; } case RPL_SASLSUCCESS: { if (plHas(2)) { m_server->appendStatusMessage(i18n("Info"), i18n("SASL authentication successful."), messageTags); m_server->capEndNegotiation(); NickInfoPtr nickInfo = m_server->getNickInfo(m_server->getNickname()); if (nickInfo) nickInfo->setIdentified(true); } break; } case ERR_SASLFAIL: { if (plHas(2)) { m_server->appendStatusMessage(i18n("Error"), i18n("SASL authentication attempt failed."), messageTags); m_server->capEndNegotiation(); } break; } case ERR_SASLABORTED: { if (plHas(2)) m_server->appendStatusMessage(i18n("Info"), i18n("SASL authentication aborted."), messageTags); break; } default: { // All yet unknown messages go into the frontmost window without the // preceding nickname qDebug() << "unknown numeric" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); m_server->appendMessageToFrontmost(QString::number(command), parameterList.join(QStringLiteral(" ")), messageTags); } } // end of numeric switch if (!_plHad) qDebug() << "numeric format error" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); } // kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on; // vim: set et sw=4 ts=4 cino=l1,cs,U1: diff --git a/src/irc/invitedialog.cpp b/src/irc/invitedialog.cpp index 24823166..1457a9b6 100644 --- a/src/irc/invitedialog.cpp +++ b/src/irc/invitedialog.cpp @@ -1,222 +1,222 @@ // Copyright 2009 Peter Simonsson // // 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 or any later version // accepted by the membership of KDE e.V. (or its successor approved // by the membership of KDE e.V.), which shall act as a proxy // defined in Section 14 of version 3 of the license. // // 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, see . #include "invitedialog.h" #include #include #include #include #include #include #include #include InviteDialog::InviteDialog(QWidget* parent) : QDialog(parent), Ui::InviteDialog() { setAttribute(Qt::WA_DeleteOnClose); setWindowTitle(i18n("Channel Invites")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &InviteDialog::slotOk); connect(buttonBox, &QDialogButtonBox::rejected, this, &InviteDialog::reject); QWidget* mainWidget = new QWidget(this); mainLayout->addWidget(mainWidget); mainLayout->addWidget(buttonBox); setupUi(mainWidget); m_iconLabel->setPixmap(QIcon::fromTheme(QStringLiteral("irc-join-channel")).pixmap(48)); m_channelModel = new InviteChannelListModel(m_channelView); m_channelView->setModel(m_channelModel); m_channelView->setRootIsDecorated(false); m_channelView->setUniformRowHeights(true); } void InviteDialog::addInvite(const QString& nickname, const QString& channel) { m_channelModel->addInvite(nickname, channel); m_channelView->resizeColumnToContents(0); } void InviteDialog::slotOk() { QString channels = m_channelModel->selectedChannels(); if(!channels.isEmpty()) emit joinChannelsRequested(channels); KConfigGroup::WriteConfigFlags flags = KConfig::Persistent; KConfigGroup cg(KSharedConfig::openConfig().data(), "Notification Messages"); cg.writeEntry("Invitation", m_joinPreferences->currentIndex(), flags); cg.sync(); accept(); } bool InviteDialog::shouldBeShown(QDialogButtonBox::StandardButton& buttonCode) { KConfigGroup cg(KSharedConfig::openConfig().data(), "Notification Messages"); cg.sync(); const QString dontAsk = cg.readEntry("Invitation", QString()).toLower(); if (dontAsk == QStringLiteral("1")) { buttonCode = QDialogButtonBox::Ok; return false; } else if (dontAsk == QStringLiteral("2")) { buttonCode = QDialogButtonBox::Cancel; return false; } return true; } // // InviteChannelListModel // InviteChannelListModel::InviteChannelListModel(QObject* parent) : QAbstractListModel(parent) { } void InviteChannelListModel::addInvite(const QString& nickname, const QString& channel) { if(m_channelMap.contains(channel)) { if(!m_channelMap[channel].nicknames.contains(nickname)) { m_channelMap[channel].nicknames += QStringLiteral(", ") + nickname; } } else { ChannelItem item; item.channel = channel; item.nicknames = nickname; item.checkState = Qt::Unchecked; m_channelMap.insert(channel, item); } beginResetModel(); endResetModel(); } int InviteChannelListModel::rowCount(const QModelIndex&) const { return m_channelMap.count(); } int InviteChannelListModel::columnCount(const QModelIndex&) const { return 3; } QVariant InviteChannelListModel::data(const QModelIndex& index, int role) const { if(!index.isValid()) return QVariant(); if(role == Qt::DisplayRole) { switch(index.column()) { case 1: return m_channelMap.values().at(index.row()).channel; case 2: return m_channelMap.values().at(index.row()).nicknames; default: return QVariant(); } } if(role == Qt::SizeHintRole) { - return QSize(0, qMax(qApp->style()->sizeFromContents(QStyle::CT_CheckBox, 0, QSize(0, 0), 0).height(), + return QSize(0, qMax(qApp->style()->sizeFromContents(QStyle::CT_CheckBox, nullptr, QSize(0, 0), nullptr).height(), qApp->fontMetrics().height())); } else if((role == Qt::CheckStateRole) && (index.column() == 0)) { return m_channelMap.values().at(index.row()).checkState; } return QVariant(); } QVariant InviteChannelListModel::headerData(int section, Qt::Orientation orientation, int role) const { if(role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); switch(section) { case 0: return i18n("Join"); case 1: return i18n("Channel"); case 2: return i18n("Nickname"); default: return QVariant(); } } Qt::ItemFlags InviteChannelListModel::flags(const QModelIndex& index) const { if(!index.isValid() || index.column() > 0) return Qt::ItemIsEnabled; else return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled; } QString InviteChannelListModel::selectedChannels() const { QStringList channels; foreach(const ChannelItem& item, m_channelMap) { if(item.checkState == Qt::Checked) channels.append(item.channel); } return channels.join(QStringLiteral(",")); } bool InviteChannelListModel::setData(const QModelIndex& index, const QVariant& value, int role) { if(!index.isValid() || !value.isValid() || index.column() > 0 || role != Qt::CheckStateRole) return false; QString channel = m_channelMap.values().at(index.row()).channel; m_channelMap[channel].checkState = (value.toBool() ? Qt::Checked : Qt::Unchecked); emit dataChanged(index, index); return true; } diff --git a/src/irc/nicklistview.cpp b/src/irc/nicklistview.cpp index d531a618..a51d8f09 100644 --- a/src/irc/nicklistview.cpp +++ b/src/irc/nicklistview.cpp @@ -1,321 +1,321 @@ /* 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) any later version. */ /* This is the class that shows the channel nick list begin: Fre Jun 7 2002 copyright: (C) 2002 by Dario Abatianni email: eisfuchs@tigress.com */ #include "nicklistview.h" #include "nick.h" #include "application.h" #include "images.h" #include "irccontextmenus.h" #include #include #include #include #include #include class NickItemDelegate : public QStyledItemDelegate { public: - NickItemDelegate(QObject *parent = 0); + NickItemDelegate(QObject *parent = nullptr); QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const Q_DECL_OVERRIDE; }; NickItemDelegate::NickItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { } QSize NickItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { const QSize& size = QStyledItemDelegate::sizeHint(option, index); return QSize(size.width(), qMax(NickListView::getMinimumRowHeight(), size.height())); } int NickListView::s_minimumRowHeight = 0; NickListView::NickListView(QWidget* parent, Channel *chan) : QTreeWidget(parent) { setWhatsThis(); channel=chan; // Enable Drag & Drop viewport()->setAcceptDrops(true); setDropIndicatorShown(false); setDragDropMode(QAbstractItemView::DropOnly); // Make the minimum height of nicklist items the height of their // icons plus two pixels. setUniformRowHeights(true); QAbstractItemDelegate *prevDelegate = itemDelegate(); setItemDelegate(new NickItemDelegate(this)); delete prevDelegate; updateMinimumRowHeight(); // General layout setRootIsDecorated(false); // single level view setColumnCount(2); setSelectionBehavior(QAbstractItemView::SelectRows); setSelectionMode(QAbstractItemView::ExtendedSelection); setAllColumnsShowFocus(true); // These two below must be called after setColumnCount(). header()->setSortIndicator(Nick::NicknameColumn, Qt::AscendingOrder); setSortingEnabled(true); header()->hide(); header()->setStretchLastSection(false); } NickListView::~NickListView() { } int NickListView::getMinimumRowHeight() { return s_minimumRowHeight; } void NickListView::updateMinimumRowHeight() { Images* images = Application::instance()->images(); s_minimumRowHeight = images->getNickIcon(Images::Normal, false).height() + 2; } bool NickListView::event(QEvent *event) { if(( event->type() == QEvent::ToolTip ) ) { QHelpEvent* helpEvent = static_cast( event ); QTreeWidgetItem *item = itemAt(viewport()->mapFromParent(helpEvent->pos())); if( item ) { Nick *nick = dynamic_cast( item ); if( nick ) { QString text = Konversation::removeIrcMarkup(nick->getChannelNick()->tooltip()); if( !text.isEmpty() ) QToolTip::showText( helpEvent->globalPos(), text, this ); else QToolTip::hideText(); } } else QToolTip::hideText(); } return QTreeWidget::event( event ); } // Make this public void NickListView::executeDelayedItemsLayout() { QTreeWidget::executeDelayedItemsLayout(); } void NickListView::setWhatsThis() { Images* images = Application::instance()->images(); if(!images->getNickIcon( Images::Normal, false).isNull()) { QTreeWidget::setWhatsThis(i18n("

This shows all the people in the channel. The nick for each person is shown, with a picture showing their status.

" "" "" "" "" "" "" "" "" "
This person has administrator privileges.
This person is a channel owner.
This person is a channel operator.
This person is a channel half-operator.
This person has voice, and can therefore talk in a moderated channel.
This person does not have any special privileges.
This, overlaid on any of the above, indicates that this person is currently away.

" "The meaning of admin, owner and halfop varies between different IRC servers.

" "Hovering over any nick shows their current status. See the Konversation Handbook for more information." "

", images->getNickIconPath(Images::Admin), images->getNickIconPath(Images::Owner), images->getNickIconPath(Images::Op), images->getNickIconPath(Images::HalfOp), images->getNickIconPath(Images::Voice), images->getNickIconPath(Images::Normal), images->getNickIconAwayPath())); } } void NickListView::refresh() { updateMinimumRowHeight(); QTreeWidgetItemIterator it(this); while (*it) { static_cast(*it)->refresh(); ++it; } setWhatsThis(); } void NickListView::setSortingEnabled(bool enable) { QTreeWidget::setSortingEnabled(enable); // We want to decouple header and this object with regard to sorting // (for performance reasons). By default there is no way to reenable // sorting without full resort (which is necessary for us) since both // header indicator change and setSortingEnabled(false/true) trigger // full resort of QTreeView. However, after disconnect, it is possible // to use header()->setSortIndicator() for this. // This could probably be done in a better way if nick list was model // based. NOTE: QTreeView::sortByColumn() won't work after this change // if sorting is enabled. if (enable && header()) { disconnect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), - this, 0); + this, nullptr); } } void NickListView::fastSetSortingEnabled(bool value) { if (value) { int sortCol = header()->sortIndicatorSection(); if (sortCol > -1) { header()->setSortIndicator(-1, header()->sortIndicatorOrder()); // since indicator section is -1, this basically sets the flag only // while setSortIndicator() is decoupled from triggerring resort. setSortingEnabled(true); // NOTE:: ResizeToContents mode on sortCol would be performance killer header()->setSortIndicator(sortCol, header()->sortIndicatorOrder()); } } else { setSortingEnabled(false); } } void NickListView::sortByColumn(int column, Qt::SortOrder order) { if (isSortingEnabled()) { // original implementation relies on sortIndicatorChanged signal model()->sort(column, order); } else QTreeWidget::sortByColumn(column, order); } void NickListView::resort() { sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder()); } int NickListView::findLowerBound(const QTreeWidgetItem& item) const { int start = 0, end = topLevelItemCount(); int mid; while (start < end) { mid = start + (end-start)/2; if ((*topLevelItem(mid)) < item) start = mid + 1; else end = mid; } return start; } void NickListView::contextMenuEvent(QContextMenuEvent* ev) { if (selectedItems().count()) { IrcContextMenus::nickMenu(ev->globalPos(), IrcContextMenus::ShowChannelActions, channel->getServer(), channel->getSelectedNickList(), channel->getName()); } } QStringList NickListView::mimeTypes () const { return KUrlMimeData::mimeDataTypes(); } bool NickListView::canDecodeMime(QDropEvent const *event) const { // Verify if the URL is not irc:// if (event->mimeData()->hasUrls()) { const QList uris = KUrlMimeData::urlsFromMimeData(event->mimeData()); if (!uris.isEmpty()) { const QUrl first = uris.first(); if (first.scheme() == QLatin1String("irc") || first.scheme() == QLatin1String("ircs") || channel->getNickList().containsNick(first.url())) { return false; } } return true; } return false; } void NickListView::dragEnterEvent(QDragEnterEvent *event) { if (canDecodeMime(event)) { QTreeWidget::dragEnterEvent(event); return; } else { event->ignore(); } } void NickListView::dragMoveEvent(QDragMoveEvent *event) { QTreeWidget::dragMoveEvent(event); if (!indexAt(event->pos()).isValid()) { event->ignore(); return; } } bool NickListView::dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action) { Q_UNUSED(index); Q_UNUSED(action); Nick* nick = dynamic_cast(parent); if (nick) { const QList uris = KUrlMimeData::urlsFromMimeData(data); channel->getServer()->sendURIs(uris, nick->getChannelNick()->getNickname()); return true; } return false; } diff --git a/src/irc/nicksonline.cpp b/src/irc/nicksonline.cpp index e23deab4..e0f67ce3 100644 --- a/src/irc/nicksonline.cpp +++ b/src/irc/nicksonline.cpp @@ -1,868 +1,868 @@ // -*- mode: c++; c-file-style: "bsd"; c-basic-offset: 4; tabs-width: 4; indent-tabs-mode: nil -*- /* 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) any later version. */ /* shows a user tree of friends per server begin: Sam Aug 31 2002 copyright: (C) 2002 by Dario Abatianni email: eisfuchs@tigress.com */ #include "nicksonline.h" #include #include "channel.h" #include "server.h" #include "application.h" #include "connectionmanager.h" #include "editnotifydialog.h" #include "images.h" #include "query.h" #include "mainwindow.h" #include "viewcontainer.h" #include "nicksonlineitem.h" #include #include #include #include #include NicksOnline::NicksOnline(QWidget* parent): ChatWindow(parent) { setName(i18n("Watched Nicks Online")); setType(ChatWindow::NicksOnline); setSpacing(0); m_toolBar = new KToolBar(this, true, true); m_addNickname = m_toolBar->addAction(QIcon::fromTheme(QStringLiteral("list-add-user")), i18n("&Add Nickname...")); m_addNickname->setWhatsThis(i18n("Click to add a new nick to the list of nicknames that appear on this screen.")); m_removeNickname = m_toolBar->addAction(QIcon::fromTheme(QStringLiteral("list-remove-user")), i18n("&Remove Nickname")); m_removeNickname->setWhatsThis(i18n("Click to remove a nick from the list of nicknames that appear on this screen.")); m_toolBar->addSeparator(); m_whois = m_toolBar->addAction(QIcon::fromTheme(QStringLiteral("office-address-book")), i18n("&Whois")); m_openQuery = m_toolBar->addAction(QIcon::fromTheme(QStringLiteral("office-address-book")), i18n("Open &Query")); m_toolBar->addSeparator(); m_joinChannel = m_toolBar->addAction(QIcon::fromTheme(QStringLiteral("irc-join-channel")), i18n("&Join Channel")); connect(m_toolBar, &KToolBar::actionTriggered, this, &NicksOnline::slotPopupMenu_Activated); m_nickListView=new QTreeWidget(this); // Set to false every 8 seconds to permit a whois on watched nicks lacking information. // Remove when server does this automatically. m_whoisRequested = true; m_onlineIcon = QIcon::fromTheme(QStringLiteral("im-user")); m_offlineIcon = QIcon::fromTheme(QStringLiteral("im-user-offline")); m_nickListView->setColumnCount(2); m_nickListView->headerItem()->setText(0, i18n("Network/Nickname/Channel")); m_nickListView->headerItem()->setText(1, i18n("Additional Information")); m_nickListView->setRootIsDecorated(true); m_nickListView->setSortingEnabled(true); Preferences::restoreColumnState(m_nickListView, QStringLiteral("NicksOnline ViewSettings")); QString nickListViewWT = i18n( "

These are all the nicknames on your Nickname Watch list, listed under the " "server network they are connected to.

" "

The Additional Information column shows the information known " "for each nickname.

" "

The channels the nickname has joined are listed underneath each nickname.

" "

Nicknames appearing under Offline are not connected to any of the " "servers in the network.

" "

Right-click with the mouse on a nickname to perform additional functions.

"); m_nickListView->setWhatsThis(nickListViewWT); m_nickListView->viewport()->installEventFilter(this); connect(m_nickListView, &QTreeWidget::itemDoubleClicked, this, &NicksOnline::processDoubleClick); - setupToolbarActions(0); + setupToolbarActions(nullptr); // Create context menu. m_popupMenu = new QMenu(this); m_popupMenu->setObjectName(QStringLiteral("nicksonline_context_menu")); m_nickListView->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_nickListView, &QTreeWidget::customContextMenuRequested, this, &NicksOnline::slotCustomContextMenuRequested); connect(m_nickListView, &QTreeWidget::itemSelectionChanged, this, &NicksOnline::slotNickListView_SelectionChanged); // Display info for all currently-connected servers. refreshAllServerOnlineLists(); // Connect and start refresh timer. m_timer = new QTimer(this); m_timer->setObjectName(QStringLiteral("nicksOnlineTimer")); connect(m_timer, &QTimer::timeout, this, &NicksOnline::timerFired); // TODO: User preference for refresh interval. m_timer->start(8000); } NicksOnline::~NicksOnline() { Preferences::saveColumnState(m_nickListView, QStringLiteral("NicksOnline ViewSettings")); m_timer->stop(); delete m_timer; delete m_nickListView; } bool NicksOnline::eventFilter(QObject*obj, QEvent* event ) { if( ( obj == m_nickListView->viewport() ) && ( event->type() == QEvent::ToolTip ) ) { QHelpEvent* helpEvent = static_cast( event ); QTreeWidgetItem *item = m_nickListView->itemAt( helpEvent->pos() ); if( item ) { NickInfoPtr nickInfo = getNickInfo(item); if ( nickInfo ) { QString text = nickInfo->tooltip(); if( !text.isEmpty() ) QToolTip::showText( helpEvent->globalPos(), text ); else QToolTip::hideText(); } else QToolTip::hideText(); } else QToolTip::hideText(); } return ChatWindow::eventFilter( obj, event ); } QTreeWidget* NicksOnline::getNickListView() { return m_nickListView; } /** * Returns the named child of parent item in a NicksOnlineItem * @param parent Pointer to a NicksOnlineItem. * @param name The name in the desired child QListViewItem, must be in column 0. * @param type The type of entry to be found * @return Pointer to the child QListViewItem or 0 if not found. */ QTreeWidgetItem* NicksOnline::findItemChild(const QTreeWidgetItem* parent, const QString& name, NicksOnlineItem::NickListViewColumn type) { - if (!parent) return 0; + if (!parent) return nullptr; for (int i = 0; i < parent->childCount(); ++i) { QTreeWidgetItem* child = parent->child(i); if(static_cast(child)->type() == type && child->text(0) == name) return child; } - return 0; + return nullptr; } /** * Returns the first occurrence of a child item of a given type in a NicksOnlineItem * @param parent Pointer to a NicksOnlineItem. * @param type The type of entry to be found * @return Pointer to the child QListViewItem or 0 if not found. */ QTreeWidgetItem* NicksOnline::findItemType(const QTreeWidgetItem* parent, NicksOnlineItem::NickListViewColumn type) { - if (!parent) return 0; + if (!parent) return nullptr; for (int i = 0; i < parent->childCount(); ++i) { QTreeWidgetItem* child = parent->child(i); if(static_cast(child)->type() == type) return child; } - return 0; + return nullptr; } /** * Returns a pointer to the network QListViewItem with the given name. * @param name The name of the network. * @return Pointer to the QListViewItem or 0 if not found. */ QTreeWidgetItem* NicksOnline::findNetworkRoot(int serverGroupId) { for (int i = 0; i < m_nickListView->invisibleRootItem()->childCount(); ++i) { QTreeWidgetItem* child = m_nickListView->invisibleRootItem()->child(i); if (child->data(0, Qt::UserRole).toInt() == serverGroupId) return child; } - return 0; + return nullptr; } /** * Return a string containing formatted additional information about a nick. * @param nickInfo A pointer to NickInfo structure for the nick. May be Null. * @return A string formatted for display containing the information * about the nick. * @return needWhois True if a WHOIS needs to be performed on the nick * to get additional information. */ QString NicksOnline::getNickAdditionalInfo(NickInfoPtr nickInfo, bool& needWhois) { Q_UNUSED(needWhois) QString niInfo; if (nickInfo) { if (nickInfo->isAway()) { niInfo += i18n("Away"); if (!nickInfo->getAwayMessage().isEmpty()) niInfo += QStringLiteral(" (") + nickInfo->getAwayMessage() + QLatin1Char(')'); } if (!nickInfo->getHostmask().isEmpty()) niInfo += QLatin1Char(' ') + nickInfo->getHostmask(); if (!nickInfo->getRealName().isEmpty()) niInfo += QStringLiteral(" (") + nickInfo->getRealName() + QLatin1Char(')'); if (!nickInfo->getNetServer().isEmpty()) { niInfo += i18n( " online via %1", nickInfo->getNetServer() ); if (!nickInfo->getNetServerInfo().isEmpty()) niInfo += QStringLiteral(" (") + nickInfo->getNetServerInfo() + QLatin1Char(')'); } if (!nickInfo->getOnlineSince().isNull()) niInfo += i18n( " since %1", nickInfo->getPrettyOnlineSince() ); } return niInfo; } /** * Refresh the nicklistview for a single server. * @param server The server to be refreshed. */ void NicksOnline::updateServerOnlineList(Server* servr) { // Return if connection is an ephemeral one, because // we cant watch them anyway. if (!servr->getServerGroup()) return; bool newNetworkRoot = false; QString serverName = servr->getServerName(); QString networkName = servr->getDisplayName(); QTreeWidgetItem* networkRoot = findNetworkRoot(servr->getServerGroup()->id()); // If network is not in our list, add it. if (!networkRoot) { networkRoot = new NicksOnlineItem(NicksOnlineItem::NetworkRootItem,m_nickListView,networkName); networkRoot->setData(0, Qt::UserRole, servr->getServerGroup()->id()); newNetworkRoot = true; } // Store server name in hidden column. // Note that there could be more than one server in the network connected, // but it doesn't matter because all the servers in a network have the same // watch list. networkRoot->setText(nlvcServerName, serverName); // Update list of servers in the network that are connected. QStringList serverList = networkRoot->text(nlvcAdditionalInfo).split(QLatin1Char(','), QString::SkipEmptyParts); if (!serverList.contains(serverName)) serverList.append(serverName); networkRoot->setText(nlvcAdditionalInfo, serverList.join(QStringLiteral(","))); // Get watch list. QStringList watchList = servr->getWatchList(); QStringList::iterator itEnd = watchList.end(); QString nickname; for (QStringList::iterator it = watchList.begin(); it != itEnd; ++it) { nickname = (*it); NickInfoPtr nickInfo = getOnlineNickInfo(networkName, nickname); if (nickInfo && nickInfo->getPrintedOnline()) { // Nick is online. // Which server did NickInfo come from? Server* server=nickInfo->getServer(); // Construct additional information string for nick. bool needWhois = false; QString nickAdditionalInfo = getNickAdditionalInfo(nickInfo, needWhois); // Add to network if not already added. QTreeWidgetItem* nickRoot = findItemChild(networkRoot, nickname, NicksOnlineItem::NicknameItem); if (!nickRoot) nickRoot = new NicksOnlineItem(NicksOnlineItem::NicknameItem, networkRoot, nickname, nickAdditionalInfo); NicksOnlineItem* nickitem = static_cast(nickRoot); nickitem->setConnectionId(server->connectionId ()); // Mark nick as online nickitem->setOffline(false); // Update icon nickitem->setIcon(nlvcNick, m_onlineIcon); nickRoot->setText(nlvcAdditionalInfo, nickAdditionalInfo); nickRoot->setText(nlvcServerName, serverName); // If no additional info available, request a WHOIS on the nick. if (!m_whoisRequested) { if (needWhois) { requestWhois(networkName, nickname); m_whoisRequested = true; } } QStringList channelList = server->getNickChannels(nickname); QStringList::iterator itEnd2 = channelList.end(); for (QStringList::iterator it2 = channelList.begin(); it2 != itEnd2; ++it2) { // Known channels where nickname is online and mode in each channel. // FIXME: If user connects to multiple servers in same network, the // channel info will differ between the servers, resulting in inaccurate // mode and led info displayed. QString channelName = (*it2); ChannelNickPtr channelNick = server->getChannelNick(channelName, nickname); QString nickMode; if (channelNick->hasVoice()) nickMode = nickMode + i18n(" Voice"); if (channelNick->isHalfOp()) nickMode = nickMode + i18n(" HalfOp"); if (channelNick->isOp()) nickMode = nickMode + i18n(" Operator"); if (channelNick->isOwner()) nickMode = nickMode + i18n(" Owner"); if (channelNick->isAdmin()) nickMode = nickMode + i18n(" Admin"); QTreeWidgetItem* channelItem = findItemChild(nickRoot, channelName, NicksOnlineItem::ChannelItem); if (!channelItem) channelItem = new NicksOnlineItem(NicksOnlineItem::ChannelItem,nickRoot, channelName, nickMode); channelItem->setText(nlvcAdditionalInfo, nickMode); // Icon for mode of nick in each channel. Images::NickPrivilege nickPrivilege = Images::Normal; if (channelNick->hasVoice()) nickPrivilege = Images::Voice; if (channelNick->isHalfOp()) nickPrivilege = Images::HalfOp; if (channelNick->isOp()) nickPrivilege = Images::Op; if (channelNick->isOwner()) nickPrivilege = Images::Owner; if (channelNick->isAdmin()) nickPrivilege = Images::Admin; - if (server->getJoinedChannelMembers(channelName) != 0) + if (server->getJoinedChannelMembers(channelName) != nullptr) channelItem->setIcon(nlvcChannel, QIcon(Application::instance()->images()->getNickIcon(nickPrivilege, false))); else channelItem->setIcon(nlvcChannel, QIcon(Application::instance()->images()->getNickIcon(nickPrivilege, true))); } // Remove channel if nick no longer in it. for (int i = 0; i < nickRoot->childCount(); ++i) { QTreeWidgetItem* child = nickRoot->child(i); if (!channelList.contains(child->text(nlvcNick))) { delete nickRoot->takeChild(i); i--; } } } else { // Nick is offline. QTreeWidgetItem* nickRoot = findItemChild(networkRoot, nickname, NicksOnlineItem::NicknameItem); if (!nickRoot) nickRoot = new NicksOnlineItem(NicksOnlineItem::NicknameItem, networkRoot, nickname); // remove channels from the nick qDeleteAll(nickRoot->takeChildren()); NicksOnlineItem* nickitem = static_cast(nickRoot); nickitem->setConnectionId(servr->connectionId ()); // Mark nick as offline nickitem->setOffline (true); // Update icon nickitem->setIcon(nlvcNick, m_offlineIcon); nickRoot->setText(nlvcServerName, serverName); nickRoot->setText(nlvcAdditionalInfo, QString()); } } // Erase nicks no longer being watched. for (int i = 0; i < networkRoot->childCount(); ++i) { QTreeWidgetItem* item = networkRoot->child(i); QString nickname = item->text(nlvcNick); if (!watchList.contains(nickname) && serverName == item->text(nlvcServerName)) { delete networkRoot->takeChild(i); i--; } } // Expand server if newly added to list. if (newNetworkRoot) { networkRoot->setExpanded(true); // Connect server NickInfo updates. connect (servr, SIGNAL(nickInfoChanged(Server*,NickInfoPtr)), this, SLOT(slotNickInfoChanged(Server*,NickInfoPtr))); } } /** * Determines if a nick is online in any of the servers in a network and returns * a NickInfo if found, otherwise 0. * @param networkName Server network name. * @param nickname Nick name. * @return NickInfo if nick is online in any server, otherwise 0. */ NickInfoPtr NicksOnline::getOnlineNickInfo(QString& networkName, QString& nickname) { // Get list of pointers to all servers. Application* konvApp = Application::instance(); const QList serverList = konvApp->getConnectionManager()->getServerList(); foreach (Server* server, serverList) { if (server->getDisplayName() == networkName) { NickInfoPtr nickInfo = server->getNickInfo(nickname); if (nickInfo) return nickInfo; } } return NickInfoPtr(); //TODO FIXME NULL NULL NULL } /** * Requests a WHOIS for a specified server network and nickname. * The request is sent to the first server found in the network. * @param groupName Server group name. * @param nickname Nick name. */ void NicksOnline::requestWhois(QString& networkName, QString& nickname) { Application* konvApp = Application::instance(); const QList serverList = konvApp->getConnectionManager()->getServerList(); foreach (Server* server, serverList) { if (server->getDisplayName() == networkName) { server->requestWhois(nickname); return; } } } /** * Updates the notify list based on the current state of the tree */ void NicksOnline::updateNotifyList() { // notify list QMap notifyList; // fill in the notify list for (int i = 0; i < m_nickListView->topLevelItemCount(); ++i) { QTreeWidgetItem* networkRoot = m_nickListView->topLevelItem(i); // nick list for this network root QStringList nicks; for (int j = 0; j < networkRoot->childCount(); ++j) { NicksOnlineItem *item = dynamic_cast(networkRoot->child(j)); if (item->type() == NicksOnlineItem::NicknameItem) { // add the nick to the list nicks << item->text(nlvcNick); } } // insert nick list to the notify list notifyList.insert(networkRoot->data(0, Qt::UserRole).toInt(), nicks); } // update notify list Preferences::setNotifyList(notifyList); // save notify list Application::instance()->saveOptions(false); } /** * Refresh the nicklistview for all servers. */ void NicksOnline::refreshAllServerOnlineLists() { Application* konvApp = Application::instance(); const QList serverList = konvApp->getConnectionManager()->getServerList(); // Remove servers no longer connected. for (int i = 0; i < m_nickListView->invisibleRootItem()->childCount(); ++i) { QTreeWidgetItem *child = m_nickListView->invisibleRootItem()->child(i); QString networkName = child->text(nlvcNetwork); QStringList serverNameList = child->text(nlvcAdditionalInfo).split(QLatin1Char(','), QString::SkipEmptyParts); QStringList::Iterator itEnd = serverNameList.end(); QStringList::Iterator it = serverNameList.begin(); while (it != itEnd) { QString serverName = *it; // Locate server in server list. bool found = false; foreach (Server* server, serverList) { if ((server->getServerName() == serverName) && (server->getDisplayName() == networkName)) found = true; } if (!found) it = serverNameList.erase(it); else ++it; } // Remove Networks with no servers connected, otherwise update list of connected // servers. if (serverNameList.empty()) { delete m_nickListView->invisibleRootItem()->takeChild(i); i--; } else child->setText(nlvcAdditionalInfo, serverNameList.join(QStringLiteral(","))); } // Display info for all currently-connected servers. foreach (Server* server, serverList) { updateServerOnlineList(server); } // Refresh buttons. slotNickListView_SelectionChanged(); } void NicksOnline::timerFired() { // Allow one WHOIS request per cycle. m_whoisRequested = false; refreshAllServerOnlineLists(); } /** * When a user double-clicks a nickname in the nicklistview, let server know so that * it can perform the user's chosen default action for that. */ void NicksOnline::processDoubleClick(QTreeWidgetItem* item, int column) { Q_UNUSED(column); NicksOnlineItem* nickitem = dynamic_cast(item); if (!nickitem || nickitem->isOffline()) return; // Only emit signal when the user double clicked a nickname rather than // a server name or channel name. if (nickitem->type() == NicksOnlineItem::NicknameItem) emit doubleClicked(nickitem->connectionId(), nickitem->text(nlvcNick)); if (nickitem->type() == NicksOnlineItem::ChannelItem) { NicksOnlineItem* nickRoot = dynamic_cast(nickitem->parent()); Server* server = Application::instance()->getConnectionManager()->getServerByConnectionId(nickRoot->connectionId()); ChatWindow* channel = server->getChannelByName(nickitem->text(nlvcChannel)); if (channel) emit showView(channel); else { // Get the server object corresponding to the connection id. server->queue( QStringLiteral("JOIN ")+ nickitem->text(nlvcChannel) ); } } } /** * Returns the server name and nickname of the specified nicklistview item. * @param item The nicklistview item. * @return serverName Name of the server for the nick at the item, or Null if not a nick. * @return nickname The nickname at the item. */ bool NicksOnline::getItemServerAndNick(const QTreeWidgetItem* item, QString& serverName, QString& nickname) { if (!item) return false; // convert into NicksOnlineItem const NicksOnlineItem* nlItem=static_cast(item); // If on a network, return false; if (nlItem->type() == NicksOnlineItem::NetworkRootItem) return false; // get server name serverName = item->text(nlvcServerName); // If on a channel, move up to the nickname. if (nlItem->type() == NicksOnlineItem::ChannelItem) { item = item->parent(); serverName = item->text(nlvcServerName); } nickname = item->text(nlvcNick); return true; } NickInfoPtr NicksOnline::getNickInfo(const QTreeWidgetItem* item) { QString serverName; QString nickname; getItemServerAndNick(item, serverName, nickname); if (serverName.isEmpty() || nickname.isEmpty()) return NickInfoPtr(); //TODO FIXME NULL NULL NULL Server* server = Application::instance()->getConnectionManager()->getServerByName(serverName); if (server) return server->getNickInfo(nickname); return NickInfoPtr(); //TODO FIXME NULL NULL NULL } /** * Given a server name and nickname, returns the item in the Nick List View displaying * the nick. * @param serverName Name of server. * @param nickname Nick name. * @return Pointer to QListViewItem displaying the nick, or 0 if not found. * * @see getItemServerAndNick */ QTreeWidgetItem* NicksOnline::getServerAndNickItem(const QString& serverName, const QString& nickname) { Server* server = Application::instance()->getConnectionManager()->getServerByName(serverName); - if (!server) return 0; + if (!server) return nullptr; QString networkName = server->getDisplayName(); QList items = m_nickListView->findItems(networkName, Qt::MatchExactly | Qt::MatchCaseSensitive, nlvcNetwork); - if (items.count() == 0) return 0; + if (items.count() == 0) return nullptr; QTreeWidgetItem* networkRoot = items.at(0); - if (!networkRoot) return 0; + if (!networkRoot) return nullptr; QTreeWidgetItem* nickRoot = findItemChild(networkRoot, nickname, NicksOnlineItem::NicknameItem); return nickRoot; } /** * Perform a command. * @param id The command id. @ref CommandIDs. * * The operation is performed on the nickname at the currently-selected item in * the nicklistview. * * Also refreshes the nicklistview display to reflect the new state * for the nick. */ void NicksOnline::doCommand(QAction* id) { - if(id == 0) + if(id == nullptr) return; if ( id == m_addNickname ) { int serverGroupId = -1; if (m_nickListView->selectedItems().count()) { NicksOnlineItem *networkRoot = dynamic_cast(m_nickListView->selectedItems().at(0)); if (networkRoot) { while (networkRoot->type() != NicksOnlineItem::NetworkRootItem) networkRoot = dynamic_cast(networkRoot->parent()); serverGroupId = networkRoot->data(0, Qt::UserRole).toInt(); } } EditNotifyDialog *end = new EditNotifyDialog(this, serverGroupId); connect(end, &EditNotifyDialog::notifyChanged, this, &NicksOnline::slotAddNickname); end->show(); return; } QString serverName; QString nickname; if (m_nickListView->selectedItems().count() == 0) return; QTreeWidgetItem* item = m_nickListView->selectedItems().at(0); NicksOnlineItem* nickitem = dynamic_cast(item); if(!nickitem) return; if ( id == m_removeNickname ) { // remove watch from the tree widget delete nickitem; // update notify list updateNotifyList(); return; } if (!getItemServerAndNick(item, serverName, nickname)) return; // Get the server object corresponding to the connection id. Server* server = Application::instance()->getConnectionManager()->getServerByConnectionId(nickitem->connectionId()); if (!server) return; if ( id == m_joinChannel ) { if (m_nickListView->selectedItems().count() > 0) { // only join real channels if (static_cast(m_nickListView->selectedItems().at(0))->type() == NicksOnlineItem::ChannelItem) { QString contactChannel = m_nickListView->selectedItems().at(0)->text(nlvcChannel); server->queue( QStringLiteral("JOIN ")+contactChannel ); } } } else if ( id == m_whois ) { server->queue(QStringLiteral("WHOIS ")+nickname); } else if ( id == m_openQuery ) { NickInfoPtr nickInfo = server->obtainNickInfo(nickname); class Query* query = server->addQuery(nickInfo, true /*we initiated*/); emit showView(query); } else refreshItem(item); } /** * Sets up toolbar actions based on the given item. * @param item Item of the nicklistview. */ void NicksOnline::setupToolbarActions(NicksOnlineItem *item) { // disable all actions m_removeNickname->setEnabled(false); m_whois->setEnabled(false); m_openQuery->setEnabled(false); m_joinChannel->setEnabled(false); // check for null - if (item == 0) + if (item == nullptr) return; // add items depending on the item type switch (item->type()) { case NicksOnlineItem::ChannelItem: m_joinChannel->setEnabled(true); break; case NicksOnlineItem::NicknameItem: m_removeNickname->setEnabled(true); if (!item->isOffline()) { m_whois->setEnabled(true); m_openQuery->setEnabled(true); } break; } } /** * Sets up popup menu actions based on the given item. * @param item Item of the nicklistview. */ void NicksOnline::setupPopupMenuActions(NicksOnlineItem *item) { // clear the popup menu m_popupMenu->clear(); // check for null - if (item == 0) + if (item == nullptr) return; // add items depending on the item type switch (item->type()) { case NicksOnlineItem::NetworkRootItem: - m_popupMenu->insertAction(0, m_addNickname); + m_popupMenu->insertAction(nullptr, m_addNickname); break; case NicksOnlineItem::ChannelItem: - m_popupMenu->insertAction(0, m_joinChannel); + m_popupMenu->insertAction(nullptr, m_joinChannel); break; case NicksOnlineItem::NicknameItem: - m_popupMenu->insertAction(0, m_removeNickname); + m_popupMenu->insertAction(nullptr, m_removeNickname); if (!item->isOffline()) { m_popupMenu->addSeparator(); - m_popupMenu->insertAction(0, m_whois); - m_popupMenu->insertAction(0, m_openQuery); + m_popupMenu->insertAction(nullptr, m_whois); + m_popupMenu->insertAction(nullptr, m_openQuery); } break; } } /** * Received when user selects a different item in the nicklistview. */ void NicksOnline::slotNickListView_SelectionChanged() { if (m_nickListView->selectedItems().count() == 0) return; setupToolbarActions(dynamic_cast(m_nickListView->selectedItems().at(0))); } /** * Received when right-clicking an item in the NickListView. */ void NicksOnline::slotCustomContextMenuRequested(const QPoint& point) { QTreeWidgetItem *item = m_nickListView->itemAt(point); - if (item == 0) + if (item == nullptr) return; // select the item item->setSelected(true); // set up actions setupPopupMenuActions(dynamic_cast(item)); // show the popup menu if (m_popupMenu->actions().count() > 0) m_popupMenu->popup(QCursor::pos()); } /** * Received from popup menu when user chooses something. */ void NicksOnline::slotPopupMenu_Activated(QAction* id) { doCommand(id); } /** * Received from server when a NickInfo changes its information. */ void NicksOnline::slotNickInfoChanged(Server* server, const NickInfoPtr nickInfo) { if (!nickInfo) return; QString nickname = nickInfo->getNickname(); if (!server) return; QString serverName = server->getServerName(); QTreeWidgetItem* item = getServerAndNickItem(serverName, nickname); refreshItem(item); } /** * Received when user added a new nick to the watched nicks. */ void NicksOnline::slotAddNickname(int serverGroupId, const QString& nickname) { Preferences::addNotify(serverGroupId, nickname); Application::instance()->saveOptions(true); } /** * Refreshes the information for the given item in the list. * @param item Pointer to listview item. */ void NicksOnline::refreshItem(QTreeWidgetItem* item) { if (!item) return; QString serverName; QString nickname; if (getItemServerAndNick(item, serverName, nickname)) { Server *server = Application::instance()->getConnectionManager()->getServerByName(serverName); if (server) { NickInfoPtr nickInfo = server->getNickInfo(nickname); QString nickAdditionalInfo; bool needWhois = false; if (nickInfo) nickAdditionalInfo = getNickAdditionalInfo(nickInfo, needWhois); item->setText(nlvcAdditionalInfo, nickAdditionalInfo); if (m_nickListView->selectedItems().count() != 0 && item == m_nickListView->selectedItems().at(0)) setupToolbarActions(dynamic_cast(m_nickListView->selectedItems().at(0))); } } } void NicksOnline::childAdjustFocus() {} // kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on; // vim: set et sw=4 ts=4 cino=l1,cs,U1: diff --git a/src/irc/outputfilter.cpp b/src/irc/outputfilter.cpp index f9e6253f..aefb2743 100644 --- a/src/irc/outputfilter.cpp +++ b/src/irc/outputfilter.cpp @@ -1,2081 +1,2081 @@ /* 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) any later version. */ /* Copyright (C) 2002 Dario Abatianni Copyright (C) 2005 Ismail Donmez Copyright (C) 2005 Peter Simonsson Copyright (C) 2005 John Tapsell Copyright (C) 2005-2009 Eike Hein */ #include "outputfilter.h" #include "application.h" #include "mainwindow.h" #include "channel.h" #include "awaymanager.h" #include "ignore.h" #include "server.h" #include "scriptlauncher.h" #include "irccharsets.h" #include "query.h" #include "viewcontainer.h" #include "outputfilterresolvejob.h" #include #ifdef HAVE_QCA2 #include "cipher.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include "ircview.h" QDebug operator<<(QDebug d, QTextDocument* document); namespace Konversation { QSet OutputFilter::m_commands; void OutputFilter::fillCommandList() { if (m_commands.size() > 0) return; QString methodSignature; for (int i = OutputFilter::staticMetaObject.methodOffset(); i < OutputFilter::staticMetaObject.methodCount(); ++i) { methodSignature = QString::fromLatin1(OutputFilter::staticMetaObject.method(i).methodSignature()); if (methodSignature.startsWith(QLatin1String("command_"))) m_commands << methodSignature.mid(8).section(QLatin1Char('('), 0, 0).toLower(); } } OutputFilter::OutputFilter(Server* server) : QObject(server) { m_server = server; fillCommandList(); } OutputFilter::~OutputFilter() { } // replace all aliases in the string and return true if anything got replaced at all bool OutputFilter::replaceAliases(QString& line, ChatWindow* context) { QStringList aliasList = Preferences::self()->aliasList(); for(int index = 0; indexcommandChar() + aliasPattern) { QString aliasReplace = aliasList[index].section(QLatin1Char(' '),1); if (context) aliasReplace = context->getServer()->parseWildcards(aliasReplace, context); if (!aliasList[index].contains(QStringLiteral("%p"))) aliasReplace.append(QLatin1Char(' ') + line.section(QLatin1Char(' '), 1)); // protect "%%" aliasReplace.replace(QStringLiteral("%%"),QStringLiteral("%\x01")); // replace %p placeholder with rest of line aliasReplace.replace(QStringLiteral("%p"), line.section(QLatin1Char(' '), 1)); // restore "%<1>" as "%%" aliasReplace.replace(QStringLiteral("%\x01"),QStringLiteral("%%")); // modify line line=aliasReplace; // return "replaced" return true; } } return false; } QStringList OutputFilter::splitForEncoding(const QString& destination, const QString& inputLine, int max, int segments) { int sublen = 0; //The encoded length since the last split int charLength = 0; //the length of this char int lastBreakPoint = 0; //FIXME should we run this through the encoder first, checking with "canEncode"? QString text = inputLine; // the text we'll send, currently in Unicode QStringList finals; // The strings we're going to output QString channelCodecName = Preferences::channelEncoding(m_server->getDisplayName(), destination); //Get the codec we're supposed to use. This must not fail. (not verified) QTextCodec* codec; // I copied this bit straight out of Server::send if (channelCodecName.isEmpty()) { codec = m_server->getIdentity()->getCodec(); } else { codec = Konversation::IRCCharsets::self()->codecForName(channelCodecName); } Q_ASSERT(codec); int index = 0; while (index < text.length() && (segments == -1 || finals.size() < segments-1)) { // The most important bit - turn the current char into a QCString so we can measure it QByteArray ch = codec->fromUnicode(QString(text[index])); charLength = ch.length(); // If adding this char puts us over the limit: if (charLength + sublen > max) { if (lastBreakPoint != 0) { finals.append(text.left(lastBreakPoint + 1)); text = text.mid(lastBreakPoint + 1); } else { finals.append(text.left(index)); text = text.mid(index); } lastBreakPoint = 0; sublen = 0; index = 0; } else if (text[index].isSpace() || text[index].isPunct()) { lastBreakPoint = index; } ++index; sublen += charLength; } if (!text.isEmpty()) { finals.append(text); } return finals; } bool OutputFilter::checkForEncodingConflict(QString *line, const QString& target) { - QTextCodec* codec = 0; + QTextCodec* codec = nullptr; QString encoding; QString oldLine(*line); if(m_server->getServerGroup()) encoding = Preferences::channelEncoding(m_server->getServerGroup()->id(), target); else encoding = Preferences::channelEncoding(m_server->getDisplayName(), target); if(encoding.isEmpty()) codec = m_server->getIdentity()->getCodec(); else codec = Konversation::IRCCharsets::self()->codecForName(encoding); QTextCodec::ConverterState state; QString newLine; if(codec) newLine = codec->fromUnicode(oldLine.constData(),oldLine.length(),&state); if(!newLine.isEmpty() && state.invalidChars) { int ret = KMessageBox::Continue; ret = KMessageBox::warningContinueCancel(m_server->getViewContainer()->getWindow(), i18n("The message you are sending includes characters " "that do not exist in your current encoding. If " "you choose to continue anyway those characters " "will be replaced by a '?'."), i18n("Encoding Conflict Warning"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "WarnEncodingConflict"); *line = newLine; //set so we show it as it's sent if (ret != KMessageBox::Continue) return true; } return false; } OutputFilterResult OutputFilter::parse(const QString& myNick, const QString& originalLine, const QString& destination, ChatWindow* inputContext) { OutputFilterInput input; input.myNick = myNick; input.destination = destination; input.context = inputContext; OutputFilterResult result; QString inputLine(originalLine); if (inputLine.isEmpty() || inputLine == "\n" || checkForEncodingConflict(&inputLine, destination)) return result; //Protect against nickserv auth being sent as a message on the off chance // someone didn't notice leading spaces { QString testNickServ(inputLine.trimmed()); if(testNickServ.startsWith(Preferences::self()->commandChar() + "nickserv", Qt::CaseInsensitive) || testNickServ.startsWith(Preferences::self()->commandChar() + "ns", Qt::CaseInsensitive)) { inputLine = testNickServ; } } //perform variable expansion according to prefs inputLine = Konversation::doVarExpansion(inputLine); QString line = inputLine.toLower(); // Convert double command chars at the beginning to single ones if(line.startsWith(Preferences::self()->commandChar() + Preferences::self()->commandChar()) && !destination.isEmpty()) { inputLine = inputLine.mid(1); goto BYPASS_COMMAND_PARSING; } // Server command? else if (line.startsWith(Preferences::self()->commandChar())) { QString command = inputLine.section(' ', 0, 0).mid(1).toLower(); input.parameter = inputLine.section(' ', 1); if (command != "topic") input.parameter = input.parameter.trimmed(); if (supportedCommands().contains(command)) { QString methodSignature("command_" + command); QMetaObject::invokeMethod(this, methodSignature.toLatin1().constData(), Qt::DirectConnection, Q_RETURN_ARG(OutputFilterResult, result), Q_ARG(OutputFilterInput, input)); } // Forward unknown commands to server else { result.toServer = inputLine.mid(1); result.type = Message; } } // Ordinary message to channel/query? else if (!destination.isEmpty()) { BYPASS_COMMAND_PARSING: QStringList outputList = splitForEncoding(destination, inputLine, m_server->getPreLength("PRIVMSG", destination)); if (outputList.count() > 1) { result.output.clear(); result.outputList = outputList; for (QStringList::ConstIterator it = outputList.constBegin(); it != outputList.constEnd(); ++it) { result.toServerList += "PRIVMSG " + destination + " :" + *it; } } else { result.output = inputLine; result.toServer = "PRIVMSG " + destination + " :" + inputLine; } result.type = Message; } // Eveything else goes to the server unchanged else { result.toServer = inputLine; result.output = inputLine; result.typeString = i18n("Raw"); result.type = Program; } return result; } OutputFilterResult OutputFilter::command_op(const OutputFilterInput& input) { return changeMode(input.parameter, input.destination, 'o', '+'); } OutputFilterResult OutputFilter::command_deop(const OutputFilterInput& input) { return changeMode(addNickToEmptyNickList(input.myNick, input.parameter), input.destination, 'o', '-'); } OutputFilterResult OutputFilter::command_hop(const OutputFilterInput& input) { return changeMode(input.parameter, input.destination, 'h', '+'); } OutputFilterResult OutputFilter::command_dehop(const OutputFilterInput& input) { return changeMode(addNickToEmptyNickList(input.myNick, input.parameter), input.destination, 'h', '-'); } OutputFilterResult OutputFilter::command_voice(const OutputFilterInput& input) { return changeMode(input.parameter, input.destination, 'v', '+'); } OutputFilterResult OutputFilter::command_unvoice(const OutputFilterInput& input) { return changeMode(addNickToEmptyNickList(input.myNick, input.parameter), input.destination, 'v', '-'); } OutputFilterResult OutputFilter::command_devoice(const OutputFilterInput& input) { return command_unvoice(input); } OutputFilterResult OutputFilter::command_join(const OutputFilterInput& _input) { OutputFilterInput input(_input); OutputFilterResult result; if (input.parameter.contains(",")) // Protect against #foo,0 tricks input.parameter = input.parameter.remove(",0"); //else if(channelName == "0") // FIXME IRC RFC 2812 section 3.2.1 if (input.parameter.isEmpty()) { if (input.destination.isEmpty() || !isAChannel(input.destination)) return usage(i18n("Usage: %1JOIN [password]", Preferences::self()->commandChar())); input.parameter = input.destination; } else if (!isAChannel(input.parameter)) input.parameter = '#' + input.parameter.trimmed(); Channel* channel = m_server->getChannelByName(input.parameter); if (channel) { // Note that this relies on the channels-flush-nicklists-on-disconnect behavior. if (!channel->numberOfNicks()) result.toServer = "JOIN " + input.parameter; if (channel->joined()) emit showView(channel); } else result.toServer = "JOIN " + input.parameter; return result; } OutputFilterResult OutputFilter::command_j(const OutputFilterInput& input) { return command_join(input); } OutputFilterResult OutputFilter::command_kick(const OutputFilterInput& input) { OutputFilterResult result; if (isAChannel(input.destination)) { // get nick to kick QString victim = input.parameter.left(input.parameter.indexOf(' ')); if (victim.isEmpty()) result = usage(i18n("Usage: %1KICK [reason]", Preferences::self()->commandChar())); else { // get kick reason (if any) QString reason = input.parameter.mid(victim.length() + 1); // if no reason given, take default reason if (reason.isEmpty()) reason = m_server->getIdentity()->getKickReason(); result.toServer = "KICK " + input.destination + ' ' + victim + " :" + reason; } } else result = error(i18n("%1KICK only works from within channels.", Preferences::self()->commandChar())); return result; } OutputFilterResult OutputFilter::command_part(const OutputFilterInput& input) { OutputFilterResult result; // No parameter, try default part message if (input.parameter.isEmpty()) { // But only if we actually are in a channel if (isAChannel(input.destination)) result.toServer = "PART " + input.destination + " :" + m_server->getIdentity()->getPartReason(); else if (m_server->getQueryByName(input.destination)) m_server->closeQuery(input.destination); else result = error(i18n("%1PART and %1LEAVE without parameters only work from within a " "channel or a query.", Preferences::self()->commandChar())); } else { // part a given channel if (isAChannel(input.parameter)) { // get channel name QString channel = input.parameter.left(input.parameter.indexOf(' ')); // get part reason (if any) QString reason = input.parameter.mid(channel.length() + 1); // if no reason given, take default reason if (reason.isEmpty()) reason = m_server->getIdentity()->getPartReason(); result.toServer = "PART " + channel + " :" + reason; } // part this channel with a given reason else { if (isAChannel(input.destination)) result.toServer = "PART " + input.destination + " :" + input.parameter; else result = error(i18n("%1PART without channel name only works from within a channel.", Preferences::self()->commandChar())); } } return result; } OutputFilterResult OutputFilter::command_leave(const OutputFilterInput& input) { return command_part(input); } OutputFilterResult OutputFilter::command_topic(const OutputFilterInput& input) { OutputFilterResult result; // No parameter, try to get current topic if (input.parameter.isEmpty()) { // But only if we actually are in a channel if (isAChannel(input.destination)) result.toServer = "TOPIC " + input.destination; else result = error(i18n("%1TOPIC without parameters only works from within a channel.", Preferences::self()->commandChar())); } else { // retrieve or set topic of a given channel if (isAChannel(input.parameter)) { // get channel name QString channel = input.parameter.left(input.parameter.indexOf(' ')); // get topic (if any) QString topic = input.parameter.mid(channel.length()+1); // if no topic given, retrieve topic if (topic.isEmpty()) m_server->requestTopic(channel); // otherwise set topic there else { result.toServer = "TOPIC " + channel + " :"; //If we get passed a ^A as a topic its a sign we should clear the topic. //Used to be a \n, but those get smashed by QStringList::split and readded later //Now is not the time to fight with that. FIXME //If anyone out there *can* actually set the topic to a single ^A, now they have to //specify it twice to get one. if (topic =="\x01\x01") result.toServer += '\x01'; else if (topic!="\x01") result.toServer += topic; } } // set this channel's topic else { if (isAChannel(input.destination)) result.toServer = "TOPIC " + input.destination + " :" + input.parameter; else result = error(i18n("%1TOPIC without channel name only works from within a channel.", Preferences::self()->commandChar())); } } return result; } OutputFilterResult OutputFilter::command_away(const OutputFilterInput& input) { if (input.parameter.isEmpty() && m_server->isAway()) m_server->requestUnaway(); else m_server->requestAway(input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_unaway(const OutputFilterInput& /* input */) { m_server->requestUnaway(); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_back(const OutputFilterInput& input) { return command_unaway(input); } OutputFilterResult OutputFilter::command_aaway(const OutputFilterInput& input) { Application::instance()->getAwayManager()->requestAllAway(input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_aunaway(const OutputFilterInput& /* input */) { Application::instance()->getAwayManager()->requestAllUnaway(); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_aback(const OutputFilterInput& input) { return command_aunaway(input); } OutputFilterResult OutputFilter::command_names(const OutputFilterInput& input) { OutputFilterResult result; result.toServer = "NAMES "; if (input.parameter.isNull()) result = error(i18n("%1NAMES with no target may disconnect you from the server. Specify " "'*' if you really want this.", Preferences::self()->commandChar())); else if (input.parameter != QChar('*')) result.toServer.append(input.parameter); return result; } OutputFilterResult OutputFilter::command_close(const OutputFilterInput& _input) { OutputFilterInput input(_input); OutputFilterResult result; if (input.parameter.isEmpty()) input.parameter = input.destination; if (isAChannel(input.parameter) && m_server->getChannelByName(input.parameter)) m_server->getChannelByName(input.parameter)->closeYourself(false); else if (m_server->getQueryByName(input.parameter)) m_server->getQueryByName(input.parameter)->closeYourself(false); else if (input.parameter.isEmpty()) // this can only mean one thing.. we're in the Server tab m_server->closeYourself(false); else result = usage(i18n("Usage: %1close [window] closes the named channel or query tab, " "or the current tab if none specified.", Preferences::self()->commandChar())); return result; } OutputFilterResult OutputFilter::command_reconnect(const OutputFilterInput& input) { emit reconnectServer(input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_disconnect(const OutputFilterInput& input) { emit disconnectServer(input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_quit(const OutputFilterInput& input) { emit disconnectServer(input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_notice(const OutputFilterInput& input) { OutputFilterResult result; QString recipient = input.parameter.left(input.parameter.indexOf(' ')); QString message = input.parameter.mid(recipient.length()+1); if (input.parameter.isEmpty() || message.isEmpty()) result = usage(i18n("Usage: %1NOTICE ", Preferences::self()->commandChar())); else { result.typeString = i18n("Notice"); result.toServer = "NOTICE " + recipient + " :" + message; result.output=i18nc("%1 is the message, %2 the recipient nickname", "Sending notice \"%1\" to %2.", message, recipient); result.type = Program; } return result; } OutputFilterResult OutputFilter::command_me(const OutputFilterInput& input) { OutputFilterResult result; if (input.destination.isEmpty()) return error(i18n("%1ME only works from a channel, query or DCC chat tab.", Preferences::self()->commandChar())); else if (input.parameter.isEmpty()) return usage(i18n("Usage: %1ME text", Preferences::self()->commandChar())); QString command("PRIVMSGACTION \x01\x01"); QStringList outputList = splitForEncoding(input.destination, input.parameter, m_server->getPreLength(command, input.destination), 2); if (outputList.count() > 1) { command = "PRIVMSG"; outputList += splitForEncoding(input.destination, outputList.at(1), m_server->getPreLength(command, input.destination)); outputList.removeAt(1); result.output.clear(); result.outputList = outputList; for (int i = 0; i < outputList.count(); ++i) { if (i == 0) result.toServerList += "PRIVMSG " + input.destination + " :" + '\x01' + "ACTION " + outputList.at(i) + '\x01'; else result.toServerList += "PRIVMSG " + input.destination + " :" + outputList.at(i); } } else { result.output = input.parameter; result.toServer = "PRIVMSG " + input.destination + " :" + '\x01' + "ACTION " + input.parameter + '\x01'; } result.type = Action; return result; } OutputFilterResult OutputFilter::command_msg(const OutputFilterInput& input) { return handleMsg(input.parameter, false); } OutputFilterResult OutputFilter::command_m(const OutputFilterInput& input) { return command_msg(input); } OutputFilterResult OutputFilter::command_query(const OutputFilterInput& input) { return handleMsg(input.parameter, true); } OutputFilterResult OutputFilter::handleMsg(const QString& parameter, bool commandIsQuery) { OutputFilterResult result; QString recipient = parameter.section(' ', 0, 0, QString::SectionSkipEmpty); QString message = parameter.section(' ', 1); QString output; bool recipientIsAChannel = false; if (recipient.isEmpty()) return error(i18n("You need to specify a recipient.")); else recipientIsAChannel = m_server->isAChannel(recipient); if (commandIsQuery && recipientIsAChannel) return error(i18n("You cannot open queries to channels.")); if (message.trimmed().isEmpty()) { // Empty result - we don't want to send any message to the server. if (!commandIsQuery) return error(i18n("You need to specify a message.")); } else { output = message; if (message.startsWith(Preferences::self()->commandChar() + "me")) result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "ACTION " + message.mid(4) + '\x01'; else result.toServer = "PRIVMSG " + recipient + " :" + message; } // If this is a /query, always open a query window. // Treat "/msg nick" as "/query nick". if (commandIsQuery || output.isEmpty()) { // Note we have to be a bit careful here. // We only want to create ('obtain') a new nickinfo if we have done /query // or "/msg nick". Not "/msg nick message". NickInfoPtr nickInfo = m_server->obtainNickInfo(recipient); ::Query* query = m_server->addQuery(nickInfo, true /*we initiated*/); // Force focus if the user did not specify any message. if (output.isEmpty() && Preferences::self()->focusNewQueries()) emit showView(query); } // Result should be completely empty; if (output.isEmpty()) return result; //FIXME: Don't do below line if query is focused. result.output = output; result.typeString = recipient; result.type = PrivateMessage; return result; } OutputFilterResult OutputFilter::command_smsg(const OutputFilterInput& input) { OutputFilterResult result; QString recipient = input.parameter.left(input.parameter.indexOf(' ')); QString message = input.parameter.mid(recipient.length() + 1); if (message.startsWith(Preferences::self()->commandChar() + "me")) result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "ACTION " + message.mid(4) + '\x01'; else result.toServer = "PRIVMSG " + recipient + " :" + message; return result; } OutputFilterResult OutputFilter::command_ping(const OutputFilterInput& input) { return handleCtcp(input.parameter.section(' ', 0, 0) + " ping"); } OutputFilterResult OutputFilter::command_ctcp(const OutputFilterInput& input) { return handleCtcp(input.parameter); } OutputFilterResult OutputFilter::handleCtcp(const QString& parameter) { OutputFilterResult result; // who is the recipient? QString recipient = parameter.section(' ', 0, 0); // what is the first word of the ctcp? QString request = parameter.section(' ', 1, 1, QString::SectionSkipEmpty).toUpper(); // what is the complete ctcp command? QString message = parameter.section(' ', 2, 0xffffff, QString::SectionSkipEmpty); QString out = request; if (!message.isEmpty()) out+= ' ' + message; if (request == "PING") { unsigned int timeT = QDateTime::currentDateTime().toTime_t(); result.toServer = QString("PRIVMSG %1 :\x01PING %2\x01").arg(recipient).arg(timeT); result.output = i18n("Sending CTCP-%1 request to %2.", QString::fromLatin1("PING"), recipient); } else { result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + out + '\x01'; result.output = i18n("Sending CTCP-%1 request to %2.", out, recipient); } result.typeString = i18n("CTCP"); result.type = Program; return result; } OutputFilterResult OutputFilter::command_ame(const OutputFilterInput& input) { if (input.parameter.isEmpty()) return usage(i18n("Usage: %1AME [-LOCAL] text", Preferences::self()->commandChar())); if (isParameter("local", input.parameter.section(' ', 0, 0))) m_server->sendToAllChannelsAndQueries(Preferences::self()->commandChar() + "me " + input.parameter.section(' ', 1)); else emit multiServerCommand("me", input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_amsg(const OutputFilterInput& input) { if (input.parameter.isEmpty()) return usage(i18n("Usage: %1AMSG [-LOCAL] text", Preferences::self()->commandChar())); if (isParameter("local", input.parameter.section(' ', 0, 0))) m_server->sendToAllChannelsAndQueries(input.parameter.section(' ', 1)); else emit multiServerCommand("msg", input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_omsg(const OutputFilterInput& input) { if (input.parameter.isEmpty()) return usage(i18n("Usage: %1OMSG text", Preferences::self()->commandChar())); OutputFilterResult result; result.toServer = "PRIVMSG @" + input.destination + " :" + input.parameter; return result; } OutputFilterResult OutputFilter::command_onotice(const OutputFilterInput& input) { OutputFilterResult result; if (!input.parameter.isEmpty()) { result.toServer = "NOTICE @" + input.destination + " :" + input.parameter; result.typeString = i18n("Notice"); result.type = Program; result.output = i18n("Sending notice \"%1\" to %2.", input.parameter, input.destination); } else result = usage(i18n("Usage: %1ONOTICE text", Preferences::self()->commandChar())); return result; } OutputFilterResult OutputFilter::command_quote(const OutputFilterInput& input) { OutputFilterResult result; if (input.parameter.isEmpty()) result = usage(i18n("Usage: %1QUOTE command list", Preferences::self()->commandChar())); else result.toServer = input.parameter; return result; } OutputFilterResult OutputFilter::command_say(const OutputFilterInput& input) { OutputFilterResult result; if (input.parameter.isEmpty()) result = usage(i18n("Usage: %1SAY text", Preferences::self()->commandChar())); else { result.toServer = "PRIVMSG " + input.destination + " :" + input.parameter; result.output = input.parameter; } return result; } OutputFilterResult OutputFilter::command_dcc(const OutputFilterInput& _input) { OutputFilterInput input(_input); OutputFilterResult result; qDebug() << input.parameter; // No parameter, just open DCC panel if (input.parameter.isEmpty()) { emit addDccPanel(); } else { QStringList parameterList = input.parameter.replace("\\ ", "%20").split(' '); QString dccType = parameterList[0].toLower(); //TODO close should not just refer to the gui-panel, let it close connections if (dccType == "close") { emit closeDccPanel(); } else if (dccType == "send") { if (parameterList.count() == 1) // DCC SEND { emit requestDccSend(); } else if (parameterList.count() == 2) // DCC SEND { emit requestDccSend(parameterList[1]); } else if (parameterList.count() > 2) // DCC SEND [file] ... { // TODO: make sure this will work: //output=i18n("Usage: %1DCC SEND nickname [filename] [filename] ...").arg(commandChar); QUrl fileURL(parameterList[2]); //We could easily check if the remote file exists, but then we might //end up asking for creditionals twice, so settle for only checking locally if (!fileURL.isLocalFile() || QFile::exists(fileURL.path())) { emit openDccSend(parameterList[1],fileURL); } else { result = error(i18n("File \"%1\" does not exist.", parameterList[2])); } } else // Don't know how this should happen, but ... result = usage(i18n("Usage: %1DCC [SEND [nickname [filename]]]", Preferences::self()->commandChar())); } else if (dccType == "get") { //dcc get [nick [file]] switch (parameterList.count()) { case 1: emit acceptDccGet(QString(),QString()); break; case 2: emit acceptDccGet(parameterList.at(1),QString()); break; case 3: emit acceptDccGet(parameterList.at(1),parameterList.at(2)); break; default: result = usage(i18n("Usage: %1DCC [GET [nickname [filename]]]", Preferences::self()->commandChar())); } } else if (dccType == "chat") { //dcc chat nick switch (parameterList.count()) { case 1: emit openDccChat(QString()); break; case 2: emit openDccChat(parameterList[1]); break; default: result = usage(i18n("Usage: %1DCC [CHAT [nickname]]", Preferences::self()->commandChar())); } } else if (dccType == "whiteboard") { //dcc whiteboard nick switch (parameterList.count()) { case 1: emit openDccWBoard(QString()); break; case 2: emit openDccWBoard(parameterList[1]); break; default: result = usage(i18n("Usage: %1DCC [WHITEBOARD [nickname]]", Preferences::self()->commandChar())); } } else result = error(i18n("Unrecognized command %1DCC %2. Possible commands are SEND, " "CHAT, CLOSE, GET, WHITEBOARD.", Preferences::self()->commandChar(), parameterList[0])); } return result; } OutputFilterResult OutputFilter::sendRequest(const QString &recipient, const QString &fileName, const QString &address, quint16 port, quint64 size) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC SEND " + fileName + ' ' + address + ' ' + QString::number(port) + ' ' + QString::number(size) + '\x01'; return result; } OutputFilterResult OutputFilter::passiveSendRequest(const QString& recipient, const QString &fileName, const QString &address, quint64 size, const QString &token) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC SEND " + fileName + ' ' + address + " 0 " + QString::number(size) + ' ' + token + '\x01'; return result; } // Accepting Resume Request OutputFilterResult OutputFilter::acceptResumeRequest(const QString &recipient, const QString &fileName, quint16 port, quint64 startAt) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC ACCEPT " + fileName + ' ' + QString::number(port) + ' ' + QString::number(startAt) + '\x01'; return result; } // Accepting Passive Resume Request OutputFilterResult OutputFilter::acceptPassiveResumeRequest(const QString &recipient, const QString &fileName, quint16 port, quint64 startAt, const QString &token) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC ACCEPT " + fileName + ' ' + QString::number(port) + ' ' + QString::number(startAt) + ' ' + token +'\x01'; return result; } // Requesting Resume Request OutputFilterResult OutputFilter::resumeRequest(const QString &sender, const QString &fileName, quint16 port, KIO::filesize_t startAt) { OutputFilterResult result; result.toServer = "PRIVMSG " + sender + " :" + '\x01' + "DCC RESUME " + fileName + ' ' + QString::number(port) + ' ' + QString::number(startAt) + '\x01'; return result; } // Requesting Passive Resume Request OutputFilterResult OutputFilter::resumePassiveRequest(const QString &sender, const QString &fileName, quint16 port, KIO::filesize_t startAt, const QString &token) { OutputFilterResult result; result.toServer = "PRIVMSG " + sender + " :" + '\x01' + "DCC RESUME " + fileName + ' ' + QString::number(port) + ' ' + QString::number(startAt) + ' ' + token + '\x01'; return result; } // Accept Passive Send Request, there active doesn't need that OutputFilterResult OutputFilter::acceptPassiveSendRequest(const QString& recipient, const QString &fileName, const QString &address, quint16 port, quint64 size, const QString &token) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC SEND " + fileName + ' ' + address + ' ' + QString::number(port) + ' ' + QString::number(size) + ' ' + token + '\x01'; return result; } // Rejecting quened dcc receive, is not send when abort an active send OutputFilterResult OutputFilter::rejectDccSend(const QString& partnerNick, const QString & fileName) { OutputFilterResult result; result.toServer = "NOTICE " + partnerNick + " :" + '\x01' + "DCC REJECT SEND " + fileName + '\x01'; return result; } OutputFilterResult OutputFilter::rejectDccChat(const QString & partnerNick, const QString& extension) { OutputFilterResult result; result.toServer = "NOTICE " + partnerNick + " :" + '\x01' + "DCC REJECT CHAT " + extension.toUpper() + '\x01'; return result; } OutputFilterResult OutputFilter::requestDccChat(const QString& partnerNick, const QString& extension, const QString& numericalOwnIp, quint16 ownPort) { OutputFilterResult result; result.toServer = "PRIVMSG " + partnerNick + " :" + '\x01' + "DCC CHAT " + extension.toUpper() + ' ' + numericalOwnIp + ' ' + QString::number(ownPort) + '\x01'; return result; } OutputFilterResult OutputFilter::passiveChatRequest(const QString& recipient, const QString& extension, const QString& address, const QString& token) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC CHAT " + extension.toUpper() + ' ' + address + " 0 " + token + '\x01'; return result; } OutputFilterResult OutputFilter::acceptPassiveChatRequest(const QString& recipient, const QString& extension, const QString& numericalOwnIp, quint16 ownPort, const QString& token) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC CHAT " + extension.toUpper() + ' ' + numericalOwnIp + ' ' + QString::number(ownPort) + ' ' + token + '\x01'; return result; } OutputFilterResult OutputFilter::command_invite(const OutputFilterInput& input) { OutputFilterResult result; if (input.parameter.isEmpty()) result = usage(i18n("Usage: %1INVITE [channel]", Preferences::self()->commandChar())); else { QString nick = input.parameter.section(' ', 0, 0, QString::SectionSkipEmpty); QString channel = input.parameter.section(' ', 1, 1, QString::SectionSkipEmpty); if (channel.isEmpty()) { if (isAChannel(input.destination)) channel = input.destination; else result = error(i18n("%1INVITE without channel name works only from within channels.", Preferences::self()->commandChar())); } if (!channel.isEmpty()) { if (isAChannel(channel)) result.toServer = "INVITE " + nick + ' ' + channel; else result = error(i18n("%1 is not a channel.", channel)); } } return result; } OutputFilterResult OutputFilter::command_exec(const OutputFilterInput& input) { OutputFilterResult result; if (input.parameter.isEmpty()) result = usage(i18n("Usage: %1EXEC [-SHOWPATH]