After quite a while, we have another big SVN update now.
authorManuel Nickschas <sputnick@quassel-irc.org>
Sun, 5 Nov 2006 16:01:54 +0000 (16:01 +0000)
committerManuel Nickschas <sputnick@quassel-irc.org>
Sun, 5 Nov 2006 16:01:54 +0000 (16:01 +0000)
Most of the GUI code has been reorganized, and we have a working buffer management now.
Also, we switched the main window to the new layout, which is probably here to stay; of
course it will be enhanced quite a bit in the future :)

Backlogs will be created and displayed whenever the GUI reconnects to the core. We limit
the shown lines to 100 to not use too much time for rendering. This will be configurable in the
future.

There is probably a lot more changes that happened over the past few days which I can't remember,
so you'll have to just see for yourself.

35 files changed:
CMakeLists.txt
Quassel.kdevelop.filelist
core/CMakeLists.txt
core/core.cpp
core/core.h
core/coreproxy.cpp
core/coreproxy.h
core/server.cpp [new file with mode: 0644]
core/server.h [new file with mode: 0644]
gui/CMakeLists.txt
gui/buffer.cpp [new file with mode: 0644]
gui/buffer.h [new file with mode: 0644]
gui/coreconnectdlg.h
gui/guiproxy.cpp
gui/guiproxy.h
gui/mainwin.cpp
gui/mainwin.h
gui/networkview.cpp [new file with mode: 0644]
gui/networkview.h [new file with mode: 0644]
gui/ui/bufferwidget.ui [new file with mode: 0644]
gui/ui/networkview.ui [new file with mode: 0644]
gui/ui/networkwidget.ui [deleted file]
main/global.h
main/main_core.cpp
main/main_gui.cpp
main/main_mono.cpp
main/message.cpp
main/message.h
main/proxy_common.h
main/util.cpp
network/CMakeLists.txt
network/buffer.cpp
network/buffer.h
network/server.cpp
network/server.h

index 589e657..d08682e 100644 (file)
@@ -29,7 +29,7 @@ SET(quassel_mono_SRCS main/main_mono.cpp)
 SET(quassel_core_SRCS main/main_core.cpp)
 SET(quassel_gui_SRCS  main/main_gui.cpp ${common_SRCS})
 SET(quassel_RCCS images/icons.qrc)
-SET(quassel_DIRS main gui core network)
+SET(quassel_DIRS main gui core)
 
 # Build correct absolute paths for subdirs to include
 SET(SDIRS "")
@@ -55,11 +55,9 @@ INCLUDE(${QT_USE_FILE})
 ADD_SUBDIRECTORY(main)
 IF(BUILD_CORE)
   ADD_SUBDIRECTORY(core)
-  ADD_SUBDIRECTORY(network)
 ENDIF(BUILD_CORE)
 IF(BUILD_MONO AND NOT BUILD_CORE)
   ADD_SUBDIRECTORY(core)
-  ADD_SUBDIRECTORY(network)
 ENDIF(BUILD_MONO AND NOT BUILD_CORE)
 
 QT4_ADD_RESOURCES(_RCCS ${quassel_RCCS})
@@ -67,7 +65,7 @@ QT4_ADD_RESOURCES(_RCCS ${quassel_RCCS})
 IF(BUILD_CORE)
   ADD_DEFINITIONS(-DBUILD_CORE)
   ADD_EXECUTABLE(quasselcore ${quassel_core_SRCS} ${_RCCS})
-  TARGET_LINK_LIBRARIES(quasselcore core network main ${QT_LIBRARIES})
+  TARGET_LINK_LIBRARIES(quasselcore core main ${QT_LIBRARIES})
 ENDIF(BUILD_CORE)
 
 IF(BUILD_GUI OR BUILD_MONO)  # OK, now we need QtGui!
@@ -81,7 +79,7 @@ IF(BUILD_GUI OR BUILD_MONO)  # OK, now we need QtGui!
   IF(BUILD_MONO)
     ADD_DEFINITIONS(-DBUILD_MONO)
     ADD_EXECUTABLE(quassel ${quassel_mono_SRCS} ${_RCCS})
-    TARGET_LINK_LIBRARIES(quassel gui core network main ${QT_LIBRARIES})
+    TARGET_LINK_LIBRARIES(quassel gui core main ${QT_LIBRARIES})
   ENDIF(BUILD_MONO)
 
   IF(BUILD_GUI)
index 0080ef1..5cc32b4 100644 (file)
@@ -48,3 +48,25 @@ network/buffer.h
 gui/channelwidgetinput.h
 gui/channelwidgetinput.cpp
 plugins/plugin.h
+gui/ui/channelwidget.ui
+gui/ui/coreconnectdlg.ui
+gui/ui/identitiesdlg.ui
+gui/ui/identitieseditdlg.ui
+gui/ui/ircwidget.ui
+gui/ui/mainwin.ui
+gui/ui/networkeditdlg.ui
+gui/ui/networkwidget.ui
+gui/ui/nickeditdlg.ui
+gui/ui/servereditdlg.ui
+gui/ui/serverlistdlg.ui
+gui/ui/settingsdlg.ui
+gui/ui/networkview.ui
+gui/networkview.cpp
+gui/networkview.h
+gui/buffer.cpp
+gui/buffer.h
+main/message.cpp
+main/message.h
+gui/ui/bufferwidget.ui
+core/server.cpp
+core/server.h
index bde1fb0..484f994 100644 (file)
@@ -1,6 +1,6 @@
-SET(core_SRCS core.cpp coreproxy.cpp)
+SET(core_SRCS core.cpp coreproxy.cpp server.cpp)
 SET(core_HDRS )
-SET(core_MOCS core.h coreproxy.h)
+SET(core_MOCS core.h coreproxy.h server.h)
 
 QT4_WRAP_CPP(_MOC ${core_MOCS})
 ADD_LIBRARY(core ${_MOC} ${core_SRCS} ${core_HDRS})
index f9f4f23..b93c96e 100644 (file)
 Core::Core() {
   if(core) qFatal("Trying to instantiate more than one Core object!");
 
+  connect(coreProxy, SIGNAL(requestServerStates()), this, SIGNAL(serverStateRequested()));
   connect(coreProxy, SIGNAL(gsRequestConnect(QStringList)), this, SLOT(connectToIrc(QStringList)));
   connect(coreProxy, SIGNAL(gsUserInput(QString, QString, QString)), this, SIGNAL(msgFromGUI(QString, QString, QString)));
-  connect(this, SIGNAL(displayMsg(QString, QString, Message)), coreProxy, SLOT(csDisplayMsg(QString, QString, Message)));
+  connect(this, SIGNAL(displayMsg(QString, Message)), coreProxy, SLOT(csDisplayMsg(QString, Message)));
   connect(this, SIGNAL(displayStatusMsg(QString, QString)), coreProxy, SLOT(csDisplayStatusMsg(QString, QString)));
 
   // Read global settings from config file
@@ -49,6 +50,19 @@ Core::Core() {
 
 }
 
+Core::~Core() {
+  //foreach(Server *s, servers) {
+  //  delete s;
+  //}
+  foreach(QDataStream *s, logStreams) {
+    delete s;
+  }
+  foreach(QFile *f, logFiles) {
+    if(f->isOpen()) f->close();
+    delete f;
+  }
+}
+
 void Core::globalDataUpdated(QString key) {
   QVariant data = global->getData(key);
   QSettings s;
@@ -61,10 +75,12 @@ void Core::connectToIrc(QStringList networks) {
 
     } else {
       Server *server = new Server(net);
+      connect(this, SIGNAL(serverStateRequested()), server, SLOT(sendState()));
       connect(this, SIGNAL(connectToIrc(QString)), server, SLOT(connectToIrc(QString)));
       connect(this, SIGNAL(disconnectFromIrc(QString)), server, SLOT(disconnectFromIrc(QString)));
       connect(this, SIGNAL(msgFromGUI(QString, QString, QString)), server, SLOT(userInput(QString, QString, QString)));
-      connect(server, SIGNAL(displayMsg(QString, Message)), this, SLOT(recvMessageFromServer(QString, Message)));
+      connect(server, SIGNAL(serverState(QString, VarMap)), coreProxy, SLOT(csServerState(QString, VarMap)));
+      connect(server, SIGNAL(displayMsg(Message)), this, SLOT(recvMessageFromServer(Message)));
       connect(server, SIGNAL(displayStatusMsg(QString)), this, SLOT(recvStatusMsgFromServer(QString)));
       connect(server, SIGNAL(modeSet(QString, QString, QString)), coreProxy, SLOT(csModeSet(QString, QString, QString)));
       connect(server, SIGNAL(topicSet(QString, QString, QString)), coreProxy, SLOT(csTopicSet(QString, QString, QString)));
@@ -75,6 +91,8 @@ void Core::connectToIrc(QStringList networks) {
       connect(server, SIGNAL(nickUpdated(QString, QString, VarMap)), coreProxy, SLOT(csNickUpdated(QString, QString, VarMap)));
       connect(server, SIGNAL(ownNickSet(QString, QString)), coreProxy, SLOT(csOwnNickSet(QString, QString)));
       // add error handling
+      connect(server, SIGNAL(connected(QString)), coreProxy, SLOT(csServerConnected(QString)));
+      connect(server, SIGNAL(disconnected(QString)), this, SLOT(serverDisconnected(QString)));
 
       server->start();
       servers[net] = server;
@@ -83,13 +101,19 @@ void Core::connectToIrc(QStringList networks) {
   }
 }
 
+void Core::serverDisconnected(QString net) {
+  delete servers[net];
+  servers.remove(net);
+  coreProxy->csServerDisconnected(net);
+}
+
 // ALL messages coming pass through these functions before going to the GUI.
 // So this is the perfect place for storing the backlog and log stuff.
-void Core::recvMessageFromServer(QString buf, Message msg) {
+void Core::recvMessageFromServer(Message msg) {
   Server *s = qobject_cast<Server*>(sender());
   Q_ASSERT(s);
-  logMessage(msg);
-  emit displayMsg(s->getNetwork(), buf, msg);
+  logMessage(s->getNetwork(), msg);
+  emit displayMsg(s->getNetwork(), msg);
 }
 
 void Core::recvStatusMsgFromServer(QString msg) {
@@ -115,35 +139,44 @@ void Core::initBackLog() {
   //  backLogEnabled = false;
   //  return;
   //}
-  QStringList logs = backLogDir.entryList(QStringList("quassel-backlog-*.bin"), QDir::Files|QDir::Readable, QDir::Name);
-  foreach(QString name, logs) {
-    QFile f(backLogDir.absolutePath() + "/" + name);
-    if(!f.open(QIODevice::ReadOnly)) {
-      qWarning(QString("Could not open \"%1\" for reading!").arg(f.fileName()).toAscii());
+  QStringList networks = backLogDir.entryList(QDir::Dirs|QDir::NoDotAndDotDot|QDir::Readable, QDir::Name);
+  foreach(QString net, networks) {
+    QDir dir(backLogDir.absolutePath() + "/" + net);
+    if(!dir.exists()) {
+      qWarning(QString("Could not change to directory \"%1\"!").arg(dir.absolutePath()).toAscii());
       continue;
     }
-    QDataStream in(&f);
-    in.setVersion(QDataStream::Qt_4_2);
-    QByteArray verstring; quint8 vernum; in >> verstring >> vernum;
-    if(verstring != BACKLOG_STRING) {
-      qWarning(QString("\"%1\" is not a Quassel backlog file!").arg(f.fileName()).toAscii());
-      f.close(); continue;
-    }
-    if(vernum != BACKLOG_FORMAT) {
-      qWarning(QString("\"%1\": Version mismatch!").arg(f.fileName()).toAscii());
-      f.close(); continue;
-    }
-    qDebug() << "Reading backlog from" << f.fileName();
-    currentLogFileDate = QDate::fromString(f.fileName(), QString("'%1/quassel-backlog-'yyyy-MM-dd'.bin'").arg(backLogDir.absolutePath()));
-    if(!currentLogFileDate.isValid()) {
-      qWarning(QString("\"%1\" has an invalid file name!").arg(f.fileName()).toAscii());
+    QStringList logs = dir.entryList(QStringList("quassel-backlog-*.bin"), QDir::Files|QDir::Readable, QDir::Name);
+    foreach(QString name, logs) {
+      QFile f(dir.absolutePath() + "/" + name);
+      if(!f.open(QIODevice::ReadOnly)) {
+        qWarning(QString("Could not open \"%1\" for reading!").arg(f.fileName()).toAscii());
+        continue;
+      }
+      QDataStream in(&f);
+      in.setVersion(QDataStream::Qt_4_2);
+      QByteArray verstring; quint8 vernum; in >> verstring >> vernum;
+      if(verstring != BACKLOG_STRING) {
+        qWarning(QString("\"%1\" is not a Quassel backlog file!").arg(f.fileName()).toAscii());
+        f.close(); continue;
+      }
+      if(vernum != BACKLOG_FORMAT) {
+        qWarning(QString("\"%1\": Version mismatch!").arg(f.fileName()).toAscii());
+        f.close(); continue;
+      }
+      qDebug() << "Reading backlog from" << f.fileName();
+      logFileDates[net] = QDate::fromString(f.fileName(),
+          QString("'%1/quassel-backlog-'yyyy-MM-dd'.bin'").arg(dir.absolutePath()));
+      if(!logFileDates[net].isValid()) {
+        qWarning(QString("\"%1\" has an invalid file name!").arg(f.fileName()).toAscii());
+      }
+      while(!in.atEnd()) {
+        Message m;
+        in >> m;
+        backLog[net].append(m);
+      }
+      f.close();
     }
-    while(!in.atEnd()) {
-      Message m;
-      in >> m;
-      backLog.append(m);
-    }
-    f.close();
   }
   backLogEnabled = true;
 }
@@ -153,22 +186,38 @@ void Core::initBackLog() {
  * The file header is the string defined by BACKLOG_STRING, followed by a quint8 specifying the format
  * version (BACKLOG_FORMAT). The rest is simply serialized Message objects.
  */
-void Core::logMessage(Message msg) {
-  backLog.append(msg);
-  if(!currentLogFileDate.isValid() || currentLogFileDate < QDate::currentDate()) {
-    if(currentLogFile.isOpen()) currentLogFile.close();
-    currentLogFileDate = QDate::currentDate();
+void Core::logMessage(QString net, Message msg) {
+  backLog[net].append(msg);
+  if(!logFileDirs.contains(net)) {
+    QDir dir(backLogDir.absolutePath() + "/" + net);
+    if(!dir.exists()) {
+      qWarning(QString("Creating backlog directory \"%1\"...").arg(dir.absolutePath()).toAscii());
+      if(!dir.mkpath(dir.absolutePath())) {
+        qWarning(QString("Could not create backlog directory!").toAscii());
+        return;
+      }
+    }
+    logFileDirs[net] = dir;
+    Q_ASSERT(!logFiles.contains(net) && !logStreams.contains(net));
+    if(!logFiles.contains(net)) logFiles[net] = new QFile();
+    if(!logStreams.contains(net)) logStreams[net] = new QDataStream();
+  }
+  if(!logFileDates[net].isValid() || logFileDates[net] < QDate::currentDate()) {
+    if(logFiles[net]->isOpen()) logFiles[net]->close();
+    logFileDates[net] = QDate::currentDate();
   }
-  if(!currentLogFile.isOpen()) {
-    currentLogFile.setFileName(backLogDir.absolutePath() + "/" + currentLogFileDate.toString("'quassel-backlog-'yyyy-MM-dd'.bin'"));
-    if(!currentLogFile.open(QIODevice::WriteOnly|QIODevice::Append)) {
-      qWarning(QString("Could not open \"%1\" for writing: %2").arg(currentLogFile.fileName()).arg(currentLogFile.errorString()).toAscii());
+  if(!logFiles[net]->isOpen()) {
+    logFiles[net]->setFileName(QString("%1/%2").arg(logFileDirs[net].absolutePath())
+        .arg(logFileDates[net].toString("'quassel-backlog-'yyyy-MM-dd'.bin'")));
+    if(!logFiles[net]->open(QIODevice::WriteOnly|QIODevice::Append|QIODevice::Unbuffered)) {
+      qWarning(QString("Could not open \"%1\" for writing: %2")
+          .arg(logFiles[net]->fileName()).arg(logFiles[net]->errorString()).toAscii());
       return;
     }
-    logStream.setDevice(&currentLogFile); logStream.setVersion(QDataStream::Qt_4_2);
-    if(!currentLogFile.size()) logStream << BACKLOG_STRING << (quint8)BACKLOG_FORMAT;
+    logStreams[net]->setDevice(logFiles[net]); logStreams[net]->setVersion(QDataStream::Qt_4_2);
+    if(!logFiles[net]->size()) *logStreams[net] << BACKLOG_STRING << (quint8)BACKLOG_FORMAT;
   }
-  logStream << msg;
+  *logStreams[net] << msg;
 }
 
 Core *core = 0;
index f5532c3..b2a3125 100644 (file)
@@ -33,35 +33,42 @@ class Core : public QObject {
   public:
 
     Core();
-    //~Core();
+    ~Core();
+    QHash<QString, QList<Message> > getBackLog() { return backLog; };
 
   public slots:
     void connectToIrc(QStringList);
 
   signals:
     void msgFromGUI(QString network, QString channel, QString message);
-    void displayMsg(QString network, QString channel, Message message);
+    void displayMsg(QString network, Message message);
     void displayStatusMsg(QString, QString);
 
     void connectToIrc(QString net);
     void disconnectFromIrc(QString net);
+    void serverStateRequested();
 
   private slots:
+    //void serverStatesRequested();
     void globalDataUpdated(QString);
     void recvStatusMsgFromServer(QString msg);
-    void recvMessageFromServer(QString buffer, Message msg);
+    void recvMessageFromServer(Message msg);
+    void serverDisconnected(QString net);
 
   private:
-    QHash<QString, Server *> servers;
-    QList<Message> backLog;
-    bool backLogEnabled;
     QDir backLogDir;
-    QFile currentLogFile;
-    QDataStream logStream;
-    QDate currentLogFileDate;
+    bool backLogEnabled;
+    QHash<QString, Server *> servers;
+    QHash<QString, QList<Message> > backLog;
+    //QHash<QString, int> netIdx;
+    QHash<QString, QFile *> logFiles;
+    QHash<QString, QDataStream *> logStreams;
+    QHash<QString, QDate> logFileDates;
+    QHash<QString, QDir> logFileDirs;
 
+    //uint getNetIdx(QString net);
     void initBackLog();
-    void logMessage(Message);
+    void logMessage(QString, Message);
 
 };
 
index 9138047..11fdf95 100644 (file)
 #include "coreproxy.h"
 #include "global.h"
 #include "util.h"
+#include "core.h"
 
 CoreProxy::CoreProxy() {
   if(coreProxy) qFatal("Trying to instantiate more than one CoreProxy object!");
+  coreProxy = this;
+  core = new Core();
 
   connect(global, SIGNAL(dataPutLocally(QString)), this, SLOT(updateGlobalData(QString)));
   connect(&server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
@@ -85,11 +88,19 @@ void CoreProxy::processClientInit(QTcpSocket *socket, const QVariant &v) {
     coreData[key] = global->getData(key);
   }
   reply["CoreData"] = coreData;
-  //QVariant payload = QByteArray(1000000, 'a');
-  //reply["payload"] = payload;
+  VarMap bl;
+  QHash<QString, QList<Message> > log = core->getBackLog();
+  foreach(QString net, log.keys()) {
+    QByteArray buf;
+    QDataStream out(&buf, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_4_2);
+    foreach(Message msg, log[net]) { out << msg; }
+    bl[net] = buf;
+  }
+  reply["CoreBackLog"] = bl;
   QList<QVariant> sigdata;
   sigdata.append(CS_CORE_STATE); sigdata.append(QVariant(reply)); sigdata.append(QVariant()); sigdata.append(QVariant());
   writeDataToDevice(socket, QVariant(sigdata));
+  emit requestServerStates();
 }
 
 void CoreProxy::processClientUpdate(QTcpSocket *socket, QString key, QVariant data) {
index 4f6185a..c8850a3 100644 (file)
@@ -41,7 +41,10 @@ class CoreProxy : public QObject {
 
   public slots:
     inline void csUpdateGlobalData(QString key, QVariant data)          { send(CS_UPDATE_GLOBAL_DATA, key, data); }
-    inline void csDisplayMsg(QString net, QString buf, Message msg)     { send(CS_DISPLAY_MSG, net, buf, QVariant::fromValue(msg)); }
+    inline void csServerConnected(QString net)                          { send(CS_SERVER_CONNECTED, net); }
+    inline void csServerDisconnected(QString net)                       { send(CS_SERVER_DISCONNECTED, net); }
+    inline void csServerState(QString net, VarMap data)                 { send(CS_SERVER_STATE, net, data); }
+    inline void csDisplayMsg(QString net, Message msg)                  { send(CS_DISPLAY_MSG, net, QVariant::fromValue(msg)); }
     inline void csDisplayStatusMsg(QString net, QString msg)            { send(CS_DISPLAY_STATUS_MSG, net, msg); }
     inline void csModeSet(QString net, QString target, QString mode)    { send(CS_MODE_SET, net, target, mode); }
     inline void csTopicSet(QString net, QString buf, QString topic)     { send(CS_TOPIC_SET, net, buf, topic); }
@@ -57,6 +60,8 @@ class CoreProxy : public QObject {
     void gsUserInput(QString, QString, QString);
     void gsRequestConnect(QStringList networks);
 
+    void requestServerStates();
+
   private:
     void send(CoreSignal, QVariant arg1 = QVariant(), QVariant arg2 = QVariant(), QVariant arg3 = QVariant());
     void recv(GUISignal, QVariant arg1 = QVariant(), QVariant arg2 = QVariant(), QVariant arg3 = QVariant());
diff --git a/core/server.cpp b/core/server.cpp
new file mode 100644 (file)
index 0000000..eeb4693
--- /dev/null
@@ -0,0 +1,657 @@
+/***************************************************************************
+ *   Copyright (C) 2005/06 by The Quassel Team                             *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#include "util.h"
+#include "global.h"
+#include "server.h"
+#include "message.h"
+
+#include <QMetaObject>
+#include <QDateTime>
+
+Server::Server(QString net) : network(net) {
+
+}
+
+Server::~Server() {
+
+}
+
+void Server::run() {
+  connect(&socket, SIGNAL(connected()), this, SLOT(socketConnected()));
+  connect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
+  connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
+  connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
+  connect(&socket, SIGNAL(readyRead()), this, SLOT(socketHasData()));
+
+  exec();
+}
+
+void Server::sendState() {
+  VarMap s;
+  VarMap n, t;
+  foreach(QString key, nicks.keys())  { n[key] = nicks[key]; }
+  foreach(QString key, topics.keys()) { t[key] = topics[key];}
+  s["Nicks"] = n;
+  s["Topics"] = t;
+  s["OwnNick"] = ownNick;
+  s["ServerSupports"] = serverSupports;
+  emit serverState(network, s);
+}
+
+void Server::connectToIrc(QString net) {
+  if(net != network) return; // not me!
+  networkSettings = global->getData("Networks").toMap()[net].toMap();
+  identity = global->getData("Identities").toMap()[networkSettings["Identity"].toString()].toMap();
+  QList<QVariant> servers = networkSettings["Servers"].toList();
+  QString host = servers[0].toMap()["Address"].toString();
+  quint16 port = servers[0].toMap()["Port"].toUInt();
+  displayStatusMsg(QString("Connecting to %1:%2...").arg(host).arg(port));
+  socket.connectToHost(host, port);
+}
+
+void Server::disconnectFromIrc(QString net) {
+  if(net != network) return; // not me!
+  socket.disconnectFromHost();
+}
+
+void Server::socketHasData() {
+  while(socket.canReadLine()) {
+    QString s = socket.readLine().trimmed();
+    qDebug() << "Read" << s;
+    emit recvRawServerMsg(s);
+    //Message *msg = Message::createFromServerString(this, s);
+    handleServerMsg(s);
+  }
+}
+
+void Server::socketError( QAbstractSocket::SocketError err ) {
+  //qDebug() << "Socket Error!";
+  //emit error(err);
+}
+
+void Server::socketConnected( ) {
+  emit connected(network);
+  putRawLine(QString("NICK :%1").arg(identity["NickList"].toStringList()[0]));
+  putRawLine(QString("USER %1 8 * :%2").arg(identity["Ident"].toString()).arg(identity["RealName"].toString()));
+}
+
+void Server::socketDisconnected( ) {
+  //qDebug() << "Socket disconnected!";
+  emit disconnected(network);
+  topics.clear();
+  nicks.clear();
+}
+
+void Server::socketStateChanged(QAbstractSocket::SocketState state) {
+  //qDebug() << "Socket state changed: " << state;
+}
+
+QString Server::updateNickFromMask(QString mask) {
+  QString user = userFromMask(mask);
+  QString host = hostFromMask(mask);
+  QString nick = nickFromMask(mask);
+  if(nicks.contains(nick) && !user.isEmpty() && !host.isEmpty()) {
+    VarMap n = nicks[nick];
+    if(n["User"].toString() != user || n["Host"].toString() != host) {
+      if(!n["User"].toString().isEmpty() || !n["Host"].toString().isEmpty())
+        qWarning(QString("Strange: Hostmask for nick %1 has changed!").arg(nick).toAscii());
+      n["User"] = user; n["Host"] = host;
+      nicks[nick] = n;
+      emit nickUpdated(network, nick, n);
+    }
+  }
+  return nick;
+}
+
+void Server::userInput(QString net, QString buf, QString msg) {
+  if(net != network) return; // not me!
+  //msg = msg.trimmed(); // remove whitespace from start and end
+  if(msg.isEmpty()) return;
+  if(!msg.startsWith('/')) {
+    msg = QString("/SAY ") + msg;
+  }
+  handleUserInput(buf, msg);
+}
+
+void Server::putRawLine(QString s) {
+  qDebug() << "SentRaw: " << s;
+  s += "\r\n";
+  socket.write(s.toAscii());
+}
+
+void Server::putCmd(QString cmd, QStringList params, QString prefix) {
+  QString m;
+  if(!prefix.isEmpty()) m += ":" + prefix + " ";
+  m += cmd.toUpper();
+  for(int i = 0; i < params.size() - 1; i++) {
+    m += " " + params[i];
+  }
+  if(!params.isEmpty()) m += " :" + params.last();
+  qDebug() << "Sent: " << m;
+  m += "\r\n";
+  socket.write(m.toAscii());
+}
+
+/** Handle a raw message string sent by the server. We try to find a suitable handler, otherwise we call a default handler. */
+void Server::handleServerMsg(QString msg) {
+  try {
+    if(msg.isEmpty()) {
+      qWarning() << "Received empty string from server!";
+      return;
+    }
+    // OK, first we split the raw message into its various parts...
+    QString prefix;
+    QString cmd;
+    QStringList params;
+    if(msg[0] == ':') {
+      msg.remove(0,1);
+      prefix = msg.section(' ', 0, 0);
+      msg = msg.section(' ', 1);
+    }
+    cmd = msg.section(' ', 0, 0).toUpper();
+    msg = msg.section(' ', 1);
+    QString left, trailing;
+    // RPL_ISUPPORT (005) can contain colons, so don't treat it like the rest of the commands
+    if(cmd.toUInt() == 5) {
+      left = msg.remove(QString(":are supported by this server"));
+    } else {
+      left = msg.section(':', 0, 0);
+      trailing = msg.section(':', 1);
+    }
+    if(!left.isEmpty()) {
+      params << left.split(' ', QString::SkipEmptyParts);
+    }
+    if(!trailing.isEmpty()) {
+      params << trailing;
+    }
+    // numeric replies usually have our own nick as first param. Remove this!
+    // BTW, this behavior is not in the RFC.
+    uint num = cmd.toUInt();
+    if(num > 1 && params.count() > 0) {  // 001 sets our nick, so we shouldn't remove anything
+      if(params[0] == ownNick) params.removeFirst();
+      else qWarning((QString("First param NOT nick: %1:%2 %3").arg(prefix).arg(cmd).arg(params.join(" "))).toAscii());
+    }
+    // Now we try to find a handler for this message. BTW, I do love the Trolltech guys ;-)
+    QString hname = cmd.toLower();
+    hname[0] = hname[0].toUpper();
+    hname = "handleServer" + hname;
+    if(!QMetaObject::invokeMethod(this, hname.toAscii(), Q_ARG(QString, prefix), Q_ARG(QStringList, params))) {
+      // Ok. Default handler it is.
+      defaultServerHandler(cmd, prefix, params);
+    }
+  } catch(Exception e) {
+    emit displayMsg(Message::error("", e.msg()));
+  }
+}
+
+void Server::defaultServerHandler(QString cmd, QString prefix, QStringList params) {
+  uint num = cmd.toUInt();
+  if(num) {
+    // A lot of server messages don't really need their own handler because they don't do much.
+    // Catch and handle these here.
+    switch(num) {
+      // Welcome, status, info messages. Just display these.
+      case 2: case 3: case 4: case 5: case 251: case 252: case 253: case 254: case 255: case 372: case 375:
+        emit displayMsg(Message::server("", params.join(" "), prefix));
+        break;
+      // Server error messages without param, just display them
+      case 409: case 411: case 412: case 422: case 424: case 431: case 445: case 446: case 451: case 462:
+      case 463: case 464: case 465: case 466: case 472: case 481: case 483: case 485: case 491: case 501: case 502:
+        emit displayMsg(Message::error("", params.join(" "), prefix));
+        break;
+      // Server error messages, display them in red. First param will be appended.
+      case 401: case 402: case 403: case 404: case 406: case 408: case 415: case 421: case 432: case 442:
+      { QString p = params.takeFirst();
+        emit displayMsg(Message::error("", params.join(" ") + " " + p, prefix));
+        break;
+      }
+      // Server error messages which will be displayed with a colon between the first param and the rest
+      case 413: case 414: case 423: case 433: case 436: case 441: case 444: case 461:
+      case 467: case 471: case 473: case 474: case 475: case 476: case 477: case 478: case 482:
+      { QString p = params.takeFirst();
+        emit displayMsg(Message::error("", p + ": " + params.join(" ")));
+        break;
+      }
+      // Ignore these commands.
+      case 366: case 376:
+        break;
+
+      // Everything else will be marked in red, so we can add them somewhere.
+      default:
+        emit displayMsg(Message::error("", cmd + " " + params.join(" "), prefix));
+    }
+    //qDebug() << prefix <<":"<<cmd<<params;
+  } else {
+    emit displayMsg(Message::error("", QString("Unknown: ") + cmd + " " + params.join(" "), prefix));
+    //qDebug() << prefix <<":"<<cmd<<params;
+  }
+}
+
+void Server::handleUserInput(QString bufname, QString usrMsg) {
+  try {
+    /* Looks like we don't need core-side buffers...
+    Buffer *buffer = 0;
+    if(!bufname.isEmpty()) {
+      Q_ASSERT(buffers.contains(bufname));
+      buffer = buffers[bufname];
+    }
+    */
+    QString cmd = usrMsg.section(' ', 0, 0).remove(0, 1).toUpper();
+    QString msg = usrMsg.section(' ', 1);
+    QString hname = cmd.toLower();
+    hname[0] = hname[0].toUpper();
+    hname = "handleUser" + hname;
+    if(!QMetaObject::invokeMethod(this, hname.toAscii(), Q_ARG(QString, bufname), Q_ARG(QString, msg))) {
+        // Ok. Default handler it is.
+      defaultUserHandler(bufname, cmd, msg);
+    }
+  } catch(Exception e) {
+    emit displayMsg(Message::error("", e.msg()));
+  }
+}
+
+void Server::defaultUserHandler(QString bufname, QString cmd, QString msg) {
+  emit displayMsg(Message::error("", QString("Error: %1 %2").arg(cmd).arg(msg)));
+
+}
+
+/**********************************************************************************/
+
+/*
+void Server::handleUser(QString bufname, QString msg) {
+
+
+}
+*/
+
+void Server::handleUserAway(QString bufname, QString msg) {
+  putCmd("AWAY", QStringList(msg));
+}
+
+void Server::handleUserDeop(QString bufname, QString msg) {
+  QStringList nicks = msg.split(' ', QString::SkipEmptyParts);
+  QString m = "-"; for(int i = 0; i < nicks.count(); i++) m += 'o';
+  QStringList params;
+  params << bufname << m << nicks;
+  putCmd("MODE", params);
+}
+
+void Server::handleUserDevoice(QString bufname, QString msg) {
+  QStringList nicks = msg.split(' ', QString::SkipEmptyParts);
+  QString m = "-"; for(int i = 0; i < nicks.count(); i++) m += 'v';
+  QStringList params;
+  params << bufname << m << nicks;
+  putCmd("MODE", params);
+}
+
+void Server::handleUserInvite(QString bufname, QString msg) {
+  QStringList params;
+  params << msg << bufname;
+  putCmd("INVITE", params);
+}
+
+void Server::handleUserJoin(QString bufname, QString msg) {
+  putCmd("JOIN", QStringList(msg));
+}
+
+void Server::handleUserKick(QString bufname, QString msg) {
+  QStringList params;
+  params << bufname << msg.split(' ', QString::SkipEmptyParts);
+  putCmd("KICK", params);
+}
+
+void Server::handleUserList(QString bufname, QString msg) {
+  putCmd("LIST", msg.split(' ', QString::SkipEmptyParts));
+}
+
+void Server::handleUserMode(QString bufname, QString msg) {
+  putCmd("MODE", msg.split(' ', QString::SkipEmptyParts));
+}
+
+void Server::handleUserMsg(QString bufname, QString msg) {
+  QString nick = msg.section(" ", 0, 0);
+  msg = msg.section(" ", 1);
+  if(nick.isEmpty() || msg.isEmpty()) return;
+  QStringList params;
+  params << nick << msg;
+  putCmd("PRIVMSG", params);
+}
+
+void Server::handleUserNick(QString bufname, QString msg) {
+  QString nick = msg.section(' ', 0, 0);
+  putCmd("NICK", QStringList(nick));
+}
+
+void Server::handleUserOp(QString bufname, QString msg) {
+  QStringList nicks = msg.split(' ', QString::SkipEmptyParts);
+  QString m = "+"; for(int i = 0; i < nicks.count(); i++) m += 'o';
+  QStringList params;
+  params << bufname << m << nicks;
+  putCmd("MODE", params);
+}
+
+void Server::handleUserPart(QString bufname, QString msg) {
+  QStringList params;
+  params << bufname << msg;
+  putCmd("PART", params);
+}
+
+void Server::handleUserQuit(QString bufname, QString msg) {
+  putCmd("QUIT", QStringList(msg));
+}
+
+void Server::handleUserQuote(QString bufname, QString msg) {
+  putRawLine(msg);
+}
+
+void Server::handleUserSay(QString bufname, QString msg) {
+  if(bufname.isEmpty()) return;  // server buffer
+  QStringList params;
+  params << bufname << msg;
+  putCmd("PRIVMSG", params);
+  emit displayMsg(Message::plain(params[0], msg, ownNick, Message::Self));
+}
+
+void Server::handleUserTopic(QString bufname, QString msg) {
+  if(bufname.isEmpty()) return;
+  QStringList params;
+  params << bufname << msg;
+  putCmd("TOPIC", params);
+}
+
+void Server::handleUserVoice(QString bufname, QString msg) {
+  QStringList nicks = msg.split(' ', QString::SkipEmptyParts);
+  QString m = "+"; for(int i = 0; i < nicks.count(); i++) m += 'v';
+  QStringList params;
+  params << bufname << m << nicks;
+  putCmd("MODE", params);
+}
+
+/**********************************************************************************/
+
+void Server::handleServerJoin(QString prefix, QStringList params) {
+  Q_ASSERT(params.count() == 1);
+  QString nick = updateNickFromMask(prefix);
+  if(nick == ownNick) {
+  //  Q_ASSERT(!buffers.contains(params[0]));  // cannot join a buffer twice!
+  //  Buffer *buf = new Buffer(params[0]);
+  //  buffers[params[0]] = buf;
+    topics[params[0]] = "";
+    emit topicSet(network, params[0], "");
+  } //else {
+    VarMap n;
+    if(nicks.contains(nick)) {
+      n = nicks[nick];
+      VarMap chans = n["Channels"].toMap();
+      // Q_ASSERT(!chans.keys().contains(params[0])); TODO uncomment
+      chans[params[0]] = VarMap();
+      n["Channels"] = chans;
+      nicks[nick] = n;
+      emit nickUpdated(network, nick, n);
+    } else {
+      VarMap chans;
+      chans[params[0]] = VarMap();
+      n["Channels"] = chans;
+      n["User"] = userFromMask(prefix);
+      n["Host"] = hostFromMask(prefix);
+      nicks[nick] = n;
+      emit nickAdded(network, nick, n);
+    }
+    emit displayMsg(Message::join(params[0], params[0], prefix));
+  //}
+}
+
+void Server::handleServerKick(QString prefix, QStringList params) {
+  QString kicker = updateNickFromMask(prefix);
+  QString nick = params[1];
+  Q_ASSERT(nicks.contains(nick));
+  VarMap n = nicks[nick];
+  VarMap chans = n["Channels"].toMap();
+  Q_ASSERT(chans.contains(params[0]));
+  chans.remove(params[0]);
+  QString msg = nick;
+  if(params.count() > 2) msg = QString("%1 %2").arg(msg).arg(params[2]);
+  emit displayMsg(Message::kick(params[0], msg, prefix));
+  if(chans.count() > 0) {
+    n["Channels"] = chans;
+    nicks[nick] = n;
+    emit nickUpdated(network, nick, n);
+  } else {
+    nicks.remove(nick);
+    emit nickRemoved(network, nick);
+  }
+  if(nick == ownNick) {
+    topics.remove(params[0]);
+  }
+}
+
+void Server::handleServerMode(QString prefix, QStringList params) {
+  if(isChannelName(params[0])) {
+    // TODO only channel-user modes supported by now
+    QString prefixes = serverSupports["PrefixModes"].toString();
+    QString modes = params[1];
+    int p = 2;
+    int m = 0;
+    bool add = true;
+    while(m < modes.length()) {
+      if(modes[m] == '+') { add = true; m++; continue; }
+      if(modes[m] == '-') { add = false; m++; continue; }
+      if(prefixes.contains(modes[m])) {  // it's a user channel mode
+        Q_ASSERT(params.count() > m);
+        QString nick = params[p++];
+        if(nicks.contains(nick)) {  // sometimes, a server might try to set a MODE on a nick that is no longer there
+          VarMap n = nicks[nick]; VarMap clist = n["Channels"].toMap(); VarMap chan = clist[params[0]].toMap();
+          QString mstr = chan["Mode"].toString();
+          add ? mstr += modes[m] : mstr.remove(modes[m]);
+          chan["Mode"] = mstr; clist[params[0]] = chan; n["Channels"] = clist; nicks[nick] = n;
+          emit nickUpdated(network, nick, n);
+        }
+        m++;
+      } else {
+        // TODO add more modes
+        m++;
+      }
+    }
+    emit displayMsg(Message::mode(params[0], params.join(" "), prefix));
+  } else {
+    //Q_ASSERT(nicks.contains(params[0]));
+    //VarMap n = nicks[params[0]].toMap();
+    //QString mode = n["Mode"].toString();
+    emit displayMsg(Message::mode("", params.join(" ")));
+  }
+}
+
+void Server::handleServerNick(QString prefix, QStringList params) {
+  QString oldnick = updateNickFromMask(prefix);
+  QString newnick = params[0];
+  VarMap v = nicks.take(oldnick);
+  nicks[newnick] = v;
+  VarMap chans = v["Channels"].toMap();
+  foreach(QString c, chans.keys()) {
+    if(oldnick != ownNick) { emit displayMsg(Message::nick(c, newnick, prefix)); }
+    else { emit displayMsg(Message::nick(c, newnick, newnick)); }
+  }
+  emit nickRenamed(network, oldnick, newnick);
+  if(oldnick == ownNick) {
+    ownNick = newnick;
+    emit ownNickSet(network, newnick);
+  }
+}
+
+void Server::handleServerNotice(QString prefix, QStringList params) {
+  //Message msg(Message::Notice, params[1], prefix);
+  if(prefix == currentServer) emit displayMsg(Message::server("", params[1], prefix));
+  else emit displayMsg(Message::notice("", params[1], prefix));
+}
+
+void Server::handleServerPart(QString prefix, QStringList params) {
+  QString nick = updateNickFromMask(prefix);
+  Q_ASSERT(nicks.contains(nick));
+  VarMap n = nicks[nick];
+  VarMap chans = n["Channels"].toMap();
+  Q_ASSERT(chans.contains(params[0]));
+  chans.remove(params[0]);
+  QString msg;
+  if(params.count() > 1) msg = params[1];
+  emit displayMsg(Message::part(params[0], msg, prefix));
+  if(chans.count() > 0) {
+    n["Channels"] = chans;
+    nicks[nick] = n;
+    emit nickUpdated(network, nick, n);
+  } else {
+    nicks.remove(nick);
+    emit nickRemoved(network, nick);
+  }
+  if(nick == ownNick) {
+    Q_ASSERT(topics.contains(params[0]));
+    topics.remove(params[0]);
+  }
+}
+
+void Server::handleServerPing(QString prefix, QStringList params) {
+  putCmd("PONG", params);
+}
+
+void Server::handleServerPrivmsg(QString prefix, QStringList params) {
+  updateNickFromMask(prefix);
+  if(params.count()<2) emit displayMsg(Message::plain(params[0], "", prefix));
+  else emit displayMsg(Message::plain(params[0], params[1], prefix));
+}
+
+void Server::handleServerQuit(QString prefix, QStringList params) {
+  QString nick = updateNickFromMask(prefix);
+  Q_ASSERT(nicks.contains(nick));
+  VarMap chans = nicks[nick]["Channels"].toMap();
+  foreach(QString c, chans.keys()) {
+    emit displayMsg(Message::quit(c, params[0], prefix));
+  }
+  nicks.remove(nick);
+  emit nickRemoved(network, nick);
+}
+
+void Server::handleServerTopic(QString prefix, QStringList params) {
+  QString nick = updateNickFromMask(prefix);
+  Q_ASSERT(nicks.contains(nick));
+  topics[params[0]] = params[1];
+  emit topicSet(network, params[0], params[1]);
+  emit displayMsg(Message::server(params[0], tr("%1 has changed topic for %2 to: \"%3\"").arg(nick).arg(params[0]).arg(params[1])));
+}
+
+/* RPL_WELCOME */
+void Server::handleServer001(QString prefix, QStringList params) {
+  currentServer = prefix;
+  ownNick = params[0];
+  VarMap n;
+  n["Channels"] = VarMap();
+  nicks[ownNick] = n;
+  emit ownNickSet(network, ownNick);
+  emit nickAdded(network, ownNick, VarMap());
+  emit displayMsg(Message::server("", params[1], prefix));
+}
+
+/* RPL_ISUPPORT */
+void Server::handleServer005(QString prefix, QStringList params) {
+  foreach(QString p, params) {
+    QString key = p.section("=", 0, 0);
+    QString val = p.section("=", 1);
+    serverSupports[key] = val;
+    // handle some special cases
+    if(key == "PREFIX") {
+      VarMap foo; QString modes, prefixes;
+      Q_ASSERT(val.contains(')') && val.startsWith('('));
+      int m = 1, p;
+      for(p = 2; p < val.length(); p++) if(val[p] == ')') break;
+      p++;
+      for(; val[m] != ')'; m++, p++) {
+        Q_ASSERT(p < val.length());
+        foo[QString(val[m])] = QString(val[p]);
+        modes += val[m]; prefixes += val[p];
+      }
+      serverSupports["PrefixModes"] = modes; serverSupports["Prefixes"] = prefixes;
+      serverSupports["ModePrefixMap"] = foo;
+    }
+  }
+}
+
+
+/* RPL_NOTOPIC */
+void Server::handleServer331(QString prefix, QStringList params) {
+  topics[params[0]] = "";
+  emit topicSet(network, params[0], "");
+  emit displayMsg(Message::server(params[0], tr("No topic is set for %1.").arg(params[0])));
+}
+
+/* RPL_TOPIC */
+void Server::handleServer332(QString prefix, QStringList params) {
+  topics[params[0]] = params[1];
+  emit topicSet(network, params[0], params[1]);
+  emit displayMsg(Message::server(params[0], tr("Topic for %1 is \"%2\"").arg(params[0]).arg(params[1])));
+}
+
+/* Topic set by... */
+void Server::handleServer333(QString prefix, QStringList params) {
+  emit displayMsg(Message::server(params[0],
+                  tr("Topic set by %1 on %2").arg(params[1]).arg(QDateTime::fromTime_t(params[2].toUInt()).toString())));
+}
+
+/* RPL_NAMREPLY */
+void Server::handleServer353(QString prefix, QStringList params) {
+  params.removeFirst(); // = or *
+  QString buf = params.takeFirst();
+  QString prefixes = serverSupports["Prefixes"].toString();
+  foreach(QString nick, params[0].split(' ')) {
+    QString mode = "", pfx = "";
+    if(prefixes.contains(nick[0])) {
+      pfx = nick[0];
+      for(int i = 0;; i++)
+        if(prefixes[i] == nick[0]) { mode = serverSupports["PrefixModes"].toString()[i]; break; }
+      nick.remove(0,1);
+    }
+    VarMap c; c["Mode"] = mode; c["Prefix"] = pfx;
+    if(nicks.contains(nick)) {
+      VarMap n = nicks[nick];
+      VarMap chans = n["Channels"].toMap();
+      chans[buf] = c;
+      n["Channels"] = chans;
+      nicks[nick] = n;
+      emit nickUpdated(network, nick, n);
+    } else {
+      VarMap n; VarMap c; VarMap chans;
+      c["Mode"] = mode;
+      chans[buf] = c;
+      n["Channels"] = chans;
+      nicks[nick] = n;
+      emit nickAdded(network, nick, n);
+    }
+  }
+}
+/***********************************************************************************/
+
+/* Exception classes for message handling */
+Server::ParseError::ParseError(QString cmd, QString prefix, QStringList params) {
+  _msg = QString("Command Parse Error: ") + cmd + params.join(" ");
+
+}
+
+Server::UnknownCmdError::UnknownCmdError(QString cmd, QString prefix, QStringList params) {
+  _msg = QString("Unknown Command: ") + cmd;
+
+}
diff --git a/core/server.h b/core/server.h
new file mode 100644 (file)
index 0000000..ba333d3
--- /dev/null
@@ -0,0 +1,161 @@
+/***************************************************************************
+ *   Copyright (C) 2005/06 by The Quassel Team                             *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef _SERVER_H_
+#define _SERVER_H_
+
+#include <QtCore>
+#include <QTimer>
+#include <QtNetwork>
+
+#include "global.h"
+#include "message.h"
+
+#define DEFAULT_PORT 6667
+
+
+/*!
+ * This is a server object, managing a single connection to an IRC server, handling the associated channels and so on.
+ * We have this running in its own thread mainly to not block other server objects or the core if something goes wrong,
+ * e.g. if some scripts starts running wild...
+ */
+
+class Server : public QThread {
+  Q_OBJECT
+
+  public:
+    Server(QString network);
+    ~Server();
+
+    // serverState state();
+    bool isConnected() { return socket.state() == QAbstractSocket::ConnectedState; }
+    QString getNetwork() { return network; }
+
+  public slots:
+    // void setServerOptions();
+    void sendState();
+    void connectToIrc(QString net);
+    void disconnectFromIrc(QString net);
+    void userInput(QString net, QString buffer, QString msg);
+
+    void putRawLine(QString input);
+    void putCmd(QString cmd, QStringList params, QString prefix = 0);
+
+    //void exitThread();
+
+  signals:
+    void serverState(QString net, VarMap data);
+    void recvRawServerMsg(QString);
+    void displayStatusMsg(QString);
+    void displayMsg(Message msg);
+    void connected(QString network);
+    void disconnected(QString network);
+
+    void nickAdded(QString network, QString nick, VarMap props);
+    void nickRenamed(QString network, QString oldnick, QString newnick);
+    void nickRemoved(QString network, QString nick);
+    void nickUpdated(QString network, QString nick, VarMap props);
+    void modeSet(QString network, QString target, QString mode);
+    void topicSet(QString network, QString buffer, QString topic);
+    void setNicks(QString network, QString buffer, QStringList nicks);
+    void ownNickSet(QString network, QString newNick);
+
+
+  private slots:
+    void run();
+    void socketHasData();
+    void socketError(QAbstractSocket::SocketError);
+    void socketConnected();
+    void socketDisconnected();
+    void socketStateChanged(QAbstractSocket::SocketState);
+
+    /* Message Handlers */
+
+    /* void handleUser(QString, QString); */
+    void handleUserAway(QString, QString);
+    void handleUserDeop(QString, QString);
+    void handleUserDevoice(QString, QString);
+    void handleUserInvite(QString, QString);
+    void handleUserJoin(QString, QString);
+    void handleUserKick(QString, QString);
+    void handleUserList(QString, QString);
+    void handleUserMode(QString, QString);
+    void handleUserMsg(QString, QString);
+    void handleUserNick(QString, QString);
+    void handleUserOp(QString, QString);
+    void handleUserPart(QString, QString);
+    void handleUserQuit(QString, QString);
+    void handleUserQuote(QString, QString);
+    void handleUserSay(QString, QString);
+    void handleUserTopic(QString, QString);
+    void handleUserVoice(QString, QString);
+
+    /* void handleServer(QString, QStringList); */
+    void handleServerJoin(QString, QStringList);
+    void handleServerKick(QString, QStringList);
+    void handleServerMode(QString, QStringList);
+    void handleServerNick(QString, QStringList);
+    void handleServerNotice(QString, QStringList);
+    void handleServerPart(QString, QStringList);
+    void handleServerPing(QString, QStringList);
+    void handleServerPrivmsg(QString, QStringList);
+    void handleServerQuit(QString, QStringList);
+    void handleServerTopic(QString, QStringList);
+
+    void handleServer001(QString, QStringList);   // RPL_WELCOME
+    void handleServer005(QString, QStringList);   // RPL_ISUPPORT
+    void handleServer331(QString, QStringList);   // RPL_NOTOPIC
+    void handleServer332(QString, QStringList);   // RPL_TOPIC
+    void handleServer333(QString, QStringList);   // Topic set by...
+    void handleServer353(QString, QStringList);   // RPL_NAMREPLY
+
+    void defaultServerHandler(QString cmd, QString prefix, QStringList params);
+    void defaultUserHandler(QString buf, QString cmd, QString msg);
+
+  private:
+    QString network;
+    QTcpSocket socket;
+    //QHash<QString, Buffer*> buffers;
+
+    QString ownNick;
+    QString currentServer;
+    VarMap networkSettings;
+    VarMap identity;
+    QHash<QString, VarMap> nicks;  // stores all known nicks for the server
+    QHash<QString, QString> topics; // stores topics for each buffer
+    VarMap serverSupports;  // stores results from RPL_ISUPPORT
+
+    void handleServerMsg(QString rawMsg);
+    void handleUserInput(QString buffer, QString usrMsg);
+
+    QString updateNickFromMask(QString mask);
+
+    class ParseError : public Exception {
+      public:
+        ParseError(QString cmd, QString prefix, QStringList params);
+    };
+
+    class UnknownCmdError : public Exception {
+      public:
+        UnknownCmdError(QString cmd, QString prefix, QStringList params);
+    };
+};
+
+#endif
index 4d6297e..8bc8b4b 100644 (file)
@@ -1,9 +1,11 @@
-SET(gui_SRCS channelwidget.cpp channelwidgetinput.cpp mainwin.cpp serverlist.cpp
identities.cpp coreconnectdlg.cpp guiproxy.cpp)
+SET(gui_SRCS channelwidgetinput.cpp mainwin.cpp serverlist.cpp buffer.cpp
            identities.cpp coreconnectdlg.cpp guiproxy.cpp networkview.cpp)
 SET(gui_HDRS )
-SET(gui_MOCS channelwidget.h channelwidgetinput.h mainwin.h serverlist.h identities.h coreconnectdlg.h guiproxy.h)
-SET(gui_UICS channelwidget.ui identitiesdlg.ui identitieseditdlg.ui networkeditdlg.ui mainwin.ui
-             nickeditdlg.ui serverlistdlg.ui servereditdlg.ui coreconnectdlg.ui ircwidget.ui)
+SET(gui_MOCS channelwidgetinput.h mainwin.h serverlist.h identities.h coreconnectdlg.h
+             guiproxy.h networkview.h buffer.h)
+SET(gui_UICS identitiesdlg.ui identitieseditdlg.ui networkeditdlg.ui mainwin.ui
+             nickeditdlg.ui serverlistdlg.ui servereditdlg.ui coreconnectdlg.ui ircwidget.ui
+             networkview.ui bufferwidget.ui)
 
 # This prepends ui/ to the UIC names, so we don't have to do this ourselves.
 FOREACH(ui ${gui_UICS})
diff --git a/gui/buffer.cpp b/gui/buffer.cpp
new file mode 100644 (file)
index 0000000..2eeda62
--- /dev/null
@@ -0,0 +1,311 @@
+/***************************************************************************
+ *   Copyright (C) 2005/06 by The Quassel Team                             *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#include "buffer.h"
+#include "util.h"
+
+Buffer::Buffer(QString netname, QString bufname) {
+  networkName = netname;
+  bufferName = bufname;
+
+  widget = 0;
+  active = false;
+}
+
+Buffer::~Buffer() {
+  delete widget;
+}
+
+void Buffer::setActive(bool a) {
+  if(a != active) {
+    active = a;
+    if(widget) widget->setActive(a);
+  }
+}
+
+void Buffer::displayMsg(Message msg) {
+  contents.append(msg);
+  if(widget) widget->displayMsg(msg);
+}
+
+void Buffer::userInput(QString msg) {
+  emit userInput(networkName, bufferName, msg);
+}
+
+void Buffer::scrollToEnd() {
+  if(!widget) return;
+  widget->scrollToEnd();
+}
+
+QWidget * Buffer::showWidget(QWidget *parent) {
+  if(widget) return qobject_cast<QWidget*>(widget);
+  widget = new BufferWidget(networkName, bufferName, isActive(), ownNick, contents, parent); 
+  widget->setTopic(topic);
+  widget->updateNickList(nicks);
+  //widget->renderContents();
+  widget->scrollToEnd();
+  connect(widget, SIGNAL(userInput(QString)), this, SLOT(userInput(QString)));
+  return qobject_cast<QWidget*>(widget);
+}
+
+void Buffer::hideWidget() {
+  delete widget;
+  widget = 0;
+}
+
+QWidget * Buffer::getWidget() {
+  return qobject_cast<QWidget*>(widget);
+}
+
+void Buffer::setTopic(QString t) {
+  topic = t;
+  if(widget) widget->setTopic(t);
+}
+
+void Buffer::addNick(QString nick, VarMap props) {
+  if(nick == ownNick) setActive(true);
+  nicks[nick] = props;
+  if(widget) widget->updateNickList(nicks);
+}
+
+void Buffer::updateNick(QString nick, VarMap props) {
+  nicks[nick] = props;
+  if(widget) widget->updateNickList(nicks);
+}
+
+void Buffer::renameNick(QString oldnick, QString newnick) {
+  QVariant v = nicks.take(oldnick);
+  nicks[newnick] = v;
+  if(widget) widget->updateNickList(nicks);
+}
+
+void Buffer::removeNick(QString nick) {
+  if(nick == ownNick) setActive(false);
+  nicks.remove(nick);
+  if(widget) widget->updateNickList(nicks);
+}
+
+void Buffer::setOwnNick(QString nick) {
+  ownNick = nick;
+  if(widget) widget->setOwnNick(nick);
+}
+
+/****************************************************************************************/
+
+BufferWidget::BufferWidget(QString netname, QString bufname, bool act, QString own, QList<Message> cont, QWidget *parent) : QWidget(parent) {
+  ui.setupUi(this);
+  networkName = netname;
+  bufferName = bufname;
+  active = act;
+  contents = cont;
+  ui.ownNick->clear();
+  ui.ownNick->addItem(own);
+  if(bufname.isEmpty()) {
+    // Server Buffer
+    ui.nickTree->hide();
+    ui.topicEdit->hide();
+    ui.chanSettingsButton->hide();
+  }
+  connect(ui.nickTree, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SLOT(itemExpansionChanged(QTreeWidgetItem*)));
+  connect(ui.nickTree, SIGNAL(itemCollapsed(QTreeWidgetItem *)), this, SLOT(itemExpansionChanged(QTreeWidgetItem*)));
+  connect(ui.inputEdit, SIGNAL(returnPressed()), this, SLOT(enterPressed()));
+
+  ui.chatWidget->setFocusProxy(ui.inputEdit);
+
+  opsExpanded = voicedExpanded = usersExpanded = true;
+
+  // Define standard colors
+  stdCol = "black";
+  inactiveCol = "grey";
+  noticeCol = "darkblue";
+  serverCol = "darkblue";
+  errorCol = "red";
+  joinCol = "green";
+  quitCol = "firebrick";
+  partCol = "firebrick";
+  kickCol = "firebrick";
+  nickCol = "magenta";
+
+  int i = contents.count() - 100;
+  if(i < 0) i = 0;
+  for(int j = 0; j < i; j++) contents.removeAt(0);
+  renderContents();
+  updateTitle();
+  show();
+}
+
+void BufferWidget::updateTitle() {
+  QString title = QString("%1 in %2 [%3]: %4").arg(ui.ownNick->currentText()).arg(bufferName).arg(networkName).arg(ui.topicEdit->text());
+  setWindowTitle(title);
+}
+
+void BufferWidget::enterPressed() {
+  QStringList lines = ui.inputEdit->text().split('\n', QString::SkipEmptyParts);
+  foreach(QString msg, lines) {
+    if(msg.isEmpty()) continue;
+    emit userInput(msg);
+  }
+  ui.inputEdit->clear();
+}
+
+void BufferWidget::setActive(bool act) {
+  if(act != active) {
+    active = act;
+    renderContents();
+  }
+}
+
+void BufferWidget::renderContents() {
+  QString html;
+  for(int i = 0; i < contents.count(); i++) {
+    html += htmlFromMsg(contents[i]);
+  }
+  ui.chatWidget->clear();
+  ui.chatWidget->setHtml(html);
+  scrollToEnd();
+}
+
+void BufferWidget::scrollToEnd() {
+  QScrollBar *sb = ui.chatWidget->verticalScrollBar();
+  sb->setValue(sb->maximum());
+  qDebug() << bufferName << "scrolled" << sb->value() << sb->maximum();
+}
+
+QString BufferWidget::htmlFromMsg(Message msg) {
+  QString s, n;
+  QString c = stdCol;
+  QString user = userFromMask(msg.sender);
+  QString host = hostFromMask(msg.sender);
+  QString nick = nickFromMask(msg.sender);
+  switch(msg.type) {
+    case Message::Plain:
+      c = stdCol; n = QString("&lt;%1&gt;").arg(nick); s = msg.text;
+      break;
+    case Message::Server:
+      c = serverCol; s = msg.text;
+      break;
+    case Message::Error:
+      c = errorCol; s = msg.text;
+      break;
+    case Message::Join:
+      c = joinCol;
+      s = QString(tr("--> %1 (%2@%3) has joined %4")).arg(nick).arg(user).arg(host).arg(bufferName);
+      break;
+    case Message::Part:
+      c = partCol;
+      s = QString(tr("<-- %1 (%2@%3) has left %4")).arg(nick).arg(user).arg(host).arg(bufferName);
+      if(!msg.text.isEmpty()) s = QString("%1 (%2)").arg(s).arg(msg.text);
+      break;
+    case Message::Kick:
+      { c = kickCol;
+        QString victim = msg.text.section(" ", 0, 0);
+        if(victim == ui.ownNick->currentText()) victim = tr("you");
+        QString kickmsg = msg.text.section(" ", 1);
+        s = QString(tr("--> %1 has kicked %2 from %3")).arg(nick).arg(victim).arg(bufferName);
+        if(!kickmsg.isEmpty()) s = QString("%1 (%2)").arg(s).arg(kickmsg);
+      }
+      break;
+    case Message::Quit:
+      c = quitCol;
+      s = QString(tr("<-- %1 (%2@%3) has quit")).arg(nick).arg(user).arg(host);
+      if(!msg.text.isEmpty()) s = QString("%1 (%2)").arg(s).arg(msg.text);
+      break;
+    case Message::Nick:
+      c = nickCol;
+      if(nick == msg.text) s = QString(tr("<-> You are now known as %1")).arg(msg.text);
+      else s = QString(tr("<-> %1 is now known as %2")).arg(nick).arg(msg.text);
+      break;
+    case Message::Mode:
+      c = serverCol;
+      if(nick.isEmpty()) s = tr("*** User mode: %1").arg(msg.text);
+      else s = tr("*** Mode %1 by %2").arg(msg.text).arg(nick);
+      break;
+    default:
+      c = stdCol; n = QString("[%1]").arg(msg.sender); s = msg.text;
+      break;
+  }
+  if(!active) c = inactiveCol;
+  s.replace('&', "&amp;"); s.replace('<', "&lt;"); s.replace('>', "&gt;");
+  QString html = QString("<table cellspacing=0 cellpadding=0><tr>"
+      "<td width=50><div style=\"color:%2;\">[%1]</div></td>")
+      .arg(msg.timeStamp.toLocalTime().toString("hh:mm:ss")).arg("darkblue");
+  if(!n.isEmpty())
+    html += QString("<td width=100><div align=right style=\"white-space:pre;margin-left:6px;color:%2;\">%1</div></td>")
+        .arg(n).arg("royalblue");
+  html += QString("<td><div style=\"white-space:pre-wrap;margin-left:6px;color:%2;\">%1</div></td>""</tr></table>").arg(s).arg(c);
+  return html;
+}
+
+void BufferWidget::displayMsg(Message msg) {
+  contents.append(msg);
+  ui.chatWidget->append(htmlFromMsg(msg));
+}
+
+void BufferWidget::setOwnNick(QString nick) {
+  ui.ownNick->clear();
+  ui.ownNick->addItem(nick);
+  updateTitle();
+}
+
+void BufferWidget::setTopic(QString topic) {
+  ui.topicEdit->setText(topic);
+  updateTitle();
+}
+
+void BufferWidget::updateNickList(VarMap nicks) {
+  ui.nickTree->clear();
+  if(nicks.count() != 1) ui.nickTree->setHeaderLabel(tr("%1 Users").arg(nicks.count()));
+  else ui.nickTree->setHeaderLabel(tr("1 User"));
+  QTreeWidgetItem *ops = new QTreeWidgetItem();
+  QTreeWidgetItem *voiced = new QTreeWidgetItem();
+  QTreeWidgetItem *users = new QTreeWidgetItem();
+  // To sort case-insensitive, we have to put all nicks in a map which is sorted by (lowercase) key...
+  QMap<QString, QString> sorted;
+  foreach(QString n, nicks.keys()) { sorted[n.toLower()] = n; }
+  foreach(QString n, sorted.keys()) {
+    QString nick = sorted[n];
+    QString mode = nicks[nick].toMap()["Channels"].toMap()[bufferName].toMap()["Mode"].toString();
+    if(mode.contains('o')) { new QTreeWidgetItem(ops, QStringList(QString("@%1").arg(nick))); }
+    else if(mode.contains('v')) { new QTreeWidgetItem(voiced, QStringList(QString("+%1").arg(nick))); }
+    else new QTreeWidgetItem(users, QStringList(nick));
+  }
+  if(ops->childCount()) {
+    ops->setText(0, tr("%1 Operators").arg(ops->childCount()));
+    ui.nickTree->addTopLevelItem(ops);
+    ops->setExpanded(opsExpanded);
+  } else delete ops;
+  if(voiced->childCount()) {
+    voiced->setText(0, tr("%1 Voiced").arg(voiced->childCount()));
+    ui.nickTree->addTopLevelItem(voiced);
+    voiced->setExpanded(voicedExpanded);
+  } else delete voiced;
+  if(users->childCount()) {
+    users->setText(0, tr("%1 Users").arg(users->childCount()));
+    ui.nickTree->addTopLevelItem(users);
+    users->setExpanded(usersExpanded);
+  } else delete users;
+}
+
+void BufferWidget::itemExpansionChanged(QTreeWidgetItem *item) {
+  if(item->child(0)->text(0).startsWith('@')) opsExpanded = item->isExpanded();
+  else if(item->child(0)->text(0).startsWith('+')) voicedExpanded = item->isExpanded();
+  else usersExpanded = item->isExpanded();
+}
+
diff --git a/gui/buffer.h b/gui/buffer.h
new file mode 100644 (file)
index 0000000..5df19aa
--- /dev/null
@@ -0,0 +1,118 @@
+/***************************************************************************
+ *   Copyright (C) 2005/06 by The Quassel Team                             *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef _BUFFER_H_
+#define _BUFFER_H_
+
+#include <QtCore>
+#include <QtGui>
+
+#include "ui_bufferwidget.h"
+
+#include "global.h"
+#include "message.h"
+
+class BufferWidget;
+
+class Buffer : public QObject {
+  Q_OBJECT
+
+  public:
+    Buffer(QString network, QString buffer);
+    ~Buffer();
+
+    bool isActive() { return active; }
+  public:
+    QWidget *getWidget();
+
+  signals:
+    void userInput(QString, QString, QString);
+    void nickListChanged(QStringList);
+
+  public slots:
+    void setActive(bool active = true);
+    void displayMsg(Message);
+    //void recvStatusMsg(QString msg);
+    void setTopic(QString);
+    //void setNicks(QStringList);
+    void addNick(QString nick, VarMap props);
+    void renameNick(QString oldnick, QString newnick);
+    void removeNick(QString nick);
+    void updateNick(QString nick, VarMap props);
+    void setOwnNick(QString nick);
+
+    QWidget * showWidget(QWidget *parent = 0);
+    void hideWidget();
+
+    void scrollToEnd();
+
+  private slots:
+    void userInput(QString);
+
+  private:
+    bool active;
+    BufferWidget *widget;
+    VarMap nicks;
+    QString topic;
+    QString ownNick;
+    QString networkName, bufferName;
+
+    QList<Message> contents;
+};
+
+class BufferWidget : public QWidget {
+  Q_OBJECT
+
+  public:
+    BufferWidget(QString netname, QString bufname, bool active, QString ownNick, QList<Message> contents, QWidget *parent = 0);
+
+    void setActive(bool act = true);
+  signals:
+    void userInput(QString);
+
+  public slots:
+    void displayMsg(Message);
+    void updateNickList(VarMap nicks);
+    void setOwnNick(QString ownNick);
+    void setTopic(QString topic);
+    void renderContents();
+    void scrollToEnd();
+
+  private slots:
+    void enterPressed();
+    void itemExpansionChanged(QTreeWidgetItem *);
+    void updateTitle();
+
+  private:
+    Ui::BufferWidget ui;
+    bool active;
+    QList<Message> contents;
+
+    QString stdCol, errorCol, noticeCol, joinCol, quitCol, partCol, kickCol, serverCol, nickCol, inactiveCol;
+    QString CSS;
+    QString networkName;
+    QString bufferName;
+
+    bool opsExpanded, voicedExpanded, usersExpanded;
+
+    QString htmlFromMsg(Message);
+};
+
+#endif
index e0e8522..b3a82f4 100644 (file)
@@ -22,7 +22,7 @@
 #define _CORECONNECTDLG_H
 
 #include "coreconnectdlg.h"
-#include "ui_coreconnectdlg.h"
+#include "gui/ui_coreconnectdlg.h"
 
 class CoreConnectDlg: public QDialog {
   Q_OBJECT
index 6dd5ea3..3657623 100644 (file)
@@ -28,9 +28,12 @@ void GUIProxy::recv(CoreSignal sig, QVariant arg1, QVariant arg2, QVariant arg3)
   //qDebug() << "[GUI] Received signal:" << sig <<arg1<<arg2<<arg3;
   switch(sig) {
     case CS_CORE_STATE: emit csCoreState(arg1); break;
+    case CS_SERVER_STATE: emit csServerState(arg1.toString(), arg2.toMap()); break;
+    case CS_SERVER_CONNECTED: emit csServerConnected(arg1.toString()); break;
+    case CS_SERVER_DISCONNECTED: emit csServerDisconnected(arg1.toString()); break;
     case CS_UPDATE_GLOBAL_DATA: emit csUpdateGlobalData(arg1.toString(), arg2); break;
     //case CS_GLOBAL_DATA_CHANGED: emit csGlobalDataChanged(arg1.toString()); break;
-    case CS_DISPLAY_MSG: emit csDisplayMsg(arg1.toString(), arg2.toString(), arg3.value<Message>()); break;
+    case CS_DISPLAY_MSG: emit csDisplayMsg(arg1.toString(), arg2.value<Message>()); break;
     case CS_DISPLAY_STATUS_MSG: emit csDisplayStatusMsg(arg1.toString(), arg2.toString()); break;
     case CS_MODE_SET: emit csModeSet(arg1.toString(), arg2.toString(), arg3.toString()); break;
     case CS_TOPIC_SET: emit csTopicSet(arg1.toString(), arg2.toString(), arg3.toString()); break;
index 92ac0d2..c8ac87e 100644 (file)
@@ -49,7 +49,10 @@ class GUIProxy : public QObject {
 
   signals:
     void csCoreState(QVariant);
-    void csDisplayMsg(QString, QString, Message);
+    void csServerState(QString, QVariant);
+    void csServerConnected(QString);
+    void csServerDisconnected(QString);
+    void csDisplayMsg(QString, Message);
     void csDisplayStatusMsg(QString, QString);
     void csUpdateGlobalData(QString key, QVariant data);
     void csGlobalDataChanged(QString key);
index 8a21a9d..6ea4091 100644 (file)
 #include <QtCore>
 
 #include "global.h"
+#include "message.h"
+#include "guiproxy.h"
 
 #include "mainwin.h"
-#include "channelwidget.h"
+#include "buffer.h"
+#include "networkview.h"
 #include "serverlist.h"
 #include "coreconnectdlg.h"
 
 MainWin::MainWin() : QMainWindow() {
   ui.setupUi(this);
+  widget = 0;
 
   setWindowTitle("Quassel IRC");
   setWindowIcon(QIcon(":/qirc-icon.png"));
@@ -41,24 +45,68 @@ MainWin::MainWin() : QMainWindow() {
   move(s.value("MainWinPos", QPoint(50, 50)).toPoint());
   s.endGroup();
 
-  //workspace = new QWorkspace(this);
-  //setCentralWidget(workspace);
+  workspace = new QWorkspace(this);
+  setCentralWidget(workspace);
   statusBar()->showMessage(tr("Waiting for core..."));
-  setEnabled(false);
+
+  netView = new NetworkView("", this);
+  netView->setAllowedAreas(Qt::RightDockWidgetArea|Qt::LeftDockWidgetArea);
+  addDockWidget(Qt::LeftDockWidgetArea, netView);
+  connect(netView, SIGNAL(bufferSelected(QString, QString)), this, SLOT(showBuffer(QString, QString)));
+  connect(this, SIGNAL(bufferSelected(QString, QString)), netView, SLOT(selectBuffer(QString, QString)));
+
+  connect(guiProxy, SIGNAL(csServerState(QString, QVariant)), this, SLOT(recvNetworkState(QString, QVariant)));
+  connect(guiProxy, SIGNAL(csServerConnected(QString)), this, SLOT(networkConnected(QString)));
+  connect(guiProxy, SIGNAL(csServerDisconnected(QString)), this, SLOT(networkDisconnected(QString)));
+  connect(guiProxy, SIGNAL(csDisplayMsg(QString, Message)), this, SLOT(recvMessage(QString, Message)));
+  connect(guiProxy, SIGNAL(csDisplayStatusMsg(QString, QString)), this, SLOT(recvStatusMsg(QString, QString)));
+  connect(guiProxy, SIGNAL(csTopicSet(QString, QString, QString)), this, SLOT(setTopic(QString, QString, QString)));
+  connect(guiProxy, SIGNAL(csSetNicks(QString, QString, QStringList)), this, SLOT(setNicks(QString, QString, QStringList)));
+  connect(guiProxy, SIGNAL(csNickAdded(QString, QString, VarMap)), this, SLOT(addNick(QString, QString, VarMap)));
+  connect(guiProxy, SIGNAL(csNickRemoved(QString, QString)), this, SLOT(removeNick(QString, QString)));
+  connect(guiProxy, SIGNAL(csNickRenamed(QString, QString, QString)), this, SLOT(renameNick(QString, QString, QString)));
+  connect(guiProxy, SIGNAL(csNickUpdated(QString, QString, VarMap)), this, SLOT(updateNick(QString, QString, VarMap)));
+  connect(guiProxy, SIGNAL(csOwnNickSet(QString, QString)), this, SLOT(setOwnNick(QString, QString)));
+  connect(this, SIGNAL(sendInput( QString, QString, QString )), guiProxy, SLOT(gsUserInput(QString, QString, QString)));
+
   show();
   syncToCore();
-  setEnabled(true);
+  statusBar()->showMessage(tr("Ready."));
+
+  buffersUpdated();
+
   serverListDlg = new ServerListDlg(this);
   serverListDlg->setVisible(serverListDlg->showOnStartup());
   setupMenus();
-  //identitiesAct = settingsMenu->addAction(QIcon(":/default/identity.png"), tr("&Identities..."), serverListDlg, SLOT(editIdentities()));
-  //showServerList();
-  IrcWidget *cw = new IrcWidget(this);
-  setCentralWidget(cw);
-  //workspace->addWindow(cw);
-  //cw->showMaximized();
-  statusBar()->showMessage(tr("Ready."));
-  cw->setFocus();
+
+  // replay backlog
+  foreach(QString net, coreBackLog.keys()) {
+    while(coreBackLog[net].count()) {
+      recvMessage(net, coreBackLog[net].takeFirst());
+    }
+  }
+  /*
+  foreach(QString key, buffers.keys()) {
+    foreach(Buffer *b, buffers[key].values()) {
+      QWidget *widget = b->showWidget(this);
+      workspace->addWindow(widget);
+      widget->show();
+    }
+  }
+  */
+  s.beginGroup("Buffers");
+  QString net = s.value("CurrentNetwork", "").toString();
+  QString buf = s.value("CurrentBuffer", "").toString();
+  s.endGroup();
+  if(!net.isEmpty()) {
+    if(buffers.contains(net)) {
+      if(buffers[net].contains(buf)) {
+        showBuffer(net, buf);
+      } else {
+        showBuffer(net, "");
+      }
+    }
+  }
 }
 
 void MainWin::setupMenus() {
@@ -66,27 +114,6 @@ void MainWin::setupMenus() {
   connect(ui.actionEditIdentities, SIGNAL(activated()), serverListDlg, SLOT(editIdentities()));
 }
 
-void MainWin::syncToCore() {
-  if(global->getData("CoreReady").toBool()) return;
-  // ok, apparently we are running as standalone GUI
-  coreConnectDlg = new CoreConnectDlg(this);
-  if(coreConnectDlg->exec() != QDialog::Accepted) {
-    //qApp->quit();
-    exit(1);
-  }
-  VarMap state = coreConnectDlg->getCoreState().toMap()["CoreData"].toMap();
-  delete coreConnectDlg;
-  QString key;
-  foreach(key, state.keys()) {
-    global->updateData(key, state[key]);
-  }
-  if(!global->getData("CoreReady").toBool()) {
-    QMessageBox::critical(this, tr("Fatal Error"), tr("<b>Could not synchronize with Quassel Core!</b><br>Quassel GUI will be aborted."), QMessageBox::Abort);
-    //qApp->quit();
-    exit(1);
-  }
-}
-
 void MainWin::showServerList() {
 //  if(!serverListDlg) {
 //    serverListDlg = new ServerListDlg(this);
@@ -102,8 +129,164 @@ void MainWin::closeEvent(QCloseEvent *event)
     s.setValue("MainWinSize", size());
     s.setValue("MainWinPos", pos());
     s.endGroup();
+    s.beginGroup("Buffers");
+    s.setValue("CurrentNetwork", currentNetwork);
+    s.setValue("CurrentBuffer", currentBuffer);
+    s.endGroup();
     event->accept();
   //} else {
     //event->ignore();
   //}
 }
+
+void MainWin::showBuffer(QString net, QString buf) {
+  currentBuffer = buf; currentNetwork = net;
+  Buffer *b = getBuffer(net, buf);
+  QWidget *old = widget;
+  widget = b->showWidget(this);
+  if(widget == old) return;
+  workspace->addWindow(widget);
+  widget->showMaximized();
+  if(old) { old->close(); old->setParent(this); }
+  workspace->setActiveWindow(widget);
+  //widget->setFocus();
+  //workspace->setFocus();
+  //widget->activateWindow();
+  widget->setFocus(Qt::MouseFocusReason);
+  focusNextChild();
+  //workspace->tile();
+  emit bufferSelected(net, buf);
+}
+
+void MainWin::networkConnected(QString net) {
+  connected[net] = true;
+  Buffer *b = getBuffer(net, "");
+  b->setActive(true);
+  b->displayMsg(Message::server("", tr("Connected.")));
+  buffersUpdated();
+}
+
+void MainWin::networkDisconnected(QString net) {
+  //getBuffer(net, "")->setActive(false);
+  foreach(QString buf, buffers[net].keys()) {
+    Buffer *b = getBuffer(net, buf);
+    b->displayMsg(Message::server(buf, tr("Server disconnected.")));
+    b->setActive(false);
+    
+  }
+  connected[net] = false;
+}
+
+Buffer * MainWin::getBuffer(QString net, QString buf) {
+  if(!buffers[net].contains(buf)) {
+    Buffer *b = new Buffer(net, buf);
+    b->setOwnNick(ownNick[net]);
+    connect(b, SIGNAL(userInput(QString, QString, QString)), this, SLOT(userInput(QString, QString, QString)));
+    buffers[net][buf] = b;
+    buffersUpdated();
+  }
+  return buffers[net][buf];
+}
+
+void MainWin::buffersUpdated() {
+  netView->buffersUpdated(buffers);
+}
+
+void MainWin::recvNetworkState(QString net, QVariant state) {
+  connected[net] = true;
+  setOwnNick(net, state.toMap()["OwnNick"].toString());
+  getBuffer(net, "")->setActive(true);
+  VarMap t = state.toMap()["Topics"].toMap();
+  VarMap n = state.toMap()["Nicks"].toMap();
+  foreach(QString buf, t.keys()) {
+    getBuffer(net, buf)->setActive(true);
+    setTopic(net, buf, t[buf].toString());
+  }
+  foreach(QString nick, n.keys()) {
+    addNick(net, nick, n[nick].toMap());
+  }
+  buffersUpdated();
+}
+
+void MainWin::recvMessage(QString net, Message msg) {
+  Buffer *b = getBuffer(net, msg.target);
+  b->displayMsg(msg);
+}
+
+void MainWin::recvStatusMsg(QString net, QString msg) {
+  recvMessage(net, Message::server("", QString("[STATUS] %1").arg(msg)));
+
+}
+
+void MainWin::userInput(QString net, QString buf, QString msg) {
+  emit sendInput(net, buf, msg);
+}
+
+void MainWin::setTopic(QString net, QString buf, QString topic) {
+  if(!connected[net]) return;
+  Buffer *b = getBuffer(net, buf);
+  b->setTopic(topic);
+  buffersUpdated();
+  //if(!b->isActive()) {
+  //  b->setActive(true);
+  //  buffersUpdated();
+  //}
+}
+
+void MainWin::setNicks(QString net, QString buf, QStringList nicks) {
+  Q_ASSERT(false);
+}
+
+void MainWin::addNick(QString net, QString nick, VarMap props) {
+  if(!connected[net]) return;
+  nicks[net][nick] = props;
+  VarMap chans = props["Channels"].toMap();
+  QStringList c = chans.keys();
+  foreach(QString bufname, c) {
+    getBuffer(net, bufname)->addNick(nick, props);
+  }
+  buffersUpdated();
+}
+
+void MainWin::renameNick(QString net, QString oldnick, QString newnick) {
+  if(!connected[net]) return;
+  QStringList chans = nicks[net][oldnick]["Channels"].toMap().keys();
+  foreach(QString c, chans) {
+    getBuffer(net, c)->renameNick(oldnick, newnick);
+  }
+  nicks[net][newnick] = nicks[net].take(oldnick);
+}
+
+void MainWin::updateNick(QString net, QString nick, VarMap props) {
+  if(!connected[net]) return;
+  QStringList oldchans = nicks[net][nick]["Channels"].toMap().keys();
+  QStringList newchans = props["Channels"].toMap().keys();
+  foreach(QString c, newchans) {
+    if(oldchans.contains(c)) getBuffer(net, c)->updateNick(nick, props);
+    else getBuffer(net, c)->addNick(nick, props);
+  }
+  foreach(QString c, oldchans) {
+    if(!newchans.contains(c)) getBuffer(net, c)->removeNick(nick);
+  }
+  nicks[net][nick] = props;
+  buffersUpdated();
+}
+
+void MainWin::removeNick(QString net, QString nick) {
+  if(!connected[net]) return;
+  VarMap chans = nicks[net][nick]["Channels"].toMap();
+  foreach(QString bufname, chans.keys()) {
+    getBuffer(net, bufname)->removeNick(nick);
+  }
+  nicks[net].remove(nick);
+  buffersUpdated();
+}
+
+void MainWin::setOwnNick(QString net, QString nick) {
+  if(!connected[net]) return;
+  ownNick[net] = nick;
+  foreach(Buffer *b, buffers[net].values()) {
+    b->setOwnNick(nick);
+  }
+}
+
index a6d1e1e..d899695 100644 (file)
 #include <QtGui>
 #include "gui/ui_mainwin.h"
 
+#include "global.h"
+#include "message.h"
+
 class ServerListDlg;
 class CoreConnectDlg;
+class NetworkView;
+class Buffer;
 
 class MainWin : public QMainWindow {
   Q_OBJECT
@@ -36,19 +41,51 @@ class MainWin : public QMainWindow {
   protected:
     void closeEvent(QCloseEvent *event);
 
+  signals:
+    void sendInput(QString network, QString buffer, QString message);
+    void bufferSelected(QString net, QString buffer);
+
   private slots:
+    void userInput(QString, QString, QString);
+    void networkConnected(QString);
+    void networkDisconnected(QString);
+    void recvNetworkState(QString, QVariant);
+    void recvMessage(QString network, Message message);
+    void recvStatusMsg(QString network, QString message);
+    void setTopic(QString, QString, QString);
+    void setNicks(QString, QString, QStringList);
+    void addNick(QString net, QString nick, VarMap props);
+    void removeNick(QString net, QString nick);
+    void renameNick(QString net, QString oldnick, QString newnick);
+    void updateNick(QString net, QString nick, VarMap props);
+    void setOwnNick(QString net, QString nick);
+
     void showServerList();
 
+    void showBuffer(QString net, QString buf);
+
   private:
     Ui::MainWin ui;
 
     void setupMenus();
-    void syncToCore();
+    void syncToCore();  // implemented in main_mono.cpp or main_gui.cpp
+    Buffer * getBuffer(QString net, QString buf);
+    void buffersUpdated();
 
     QWorkspace *workspace;
+    QWidget *widget;
 
     ServerListDlg *serverListDlg;
     CoreConnectDlg *coreConnectDlg;
+
+    QString currentNetwork, currentBuffer;
+    QHash<QString, QHash<QString, Buffer*> > buffers;
+    QHash<QString, QHash<QString, VarMap> > nicks;
+    QHash<QString, bool> connected;
+    QHash<QString, QString> ownNick;
+    QHash<QString, QList<Message> > coreBackLog;
+
+    NetworkView *netView;
 };
 
 #endif
diff --git a/gui/networkview.cpp b/gui/networkview.cpp
new file mode 100644 (file)
index 0000000..be08b3b
--- /dev/null
@@ -0,0 +1,91 @@
+/***************************************************************************
+ *   Copyright (C) 2005/06 by The Quassel Team                             *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#include "global.h"
+#include "networkview.h"
+
+NetworkViewWidget::NetworkViewWidget(QWidget *parent) : QWidget(parent) {
+  ui.setupUi(this);
+
+}
+
+QSize NetworkViewWidget::sizeHint() const {
+  return QSize(150,100);
+
+}
+
+/**************************************************************************/
+
+NetworkView::NetworkView(QString net, QWidget *parent) : network(net), QDockWidget(parent) {
+  if(!net.isEmpty()) setWindowTitle(net);
+  else setWindowTitle(tr("All Buffers"));
+  setWidget(new NetworkViewWidget(this));
+  tree = qobject_cast<NetworkViewWidget*>(widget())->tree();
+  tree->setSortingEnabled(false);
+  connect(tree, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(itemClicked(QTreeWidgetItem*)));
+}
+
+void NetworkView::buffersUpdated(BufferHash buffers) {
+  tree->clear(); items.clear();
+  if(network.isEmpty()) {
+    tree->setHeaderLabel(tr("Networks"));
+    foreach(QString net, buffers.keys()) {
+      QTreeWidgetItem *netItem = new QTreeWidgetItem(QStringList(net));
+      foreach(QString buf, buffers[net].keys()) {
+        Buffer *b = buffers[net][buf];
+        QStringList label;
+        if(buf.isEmpty()) label << tr("Status");
+        else label << buf;
+        QTreeWidgetItem *item = new QTreeWidgetItem(netItem, label);
+        items[net][buf] = item;
+        VarMap d;
+        d["Network"] = net;
+        d["Buffer"] = buf;
+        item->setData(0, Qt::UserRole, d);
+        if(!b->isActive()) {
+          item->setForeground(0, QColor("grey"));
+        }
+      }
+      VarMap d;
+      d["Network"] = net; d["Buffer"] = "";
+      netItem->setData(0, Qt::UserRole, d);
+      netItem->setFlags(netItem->flags() & ~Qt::ItemIsSelectable);
+      tree->addTopLevelItem(netItem);
+      netItem->setExpanded(true);
+    }
+    if(items[currentNetwork][currentBuffer]) items[currentNetwork][currentBuffer]->setSelected(true);
+  }
+}
+
+void NetworkView::itemClicked(QTreeWidgetItem *item) {
+  if(network.isEmpty()) {
+    VarMap d = item->data(0, Qt::UserRole).toMap();
+    QString net = d["Network"].toString(); QString buf = d["Buffer"].toString();
+    if(!net.isEmpty()) {
+      emit bufferSelected(net, buf);
+      currentNetwork = net; currentBuffer = buf;
+    }
+  }
+}
+
+void NetworkView::selectBuffer(QString net, QString buf) {
+  if(items[net][buf]) items[net][buf]->setSelected(true);
+  currentNetwork = net; currentBuffer = buf;
+}
diff --git a/gui/networkview.h b/gui/networkview.h
new file mode 100644 (file)
index 0000000..ac87077
--- /dev/null
@@ -0,0 +1,79 @@
+/***************************************************************************
+ *   Copyright (C) 2005/06 by The Quassel Team                             *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef _NETWORKVIEW_H_
+#define _NETWORKVIEW_H_
+
+#include <QtGui>
+#include "ui_networkview.h"
+#include "buffer.h"
+
+typedef QHash<QString, QHash<QString, Buffer*> > BufferHash;
+
+class NetworkViewWidget : public QWidget {
+  Q_OBJECT
+
+  public:
+    NetworkViewWidget(QWidget *parent = 0);
+    QTreeWidget *tree() { return ui.tree; }
+
+    virtual QSize sizeHint () const;
+
+  public slots:
+
+
+  signals:
+    void bufferSelected(QString net, QString buf);
+
+  private slots:
+
+
+  private:
+    Ui::NetworkViewWidget ui;
+
+};
+
+class NetworkView : public QDockWidget {
+  Q_OBJECT
+
+  public:
+    NetworkView(QString network, QWidget *parent = 0);
+
+  public slots:
+    void buffersUpdated(BufferHash);
+    void selectBuffer(QString net, QString buf);
+
+  signals:
+    void bufferSelected(QString net, QString buf);
+
+  private slots:
+    void itemClicked(QTreeWidgetItem *item);
+
+  private:
+    QString network;
+    QString currentNetwork, currentBuffer;
+    QHash<QString, QHash<QString, Buffer*> > buffers;
+    QHash<QString, QHash<QString, QTreeWidgetItem *> > items;
+    QTreeWidget *tree;
+
+};
+
+
+#endif
diff --git a/gui/ui/bufferwidget.ui b/gui/ui/bufferwidget.ui
new file mode 100644 (file)
index 0000000..b3bab0d
--- /dev/null
@@ -0,0 +1,271 @@
+<ui version="4.0" >
+ <class>BufferWidget</class>
+ <widget class="QWidget" name="BufferWidget" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>713</width>
+    <height>519</height>
+   </rect>
+  </property>
+  <property name="sizePolicy" >
+   <sizepolicy>
+    <hsizetype>3</hsizetype>
+    <vsizetype>3</vsizetype>
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="minimumSize" >
+   <size>
+    <width>200</width>
+    <height>100</height>
+   </size>
+  </property>
+  <property name="contextMenuPolicy" >
+   <enum>Qt::DefaultContextMenu</enum>
+  </property>
+  <property name="windowTitle" >
+   <string>YourNickname #quassel Network: The Topic</string>
+  </property>
+  <layout class="QVBoxLayout" >
+   <property name="margin" >
+    <number>1</number>
+   </property>
+   <property name="spacing" >
+    <number>6</number>
+   </property>
+   <item>
+    <layout class="QHBoxLayout" >
+     <property name="margin" >
+      <number>0</number>
+     </property>
+     <property name="spacing" >
+      <number>6</number>
+     </property>
+     <item>
+      <widget class="QLineEdit" name="topicEdit" >
+       <property name="focusPolicy" >
+        <enum>Qt::ClickFocus</enum>
+       </property>
+       <property name="readOnly" >
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QToolButton" name="chanSettingsButton" >
+       <property name="windowModality" >
+        <enum>Qt::NonModal</enum>
+       </property>
+       <property name="sizePolicy" >
+        <sizepolicy>
+         <hsizetype>0</hsizetype>
+         <vsizetype>0</vsizetype>
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="layoutDirection" >
+        <enum>Qt::LeftToRight</enum>
+       </property>
+       <property name="text" >
+        <string>...</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QSplitter" name="splitter" >
+     <property name="sizePolicy" >
+      <sizepolicy>
+       <hsizetype>7</hsizetype>
+       <vsizetype>5</vsizetype>
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="lineWidth" >
+      <number>1</number>
+     </property>
+     <property name="orientation" >
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <widget class="QTextBrowser" name="chatWidget" >
+      <property name="sizePolicy" >
+       <sizepolicy>
+        <hsizetype>7</hsizetype>
+        <vsizetype>5</vsizetype>
+        <horstretch>4</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="font" >
+       <font>
+        <family>Bitstream Vera Sans Mono</family>
+        <pointsize>8</pointsize>
+        <weight>50</weight>
+        <bold>false</bold>
+       </font>
+      </property>
+      <property name="html" >
+       <string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
+p, li { white-space: pre-wrap; }
+&lt;/style>&lt;/head>&lt;body style=" font-family:'Bitstream Vera Sans Mono'; font-size:8pt; font-weight:400; font-style:normal; text-decoration:none;">
+&lt;p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Nimbus Mono L';">&lt;/p>&lt;/body>&lt;/html></string>
+      </property>
+     </widget>
+     <widget class="QTreeWidget" name="nickTree" >
+      <property name="sizePolicy" >
+       <sizepolicy>
+        <hsizetype>5</hsizetype>
+        <vsizetype>5</vsizetype>
+        <horstretch>1</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="layoutDirection" >
+       <enum>Qt::LeftToRight</enum>
+      </property>
+      <property name="dragEnabled" >
+       <bool>true</bool>
+      </property>
+      <property name="textElideMode" >
+       <enum>Qt::ElideRight</enum>
+      </property>
+      <property name="rootIsDecorated" >
+       <bool>true</bool>
+      </property>
+      <property name="sortingEnabled" >
+       <bool>false</bool>
+      </property>
+      <property name="animated" >
+       <bool>false</bool>
+      </property>
+      <property name="columnCount" >
+       <number>1</number>
+      </property>
+      <column>
+       <property name="text" >
+        <string>32 Users</string>
+       </property>
+      </column>
+      <item>
+       <property name="text" >
+        <string>@ Operators</string>
+       </property>
+       <item>
+        <property name="text" >
+         <string>New Sub Item</string>
+        </property>
+       </item>
+       <item>
+        <property name="text" >
+         <string>New Item</string>
+        </property>
+       </item>
+       <item>
+        <property name="text" >
+         <string>New Item</string>
+        </property>
+       </item>
+      </item>
+      <item>
+       <property name="text" >
+        <string>4 Voiced</string>
+       </property>
+       <item>
+        <property name="text" >
+         <string>New Item</string>
+        </property>
+       </item>
+       <item>
+        <property name="text" >
+         <string>New Item</string>
+        </property>
+       </item>
+       <item>
+        <property name="text" >
+         <string>New Item</string>
+        </property>
+       </item>
+      </item>
+      <item>
+       <property name="text" >
+        <string>19 Users</string>
+       </property>
+       <item>
+        <property name="text" >
+         <string>New Sub Item</string>
+        </property>
+       </item>
+       <item>
+        <property name="text" >
+         <string>New Item</string>
+        </property>
+       </item>
+       <item>
+        <property name="text" >
+         <string>New Item</string>
+        </property>
+       </item>
+       <item>
+        <property name="text" >
+         <string>New Item</string>
+        </property>
+       </item>
+      </item>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" >
+     <property name="margin" >
+      <number>0</number>
+     </property>
+     <property name="spacing" >
+      <number>6</number>
+     </property>
+     <item>
+      <widget class="QComboBox" name="ownNick" >
+       <property name="sizeAdjustPolicy" >
+        <enum>QComboBox::AdjustToContents</enum>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="ChannelWidgetInput" name="inputEdit" >
+       <property name="sizePolicy" >
+        <sizepolicy>
+         <hsizetype>3</hsizetype>
+         <vsizetype>0</vsizetype>
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>ChannelWidgetInput</class>
+   <extends>QLineEdit</extends>
+   <header>channelwidgetinput.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>inputEdit</tabstop>
+  <tabstop>ownNick</tabstop>
+  <tabstop>nickTree</tabstop>
+  <tabstop>topicEdit</tabstop>
+  <tabstop>chanSettingsButton</tabstop>
+  <tabstop>chatWidget</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/ui/networkview.ui b/gui/ui/networkview.ui
new file mode 100644 (file)
index 0000000..a30e868
--- /dev/null
@@ -0,0 +1,52 @@
+<ui version="4.0" >
+ <class>NetworkViewWidget</class>
+ <widget class="QWidget" name="NetworkViewWidget" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>151</width>
+    <height>294</height>
+   </rect>
+  </property>
+  <property name="sizePolicy" >
+   <sizepolicy>
+    <hsizetype>5</hsizetype>
+    <vsizetype>1</vsizetype>
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle" >
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" >
+   <property name="margin" >
+    <number>9</number>
+   </property>
+   <property name="spacing" >
+    <number>6</number>
+   </property>
+   <item>
+    <widget class="QTreeWidget" name="tree" >
+     <property name="sizePolicy" >
+      <sizepolicy>
+       <hsizetype>5</hsizetype>
+       <vsizetype>1</vsizetype>
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="maximumSize" >
+      <size>
+       <width>16777215</width>
+       <height>16777215</height>
+      </size>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/ui/networkwidget.ui b/gui/ui/networkwidget.ui
deleted file mode 100644 (file)
index 65f2028..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<ui version="4.0" >
- <class>NetworkWidget</class>
- <widget class="QWidget" name="NetworkWidget" >
-  <property name="geometry" >
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>236</width>
-    <height>358</height>
-   </rect>
-  </property>
-  <property name="windowTitle" >
-   <string>Form</string>
-  </property>
-  <layout class="QHBoxLayout" >
-   <property name="margin" >
-    <number>9</number>
-   </property>
-   <property name="spacing" >
-    <number>6</number>
-   </property>
-   <item>
-    <widget class="QTreeWidget" name="tree" />
-   </item>
-  </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
index 1faf4d1..1c36661 100644 (file)
@@ -24,7 +24,7 @@
 /** The protocol version we use fo the communication between core and GUI */
 #define GUI_PROTOCOL 1
 
-#define BACKLOG_FORMAT 1
+#define BACKLOG_FORMAT 2
 #define BACKLOG_STRING "QuasselIRC Backlog File"
 
 class Global;
index b7f0a8e..607aada 100644 (file)
@@ -40,7 +40,6 @@ int main(int argc, char **argv) {
 
   global = new Global();
   coreProxy = new CoreProxy();
-  core = new Core();
 
   //Logger *logger = new Logger();
   //Quassel::setLogger(logger);
index 7dc2d53..b6e15b0 100644 (file)
 
 #include <iostream>
 
+#include <QtGui>
 #include <QApplication>
 
 #include "global.h"
 #include "guiproxy.h"
+#include "coreconnectdlg.h"
 #include "util.h"
 
 #include "mainwin.h"
@@ -46,6 +48,37 @@ int main(int argc, char **argv) {
   delete global;
 }
 
+void MainWin::syncToCore() {
+  Q_ASSERT(!global->getData("CoreReady").toBool());
+  // ok, we are running as standalone GUI
+  coreConnectDlg = new CoreConnectDlg(this);
+  if(coreConnectDlg->exec() != QDialog::Accepted) {
+    //qApp->quit();
+    exit(1);
+  }
+  VarMap state = coreConnectDlg->getCoreState().toMap();
+  delete coreConnectDlg;
+  VarMap data = state["CoreData"].toMap();
+  QString key;
+  foreach(key, data.keys()) {
+    global->updateData(key, data[key]);
+  }
+  if(!global->getData("CoreReady").toBool()) {
+    QMessageBox::critical(this, tr("Fatal Error"), tr("<b>Could not synchronize with Quassel Core!</b><br>Quassel GUI will be aborted."), QMessageBox::Abort);
+    //qApp->quit();
+    exit(1);
+  }
+  foreach(QString net, state["CoreBackLog"].toMap().keys()) {
+    QByteArray logbuf = state["CoreBackLog"].toMap()[net].toByteArray();
+    QDataStream in(&logbuf, QIODevice::ReadOnly); in.setVersion(QDataStream::Qt_4_2);
+    while(!in.atEnd()) {
+      Message msg; in >> msg;
+      coreBackLog[net].append(msg);
+    }
+    qDebug() << net << coreBackLog[net].count();
+  }
+}
+
 GUIProxy::GUIProxy() {
   if(guiProxy) qFatal("Trying to instantiate more than one CoreProxy object!");
 
index 7700b27..9384294 100644 (file)
@@ -41,7 +41,6 @@ int main(int argc, char **argv) {
   global = new Global();
   guiProxy = new GUIProxy();
   coreProxy = new CoreProxy();
-  core = new Core();
 
   MainWin mainWin;
   mainWin.show();
@@ -53,6 +52,13 @@ int main(int argc, char **argv) {
   return exitCode;
 }
 
+void MainWin::syncToCore() {
+  Q_ASSERT(global->getData("CoreReady").toBool());
+  coreBackLog = core->getBackLog();
+  // NOTE: We don't need to request server states, because in the monolithic version there can't be
+  //       any servers connected at this stage...
+}
+
 void CoreProxy::sendToGUI(CoreSignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
   guiProxy->recv(sig, arg1, arg2, arg3);
 }
index e19e74c..160c828 100644 (file)
 #include "message.h"
 #include <QDataStream>
 
+Message Message::plain(QString _target, QString _text, QString _sender, Flags _flags) {
+  return Message(_target, Plain, _text, _sender, _flags);
+}
+
+Message Message::notice(QString _target, QString _text, QString _sender, Flags _flags) {
+  return Message(_target, Notice, _text, _sender, _flags);
+}
+
+Message Message::action(QString _target, QString _text, QString _sender, Flags _flags) {
+  return Message(_target, Action, _text, _sender, _flags);
+}
+
+Message Message::kick(QString _target, QString _text, QString _sender, Flags _flags) {
+  return Message(_target, Kick, _text, _sender, _flags);
+}
+
+Message Message::join(QString _target, QString _text, QString _sender, Flags _flags) {
+  return Message(_target, Join, _text, _sender, _flags);
+}
+
+Message Message::part(QString _target, QString _text, QString _sender, Flags _flags) {
+  return Message(_target, Part, _text, _sender, _flags);
+}
+
+Message Message::nick(QString _target, QString _text, QString _sender, Flags _flags) {
+  return Message(_target, Nick, _text, _sender, _flags);
+}
+
+Message Message::mode(QString _target, QString _text, QString _sender, Flags _flags) {
+  return Message(_target, Mode, _text, _sender, _flags);
+}
+
+Message Message::quit(QString _target, QString _text, QString _sender, Flags _flags) {
+  return Message(_target, Quit, _text, _sender, _flags);
+}
+
+Message Message::kill(QString _target, QString _text, QString _sender, Flags _flags) {
+  return Message(_target, Kill, _text, _sender, _flags);
+}
+
+Message Message::server(QString _target, QString _text, QString _sender, Flags _flags) {
+  return Message(_target, Server, _text, _sender, _flags);
+}
+
+Message Message::info(QString _target, QString _text, QString _sender, Flags _flags) {
+  return Message(_target, Info, _text, _sender, _flags);
+}
+
+Message Message::error(QString _target, QString _text, QString _sender, Flags _flags) {
+  return Message(_target, Error, _text, _sender, _flags);
+}
+
 QDataStream &operator<<(QDataStream &out, const Message &msg) {
-  out << (quint32)msg.timeStamp.toTime_t() << (quint8)msg.type << (quint8)msg.flags << msg.sender.toUtf8() << msg.msg.toUtf8();
+  out << (quint32)msg.timeStamp.toTime_t() << (quint8)msg.type << (quint8)msg.flags
+      << msg.target.toUtf8() << msg.sender.toUtf8() << msg.text.toUtf8();
   return out;
 }
 
 QDataStream &operator>>(QDataStream &in, Message &msg) {
   quint8 t, f;
   quint32 ts;
-  QByteArray s, m;
-  in >> ts >> t >> f >> s >> m;
+  QByteArray s, m, targ;
+  in >> ts >> t >> f >> targ >> s >> m;
   msg.type = (Message::Type)t;
   msg.flags = (Message::Flags)f;
   msg.timeStamp = QDateTime::fromTime_t(ts);
+  msg.target = QString::fromUtf8(targ);
   msg.sender = QString::fromUtf8(s);
-  msg.msg = QString::fromUtf8(m);
+  msg.text = QString::fromUtf8(m);
   return in;
 }
index 80b2b8b..711d7ad 100644 (file)
 struct Message {
 
   /** The different types a message can have for display */
-  enum Type { Msg, Notice, Action, Nick, Mode, Join, Part, Quit, Kick, Kill, Server, Info, Error };
+  enum Type { Plain, Notice, Action, Nick, Mode, Join, Part, Quit, Kick, Kill, Server, Info, Error };
   enum Flags { None = 0, Self = 1, Highlight = 2 };
 
   Type type;
   Flags flags;
+  QString target;
   QString sender;
-  QString msg;
+  QString text;
   QDateTime timeStamp;
 
-  Message(Type _type = Msg, QString _msg = "", QString _sender = "", Flags _flags = None)
-  : msg(_msg), sender(_sender), type(_type), flags(_flags) { timeStamp = QDateTime::currentDateTime().toUTC(); };
-
+  Message(QString _target = "", Type _type = Plain, QString _text = "", QString _sender = "", Flags _flags = None)
+  : target(_target), text(_text), sender(_sender), type(_type), flags(_flags) { timeStamp = QDateTime::currentDateTime().toUTC(); }
+
+  static Message plain(QString _target, QString _text, QString _sender = "", Flags _flags = None);
+  static Message notice(QString _target, QString _text, QString _sender = "", Flags _flags = None);
+  static Message action(QString _target, QString _text, QString _sender = "", Flags _flags = None);
+  static Message nick(QString _target, QString _text, QString _sender = "", Flags _flags = None);
+  static Message mode(QString _target, QString _text, QString _sender = "", Flags _flags = None);
+  static Message join(QString _target, QString _text, QString _sender = "", Flags _flags = None);
+  static Message part(QString _target, QString _text, QString _sender = "", Flags _flags = None);
+  static Message quit(QString _target, QString _text, QString _sender = "", Flags _flags = None);
+  static Message kick(QString _target, QString _text, QString _sender = "", Flags _flags = None);
+  static Message kill(QString _target, QString _text, QString _sender = "", Flags _flags = None);
+  static Message server(QString _target, QString _text, QString _sender = "", Flags _flags = None);
+  static Message info(QString _target, QString _text, QString _sender = "", Flags _flags = None);
+  static Message error(QString _target, QString _text, QString _sender = "", Flags _flags = None);
 };
 
 QDataStream &operator<<(QDataStream &out, const Message &msg);
 QDataStream &operator>>(QDataStream &in, Message &msg);
 
-Q_DECLARE_METATYPE(Message)
+Q_DECLARE_METATYPE(Message);
 
 #endif
index e06a9ac..5fefd56 100644 (file)
@@ -25,7 +25,8 @@ enum GUISignal { GS_CLIENT_INIT, GS_USER_INPUT, GS_REQUEST_CONNECT, GS_UPDATE_GL
 
 };
 
-enum CoreSignal { CS_CORE_STATE, CS_DISPLAY_MSG, CS_DISPLAY_STATUS_MSG, CS_UPDATE_GLOBAL_DATA,
+enum CoreSignal { CS_CORE_STATE, CS_SERVER_CONNECTED, CS_SERVER_DISCONNECTED, CS_SERVER_STATE,
+  CS_DISPLAY_MSG, CS_DISPLAY_STATUS_MSG, CS_UPDATE_GLOBAL_DATA,
   CS_MODE_SET, CS_TOPIC_SET, CS_SET_NICKS, CS_NICK_ADDED, CS_NICK_REMOVED, CS_NICK_RENAMED, CS_NICK_UPDATED,
   CS_OWN_NICK_SET,
 
index e4c6478..b865253 100644 (file)
@@ -45,7 +45,7 @@ bool isChannelName(QString str) {
 void writeDataToDevice(QIODevice *dev, const QVariant &item) {
   QByteArray block;
   QDataStream out(&block, QIODevice::WriteOnly);
-  out.setVersion(QDataStream::Qt_4_1);
+  out.setVersion(QDataStream::Qt_4_2);
   out << (quint32)0 << item;
   out.device()->seek(0);
   out << (quint32)(block.size() - sizeof(quint32));
@@ -54,7 +54,7 @@ void writeDataToDevice(QIODevice *dev, const QVariant &item) {
 
 bool readDataFromDevice(QIODevice *dev, quint32 &blockSize, QVariant &item) {
   QDataStream in(dev);
-  in.setVersion(QDataStream::Qt_4_1);
+  in.setVersion(QDataStream::Qt_4_2);
 
   if(blockSize == 0) {
     if(dev->bytesAvailable() < (int)sizeof(quint32)) return false;
index 0ce6c67..693eced 100644 (file)
@@ -1,6 +1,6 @@
-SET(network_SRCS server.cpp buffer.cpp)
+SET(network_SRCS server.cpp)
 SET(network_HDRS)
-SET(network_MOCS server.h buffer.h)
+SET(network_MOCS server.h)
 
 QT4_WRAP_CPP(_MOC ${network_MOCS})
 ADD_LIBRARY(network ${_MOC} ${network_SRCS} ${network_HDRS})
index 61cd6b3..d0b500a 100644 (file)
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
-#include "buffer.h"
+/* OBSOLETE */
 
+#include "buffer.h"
+/*
 Buffer::Buffer(QString n) {
   _name = n;
 
@@ -27,3 +29,4 @@ Buffer::Buffer(QString n) {
 
 }
 
+*/
\ No newline at end of file
index cb77fff..8429cfc 100644 (file)
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
-#ifndef _BUFFER_H_
-#define _BUFFER_H_
+/* THIS CODE IS OBSOLETE, PENDING REMOVAL */
+
+#ifndef _BUFFER_OLD_H_
+#define _BUFFER_OLD_H_
 
 #include <QtCore>
 
-class Buffer : public QObject {
+class Buffer_old : public QObject {
   Q_OBJECT
 
   public:
index c27117e..ffc9054 100644 (file)
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
+/* OBSOLETE CODE, MOVED TO core/ */
+
+#error "obsolete code"
+
 #include "util.h"
 #include "global.h"
 #include "server.h"
index 03e33aa..f0f3a85 100644 (file)
@@ -18,6 +18,8 @@
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
+#error "obsolete code"
+
 #ifndef _SERVER_H_
 #define _SERVER_H_
 
@@ -63,7 +65,7 @@ class Server : public QThread {
   signals:
     void recvRawServerMsg(QString);
     void displayStatusMsg(QString);
-    void displayMsg(QString buffer, Message msg);
+    void displayMsg(Message msg);
     void disconnected();
 
     void nickAdded(QString network, QString nick, VarMap props);