From 9ac6ae4ee26819874d8843ba09f38a24088d663d Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Fri, 21 Dec 2007 01:41:01 +0000 Subject: [PATCH] The Quassel Core now remembers on exit which networks where connected and which channels everybody has joined, and restores this information upon restart (so all networks will automagically connected and the appropriate channels rejoined). Has not been tested with more than one active CoreSessions. You can start the core without session restore by using --norestore as parameter. Closes BR #63. --- Quassel.kdevelop.filelist | 3 +++ src/common/main.cpp | 10 ++++++++++ src/common/networkinfo.cpp | 2 +- src/common/networkinfo.h | 4 +++- src/core/core.cpp | 29 +++++++++++++++++++++++++++ src/core/core.h | 3 +++ src/core/coresession.cpp | 34 ++++++++++++++++++++++++++++++-- src/core/coresession.h | 25 +++++++++++++---------- src/core/coresettings.cpp | 8 ++++++++ src/core/coresettings.h | 13 +++++++----- src/core/ircserverhandler.cpp | 1 + src/core/server.cpp | 37 ++++++++++++++++++++++++++++++++--- src/core/server.h | 23 ++++++++++++++-------- src/qtui/mainwin.cpp | 2 +- src/qtui/ui/settingsdlg.ui | 2 +- 15 files changed, 164 insertions(+), 32 deletions(-) diff --git a/Quassel.kdevelop.filelist b/Quassel.kdevelop.filelist index 0bcef570..dfd181fe 100644 --- a/Quassel.kdevelop.filelist +++ b/Quassel.kdevelop.filelist @@ -199,8 +199,11 @@ src/uisupport/inputline.h src/uisupport/nickview.cpp src/uisupport/nickview.h src/uisupport/settingspage.cpp +src/uisupport/settingspage.h src/uisupport/tabcompleter.cpp src/uisupport/tabcompleter.h +src/uisupport/uisettings.cpp +src/uisupport/uisettings.h src/uisupport/uistyle.cpp src/uisupport/uistyle.h src/uisupport/uisupport.pri diff --git a/src/common/main.cpp b/src/common/main.cpp index 00d7c86e..cb935e72 100644 --- a/src/common/main.cpp +++ b/src/common/main.cpp @@ -98,8 +98,18 @@ int main(int argc, char **argv) { gui->init(); #endif +#ifndef BUILD_QTUI + if(!QCoreApplication::arguments().contains("--norestore")) { + Core::restoreState(); + } +#endif + int exitCode = app.exec(); +#ifndef BUILD_QTUI + Core::saveState(); +#endif + #ifndef BUILD_CORE // the mainWin has to be deleted before the Core // if not Quassel will crash on exit under certain conditions since the gui diff --git a/src/common/networkinfo.cpp b/src/common/networkinfo.cpp index 8836a702..89e88ccd 100644 --- a/src/common/networkinfo.cpp +++ b/src/common/networkinfo.cpp @@ -75,7 +75,7 @@ bool NetworkInfo::isMyNick(const QString &nick) const { } bool NetworkInfo::isMyNick(IrcUser *ircuser) const { - return (ircuser->nick().toLower() == myNick()); + return (ircuser->nick().toLower() == myNick().toLower()); } bool NetworkInfo::isChannelName(const QString &channelname) const { diff --git a/src/common/networkinfo.h b/src/common/networkinfo.h index e27c66b4..0c6516bd 100644 --- a/src/common/networkinfo.h +++ b/src/common/networkinfo.h @@ -28,6 +28,8 @@ #include #include +#include "types.h" + class SignalProxy; class IrcUser; class IrcChannel; @@ -44,7 +46,7 @@ public: NetworkInfo(const uint &networkid, QObject *parent = 0); //virtual ~NetworkInfo(); - uint networkId() const; + NetworkId networkId() const; bool initialized() const; SignalProxy *proxy() const; diff --git a/src/core/core.cpp b/src/core/core.cpp index b3abba62..ee93bcd8 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -64,6 +64,7 @@ void Core::init() { connect(&server, SIGNAL(newConnection()), this, SLOT(incomingConnection())); startListening(s.port()); guiUser = 0; + } bool Core::initStorageSqlite(QVariantMap dbSettings, bool setup) { @@ -92,6 +93,34 @@ Core::~Core() { } } +void Core::restoreState() { + Q_ASSERT(!instance()->sessions.count()); + CoreSettings s; + QList users = s.coreState().toList(); + if(users.count() > 0) { + qDebug() << "Restoring previous core state..."; + foreach(QVariant v, users) { + QVariantMap m = v.toMap(); + if(m.contains("UserId")) { + CoreSession *sess = createSession(m["UserId"].toUInt()); + sess->restoreState(m["State"]); + } + } + } +} + +void Core::saveState() { + CoreSettings s; + QList users; + foreach(CoreSession *sess, instance()->sessions.values()) { + QVariantMap m; + m["UserId"] = sess->userId(); + m["State"] = sess->state(); + users << m; + } + s.setCoreState(users); +} + CoreSession *Core::session(UserId uid) { Core *core = instance(); if(core->sessions.contains(uid)) return core->sessions[uid]; diff --git a/src/core/core.h b/src/core/core.h index 85fcd49e..fc20772c 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -46,6 +46,9 @@ class Core : public QObject { static QVariant connectLocalClient(QString user, QString passwd); static void disconnectLocalClient(); + static void saveState(); + static void restoreState(); + private slots: bool startListening(uint port = DEFAULT_PORT); void stopListening(); diff --git a/src/core/coresession.cpp b/src/core/coresession.cpp index dcc5641f..e8072894 100644 --- a/src/core/coresession.cpp +++ b/src/core/coresession.cpp @@ -60,6 +60,7 @@ CoreSession::CoreSession(UserId uid, Storage *_storage, QObject *parent) p->attachSignal(this, SIGNAL(sessionDataChanged(const QString &, const QVariant &)), SIGNAL(coreSessionDataChanged(const QString &, const QVariant &))); p->attachSlot(SIGNAL(clientSessionDataChanged(const QString &, const QVariant &)), this, SLOT(storeSessionData(const QString &, const QVariant &))); /* Autoconnect. (When) do we actually do this? + --> session restore should be enough! QStringList list; QVariantMap networks = retrieveSessionData("Networks").toMap(); foreach(QString net, networks.keys()) { @@ -69,6 +70,7 @@ CoreSession::CoreSession(UserId uid, Storage *_storage, QObject *parent) } qDebug() << list; if(list.count()) connectToIrc(list); */ + } CoreSession::~CoreSession() { @@ -78,6 +80,34 @@ UserId CoreSession::userId() const { return user; } +QVariant CoreSession::state() const { + QVariantMap res; + QList conn; + foreach(Server *server, servers.values()) { + if(server->isConnected()) { + QVariantMap m; + m["Network"] = server->networkName(); + m["State"] = server->state(); + conn << m; + } + } + res["ConnectedServers"] = conn; + return res; +} + +void CoreSession::restoreState(const QVariant &previousState) { + // Session restore + QVariantMap state = previousState.toMap(); + if(state.contains("ConnectedServers")) { + foreach(QVariant v, state["ConnectedServers"].toList()) { + QVariantMap m = v.toMap(); + QString net = m["Network"].toString(); + if(!net.isEmpty()) connectToNetwork(net, m["State"]); + } + } +} + + void CoreSession::storeSessionData(const QString &key, const QVariant &data) { QSettings s; s.beginGroup(QString("SessionData/%1").arg(user)); @@ -100,14 +130,14 @@ QVariant CoreSession::retrieveSessionData(const QString &key, const QVariant &de } // FIXME switch to NetworkIDs -void CoreSession::connectToNetwork(QString network) { +void CoreSession::connectToNetwork(QString network, const QVariant &previousState) { uint networkid = getNetworkId(network); if(networkid == 0) { qWarning() << "unable to connect to Network" << network << "(User:" << userId() << "): unable to determine NetworkId"; return; } if(!servers.contains(networkid)) { - Server *server = new Server(userId(), networkid, network); + Server *server = new Server(userId(), networkid, network, previousState); servers[networkid] = server; attachServer(server); server->start(); diff --git a/src/core/coresession.h b/src/core/coresession.h index 0e183151..ce64b090 100644 --- a/src/core/coresession.h +++ b/src/core/coresession.h @@ -38,18 +38,22 @@ public: CoreSession(UserId, Storage *, QObject *parent = 0); virtual ~CoreSession(); - uint getNetworkId(const QString &network) const; + NetworkId getNetworkId(const QString &network) const; QList buffers() const; UserId userId() const; QVariant sessionState(); //! Retrieve a piece of session-wide data. QVariant retrieveSessionData(const QString &key, const QVariant &def = QVariant()); - + SignalProxy *signalProxy() const; - + void attachServer(Server *server); - + + //! Return necessary data for restoring the session after restarting the core + QVariant state() const; + void restoreState(const QVariant &previousState); + public slots: //! Store a piece session-wide data and distribute it to connected clients. void storeSessionData(const QString &key, const QVariant &data); @@ -57,18 +61,19 @@ public slots: void serverStateRequested(); void addClient(QIODevice *connection); - - void connectToNetwork(QString); - + + void connectToNetwork(QString, const QVariant &previousState = QVariant()); + //void connectToNetwork(NetworkId); + //void processSignal(ClientSignal, QVariant, QVariant, QVariant); void sendBacklog(BufferInfo, QVariant, QVariant); void msgFromGui(BufferInfo, QString message); - + signals: void msgFromGui(uint netid, QString buf, QString message); void displayMsg(Message message); void displayStatusMsg(QString, QString); - + void connectToIrc(QString net); void disconnectFromIrc(QString net); @@ -89,7 +94,7 @@ private: SignalProxy *_signalProxy; Storage *storage; - QHash servers; + QHash servers; QVariantMap sessionData; QMutex mutex; diff --git a/src/core/coresettings.cpp b/src/core/coresettings.cpp index 691a3eb8..3c38b232 100644 --- a/src/core/coresettings.cpp +++ b/src/core/coresettings.cpp @@ -44,6 +44,14 @@ uint CoreSettings::port(const uint &def) { return localValue("Port", def).toUInt(); } +void CoreSettings::setCoreState(const QVariant &data) { + setLocalValue("CoreState", data); +} + +QVariant CoreSettings::coreState(const QVariant &def) { + return localValue("CoreState", def); +} + QStringList CoreSettings::sessionKeys() { Q_ASSERT(false); return QStringList(); diff --git a/src/core/coresettings.h b/src/core/coresettings.h index 6d7bd859..ea1b51b0 100644 --- a/src/core/coresettings.h +++ b/src/core/coresettings.h @@ -25,21 +25,24 @@ #include "settings.h" class CoreSettings : public Settings { - + public: virtual ~CoreSettings(); CoreSettings(); - + void setDatabaseSettings(const QVariant &data); QVariant databaseSettings(const QVariant &def = QVariant()); - + void setPort(const uint &port); uint port(const uint &def = DEFAULT_PORT); - + + void setCoreState(const QVariant &data); + QVariant coreState(const QVariant &def = QVariant()); + private: //virtual QStringList allSessionKeys() = 0; virtual QStringList sessionKeys(); - + virtual void setSessionValue(const QString &key, const QVariant &data); virtual QVariant sessionValue(const QString &key, const QVariant &def = QVariant()); }; diff --git a/src/core/ircserverhandler.cpp b/src/core/ircserverhandler.cpp index ec39c57b..acac5a22 100644 --- a/src/core/ircserverhandler.cpp +++ b/src/core/ircserverhandler.cpp @@ -316,6 +316,7 @@ void IrcServerHandler::handle001(QString prefix, QStringList params) { networkInfo()->setMyNick(nickFromMask(myhostmask)); emit displayMsg(Message::Server, "", params[0], prefix); + // TODO: reimplement perform List! //// send performlist diff --git a/src/core/server.cpp b/src/core/server.cpp index b16b8372..f88efab4 100644 --- a/src/core/server.cpp +++ b/src/core/server.cpp @@ -27,20 +27,23 @@ #include "core.h" #include "coresession.h" +#include "ircuser.h" #include "networkinfo.h" #include "ircserverhandler.h" #include "userinputhandler.h" #include "ctcphandler.h" -Server::Server(UserId uid, uint networkId, QString net) +Server::Server(UserId uid, NetworkId networkId, QString net, const QVariant &state) : _userId(uid), _networkId(networkId), _ircServerHandler(new IrcServerHandler(this)), _userInputHandler(new UserInputHandler(this)), _ctcpHandler(new CtcpHandler(this)), - _networkInfo(new NetworkInfo(networkId, this)) + _networkInfo(new NetworkInfo(networkId, this)), + _previousState(state) { + connect(networkInfo(), SIGNAL(currentServerSet(const QString &)), this, SLOT(sendPerform())); networkInfo()->setNetworkName(net); networkInfo()->setProxy(coreSession()->signalProxy()); } @@ -78,6 +81,34 @@ void Server::connectToIrc(QString net) { socket.connectToHost(host, port); } +void Server::sendPerform() { + // TODO: reimplement perform List! + //// send performlist + //QStringList performList = networkSettings["Perform"].toString().split( "\n" ); + //int count = performList.count(); + //for(int a = 0; a < count; a++) { + // if(!performList[a].isEmpty() ) { + // userInput(network, "", performList[a]); + // } + //} + + // rejoin channels we've been in + QStringList chans = _previousState.toStringList(); + if(chans.count() > 0) { + qDebug() << "autojoining" << chans; + QString list = chans.join(","); + putCmd("join", QStringList(list)); + } + // delete _previousState, we won't need it again + _previousState = QVariant(); +} + +QVariant Server::state() { + IrcUser *me = networkInfo()->ircUser(networkInfo()->myNick()); + if(!me) return QVariant(); // this shouldn't really happen, I guess + return me->channels(); +} + void Server::disconnectFromIrc(QString net) { if(net != networkName()) return; // not me! @@ -143,7 +174,7 @@ uint Server::networkId() const { return _networkId; } -QString Server::networkName() { +QString Server::networkName() const { return networkInfo()->networkName(); } diff --git a/src/core/server.h b/src/core/server.h index c99174cd..32060859 100644 --- a/src/core/server.h +++ b/src/core/server.h @@ -48,7 +48,7 @@ class Server : public QThread { Q_OBJECT public: - Server(UserId uid, uint networkId, QString network); + Server(UserId uid, NetworkId networkId, QString network, const QVariant &previousState = QVariant()); ~Server(); UserId userId() const { return _userId; } @@ -56,13 +56,15 @@ public: // serverState state(); bool isConnected() const { return socket.state() == QAbstractSocket::ConnectedState; } - uint networkId() const; - QString networkName(); // hasbeen getNetwork() + NetworkId networkId() const; + QString networkName() const; // hasbeen getNetwork() - NetworkInfo *networkInfo() { return _networkInfo; } - IrcServerHandler *ircServerHandler() {return _ircServerHandler; } - UserInputHandler *userInputHandler() {return _userInputHandler; } - CtcpHandler *ctcpHandler() {return _ctcpHandler; } + NetworkInfo *networkInfo() const { return _networkInfo; } + IrcServerHandler *ircServerHandler() const { return _ircServerHandler; } + UserInputHandler *userInputHandler() const { return _userInputHandler; } + CtcpHandler *ctcpHandler() const { return _ctcpHandler; } + + QVariant state(); ///< Return data necessary to restore the server's state upon core restart public slots: // void setServerOptions(); @@ -76,6 +78,7 @@ public slots: private slots: void threadFinished(); + void sendPerform(); signals: void serverState(QString net, QVariantMap data); @@ -86,6 +89,8 @@ signals: void connected(uint networkId); void disconnected(uint networkId); + void connectionInitialized(); ///< Emitted after receipt of 001 to indicate that we can now send data to the IRC server + void synchronizeClients(); void queryRequested(QString network, QString nick); @@ -100,7 +105,7 @@ private slots: private: UserId _userId; - uint _networkId; + NetworkId _networkId; QTcpSocket socket; @@ -113,6 +118,8 @@ private: QVariantMap networkSettings; QVariantMap identity; + QVariant _previousState; + CoreSession *coreSession() const; class ParseError : public Exception { diff --git a/src/qtui/mainwin.cpp b/src/qtui/mainwin.cpp index 963ee4b5..10b2b98b 100644 --- a/src/qtui/mainwin.cpp +++ b/src/qtui/mainwin.cpp @@ -150,7 +150,7 @@ void MainWin::setupMenus() { connect(ui.actionNetworkList, SIGNAL(triggered()), this, SLOT(showServerList())); connect(ui.actionEditIdentities, SIGNAL(triggered()), serverListDlg, SLOT(editIdentities())); connect(ui.actionSettingsDlg, SIGNAL(triggered()), this, SLOT(showSettingsDlg())); - ui.actionSettingsDlg->setEnabled(false); + //ui.actionSettingsDlg->setEnabled(false); connect(ui.actionAboutQt, SIGNAL(triggered()), QApplication::instance(), SLOT(aboutQt())); } diff --git a/src/qtui/ui/settingsdlg.ui b/src/qtui/ui/settingsdlg.ui index 683c82d1..8d87a7c1 100644 --- a/src/qtui/ui/settingsdlg.ui +++ b/src/qtui/ui/settingsdlg.ui @@ -48,7 +48,7 @@ - + 0 0 -- 2.20.1