diff --git a/src/common.h b/src/common.h --- a/src/common.h +++ b/src/common.h @@ -13,6 +13,7 @@ #ifndef COMMON_H #define COMMON_H +#include #include #include #include @@ -22,6 +23,8 @@ namespace Konversation { + Q_NAMESPACE + static QRegExp colorRegExp(QStringLiteral("((\003([0-9]|0[0-9]|1[0-5])(,([0-9]|0[0-9]|1[0-5])|)|\017)|\x02|\x03|\x09|\x13|\x15|\x16|\x1d|\x1f)")); static QRegExp urlPattern(QString(QStringLiteral("\\b((?:(?:([a-z][\\w\\.-]+:/{1,3})|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|\\}\\]|[^\\s`!()\\[\\]{};:'\".,<>?%1%2%3%4%5%6])|[a-z0-9.\\-+_]+@[a-z0-9.\\-]+[.][a-z]{1,5}[^\\s/`!()\\[\\]{};:'\".,<>?%1%2%3%4%5%6]))")).arg(QChar(0x00AB)).arg(QChar(0x00BB)).arg(QChar(0x201C)).arg(QChar(0x201D)).arg(QChar(0x2018)).arg(QChar(0x2019))); static QRegExp chanExp(QString(QStringLiteral("(^|\\s|^\"|\\s\"|,|'|\\(|\\:|!|@|%|\\+)(#[^,\\s;\\)\\:\\/\\(\\<\\>]*[^.,\\s;\\)\\:\\/\\(\"\''\\<\\>?%1%2%3%4%5%6])")).arg(QChar(0x00AB)).arg(QChar(0x00BB)).arg(QChar(0x201C)).arg(QChar(0x201D)).arg(QChar(0x2018)).arg(QChar(0x2019))); @@ -39,13 +42,14 @@ enum ConnectionState { - SSNeverConnected, - SSDeliberatelyDisconnected, - SSInvoluntarilyDisconnected, - SSScheduledToConnect, - SSConnecting, - SSConnected + NeverConnected, + DeliberatelyDisconnected, + InvoluntarilyDisconnected, + ScheduledToConnect, + Connecting, + Connected }; + Q_ENUM_NS(ConnectionState) enum ConnectionFlag { diff --git a/src/connectionmanager.h b/src/connectionmanager.h --- a/src/connectionmanager.h +++ b/src/connectionmanager.h @@ -81,7 +81,7 @@ private Q_SLOTS: void delistConnection(int connectionId); - void handleConnectionStateChange(Server* server, Konversation::ConnectionState state); + void handleConnectionStateChange(Konversation::ConnectionState state); void handleReconnect(Server* server); diff --git a/src/connectionmanager.cpp b/src/connectionmanager.cpp --- a/src/connectionmanager.cpp +++ b/src/connectionmanager.cpp @@ -156,8 +156,7 @@ connect(server, &Server::destroyed, this, &ConnectionManager::delistConnection); - connect(server, SIGNAL(connectionStateChanged(Server*,Konversation::ConnectionState)), - this, SLOT(handleConnectionStateChange(Server*,Konversation::ConnectionState))); + connect(server, &Server::connectionStateChanged, this, &ConnectionManager::handleConnectionStateChange); connect(server, &Server::awayState, this, &ConnectionManager::connectionChangedAwayState); @@ -184,13 +183,14 @@ emit connectionListChanged(); } -void ConnectionManager::handleConnectionStateChange(Server* server, Konversation::ConnectionState state) +void ConnectionManager::handleConnectionStateChange(Konversation::ConnectionState state) { + auto server = static_cast(sender()); emit connectionChangedState(server, state); int identityId = server->getIdentity()->id(); - if (state == Konversation::SSConnected) + if (state == Konversation::Connected) { m_overrideAutoReconnect = false; @@ -201,7 +201,7 @@ emit identityOnline(identityId); } } - else if (state != Konversation::SSConnecting) + else if (state != Konversation::Connecting) { if (m_activeIdentities.contains(identityId)) { @@ -211,14 +211,14 @@ } } - if (state == Konversation::SSInvoluntarilyDisconnected && !m_overrideAutoReconnect) + if (state == Konversation::InvoluntarilyDisconnected && !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) + else if (state == Konversation::InvoluntarilyDisconnected && m_overrideAutoReconnect) { server->getStatusView()->appendServerMessage(i18n("Info"), i18n ("Network is down, will reconnect automatically when it is back up.")); } diff --git a/src/irc/channel.h b/src/irc/channel.h --- a/src/irc/channel.h +++ b/src/irc/channel.h @@ -248,7 +248,7 @@ void setAutoJoin(bool autojoin); - void connectionStateChanged(Server*, Konversation::ConnectionState); + void connectionStateChanged(Konversation::ConnectionState state); protected Q_SLOTS: void completeNick(); ///< I guess this is a GUI function, might be nice to have at DCOP level though --argonel diff --git a/src/irc/channel.cpp b/src/irc/channel.cpp --- a/src/irc/channel.cpp +++ b/src/irc/channel.cpp @@ -324,8 +324,7 @@ { if (m_server != server) { - connect(server, SIGNAL(connectionStateChanged(Server*,Konversation::ConnectionState)), - SLOT(connectionStateChanged(Server*,Konversation::ConnectionState))); + connect(server, &Server::connectionStateChanged, this, &Channel::connectionStateChanged); connect(server, SIGNAL(nickInfoChanged()), this, SLOT(updateNickInfos())); connect(server, SIGNAL(channelNickChanged(QString)), @@ -343,11 +342,12 @@ connect(awayLabel, SIGNAL(awayMessageChanged(QString)), m_server, SLOT(requestAway(QString))); } -void Channel::connectionStateChanged(Server* server, Konversation::ConnectionState state) +void Channel::connectionStateChanged(Konversation::ConnectionState state) { + Server *server = static_cast(sender()); if (server == m_server) { - if (state != Konversation::SSConnected) + if (state != Konversation::Connected) { m_joined = false; diff --git a/src/irc/query.h b/src/irc/query.h --- a/src/irc/query.h +++ b/src/irc/query.h @@ -15,6 +15,7 @@ #include "chatwindow.h" #include "nickinfo.h" +#include "server.h" #include @@ -83,7 +84,7 @@ void sendText(const QString& text) Q_DECL_OVERRIDE; void indicateAway(bool show) Q_DECL_OVERRIDE; void setEncryptedOutput(bool); - void connectionStateChanged(Server*, Konversation::ConnectionState); + void connectionStateChanged(Konversation::ConnectionState); protected Q_SLOTS: void queryTextEntered(); diff --git a/src/irc/query.cpp b/src/irc/query.cpp --- a/src/irc/query.cpp +++ b/src/irc/query.cpp @@ -114,8 +114,8 @@ { if (m_server != newServer) { - connect(newServer, SIGNAL(connectionStateChanged(Server*,Konversation::ConnectionState)), - SLOT(connectionStateChanged(Server*,Konversation::ConnectionState))); + connect(newServer, &Server::connectionStateChanged, + this, &Query::connectionStateChanged); connect(newServer, SIGNAL(nickInfoChanged(Server*,NickInfoPtr)), this, SLOT(updateNickInfo(Server*,NickInfoPtr))); } @@ -129,13 +129,14 @@ connect(awayLabel, SIGNAL(awayMessageChanged(QString)), m_server, SLOT(requestAway(QString))); } -void Query::connectionStateChanged(Server* server, Konversation::ConnectionState state) +void Query::connectionStateChanged(Konversation::ConnectionState state) { + auto server = static_cast(sender()); if (server == m_server) { ViewContainer* viewContainer = Application::instance()->getMainWindow()->getViewContainer(); - if (state == Konversation::SSConnected) + if (state == Konversation::Connected) { //HACK the way the notification priorities work sucks, this forces the tab text color to ungray right now. if (viewContainer->getFrontView() == this diff --git a/src/irc/server.h b/src/irc/server.h --- a/src/irc/server.h +++ b/src/irc/server.h @@ -74,6 +74,7 @@ Q_PROPERTY(QString nickname READ getNickname WRITE setNickname NOTIFY nicknameChanged) Q_PROPERTY(UserModel* userModel READ getUserModel CONSTANT) // WIPQTQUICK + Q_PROPERTY(Konversation::ConnectionState connectionState READ getConnectionState NOTIFY connectionStateChanged) public: enum QueuePriority @@ -119,9 +120,9 @@ Konversation::ConnectionState getConnectionState() { return m_connectionState; } - bool isConnected() { return (m_connectionState == Konversation::SSConnected); } - bool isConnecting() { return (m_connectionState == Konversation::SSConnecting); } - bool isScheduledToConnect() { return (m_connectionState == Konversation::SSScheduledToConnect); } + bool isConnected() { return (m_connectionState == Konversation::Connected); } + bool isConnecting() { return (m_connectionState == Konversation::Connecting); } + bool isScheduledToConnect() { return (m_connectionState == Konversation::ScheduledToConnect); } bool getUseSSL() const; QString getSSLInfo() const; @@ -467,7 +468,7 @@ void sslInitFailure(); void sslConnected(Server* server); - void connectionStateChanged(Server* server, Konversation::ConnectionState state); + void connectionStateChanged(Konversation::ConnectionState state); void showView(QObject* view); // WIPQTQUICK void addDccPanel(); diff --git a/src/irc/server.cpp b/src/irc/server.cpp --- a/src/irc/server.cpp +++ b/src/irc/server.cpp @@ -63,7 +63,7 @@ setConnectionSettings(settings); - m_connectionState = Konversation::SSNeverConnected; + m_connectionState = Konversation::NeverConnected; m_recreationScheduled = false; @@ -148,7 +148,7 @@ if (getIdentity()->getShellCommand().isEmpty()) connectSignals(); // TODO FIXME this disappeared in a merge, ensure it should have - updateConnectionState(Konversation::SSNeverConnected); + updateConnectionState(Konversation::NeverConnected); m_nickInfoChangedTimer = new QTimer(this); m_nickInfoChangedTimer->setSingleShot(true); @@ -455,11 +455,11 @@ // Reenable check when it works reliably for all backends // if(Solid::Networking::status() != Solid::Networking::Connected) // { -// updateConnectionState(Konversation::SSInvoluntarilyDisconnected); +// updateConnectionState(Konversation::InvoluntarilyDisconnected); // return; // } - updateConnectionState(Konversation::SSConnecting); + updateConnectionState(Konversation::Connecting); m_ownIpByUserhost.clear(); @@ -530,7 +530,7 @@ m_delayedConnectTimer->setInterval(delay * 1000); m_delayedConnectTimer->start(); - updateConnectionState(Konversation::SSScheduledToConnect); + updateConnectionState(Konversation::ScheduledToConnect); } void Server::showSSLDialog() @@ -889,7 +889,7 @@ void Server::broken(KTcpSocket::Error error) { Q_UNUSED(error); - qDebug() << "Connection broken with state" << m_connectionState << "and error:" << m_socket->errorString(); + qDebug() << "Connection broken with state" << static_cast(m_connectionState) << "and error:" << m_socket->errorString(); m_socket->blockSignals(true); @@ -924,9 +924,9 @@ getConnectionSettings().server().host(), QString::number(getConnectionSettings().server().port()))); - updateConnectionState(SSDeliberatelyDisconnected); + updateConnectionState(Konversation::DeliberatelyDisconnected); } - else if (getConnectionState() == Konversation::SSDeliberatelyDisconnected) + else if (getConnectionState() == Konversation::DeliberatelyDisconnected) { if (m_reconnectImmediately) { @@ -946,7 +946,7 @@ getStatusView()->appendServerMessage(i18n("Error"), error); - updateConnectionState(Konversation::SSInvoluntarilyDisconnected); + updateConnectionState(Konversation::InvoluntarilyDisconnected); } // HACK Only show one nick change dialog at connection time. @@ -1012,7 +1012,7 @@ { // Don't auto-reconnect if the user chose to ignore the SSL errors -- // treat it as a deliberate disconnect. - updateConnectionState(Konversation::SSDeliberatelyDisconnected); + updateConnectionState(Konversation::DeliberatelyDisconnected); QString errorReason; @@ -1040,7 +1040,7 @@ if (!ownHost.isEmpty()) QHostInfo::lookupHost(ownHost, this, SLOT(gotOwnResolvedHostByWelcome(QHostInfo))); - updateConnectionState(Konversation::SSConnected); + updateConnectionState(Konversation::Connected); // Make a helper object to build ISON (notify) list. // TODO: Give the object a kick to get it started? @@ -1101,12 +1101,12 @@ { m_connectionState = state; - if (m_connectionState == Konversation::SSConnected) + if (m_connectionState == Konversation::Connected) emit serverOnline(true); - else if (m_connectionState != Konversation::SSConnecting) + else if (m_connectionState != Konversation::Connecting) emit serverOnline(false); - emit connectionStateChanged(this, state); + emit connectionStateChanged(state); } } @@ -1140,7 +1140,7 @@ // Make clear this is deliberate even if the QUIT never actually goes through the queue // (i.e. this is not redundant with _send_internal()'s updateConnectionState() call for // a QUIT). - updateConnectionState(Konversation::SSDeliberatelyDisconnected); + updateConnectionState(Konversation::DeliberatelyDisconnected); if (!m_socket) return; @@ -1570,7 +1570,7 @@ if (outboundCommand == 0 && outputLineSplit.count() >= 2) //"WHO" m_inputFilter.addWhoRequest(outputLineSplit[1]); else if (outboundCommand == 1) //"QUIT" - updateConnectionState(Konversation::SSDeliberatelyDisconnected); + updateConnectionState(Konversation::DeliberatelyDisconnected); // set channel encoding if specified QString channelCodecName; @@ -4435,17 +4435,17 @@ void Server::involuntaryQuit() { - if((m_connectionState == Konversation::SSConnected || m_connectionState == Konversation::SSConnecting) && + if((m_connectionState == Konversation::Connected || m_connectionState == Konversation::Connecting) && (m_socket->peerAddress() != QHostAddress(QHostAddress::LocalHost) && m_socket->peerAddress() != QHostAddress(QHostAddress::LocalHostIPv6))) { quitServer(); - updateConnectionState(Konversation::SSInvoluntarilyDisconnected); + updateConnectionState(Konversation::InvoluntarilyDisconnected); } } void Server::reconnectInvoluntary() { - if(m_connectionState == Konversation::SSInvoluntarilyDisconnected) + if(m_connectionState == Konversation::InvoluntarilyDisconnected) reconnectServer(); } diff --git a/src/mainwindow.h b/src/mainwindow.h --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -19,6 +19,7 @@ #include "common.h" #include "preferences.h" #include "ssllabel.h" +#include "server.h" #include diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -121,9 +121,15 @@ qputenv("QT_QUICK_CONTROLS_STYLE", "org.kde.desktop"); m_qmlEngine = new QQmlApplicationEngine(this); + + // register common enums needed in QML + qRegisterMetaType("Konversation::ConnectionState"); // C++ -> QML signal + qmlRegisterUncreatableMetaObject(Konversation::staticMetaObject, "org.kde.konversation", 1, 0, "Konversation", "Enums only"); qmlRegisterUncreatableType("org.kde.konversation", 1, 0, "MessageModel", ""); qmlRegisterUncreatableType("org.kde.konversation", 1, 0, "InputHistoryModel", ""); qmlRegisterUncreatableType("org.kde.konversation", 1, 0, "IrcContextMenus", ""); + + // setup qml context m_qmlEngine->rootContext()->setContextProperty(QStringLiteral("konvApp"), Application::instance()); m_qmlEngine->rootContext()->setContextProperty(QStringLiteral("viewModel"), m_viewContainer); m_qmlEngine->rootContext()->setContextProperty(QStringLiteral("viewListModel"), viewListModel); @@ -152,9 +158,8 @@ connect(m_viewContainer, SIGNAL(autoConnectOnStartupToggled(Konversation::ServerGroupSettingsPtr)), Application::instance(), SIGNAL(serverGroupsChanged(Konversation::ServerGroupSettingsPtr))); connect(m_viewContainer, SIGNAL(setWindowCaption(QString)), this, SLOT(setCaption(QString))); - connect(Application::instance()->getConnectionManager(), - SIGNAL(connectionChangedState(Server*,Konversation::ConnectionState)), - m_viewContainer, SLOT(connectionStateChanged(Server*,Konversation::ConnectionState))); + connect(Application::instance()->getConnectionManager(), &ConnectionManager::connectionChangedState, + m_viewContainer, &ViewContainer::connectionStateChanged); connect(this, SIGNAL(triggerRememberLine()), m_viewContainer, SLOT(insertRememberLine())); connect(this, SIGNAL(triggerRememberLines(Server*)), m_viewContainer, SLOT(insertRememberLines(Server*))); connect(this, SIGNAL(cancelRememberLine()), m_viewContainer, SLOT(cancelRememberLine())); diff --git a/src/qtquick/uipackages/default/contents/ViewSwitcher.qml b/src/qtquick/uipackages/default/contents/ViewSwitcher.qml --- a/src/qtquick/uipackages/default/contents/ViewSwitcher.qml +++ b/src/qtquick/uipackages/default/contents/ViewSwitcher.qml @@ -15,6 +15,8 @@ import org.kde.kirigami 2.2 as Kirigami +import org.kde.konversation 1.0 + Item { id: viewSwitcher @@ -134,7 +136,10 @@ readonly property int row: index readonly property bool hasActivity: model.HasActivity readonly property int unreadMentions: model.ViewRole.unreadMentions + property int connectionState: model.ViewRole ? model.ViewRole.server.connectionState : Konversation.NeverConnected property Item unreadMentionsCounter: null + property Item loadingBar: null + text: model.display textMarginLeft: model.IsChild ? Kirigami.Units.gridUnit * 2 : Kirigami.Units.gridUnit @@ -155,6 +160,17 @@ Qt.callLater(ListView.view.updateOffViewportOverlay); } + onConnectionStateChanged: { + if (!loadingBar && connectionState == Konversation.Connecting) { + console.log("Adding loading bar for " + text); + loadingBar = loadingBarComponent.createObject(viewListItem); + } else if (loadingBar) { + console.log("Destroying loading bar for " + text); + loadingBar.destroy(); + loadingBar = null; + } + } + onClicked: { viewListView.forceActiveFocus(); @@ -166,6 +182,21 @@ } } + Component { + id: loadingBarComponent + + QQC2.BusyIndicator { + id: loadingAnimator + running: true + anchors.right: parent.right + // we assume there won't be intersection with showing busy indicator and unread count + anchors.rightMargin: verticalScrollbar.visible ? verticalScrollbar.width : Kirigami.Units.smallSpacing + anchors.verticalCenter: parent.verticalCenter + height: Kirigami.Units.iconSizes.smallMedium + width: Kirigami.Units.iconSizes.smallMedium + } + } + Component { id: unreadMentionsCounterComponent diff --git a/src/viewer/chatwindow.h b/src/viewer/chatwindow.h --- a/src/viewer/chatwindow.h +++ b/src/viewer/chatwindow.h @@ -34,6 +34,7 @@ Q_PROPERTY(QString description READ getDescription NOTIFY descriptionChanged) Q_PROPERTY(IrcContextMenus::MenuOptions contextMenuOptions READ contextMenuOptions CONSTANT) Q_PROPERTY(int unreadMentions READ unreadMentions NOTIFY unreadMentionsChanged) + Q_PROPERTY(Server * server READ getServer NOTIFY serverChanged) public: explicit ChatWindow(QWidget* parent); @@ -156,6 +157,7 @@ void nameChanged(ChatWindow* view, const QString& newName); void titleChanged(); void descriptionChanged(); + void serverChanged(); //void online(ChatWindow* myself, bool state); /** Emit this signal when you want to change the status bar text for this tab. * It is ignored if this tab isn't focused. diff --git a/src/viewer/chatwindow.cpp b/src/viewer/chatwindow.cpp --- a/src/viewer/chatwindow.cpp +++ b/src/viewer/chatwindow.cpp @@ -217,18 +217,19 @@ { if (!newServer) { - qDebug() << "ChatWindow::setServer(0)!"; + qDebug() << "ChatWindow::setServer(nullptr)!"; } else if (m_server != newServer) { - m_server=newServer; + m_server = newServer; + emit serverChanged(); connect(m_server, &Server::serverOnline, this, &ChatWindow::serverOnline); // check if we need to set up the signals if(getType() != ChannelList) { if(textView) textView->setServer(newServer); - else qDebug() << "textView==0!"; + else qDebug() << "textView == nullptr!"; } serverOnline(m_server->isConnected()); diff --git a/src/viewer/viewcontainer.cpp b/src/viewer/viewcontainer.cpp --- a/src/viewer/viewcontainer.cpp +++ b/src/viewer/viewcontainer.cpp @@ -432,30 +432,32 @@ QModelIndex ViewContainer::index(int row, int column, const QModelIndex& parent) const { if (!m_tabWidget || column != 0) { + // we have only one column return QModelIndex(); } int tabIndex = -1; - if (parent.isValid()) { + // This looks like one of server tabs int parentTabIndex = m_tabWidget->indexOf(static_cast(parent.internalPointer())); - if (parentTabIndex != -1) { tabIndex = parentTabIndex + row + 1; } else { return QModelIndex(); } } else { + // We don't have parent, this may be top-level view int count = -1; - for (int i = 0; i < m_tabWidget->count(); ++i) { - if (static_cast(m_tabWidget->widget(i))->isTopLevelView()) { + // There can be more than one connected server, we should + // find correct top-level window for this row + auto window = static_cast(m_tabWidget->widget(i)); + if (window->isTopLevelView()) { ++count; } if (count == row) { tabIndex = i; - break; } } @@ -561,7 +563,7 @@ } else if (role == HighlightRole) { return (row == m_popupViewIndex); } else if (role == ViewRole) { - return qVariantFromValue(static_cast(index.internalPointer())); + return qVariantFromValue(static_cast(index.internalPointer())); } else if (role == IsChild) { return index.parent().isValid(); } else if (role == HasActivity) { @@ -2866,20 +2868,20 @@ { QAction* action = actionCollection()->action("disconnect_server"); if (action) - action->setEnabled(state == Konversation::SSConnected || state == Konversation::SSConnecting || state == Konversation::SSScheduledToConnect); + action->setEnabled(state == Konversation::Connected || state == Konversation::Connecting || state == Konversation::ScheduledToConnect); action = actionCollection()->action("join_channel"); if (action) - action->setEnabled(state == Konversation::SSConnected); + action->setEnabled(state == Konversation::Connected); if (m_frontView && m_frontView->getServer() == server && m_frontView->getType() == ChatWindow::Channel) { ChatWindow* view = m_frontView; Channel* channel = static_cast(view); action = actionCollection()->action("rejoin_channel"); - if (action) action->setEnabled(state == Konversation::SSConnected && channel->rejoinable()); + if (action) action->setEnabled(state == Konversation::Connected && channel->rejoinable()); } } }