Big, big, major commit this time:
authorManuel Nickschas <sputnick@quassel-irc.org>
Fri, 20 Oct 2006 23:32:26 +0000 (23:32 +0000)
committerManuel Nickschas <sputnick@quassel-irc.org>
Fri, 20 Oct 2006 23:32:26 +0000 (23:32 +0000)
* Separation of GUI and Core finished, including synchronized global data in Global *global
* Speaking of which, the Quassel class has been renamed to Global, way cooler!
* TCP/IP between GUI and Core works, though the server port is still hardcoded to 4242
* The build system now handles any combination of "mono", "core" and "gui" for cmake's -DBUILD
* A lot of fixes and stuff
* More to come, but the basic framework should be stabilizing now.

29 files changed:
CMakeLists.txt
Doxyfile
Quassel.kdevelop.filelist
core/core.cpp
core/core.h
core/coreproxy.cpp
core/coreproxy.h
gui/CMakeLists.txt
gui/coreconnectdlg.cpp [new file with mode: 0644]
gui/coreconnectdlg.h [new file with mode: 0644]
gui/coreconnectdlg.ui [new file with mode: 0644]
gui/guiproxy.cpp
gui/guiproxy.h
gui/mainwin.cpp
gui/mainwin.h
gui/serverlist.cpp
gui/serverlist.h
images/icons.qrc
images/qirc-icon.png [new file with mode: 0644]
main/CMakeLists.txt
main/main_core.cpp
main/main_gui.cpp
main/main_mono.cpp
main/proxy_common.h
main/quassel.cpp
main/quassel.h
main/util.cpp [new file with mode: 0644]
main/util.h [new file with mode: 0644]
network/server.h

index 77c3b41..58a6b95 100644 (file)
@@ -1,32 +1,33 @@
 PROJECT(Quassel)
 
-# CMAKE_MINIMUM_REQUIRED(VERSION 2.4.2)
+# 2.4.2 had a bug with out-of-source builds and UIC dependencies.
+CMAKE_MINIMUM_REQUIRED(VERSION 2.4.3 FATAL_ERROR)
 
 # Select if Quassel should be built in client, server or monolithic mode
-SET(BUILD "mono" CACHE STRING "Defines which Quassel parts are to be built. Can contain 'core', 'gui' and/or 'monolithic' (which is the default).")
+SET(BUILD "mono" CACHE STRING "Defines which Quassel parts are to be built. Can contain 'core', 'gui' and/or 'monolithic' (which is the default), or 'all' to build everything.")
 SET(BUILD_CORE )
 SET(BUILD_GUI )
 SET(BUILD_MONO )
-IF(BUILD MATCHES "core")
+IF(BUILD MATCHES "core" OR BUILD MATCHES "all")
   SET(BUILD_CORE true)
-  MESSAGE(STATUS "Building Quassel core.")
-ENDIF(BUILD MATCHES "core")
-IF(BUILD MATCHES "gui")
+  MESSAGE("Building Quassel core.")
+ENDIF(BUILD MATCHES "core" OR BUILD MATCHES "all")
+IF(BUILD MATCHES "gui" OR BUILD MATCHES "all")
   SET(BUILD_GUI true)
-  MESSAGE(STATUS "Building Quassel GUI.")
-ENDIF(BUILD MATCHES "gui")
-IF(BUILD MATCHES "mono")
+  MESSAGE("Building Quassel GUI.")
+ENDIF(BUILD MATCHES "gui" OR BUILD MATCHES "all")
+IF(BUILD MATCHES "mono" OR BUILD MATCHES "all")
   SET(BUILD_MONO true)
-  MESSAGE(STATUS "Building monolithic Quassel.")
-ENDIF(BUILD MATCHES "mono")
+  MESSAGE("Building monolithic Quassel.")
+ENDIF(BUILD MATCHES "mono" OR BUILD MATCHES "all")
 IF(NOT BUILD_MONO AND NOT BUILD_CORE AND NOT BUILD_GUI)
-  MESSAGE(FATAL_ERROR "You have not selected which parts of Quassel I should build. Aborting.\nRun 'cmake -DBUILD=<part>', where <part> contains one or more of 'core', 'gui' or 'monolithic'.")
+  MESSAGE(FATAL_ERROR "\nYou have not selected which parts of Quassel I should build. Aborting.\nRun 'cmake <path> -DBUILD=<part>', where <part> contains one or more of 'core', 'gui' or 'monolithic', or 'all' to build everything.\n")
 ENDIF(NOT BUILD_MONO AND NOT BUILD_CORE AND NOT BUILD_GUI)
 
 # Define files
 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_gui_SRCS  main/main_gui.cpp ${common_SRCS})
 SET(quassel_RCCS images/icons.qrc)
 SET(quassel_DIRS main gui core network)
 
@@ -46,13 +47,23 @@ SET(QT_USE_QTNETWORK true)
 SET(QT_DONT_USE_QTGUI true)   # This is added later if GUI is requested
 INCLUDE(${QT_USE_FILE})
 
+# Define subdirs. CMake complains if a directory is added twice, so make sure this
+# does not happen in any combination of the requested targets.
+
 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})
 
 IF(BUILD_CORE)
   ADD_DEFINITIONS(-DBUILD_CORE)
-  ADD_SUBDIRECTORY(network)
-  ADD_SUBDIRECTORY(core)
   ADD_EXECUTABLE(quasselcore ${quassel_core_SRCS} ${_RCCS})
   TARGET_LINK_LIBRARIES(quasselcore core network main ${QT_LIBRARIES})
 ENDIF(BUILD_CORE)
@@ -63,19 +74,16 @@ IF(BUILD_GUI OR BUILD_MONO)  # OK, now we need QtGui!
   SET(QT_INCLUDE_DIR "")
   SET(QT_LIBRARIES "")
   INCLUDE(${QT_USE_FILE})
+  ADD_SUBDIRECTORY(gui)
 
   IF(BUILD_MONO)
     ADD_DEFINITIONS(-DBUILD_MONO)
-    ADD_SUBDIRECTORY(gui)
-    ADD_SUBDIRECTORY(network)
-    ADD_SUBDIRECTORY(core)
     ADD_EXECUTABLE(quassel ${quassel_mono_SRCS} ${_RCCS})
     TARGET_LINK_LIBRARIES(quassel gui core network main ${QT_LIBRARIES})
   ENDIF(BUILD_MONO)
 
   IF(BUILD_GUI)
     ADD_DEFINITIONS(-DBUILD_GUI)
-    ADD_SUBDIRECTORY(gui)
     ADD_EXECUTABLE(quasselgui ${quassel_gui_SRCS} ${_RCCS})
     TARGET_LINK_LIBRARIES(quasselgui gui main ${QT_LIBRARIES})
   ENDIF(BUILD_GUI)
index e559841..e21b559 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
@@ -1,11 +1,11 @@
-# Doxyfile 1.4.1-KDevelop
+# Doxyfile 1.4.7
 
 #---------------------------------------------------------------------------
 # Project related configuration options
 #---------------------------------------------------------------------------
-PROJECT_NAME           = quassel.kdevelop
+PROJECT_NAME           = "Quassel IRC"
 PROJECT_NUMBER         = 0.1
-OUTPUT_DIRECTORY       = 
+OUTPUT_DIRECTORY       = doc
 CREATE_SUBDIRS         = NO
 OUTPUT_LANGUAGE        = English
 USE_WINDOWS_ENCODING   = NO
@@ -24,28 +24,30 @@ ABBREVIATE_BRIEF       = "The $name class" \
                          the
 ALWAYS_DETAILED_SEC    = YES
 INLINE_INHERITED_MEMB  = YES
-FULL_PATH_NAMES        = YES
+FULL_PATH_NAMES        = NO
 STRIP_FROM_PATH        = /
 STRIP_FROM_INC_PATH    = 
 SHORT_NAMES            = YES
-JAVADOC_AUTOBRIEF      = YES
+JAVADOC_AUTOBRIEF      = NO
 MULTILINE_CPP_IS_BRIEF = NO
 DETAILS_AT_TOP         = YES
 INHERIT_DOCS           = YES
-DISTRIBUTE_GROUP_DOC   = NO
+SEPARATE_MEMBER_PAGES  = NO
 TAB_SIZE               = 8
 ALIASES                = 
-OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_FOR_C  = NO
 OPTIMIZE_OUTPUT_JAVA   = NO
-SUBGROUPING            = NO
+BUILTIN_STL_SUPPORT    = NO
+DISTRIBUTE_GROUP_DOC   = NO
+SUBGROUPING            = YES
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
 EXTRACT_ALL            = YES
 EXTRACT_PRIVATE        = YES
 EXTRACT_STATIC         = YES
-EXTRACT_LOCAL_CLASSES  = YES
-EXTRACT_LOCAL_METHODS  = YES
+EXTRACT_LOCAL_CLASSES  = NO
+EXTRACT_LOCAL_METHODS  = NO
 HIDE_UNDOC_MEMBERS     = NO
 HIDE_UNDOC_CLASSES     = NO
 HIDE_FRIEND_COMPOUNDS  = NO
@@ -64,7 +66,7 @@ GENERATE_BUGLIST       = YES
 GENERATE_DEPRECATEDLIST= YES
 ENABLED_SECTIONS       = 
 MAX_INITIALIZER_LINES  = 30
-SHOW_USED_FILES        = NO
+SHOW_USED_FILES        = YES
 SHOW_DIRECTORIES       = YES
 FILE_VERSION_FILTER    = 
 #---------------------------------------------------------------------------
@@ -80,7 +82,7 @@ WARN_LOGFILE           =
 #---------------------------------------------------------------------------
 # configuration options related to the input files
 #---------------------------------------------------------------------------
-INPUT                  = /home/sputnick/devel/quassel
+INPUT                  = /home/sputnick/shared/Development/quassel/
 FILE_PATTERNS          = *.c \
                          *.cc \
                          *.cxx \
@@ -127,7 +129,7 @@ FILE_PATTERNS          = *.c \
                          *.moc \
                          *.xpm \
                          *.dox
-RECURSIVE              = yes
+RECURSIVE              = YES
 EXCLUDE                = 
 EXCLUDE_SYMLINKS       = NO
 EXCLUDE_PATTERNS       = 
@@ -143,10 +145,12 @@ FILTER_SOURCE_FILES    = NO
 #---------------------------------------------------------------------------
 SOURCE_BROWSER         = YES
 INLINE_SOURCES         = NO
-STRIP_CODE_COMMENTS    = NO
-REFERENCED_BY_RELATION = NO
-REFERENCES_RELATION    = NO
-VERBATIM_HEADERS       = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION    = YES
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = YES
 #---------------------------------------------------------------------------
 # configuration options related to the alphabetical class index
 #---------------------------------------------------------------------------
@@ -207,7 +211,7 @@ MAN_LINKS              = NO
 #---------------------------------------------------------------------------
 # configuration options related to the XML output
 #---------------------------------------------------------------------------
-GENERATE_XML           = yes
+GENERATE_XML           = NO
 XML_OUTPUT             = xml
 XML_SCHEMA             = 
 XML_DTD                = 
@@ -226,9 +230,9 @@ PERLMOD_MAKEVAR_PREFIX =
 #---------------------------------------------------------------------------
 # Configuration options related to the preprocessor   
 #---------------------------------------------------------------------------
-ENABLE_PREPROCESSING   = YES
+ENABLE_PREPROCESSING   = NO
 MACRO_EXPANSION        = NO
-EXPAND_ONLY_PREDEF     = NO
+EXPAND_ONLY_PREDEF     = YES
 SEARCH_INCLUDES        = YES
 INCLUDE_PATH           = 
 INCLUDE_FILE_PATTERNS  = 
@@ -240,7 +244,7 @@ SKIP_FUNCTION_MACROS   = YES
 #---------------------------------------------------------------------------
 TAGFILES               = 
 GENERATE_TAGFILE       = quassel.tag
-ALLEXTERNALS           = NO
+ALLEXTERNALS           = YES
 EXTERNAL_GROUPS        = YES
 PERL_PATH              = /usr/bin/perl
 #---------------------------------------------------------------------------
@@ -252,11 +256,12 @@ HAVE_DOT               = YES
 CLASS_GRAPH            = YES
 COLLABORATION_GRAPH    = YES
 GROUP_GRAPHS           = YES
-UML_LOOK               = NO
+UML_LOOK               = YES
 TEMPLATE_RELATIONS     = NO
 INCLUDE_GRAPH          = YES
 INCLUDED_BY_GRAPH      = YES
 CALL_GRAPH             = NO
+CALLER_GRAPH           = NO
 GRAPHICAL_HIERARCHY    = YES
 DIRECTORY_GRAPH        = YES
 DOT_IMAGE_FORMAT       = png
index a8934b6..50baa35 100644 (file)
@@ -35,3 +35,8 @@ main/quassel.h
 main/main_gui.cpp
 core/coreproxy.cpp
 core/coreproxy.h
+main/util.cpp
+main/util.h
+gui/coreconnectdlg.ui
+gui/coreconnectdlg.cpp
+gui/coreconnectdlg.h
index 7c341d5..169d28a 100644 (file)
@@ -33,30 +33,24 @@ Core::Core() {
 
   connect(&server, SIGNAL(recvLine(QString)), coreProxy, SLOT(csCoreMessage(QString)));
 
+  // Read global settings from config file 
   QSettings s;
-  VarMap identities = s.value("Network/Identities").toMap();
-  //VarMap networks   = s.value("Network/
-  quassel->putData("Identities", identities);
+  s.beginGroup("Global");
+  QString key;
+  foreach(key, s.childKeys()) {
+    global->updateData(key, s.value(key));
+  }
+  global->updateData("CoreReady", true);
+  // Now that we are in sync, we can connect signals to automatically store further updates.
+  // I don't think we care if global data changed locally or if it was updated by a client. 
+  connect(global, SIGNAL(dataUpdatedRemotely(QString)), SLOT(globalDataUpdated(QString)));
+  connect(global, SIGNAL(dataPutLocally(QString)), SLOT(globalDataUpdated(QString)));
 
   server.start();
 }
 
-void Core::init() {
-
-
-}
-
-/*
-void Core::run() {
-
-  connect(&server, SIGNAL(recvLine(const QString &)), this, SIGNAL(outputLine(const QString &)));
-  //connect(
-  server.start();
-  exec();
-}
-*/
-
 void Core::connectToIrc(const QString &h, quint16 port) {
+  if(server.isConnected()) return;
   qDebug() << "Core: Connecting to " << h << ":" << port;
   server.connectToIrc(h, port);
 }
@@ -66,14 +60,10 @@ void Core::inputLine(QString s) {
 
 }
 
-VarMap Core::loadIdentities() {
-  QSettings s;
-  return s.value("Network/Identities").toMap();
-}
-
-void Core::storeIdentities(VarMap identities) {
+void Core::globalDataUpdated(QString key) {
+  QVariant data = global->getData(key);
   QSettings s;
-  s.setValue("Network/Identities", identities);
+  s.setValue(QString("Global/")+key, data);
 }
 
-Core *core;
+Core *core = 0;
index 96cba3a..46258ae 100644 (file)
@@ -46,6 +46,9 @@ class Core : public QObject {
   signals:
     void outputLine(const QString &);  // temp
 
+  private slots:
+    void globalDataUpdated(QString);
+
   private:
     //void run();
 
index ab00384..dd14c81 100644 (file)
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
-#include "coreproxy.h"
 #include <QDebug>
 
+#include "coreproxy.h"
+#include "quassel.h"
+#include "util.h"
+
 CoreProxy::CoreProxy() {
   if(coreProxy) qFatal("Trying to instantiate more than one CoreProxy object!");
 
+  connect(global, SIGNAL(dataPutLocally(QString)), this, SLOT(updateGlobalData(QString)));
+  connect(&server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
+  if(!server.listen(QHostAddress::Any, 4242)) {
+    qFatal(QString(QString("Could not open GUI client port %1: %2").arg(4242).arg(server.errorString())).toAscii());
+  }
+  qDebug() << "Listening for GUI clients on port" << server.serverPort() << ".";
+}
+
+void CoreProxy::incomingConnection() {
+  QTcpSocket *socket = server.nextPendingConnection();
+  connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
+  connect(socket, SIGNAL(readyRead()), this, SLOT(clientHasData()));
+  clients.append(socket);
+  blockSizes.insert(socket, (quint32)0);
+  qDebug() << "Client connected from " << socket->peerAddress().toString();
+}
+
+void CoreProxy::clientHasData() {
+  QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
+  Q_ASSERT(socket && blockSizes.contains(socket));
+  quint32 bsize = blockSizes.value(socket);
+  QVariant item;
+  while(readDataFromDevice(socket, bsize, item)) {
+    QList<QVariant> sigdata = item.toList();
+    Q_ASSERT(sigdata.size() == 4);
+    switch((GUISignal)sigdata[0].toInt()) {
+      case GS_CLIENT_INIT:  processClientInit(socket, sigdata[1]); break;
+      case GS_UPDATE_GLOBAL_DATA: processClientUpdate(socket, sigdata[1].toString(), sigdata[2]); break;
+      //case GS_CLIENT_READY: processClientReady(sigdata[1], sigdata[2], sigdata[3]); break;
+      default: recv((GUISignal)sigdata[0].toInt(), sigdata[1], sigdata[2], sigdata[3]); break;
+    }
+    blockSizes[socket] = bsize = 0;
+  }
+  blockSizes[socket] = bsize;
+}
+
+void CoreProxy::clientDisconnected() {
+  QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
+  blockSizes.remove(socket);
+  clients.removeAll(socket);
+  qDebug() << "Client disconnected.";
+}
+
+void CoreProxy::processClientInit(QTcpSocket *socket, const QVariant &v) {
+  VarMap msg = v.toMap();
+  if(msg["GUIProtocol"].toUInt() != GUI_PROTOCOL) {
+    qDebug() << "Client version mismatch. Disconnecting.";
+    socket->close();
+    return;
+  }
+  VarMap reply;
+  VarMap coreData;
+  QStringList dataKeys = global->getKeys();
+  QString key;
+  foreach(key, dataKeys) {
+    coreData[key] = global->getData(key);
+  }
+  reply["CoreData"] = coreData;
+  //QVariant payload = QByteArray(1000000, 'a');
+  //reply["payload"] = payload;
+  QList<QVariant> sigdata;
+  sigdata.append(CS_CORE_STATE); sigdata.append(QVariant(reply)); sigdata.append(QVariant()); sigdata.append(QVariant());
+  writeDataToDevice(socket, QVariant(sigdata));
+}
+
+void CoreProxy::processClientUpdate(QTcpSocket *socket, QString key, QVariant data) {
+  global->updateData(key, data);
+  QList<QVariant> sigdata;
+  sigdata.append(CS_UPDATE_GLOBAL_DATA); sigdata.append(key); sigdata.append(data); sigdata.append(QVariant());
+  QTcpSocket *s;
+  foreach(s, clients) {
+    if(s != socket) writeDataToDevice(s, QVariant(sigdata));
+  }
+}
+
+void CoreProxy::updateGlobalData(QString key) {
+  QVariant data = global->getData(key);
+  emit csUpdateGlobalData(key, data);
+}
+
+void CoreProxy::send(CoreSignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
+  sendToGUI(sig, arg1, arg2, arg3);
+  QList<QVariant> sigdata;
+  sigdata.append(sig); sigdata.append(arg1); sigdata.append(arg2); sigdata.append(arg3);
+  //qDebug() << "Sending signal: " << sigdata;
+  QTcpSocket *socket;
+  foreach(socket, clients) {
+    writeDataToDevice(socket, QVariant(sigdata));
+  }
 }
 
-void CoreProxy::csCoreMessage(QString s) {
-  send(CS_CORE_MESSAGE, s);
+void CoreProxy::recv(GUISignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
+  //qDebug() << "[CORE] Received signal" << sig << ":" << arg1<<arg2<<arg3;
+  switch(sig) {
+    case GS_UPDATE_GLOBAL_DATA: emit gsPutGlobalData(arg1.toString(), arg2); break;
+    case GS_USER_INPUT: emit gsUserInput(arg1.toString()); break;
+    case GS_REQUEST_CONNECT: emit gsRequestConnect(arg1.toString(), arg2.toUInt()); break;
+    default: qWarning() << "Unknown signal in CoreProxy::recv: " << sig;
+  }
 }
 
 CoreProxy *coreProxy;
index bd52bb0..1bded17 100644 (file)
@@ -23,8 +23,9 @@
 
 #include "proxy_common.h"
 
-#include <QObject>
-#include <QVariant>
+#include <QtCore>
+#include <QTcpSocket>
+#include <QTcpServer>
 
 /** This class is the Core side of the proxy. The Core connects its signals and slots to it,
  *  and the calls are marshalled and sent to (or received and unmarshalled from) the GUIProxy.
 class CoreProxy : public QObject {
   Q_OBJECT
 
-  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());
-
   public:
     CoreProxy();
 
   public slots:
-    void csCoreMessage(QString);
-
+    inline void csCoreMessage(QString s)                           { send(CS_CORE_MESSAGE, s); }
+    inline void csUpdateGlobalData(QString key, QVariant data)     { send(CS_UPDATE_GLOBAL_DATA, key, data); }
 
   signals:
+    void gsPutGlobalData(QString, QVariant);
     void gsUserInput(QString);
     void gsRequestConnect(QString, quint16);
 
+  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());
+    void sendToGUI(CoreSignal, QVariant arg1, QVariant arg2, QVariant arg3);
+
+    void processClientInit(QTcpSocket *socket, const QVariant &v);
+    void processClientUpdate(QTcpSocket *, QString key, QVariant data);
+
+  private slots:
+    void incomingConnection();
+    void clientHasData();
+    void clientDisconnected();
+    void updateGlobalData(QString key);
+
+  private:
+    QTcpServer server;
+    QList<QTcpSocket *> clients;
+    QHash<QTcpSocket *, quint32> blockSizes;
+
   friend class GUIProxy;
 };
 
index b622620..8a8c328 100644 (file)
@@ -1,13 +1,14 @@
-SET(gui_SRCS channelwidget.cpp mainwin.cpp serverlist.cpp guiproxy.cpp)
+SET(gui_SRCS channelwidget.cpp mainwin.cpp serverlist.cpp coreconnectdlg.cpp guiproxy.cpp)
 SET(gui_HDRS )
-SET(gui_MOCS channelwidget.h mainwin.h serverlist.h guiproxy.h)
-SET(gui_UICS channelwidget.ui identitiesdlg.ui identitieseditdlg.ui networkeditdlg.ui nickeditdlg.ui serverlistdlg.ui)
+SET(gui_MOCS channelwidget.h mainwin.h serverlist.h coreconnectdlg.h guiproxy.h)
+SET(gui_UICS channelwidget.ui identitiesdlg.ui identitieseditdlg.ui networkeditdlg.ui nickeditdlg.ui serverlistdlg.ui coreconnectdlg.ui)
 
 QT4_WRAP_UI(_UIC ${gui_UICS})
 QT4_WRAP_CPP(_MOC ${gui_MOCS})
 
 # We need to work around a dependency bug with out-of-source builds...
-SET_SOURCE_FILES_PROPERTIES(${gui_SRCS} PROPERTIES OBJECT_DEPENDS "${_UIC}")
+# Seems to be fixed!
+#SET_SOURCE_FILES_PROPERTIES(${gui_SRCS} PROPERTIES OBJECT_DEPENDS "${_UIC}")
 INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
 
 ADD_LIBRARY(gui ${gui_HDRS} ${gui_SRCS} ${_MOC} ${_UIC})
diff --git a/gui/coreconnectdlg.cpp b/gui/coreconnectdlg.cpp
new file mode 100644 (file)
index 0000000..477ac04
--- /dev/null
@@ -0,0 +1,100 @@
+/***************************************************************************
+ *   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 <QtGui>
+#include "coreconnectdlg.h"
+#include "guiproxy.h"
+#include "quassel.h"
+
+CoreConnectDlg::CoreConnectDlg(QWidget *parent) : QDialog(parent) {
+  ui.setupUi(this);
+  ui.progressBar->hide();
+  coreState = 0;
+  QSettings s;
+  connect(ui.hostName, SIGNAL(textChanged(QString)), this, SLOT(hostEditChanged(QString)));
+  connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(hostSelected()));
+
+  ui.hostName->setText(s.value("GUI/CoreHost", "localhost").toString());
+  ui.hostName->setSelection(0, ui.hostName->text().length());
+  ui.hostPort->setValue(s.value("GUI/CorePort", 4242).toInt());
+  ui.autoConnect->setChecked(s.value("GUI/CoreAutoConnect", true).toBool());
+  if(s.value("GUI/CoreAutoConnect").toBool()) {
+    hostSelected();
+  }
+}
+
+void CoreConnectDlg::setStartState() {
+  ui.hostName->show(); ui.hostPort->show(); ui.hostLabel->show(); ui.portLabel->show();
+  ui.statusText->setText(tr("Connect to Quassel Core running on:"));
+  ui.buttonBox->button(QDialogButtonBox::Ok)->show();
+  ui.hostName->setEnabled(true); ui.hostPort->setEnabled(true);
+  ui.hostName->setSelection(0, ui.hostName->text().length());
+}
+
+void CoreConnectDlg::hostEditChanged(QString txt) {
+  ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(txt.length());
+}
+
+void CoreConnectDlg::hostSelected() {
+  ui.hostName->hide(); ui.hostPort->hide(); ui.hostLabel->hide(); ui.portLabel->hide();
+  ui.statusText->setText(tr("Connecting to %1:%2" ).arg(ui.hostName->text()).arg(ui.hostPort->value()));
+  ui.buttonBox->button(QDialogButtonBox::Ok)->hide();
+  connect(guiProxy, SIGNAL(coreConnected()), this, SLOT(coreConnected()));
+  connect(guiProxy, SIGNAL(coreConnectionError(QString)), this, SLOT(coreConnectionError(QString)));
+  guiProxy->connectToCore(ui.hostName->text(), ui.hostPort->value());
+
+}
+
+void CoreConnectDlg::coreConnected() {
+  ui.hostLabel->hide(); ui.hostName->hide(); ui.portLabel->hide(); ui.hostPort->hide();
+  ui.statusText->setText(tr("Synchronizing..."));
+  QSettings s;
+  s.setValue("GUI/CoreHost", ui.hostName->text());
+  s.setValue("GUI/CorePort", ui.hostPort->value());
+  s.setValue("GUI/CoreAutoConnect", ui.autoConnect->isChecked());
+  connect(guiProxy, SIGNAL(recvPartialItem(quint32, quint32)), this, SLOT(updateProgressBar(quint32, quint32)));
+  connect(guiProxy, SIGNAL(csCoreState(QVariant)), this, SLOT(recvCoreState(QVariant)));
+  ui.progressBar->show();
+  VarMap initmsg;
+  initmsg["GUIProtocol"] = GUI_PROTOCOL;
+  guiProxy->send(GS_CLIENT_INIT, QVariant(initmsg));
+}
+
+void CoreConnectDlg::coreConnectionError(QString err) {
+  QMessageBox::warning(this, tr("Connection Error"), tr("<b>Could not connect to Quassel Core!</b><br>\n") + err, QMessageBox::Retry);
+  disconnect(guiProxy, 0, this, 0);
+  ui.autoConnect->setChecked(false);
+  setStartState();
+}
+
+void CoreConnectDlg::updateProgressBar(quint32 recv, quint32 avail) {
+  ui.progressBar->setMaximum(avail);
+  ui.progressBar->setValue(recv);
+}
+
+void CoreConnectDlg::recvCoreState(QVariant state) {
+  ui.progressBar->hide();
+  coreState = state;
+  accept();
+}
+
+QVariant CoreConnectDlg::getCoreState() {
+  return coreState;
+}
diff --git a/gui/coreconnectdlg.h b/gui/coreconnectdlg.h
new file mode 100644 (file)
index 0000000..e0e8522
--- /dev/null
@@ -0,0 +1,50 @@
+/***************************************************************************
+ *   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 _CORECONNECTDLG_H
+#define _CORECONNECTDLG_H
+
+#include "coreconnectdlg.h"
+#include "ui_coreconnectdlg.h"
+
+class CoreConnectDlg: public QDialog {
+  Q_OBJECT
+
+  public:
+    CoreConnectDlg(QWidget *);
+    QVariant getCoreState();
+
+  private slots:
+    void hostEditChanged(QString);
+    void hostSelected();
+
+    void coreConnected();
+    void coreConnectionError(QString);
+    void updateProgressBar(quint32 bytes, quint32 avail);
+    void recvCoreState(QVariant);
+
+  private:
+    Ui::CoreConnectDlg ui;
+    QVariant coreState;
+
+    void setStartState();
+};
+
+#endif
diff --git a/gui/coreconnectdlg.ui b/gui/coreconnectdlg.ui
new file mode 100644 (file)
index 0000000..12b0be0
--- /dev/null
@@ -0,0 +1,172 @@
+<ui version="4.0" >
+ <class>CoreConnectDlg</class>
+ <widget class="QDialog" name="CoreConnectDlg" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>499</width>
+    <height>144</height>
+   </rect>
+  </property>
+  <property name="sizePolicy" >
+   <sizepolicy>
+    <hsizetype>5</hsizetype>
+    <vsizetype>3</vsizetype>
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle" >
+   <string>Connect to Quassel Core</string>
+  </property>
+  <property name="windowIcon" >
+   <iconset resource="../images/icons.qrc" >:/default/server.png</iconset>
+  </property>
+  <property name="modal" >
+   <bool>false</bool>
+  </property>
+  <layout class="QVBoxLayout" >
+   <property name="margin" >
+    <number>9</number>
+   </property>
+   <property name="spacing" >
+    <number>6</number>
+   </property>
+   <item>
+    <widget class="QLabel" name="statusText" >
+     <property name="text" >
+      <string>Connect to Quassel Core running on:</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" >
+     <property name="margin" >
+      <number>0</number>
+     </property>
+     <property name="spacing" >
+      <number>6</number>
+     </property>
+     <item>
+      <widget class="QLabel" name="hostLabel" >
+       <property name="text" >
+        <string>Host</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="hostName" />
+     </item>
+     <item>
+      <widget class="QLabel" name="portLabel" >
+       <property name="text" >
+        <string>Port</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSpinBox" name="hostPort" >
+       <property name="maximum" >
+        <number>65535</number>
+       </property>
+       <property name="minimum" >
+        <number>1024</number>
+       </property>
+       <property name="value" >
+        <number>4242</number>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QProgressBar" name="progressBar" >
+     <property name="value" >
+      <number>0</number>
+     </property>
+     <property name="orientation" >
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <spacer>
+     <property name="orientation" >
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" >
+      <size>
+       <width>20</width>
+       <height>53</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" >
+     <property name="margin" >
+      <number>0</number>
+     </property>
+     <property name="spacing" >
+      <number>6</number>
+     </property>
+     <item>
+      <widget class="QCheckBox" name="autoConnect" >
+       <property name="text" >
+        <string>Connect automatically</string>
+       </property>
+       <property name="checked" >
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer>
+       <property name="orientation" >
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" >
+        <size>
+         <width>101</width>
+         <height>31</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox" >
+       <property name="orientation" >
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons" >
+        <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="../images/icons.qrc" />
+ </resources>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>CoreConnectDlg</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel" >
+     <x>507</x>
+     <y>273</y>
+    </hint>
+    <hint type="destinationlabel" >
+     <x>297</x>
+     <y>151</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
index 4875d06..256f5a2 100644 (file)
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
-#include "guiproxy.h"
-
-GUIProxy::GUIProxy() {
-  if(guiProxy) qFatal("Trying to instantiate more than one CoreProxy object!");
-
-}
+#include <QtDebug>
 
-void GUIProxy::gsUserInput(QString s) {
-  send(GS_USER_INPUT, s);
-}
+#include "guiproxy.h"
+#include "util.h"
 
-void GUIProxy::gsRequestConnect(QString h, quint16 p) {
-  send(GS_REQUEST_CONNECT, h, p);
+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_UPDATE_GLOBAL_DATA: emit csUpdateGlobalData(arg1.toString(), arg2); break;
+    //case CS_GLOBAL_DATA_CHANGED: emit csGlobalDataChanged(arg1.toString()); break;
+    case CS_CORE_MESSAGE: emit csCoreMessage(arg1.toString()); break;
+    default: qWarning() << "Unknown signal in GUIProxy::recv: " << sig;
+  }
 }
 
 GUIProxy *guiProxy;
index f3b48c0..d1f5534 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <QObject>
 #include <QVariant>
+#include <QTcpSocket>
 
 /** This class is the GUI side of the proxy. The GUI connects its signals and slots to it,
  *  and the calls are marshalled and sent to (or received and unmarshalled from) the CoreProxy.
 class GUIProxy : public QObject {
   Q_OBJECT
 
-  private:
-    void send(GUISignal, QVariant arg1 = QVariant(), QVariant arg2 = QVariant(), QVariant arg3 = QVariant());
-    void recv(CoreSignal, QVariant arg1 = QVariant(), QVariant arg2 = QVariant(), QVariant arg3 = QVariant());
-
   public:
     GUIProxy();
 
   public slots:
-    void gsUserInput(QString);
-    void gsRequestConnect(QString, quint16);
+    inline void gsUserInput(QString s)                             { send(GS_USER_INPUT, s); }
+    inline void gsRequestConnect(QString host, quint16 port)       { send(GS_REQUEST_CONNECT, host, port); }
+    //inline void gsPutQuasselData(QString key, QVariant data)       { send(GS_PUT_QUASSEL_DATA, key, data); }
 
+    void connectToCore(QString host, quint16 port);
+    void disconnectFromCore();
 
   signals:
+    void csCoreState(QVariant);
     void csCoreMessage(QString);
+    void csUpdateGlobalData(QString key, QVariant data);
+    void csGlobalDataChanged(QString key);
+
+    void coreConnected();
+    void coreDisconnected();
+    void coreConnectionError(QString errorMsg);
+
+    void recvPartialItem(quint32 avail, quint32 size);
+
+  public:
+    void send(GUISignal, QVariant arg1 = QVariant(), QVariant arg2 = QVariant(), QVariant arg3 = QVariant());
+    void recv(CoreSignal, QVariant arg1 = QVariant(), QVariant arg2 = QVariant(), QVariant arg3 = QVariant());
+
+  private slots:
+    void updateCoreData(QString);
+
+    void serverError(QAbstractSocket::SocketError);
+    void serverHasData();
+
+  private:
+    QTcpSocket socket;
+    quint32 blockSize;
 
   friend class CoreProxy;
 
index d67309d..ec6d8d4 100644 (file)
 
 #include <QtGui>
 
+#include "quassel.h"
+
 #include "mainwin.h"
 #include "channelwidget.h"
 #include "serverlist.h"
-
-#include "core.h"
-#include "server.h"
+#include "coreconnectdlg.h"
 
 MainWin::MainWin() : QMainWindow() {
 
   setWindowTitle("Quassel IRC");
-  setWindowIcon(QIcon(":/default/tux.png"));
+  setWindowIcon(QIcon(":/qirc-icon.png"));
   setWindowIconText("Quassel IRC");
   //workspace = new QWorkspace(this);
   //setCentralWidget(workspace);
-  ChannelWidget *cw = new ChannelWidget(this);
+  //ChannelWidget *cw = new ChannelWidget(this);
   //workspace->addWindow(cw);
-  setCentralWidget(cw);
+  //setCentralWidget(cw);
+  statusBar()->showMessage(tr("Waiting for core..."));
+  setEnabled(false);
+  show();
+  syncToCore();
+  setEnabled(true);
   serverListDlg = new ServerListDlg(this);
   serverListDlg->setVisible(serverListDlg->showOnStartup());
-  //showServerList();
-
   setupMenus();
-  statusBar()->showMessage(tr("Ready"));
+  //identitiesAct = settingsMenu->addAction(QIcon(":/default/identity.png"), tr("&Identities..."), serverListDlg, SLOT(editIdentities()));
+  //showServerList();
+  ChannelWidget *cw = new ChannelWidget(this);
+  setCentralWidget(cw);
+  //setEnabled(true);
+  statusBar()->showMessage(tr("Ready."));
+}
 
+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::setupMenus() {
@@ -75,7 +103,6 @@ void MainWin::setupMenus() {
   aboutAct->setEnabled(0);
   aboutQtAct = helpMenu->addAction(tr("About &Qt"), qApp, SLOT(aboutQt()));
 
-
   //toolBar = new QToolBar("Test", this);
   //toolBar->addAction(identitiesAct);
   //addToolBar(Qt::TopToolBarArea, toolBar);
index 6ca7f24..faaae57 100644 (file)
@@ -27,6 +27,7 @@ class QMenu;
 class QWorkspace;
 
 class ServerListDlg;
+class CoreConnectDlg;
 
 class MainWin : public QMainWindow {
   Q_OBJECT
@@ -38,10 +39,11 @@ class MainWin : public QMainWindow {
 
   private slots:
     void showServerList();
-    
+
   private:
     void setupMenus();
-    
+    void syncToCore();
+
     QWorkspace *workspace;
     QToolBar *toolBar;
     QMenu *fileMenu, *editMenu, *ircMenu, *serverMenu, *windowMenu, *helpMenu, *settingsMenu;
@@ -50,6 +52,7 @@ class MainWin : public QMainWindow {
     QAction *identitiesAct, *configAct;
 
     ServerListDlg *serverListDlg;
+    CoreConnectDlg *coreConnectDlg;
 };
 
 #endif
index 677d3f4..06e73a6 100644 (file)
 /* NOTE: This dialog holds not only the server list, but also the identities.
  *       This makes perfect sense given the fact that connections are initiated from
  *       this dialog, and that the dialog exists during the lifetime of the program.
- *       This data is also only used from within the GUI, which means it shouldn't be
- *       part of the global Quassel class (me thinks).
  */
 
 ServerListDlg::ServerListDlg(QWidget *parent) : QDialog(parent) {
   ui.setupUi(this);
 
   QSettings settings;
-  settings.beginGroup("Network");
+  settings.beginGroup("GUI");
   ui.showOnStartup->setChecked(settings.value("ShowServerListOnStartup", true).toBool());
   // create some default entries
   VarMap s1, s2, s3, s4;
@@ -114,22 +112,14 @@ void ServerListDlg::storeNetworks() {
 }
 
 void ServerListDlg::loadIdentities() {
- //QSettings s;
-  //s.beginGroup("Identities");
-  //identities = s.value("Network/Identities").toMap();
-  //identities = GuiProxy::loadIdentities();
-  identities = quassel->getData("Identities").toMap();
+  identities = global->getData("Identities", VarMap()).toMap();
   while(!identities.contains("Default")) {
-    identities = VarMap();
     editIdentities();
   }
 }
 
 void ServerListDlg::storeIdentities() {
-  //QSettings s;
-  //s.setValue("Network/Identities", identities);
-  //GuiProxy::storeIdentities(identities);
-  quassel->putData("Identities", identities);
+  global->putData("Identities", identities);
 }
 
 void ServerListDlg::editIdentities() {
@@ -137,7 +127,7 @@ void ServerListDlg::editIdentities() {
   if(dlg.exec() == QDialog::Accepted) {
     identities = dlg.getIdentities();
     QMap<QString, QString> mapping = dlg.getNameMapping();
-    // add mapping here
+    // add mapping here  <-- well, I don't fucking know anymore what I meant by this back in 2005...
 
     //
     storeIdentities();
@@ -147,7 +137,7 @@ void ServerListDlg::editIdentities() {
 
 void ServerListDlg::on_showOnStartup_stateChanged(int) {
   QSettings s;
-  s.setValue("Network/ShowServerListOnStartup", ui.showOnStartup->isChecked());
+  s.setValue("GUI/ShowServerListOnStartup", ui.showOnStartup->isChecked());
 }
 
 /***************************************************************************/
@@ -177,6 +167,8 @@ VarMap NetworkEditDlg::createDefaultNetwork() {
 
 IdentitiesDlg::IdentitiesDlg(QWidget *parent, VarMap _identities) : QDialog(parent) {
   ui.setupUi(this);
+  connect(global, SIGNAL(dataUpdatedRemotely(QString)), this, SLOT(globalDataUpdated(QString)));
+
   connect(ui.enableAutoAway, SIGNAL(stateChanged(int)), this, SLOT(autoAwayChecked()));
 
   identities = _identities;
@@ -205,6 +197,17 @@ IdentitiesDlg::IdentitiesDlg(QWidget *parent, VarMap _identities) : QDialog(pare
   connect(ui.downNickButton, SIGNAL(clicked()), this, SLOT(downNick()));
 }
 
+void IdentitiesDlg::globalDataUpdated(QString key) {
+  if(key == "Identities") {
+    if(QMessageBox::warning(this, tr("Data changed remotely!"), tr("<b>Some other GUI client changed the identities data!</b><br>"
+                                                                "Apply updated settings, losing all changes done locally?"),
+                                                                QMessageBox::Apply|QMessageBox::Discard) == QMessageBox::Apply) {
+      identities = global->getData(key).toMap();
+      updateWidgets();
+    }
+  }
+}
+
 VarMap IdentitiesDlg::createDefaultIdentity() {
   VarMap id;
   id["RealName"] = "foo";
index 854b240..4ce25d7 100644 (file)
@@ -103,6 +103,8 @@ class IdentitiesDlg : public QDialog {
 
     void editIdentities();
 
+    void globalDataUpdated(QString);
+
   private:
     Ui::IdentitiesDlg ui;
     VarMap identities;
index 4f8d0ce..9b6281c 100644 (file)
@@ -89,5 +89,6 @@
         <file>default/view_split.png</file>
         <file>default/view_top_bottom.png</file>
         <file>default/wizard.png</file>
+       <file>qirc-icon.png</file>
     </qresource>
 </RCC>
diff --git a/images/qirc-icon.png b/images/qirc-icon.png
new file mode 100644 (file)
index 0000000..5e438ef
Binary files /dev/null and b/images/qirc-icon.png differ
index 268a027..17ce279 100644 (file)
@@ -1,5 +1,5 @@
-SET(main_SRCS quassel.cpp logger.cpp)
-SET(main_HDRS )
+SET(main_SRCS quassel.cpp logger.cpp util.cpp)
+SET(main_HDRS util.h)
 SET(main_MOCS quassel.h logger.h)
 
 QT4_WRAP_CPP(_MOC ${main_MOCS})
index 90f46c1..1997034 100644 (file)
 #include <iostream>
 
 #include <QCoreApplication>
+#include <QtNetwork>
+#include <QtCore>
+#include <QtDebug>
 
 #include "quassel.h"
 #include "core.h"
 #include "coreproxy.h"
+#include "util.h"
 
 int main(int argc, char **argv) {
   QCoreApplication app(argc, argv);
@@ -31,32 +35,22 @@ int main(int argc, char **argv) {
   QCoreApplication::setApplicationName("Quassel IRC");
   QCoreApplication::setOrganizationName("The Quassel Team");
 
-  Quassel::runMode = Quassel::CoreOnly;
-  quassel = new Quassel();
+  Global::runMode = Global::CoreOnly;
+  global = new Global();
   coreProxy = new CoreProxy();
   core = new Core();
 
   //Logger *logger = new Logger();
   //Quassel::setLogger(logger);
 
-  core->init();
-
   int exitCode = app.exec();
   delete core;
   delete coreProxy;
-  delete quassel;
+  delete global;
   return exitCode;
 }
 
-Core *core = 0;
-
-//GUIProxy::send(uint func, QVariant arg) {
-  /*
-  switch(func) {
-    case LOAD_IDENTITIES: return (QVariant) CoreProxy::loadIdentities();
-    case STORE_IDENTITIES: CoreProxy::storeIdentities(arg.toMap()); return 0;
-
-  }
-  */
+void CoreProxy::sendToGUI(CoreSignal, QVariant, QVariant, QVariant) {
+  // dummy function, no GUI available!
+}
 
-//}
index 10bc53e..bf6ed14 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "quassel.h"
 #include "guiproxy.h"
+#include "util.h"
 
 #include "mainwin.h"
 
@@ -33,25 +34,68 @@ int main(int argc, char **argv) {
   QApplication::setApplicationName("Quassel IRC");
   QApplication::setOrganizationName("The Quassel Team");
 
-  Quassel::runMode = Quassel::GUIOnly;
-  quassel = new Quassel();
+  Global::runMode = Global::GUIOnly;
+  global = new Global();
   guiProxy = new GUIProxy();
 
   MainWin mainWin;
-  mainWin.show();
   int exitCode = app.exec();
   delete guiProxy;
-  delete quassel;
+  delete global;
 }
 
-void GUIProxy::send(GUISignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
+GUIProxy::GUIProxy() {
+  if(guiProxy) qFatal("Trying to instantiate more than one CoreProxy object!");
+
+  blockSize = 0;
+
+  connect(&socket, SIGNAL(readyRead()), this, SLOT(serverHasData()));
+  connect(&socket, SIGNAL(connected()), this, SIGNAL(coreConnected()));
+  connect(&socket, SIGNAL(disconnected()), this, SIGNAL(coreDisconnected()));
+  connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(serverError(QAbstractSocket::SocketError)));
 
+  connect(global, SIGNAL(dataPutLocally(QString)), this, SLOT(updateCoreData(QString)));
+  connect(this, SIGNAL(csUpdateGlobalData(QString, QVariant)), global, SLOT(updateData(QString, QVariant)));
+
+}
 
+void GUIProxy::connectToCore(QString host, quint16 port) {
+  socket.connectToHost(host, port);
+  //VarMap initmsg;
+  //initmsg["GUIProtocol"] = GUI_PROTOCOL;
+  //send(GS_CLIENT_INIT, QVariant(initmsg));
+}
 
+void GUIProxy::disconnectFromCore() {
+  socket.close();
 }
 
-void GUIProxy::recv(CoreSignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
+void GUIProxy::serverError(QAbstractSocket::SocketError) {
+  emit coreConnectionError(socket.errorString());
+}
 
+void GUIProxy::serverHasData() {
+  QVariant item;
+  while(readDataFromDevice(&socket, blockSize, item)) {
+    emit recvPartialItem(1,1);
+    QList<QVariant> sigdata = item.toList();
+    Q_ASSERT(sigdata.size() == 4);
+    recv((CoreSignal)sigdata[0].toInt(), sigdata[1], sigdata[2], sigdata[3]);
+    blockSize = 0;
+  }
+  if(blockSize > 0) {
+    emit recvPartialItem(socket.bytesAvailable(), blockSize);
+  }
+}
 
+void GUIProxy::send(GUISignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
+  QList<QVariant> sigdata;
+  sigdata.append(sig); sigdata.append(arg1); sigdata.append(arg2); sigdata.append(arg3);
+  //qDebug() << "Sending signal: " << sigdata;
+  writeDataToDevice(&socket, QVariant(sigdata));
+}
 
+void GUIProxy::updateCoreData(QString key) {
+  QVariant data = global->getData(key);
+  send(GS_UPDATE_GLOBAL_DATA, key, data);
 }
index c7fb094..0a37bcb 100644 (file)
@@ -35,43 +35,39 @@ int main(int argc, char **argv) {
   QApplication::setApplicationName("Quassel IRC");
   QApplication::setOrganizationName("The Quassel Team");
 
-  Quassel::runMode = Quassel::Monolithic;
-  quassel = new Quassel();
+  Global::runMode = Global::Monolithic;
+  global = new Global();
   guiProxy = new GUIProxy();
   coreProxy = new CoreProxy();
   core = new Core();
 
-  core->init();
-
   MainWin mainWin;
   mainWin.show();
   int exitCode = app.exec();
   delete core;
   delete guiProxy;
   delete coreProxy;
-  delete quassel;
+  delete global;
   return exitCode;
 }
 
-void GUIProxy::send(GUISignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
-  coreProxy->recv(sig, arg1, arg2, arg3);
+void CoreProxy::sendToGUI(CoreSignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
+  guiProxy->recv(sig, arg1, arg2, arg3);
 }
 
-void CoreProxy::recv(GUISignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
-  switch(sig) {
-    case GS_USER_INPUT: emit gsUserInput(arg1.toString()); break;
-    case GS_REQUEST_CONNECT: emit gsRequestConnect(arg1.toString(), arg2.toUInt()); break;
-    default: qWarning() << "Unknown signal in CoreProxy::recv: " << sig;
-  }
+GUIProxy::GUIProxy() {
+  if(guiProxy) qFatal("Trying to instantiate more than one CoreProxy object!");
 }
 
-void CoreProxy::send(CoreSignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
-  guiProxy->recv(sig, arg1, arg2, arg3);
+void GUIProxy::send(GUISignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
+  coreProxy->recv(sig, arg1, arg2, arg3);
 }
 
-void GUIProxy::recv(CoreSignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
-  switch(sig) {
-    case CS_CORE_MESSAGE: emit csCoreMessage(arg1.toString()); break;
-    default: qWarning() << "Unknown signal in GUIProxy::recv: " << sig;
-  }
-}
+// Dummy function definitions
+// These are not needed, since we don't have a network connection to the core.
+void GUIProxy::serverHasData() {}
+void GUIProxy::connectToCore(QString, quint16) {}
+void GUIProxy::disconnectFromCore() {}
+void GUIProxy::updateCoreData(QString) {}
+void GUIProxy::serverError(QAbstractSocket::SocketError) {}
+
index a25864c..197e3de 100644 (file)
 #ifndef _PROXY_COMMON_H_
 #define _PROXY_COMMON_H_
 
-enum GUISignal { GS_USER_INPUT, GS_REQUEST_CONNECT,
+enum GUISignal { GS_CLIENT_INIT, GS_USER_INPUT, GS_REQUEST_CONNECT, GS_UPDATE_GLOBAL_DATA,
 
 };
 
-enum CoreSignal { CS_CORE_MESSAGE
+enum CoreSignal { CS_CORE_STATE, CS_CORE_MESSAGE, CS_UPDATE_GLOBAL_DATA,
 
 };
 
index 7ba190b..cb35cc2 100644 (file)
 
 #include "quassel.h"
 #include "logger.h"
-//#include "proxy.h"
 #include "core.h"
 
 #include <QString>
+#include <QStringList>
 #include <QDomDocument>
 
 extern void messageHandler(QtMsgType type, const char *msg);
 
-Quassel::Quassel() {
-  if(quassel) qFatal("Trying to instantiate more than one Quassel object!");
+Global::Global() {
+  if(global) qFatal("Trying to instantiate more than one Global object!");
   qInstallMsgHandler(messageHandler);
   //initIconMap();
 }
 
 /*
-void Quassel::setLogger(Logger *) {
+void Global::setLogger(Logger *) {
 
 
 };
 */
 
-QVariant Quassel::getData(QString key) {
+QVariant Global::getData(QString key, QVariant defval) {
+  QVariant d;
   mutex.lock();
-  QVariant d = data[key];
+  if(data.contains(key)) d = data[key];
+  else d = defval;
   mutex.unlock();
-  qDebug() << "getData("<<key<<"): " << d;
+  //qDebug() << "getData("<<key<<"): " << d;
   return d;
 }
 
-void Quassel::putData(QString key, const QVariant &d) {
+QStringList Global::getKeys() {
+  QStringList k;
+  mutex.lock();
+  k = data.keys();
+  mutex.unlock();
+  return k;
+}
+
+void Global::putData(QString key, QVariant d) {
+  mutex.lock();
+  data[key] = d;
+  mutex.unlock();
+  emit dataPutLocally(key);
+}
+
+void Global::updateData(QString key, QVariant d) {
   mutex.lock();
   data[key] = d;
   mutex.unlock();
-  emit dataChanged(key, d);
-  qDebug() << "putData("<<key<<"): " << d;
-  qDebug() << "data: " << data;
+  emit dataUpdatedRemotely(key);
 }
 
 /* not done yet */
-void Quassel::initIconMap() {
+void Global::initIconMap() {
 // Do not depend on GUI in core!
 /*
   QDomDocument doc("IconMap");
@@ -85,11 +100,11 @@ void Quassel::initIconMap() {
  * @return Pointer to a newly created QIcon
  */
 //
-//QIcon *Quassel::getIcon(QString /*symbol*/) {
+//QIcon *Global::getIcon(QString /*symbol*/) {
   //if(symbol == "connect"
 
 //  return 0;
 //}
 
-Quassel *quassel = 0;
-Quassel::RunMode Quassel::runMode;
+Global *global = 0;
+Global::RunMode Global::runMode;
index 3d2b541..ad4cb1d 100644 (file)
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
-#ifndef _QUASSEL_H_
-#define _QUASSEL_H_
+#ifndef _GLOBAL_H_
+#define _GLOBAL_H_
 
-class Quassel;
+/** The protocol version we use fo the communication between core and GUI */
+#define GUI_PROTOCOL 1
+
+class Global;
 
 #include <QtCore>
 //#include <QMutex>
 
 /* Some global stuff */
 typedef QMap<QString, QVariant> VarMap;
-extern Quassel *quassel;
+extern Global *global;
 
 /**
- * A static class containing global data.
- * This is used in both core and GUI modules. Where appropriate, accessors are thread-safe
- * to account for that fact.
- */
-class Quassel : public QObject {
+ * This class is mostly a globally synchronized data store, meant for storing systemwide settings such
+ * as identities or network lists. This class is a singleton, but not static as we'd like to use signals and
+ * slots with it.
+ * The global object is used in both Core and GUI clients. Storing and retrieving data is thread-safe.
+ * \note While updated data is propagated to all the remote parts of Quassel quite quickly, the synchronization
+ *       protocol is in no way designed to guarantee strict consistency at all times. In other words, it may
+ *       well happen that different instances of global data differ from one another for a little while until
+ *       all update messages have been processed. You should never rely on all global data stores being consistent.
+*/
+class Global : public QObject {
   Q_OBJECT
 
   public:
-    Quassel();
+    Global();
     //static Logger *getLogger();
     //static void setLogger(Logger *);
 
 //    static QIcon *getIcon(QString symbol);
 
-    QVariant getData(QString key);
+    QVariant getData(QString key, QVariant defaultValue = QVariant());
+    QStringList getKeys();
 
   public slots:
-    void putData(QString key, const QVariant &data);
+    void putData(QString key, QVariant data);      ///< Store data changed locally, will be propagated to all other clients and the core
+    void updateData(QString key, QVariant data);   ///< Update stored data if requested by the core or other clients
 
   signals:
-    void dataChanged(QString key, const QVariant &data);
+    void dataPutLocally(QString key);
+    void dataUpdatedRemotely(QString key);  // sent by remote update only!
 
   public:
     enum RunMode { Monolithic, GUIOnly, CoreOnly };
@@ -59,7 +70,7 @@ class Quassel : public QObject {
 
   private:
     static void initIconMap();
-    
+
     //static Logger *logger;
 
 //    static QString iconPath;
diff --git a/main/util.cpp b/main/util.cpp
new file mode 100644 (file)
index 0000000..71c1913
--- /dev/null
@@ -0,0 +1,46 @@
+/***************************************************************************
+ *   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 <QtCore>
+
+void writeDataToDevice(QIODevice *dev, const QVariant &item) {
+  QByteArray block;
+  QDataStream out(&block, QIODevice::WriteOnly);
+  out.setVersion(QDataStream::Qt_4_1);
+  out << (quint32)0 << item;
+  out.device()->seek(0);
+  out << (quint32)(block.size() - sizeof(quint32));
+  dev->write(block);
+}
+
+bool readDataFromDevice(QIODevice *dev, quint32 &blockSize, QVariant &item) {
+  QDataStream in(dev);
+  in.setVersion(QDataStream::Qt_4_1);
+
+  if(blockSize == 0) {
+    if(dev->bytesAvailable() < (int)sizeof(quint32)) return false;
+    in >> blockSize;
+  }
+  if(dev->bytesAvailable() < blockSize) return false;
+  in >> item;
+  return true;
+}
diff --git a/main/util.h b/main/util.h
new file mode 100644 (file)
index 0000000..1df034c
--- /dev/null
@@ -0,0 +1,44 @@
+/***************************************************************************
+ *   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 _UTIL_H_
+#define _UTIL_H_
+
+#include <QIODevice>
+#include <QVariant>
+
+/**
+ *  Writes a QVariant to a device. The data item is prefixed with the resulting blocksize,
+ *  so the corresponding function readDataFromDevice() can check if enough data is available
+ *  at the device to reread the item.
+ */
+void writeDataToDevice(QIODevice *, const QVariant &);
+
+/** Reads a data item from a device that has previously been written by writeDataToDevice().
+ *  If not enough data bytes are available, the function returns false and the QVariant reference
+ *  remains untouched.
+ */
+bool readDataFromDevice(QIODevice *, quint32 &, QVariant &);
+
+
+
+
+
+#endif
index b0077c1..5e61958 100644 (file)
@@ -29,8 +29,9 @@
 
 #define DEFAULT_PORT 6667
 
+/*! \file */
 
-/**
+/*! \class Server
  * This is a server object, managing a single connection to an IRC server, handling the associated channels and so on.
  * We have this run 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...
@@ -47,6 +48,7 @@ class Server : public QThread {
     void run();
 
     // serverState state();
+    bool isConnected() { return socket.state() == QAbstractSocket::ConnectedState; }
 
   public slots:
     // void setServerOptions();