Merged changes from branch "sput" r56:61 back into trunk.
authorManuel Nickschas <sputnick@quassel-irc.org>
Fri, 4 May 2007 15:08:18 +0000 (15:08 +0000)
committerManuel Nickschas <sputnick@quassel-irc.org>
Fri, 4 May 2007 15:08:18 +0000 (15:08 +0000)
This contains a lot of new stuff in all parts of quassel. It also contains a serious
bug, which for some reason causes quassel to crash every few starts. Merging this only
because of public request.

So don't complain - I've warned you!

45 files changed:
CMakeLists.txt
Doxyfile
core/core.cpp
core/core.h
core/coreproxy.h
core/server.cpp
core/server.h
gui/CMakeLists.txt
gui/buffer.cpp
gui/buffer.h
gui/bufferwidget.cpp [new file with mode: 0644]
gui/bufferwidget.h [new file with mode: 0644]
gui/channelwidgetinput.cpp
gui/chatwidget.cpp
gui/chatwidget.h
gui/guiproxy.cpp
gui/guiproxy.h
gui/mainwin.cpp
gui/mainwin.h
gui/networkview.cpp
gui/networkview.h
gui/serverlist.cpp
gui/settingsdlg.cpp [new file with mode: 0644]
gui/settingsdlg.h [moved from gui/settings.h with 89% similarity]
gui/settingspages.cpp [moved from gui/settings.cpp with 74% similarity]
gui/settingspages.h [new file with mode: 0644]
gui/style.cpp
gui/style.h
gui/ui/aboutdlg.ui [new file with mode: 0644]
gui/ui/buffermgmntsettingspage.ui [new file with mode: 0644]
gui/ui/bufferwidget.ui
gui/ui/connectionsettingspage.ui [new file with mode: 0644]
gui/ui/coresettingspage.ui [new file with mode: 0644]
gui/ui/mainwin.ui
gui/ui/networkview.ui
gui/ui/settingsdlg.ui
main/CMakeLists.txt
main/main_gui.cpp
main/main_mono.cpp
main/message.cpp
main/message.h
main/proxy_common.h
main/settings.cpp [new file with mode: 0644]
main/settings.h [new file with mode: 0644]
plugins/plugin.h

index 9f80077..92043db 100644 (file)
@@ -24,7 +24,7 @@ IF(NOT BUILD_MONO AND NOT BUILD_CORE AND NOT BUILD_GUI)
   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)
 
   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)
 
-SET(CMAKE_BUILD_TYPE Debug)
+#SET(CMAKE_BUILD_TYPE Debug)
 
 # Define files
 SET(quassel_mono_SRCS main/main_mono.cpp)
 
 # Define files
 SET(quassel_mono_SRCS main/main_mono.cpp)
@@ -47,6 +47,7 @@ FIND_PACKAGE(Qt4 REQUIRED)
 
 # Set needed libraries
 SET(QT_USE_QTXML true)
 
 # Set needed libraries
 SET(QT_USE_QTXML true)
+SET(QT_USE_QTSQL true)
 SET(QT_USE_QTNETWORK true)
 SET(QT_DONT_USE_QTGUI true)   # This is added later if GUI is requested
 INCLUDE(${QT_USE_FILE})
 SET(QT_USE_QTNETWORK true)
 SET(QT_DONT_USE_QTGUI true)   # This is added later if GUI is requested
 INCLUDE(${QT_USE_FILE})
index 0272fd8..f10fed8 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
@@ -263,8 +263,8 @@ UML_LOOK               = YES
 TEMPLATE_RELATIONS     = NO
 INCLUDE_GRAPH          = YES
 INCLUDED_BY_GRAPH      = YES
 TEMPLATE_RELATIONS     = NO
 INCLUDE_GRAPH          = YES
 INCLUDED_BY_GRAPH      = YES
-CALL_GRAPH             = NO
-CALLER_GRAPH           = NO
+CALL_GRAPH             = YES
+CALLER_GRAPH           = YES
 GRAPHICAL_HIERARCHY    = YES
 DIRECTORY_GRAPH        = YES
 DOT_IMAGE_FORMAT       = png
 GRAPHICAL_HIERARCHY    = YES
 DIRECTORY_GRAPH        = YES
 DOT_IMAGE_FORMAT       = png
index b93c96e..5bed7ad 100644 (file)
 #include "core.h"
 #include "server.h"
 #include "global.h"
 #include "core.h"
 #include "server.h"
 #include "global.h"
+#include "util.h"
 #include "coreproxy.h"
 
 #include "coreproxy.h"
 
+#include <QtSql>
 #include <QSettings>
 
 Core::Core() {
 #include <QSettings>
 
 Core::Core() {
@@ -41,7 +43,7 @@ Core::Core() {
   foreach(key, s.childKeys()) {
     global->updateData(key, s.value(key));
   }
   foreach(key, s.childKeys()) {
     global->updateData(key, s.value(key));
   }
-  initBackLog();
+  initBackLogOld();
   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. 
   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. 
@@ -61,6 +63,7 @@ Core::~Core() {
     if(f->isOpen()) f->close();
     delete f;
   }
     if(f->isOpen()) f->close();
     delete f;
   }
+  logDb.close();
 }
 
 void Core::globalDataUpdated(QString key) {
 }
 
 void Core::globalDataUpdated(QString key) {
@@ -90,6 +93,7 @@ void Core::connectToIrc(QStringList networks) {
       connect(server, SIGNAL(nickRemoved(QString, QString)), coreProxy, SLOT(csNickRemoved(QString, QString)));
       connect(server, SIGNAL(nickUpdated(QString, QString, VarMap)), coreProxy, SLOT(csNickUpdated(QString, QString, VarMap)));
       connect(server, SIGNAL(ownNickSet(QString, QString)), coreProxy, SLOT(csOwnNickSet(QString, QString)));
       connect(server, SIGNAL(nickRemoved(QString, QString)), coreProxy, SLOT(csNickRemoved(QString, QString)));
       connect(server, SIGNAL(nickUpdated(QString, QString, VarMap)), coreProxy, SLOT(csNickUpdated(QString, QString, VarMap)));
       connect(server, SIGNAL(ownNickSet(QString, QString)), coreProxy, SLOT(csOwnNickSet(QString, QString)));
+      connect(server, SIGNAL(queryRequested(QString, QString)), coreProxy, SLOT(csQueryRequested(QString, QString)));
       // add error handling
       connect(server, SIGNAL(connected(QString)), coreProxy, SLOT(csServerConnected(QString)));
       connect(server, SIGNAL(disconnected(QString)), this, SLOT(serverDisconnected(QString)));
       // add error handling
       connect(server, SIGNAL(connected(QString)), coreProxy, SLOT(csServerConnected(QString)));
       connect(server, SIGNAL(disconnected(QString)), this, SLOT(serverDisconnected(QString)));
@@ -112,7 +116,7 @@ void Core::serverDisconnected(QString net) {
 void Core::recvMessageFromServer(Message msg) {
   Server *s = qobject_cast<Server*>(sender());
   Q_ASSERT(s);
 void Core::recvMessageFromServer(Message msg) {
   Server *s = qobject_cast<Server*>(sender());
   Q_ASSERT(s);
-  logMessage(s->getNetwork(), msg);
+  logMessageOld(s->getNetwork(), msg);
   emit displayMsg(s->getNetwork(), msg);
 }
 
   emit displayMsg(s->getNetwork(), msg);
 }
 
@@ -122,8 +126,40 @@ void Core::recvStatusMsgFromServer(QString msg) {
   emit displayStatusMsg(s->getNetwork(), msg);
 }
 
   emit displayStatusMsg(s->getNetwork(), msg);
 }
 
-// file name scheme: quassel-backlog-2006-29-10.bin
 void Core::initBackLog() {
 void Core::initBackLog() {
+  QDir backLogDir = QDir(Global::quasselDir);
+  if(!backLogDir.exists()) {
+    qWarning(QString("Creating backlog directory \"%1\"...").arg(backLogDir.absolutePath()).toAscii());
+    if(!backLogDir.mkpath(backLogDir.absolutePath())) {
+      qWarning(QString("Could not create backlog directory! Disabling logging...").toAscii());
+      backLogEnabled = false;
+      return;
+    }
+  }
+  QString backLogFile = Global::quasselDir + "/quassel-backlog.sqlite";
+  logDb = QSqlDatabase::addDatabase("QSQLITE", "backlog");
+  logDb.setDatabaseName(backLogFile);
+  bool ok = logDb.open();
+  if(!ok) {
+    qWarning(tr("Could not open backlog database: %1").arg(logDb.lastError().text()).toAscii());
+    qWarning(tr("Disabling logging...").toAscii());
+  }
+  // TODO store database version
+  QSqlQuery query = logDb.exec("CREATE TABLE IF NOT EXISTS backlog ("
+                         "Time INTEGER, User TEXT, Network TEXT, Buffer TEXT, Message BLOB"
+                         ");");
+  if(query.lastError().isValid()) {
+    qWarning(tr("Could not create backlog table: %1").arg(query.lastError().text()).toAscii());
+    qWarning(tr("Disabling logging...").toAscii());
+    backLogEnabled = false;
+    return;
+  }
+
+  backLogEnabled = true;
+}
+
+// file name scheme: quassel-backlog-2006-29-10.bin
+void Core::initBackLogOld() {
   backLogDir = QDir(Global::quasselDir + "/backlog");
   if(!backLogDir.exists()) {
     qWarning(QString("Creating backlog directory \"%1\"...").arg(backLogDir.absolutePath()).toAscii());
   backLogDir = QDir(Global::quasselDir + "/backlog");
   if(!backLogDir.exists()) {
     qWarning(QString("Creating backlog directory \"%1\"...").arg(backLogDir.absolutePath()).toAscii());
@@ -164,7 +200,7 @@ void Core::initBackLog() {
         qWarning(QString("\"%1\": Version mismatch!").arg(f.fileName()).toAscii());
         f.close(); continue;
       }
         qWarning(QString("\"%1\": Version mismatch!").arg(f.fileName()).toAscii());
         f.close(); continue;
       }
-      qDebug() << "Reading backlog from" << f.fileName();
+      //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()) {
       logFileDates[net] = QDate::fromString(f.fileName(),
           QString("'%1/quassel-backlog-'yyyy-MM-dd'.bin'").arg(dir.absolutePath()));
       if(!logFileDates[net].isValid()) {
@@ -181,12 +217,32 @@ void Core::initBackLog() {
   backLogEnabled = true;
 }
 
   backLogEnabled = true;
 }
 
+void Core::logMessage(QString net, Message msg) {
+  if(!backLogEnabled) return;
+  QString buf;
+  if(msg.flags & Message::PrivMsg) {
+    // query
+    if(msg.flags & Message::Self) buf = msg.target;
+    else buf = nickFromMask(msg.sender);
+  } else {
+    buf = msg.target;
+  }
+  QSqlQuery query = logDb.exec(QString("INSERT INTO backlog Time, User, Network, Buffer, Message "
+                               "VALUES %1, %2, %3, %4, %5;")
+      .arg(msg.timeStamp.toTime_t()).arg("Default").arg(net).arg(buf).arg(msg.text));
+  if(query.lastError().isValid()) {
+    qWarning(tr("Error while logging to database: %1").arg(query.lastError().text()).toAscii());
+  }
+
+}
+
+
 /** Log a core message (emitted via a displayMsg() signal) to the backlog file.
  * If a file for the current day does not exist, one will be created. Otherwise, messages will be appended.
  * 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.
  */
 /** Log a core message (emitted via a displayMsg() signal) to the backlog file.
  * If a file for the current day does not exist, one will be created. Otherwise, messages will be appended.
  * 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(QString net, Message msg) {
+void Core::logMessageOld(QString net, Message msg) {
   backLog[net].append(msg);
   if(!logFileDirs.contains(net)) {
     QDir dir(backLogDir.absolutePath() + "/" + net);
   backLog[net].append(msg);
   if(!logFileDirs.contains(net)) {
     QDir dir(backLogDir.absolutePath() + "/" + net);
index b2a3125..6417825 100644 (file)
@@ -24,6 +24,7 @@
 #include <QMap>
 #include <QString>
 #include <QVariant>
 #include <QMap>
 #include <QString>
 #include <QVariant>
+#include <QSqlDatabase>
 
 #include "server.h"
 
 
 #include "server.h"
 
@@ -66,9 +67,13 @@ class Core : public QObject {
     QHash<QString, QDate> logFileDates;
     QHash<QString, QDir> logFileDirs;
 
     QHash<QString, QDate> logFileDates;
     QHash<QString, QDir> logFileDirs;
 
+    QSqlDatabase logDb;
+
     //uint getNetIdx(QString net);
     void initBackLog();
     //uint getNetIdx(QString net);
     void initBackLog();
+    void initBackLogOld();
     void logMessage(QString, Message);
     void logMessage(QString, Message);
+    void logMessageOld(QString, Message);
 
 };
 
 
 };
 
index c8850a3..8aecb18 100644 (file)
@@ -54,6 +54,7 @@ class CoreProxy : public QObject {
     inline void csNickRenamed(QString net, QString oldn, QString newn)  { send(CS_NICK_RENAMED, net, oldn, newn); }
     inline void csNickUpdated(QString net, QString nick, VarMap props)  { send(CS_NICK_UPDATED, net, nick, props); }
     inline void csOwnNickSet(QString net, QString nick)                 { send(CS_OWN_NICK_SET, net, nick); }
     inline void csNickRenamed(QString net, QString oldn, QString newn)  { send(CS_NICK_RENAMED, net, oldn, newn); }
     inline void csNickUpdated(QString net, QString nick, VarMap props)  { send(CS_NICK_UPDATED, net, nick, props); }
     inline void csOwnNickSet(QString net, QString nick)                 { send(CS_OWN_NICK_SET, net, nick); }
+    inline void csQueryRequested(QString net, QString nick)             { send(CS_QUERY_REQUESTED, net, nick); }
 
   signals:
     void gsPutGlobalData(QString, QVariant);
 
   signals:
     void gsPutGlobalData(QString, QVariant);
index 541816b..89fb0aa 100644 (file)
@@ -75,7 +75,7 @@ void Server::disconnectFromIrc(QString net) {
 void Server::socketHasData() {
   while(socket.canReadLine()) {
     QString s = socket.readLine().trimmed();
 void Server::socketHasData() {
   while(socket.canReadLine()) {
     QString s = socket.readLine().trimmed();
-    qDebug() << "Read" << s;
+    //qDebug() << "Read" << s;
     emit recvRawServerMsg(s);
     //Message *msg = Message::createFromServerString(this, s);
     handleServerMsg(s);
     emit recvRawServerMsg(s);
     //Message *msg = Message::createFromServerString(this, s);
     handleServerMsg(s);
@@ -132,7 +132,7 @@ void Server::userInput(QString net, QString buf, QString msg) {
 }
 
 void Server::putRawLine(QString s) {
 }
 
 void Server::putRawLine(QString s) {
-  qDebug() << "SentRaw: " << s;
+//  qDebug() << "SentRaw: " << s;
   s += "\r\n";
   socket.write(s.toAscii());
 }
   s += "\r\n";
   socket.write(s.toAscii());
 }
@@ -145,7 +145,7 @@ void Server::putCmd(QString cmd, QStringList params, QString prefix) {
     m += " " + params[i];
   }
   if(!params.isEmpty()) m += " :" + params.last();
     m += " " + params[i];
   }
   if(!params.isEmpty()) m += " :" + params.last();
-  qDebug() << "Sent: " << m;
+//  qDebug() << "Sent: " << m;
   m += "\r\n";
   socket.write(m.toAscii());
 }
   m += "\r\n";
   socket.write(m.toAscii());
 }
@@ -354,6 +354,11 @@ void Server::handleUserPart(QString bufname, QString msg) {
   putCmd("PART", params);
 }
 
   putCmd("PART", params);
 }
 
+void Server::handleUserQuery(QString bufname, QString msg) {
+  QString nick = msg.section(' ', 0, 0);
+  if(!nick.isEmpty()) emit queryRequested(network, nick);
+}
+
 void Server::handleUserQuit(QString bufname, QString msg) {
   putCmd("QUIT", QStringList(msg));
 }
 void Server::handleUserQuit(QString bufname, QString msg) {
   putCmd("QUIT", QStringList(msg));
 }
@@ -367,7 +372,11 @@ void Server::handleUserSay(QString bufname, QString msg) {
   QStringList params;
   params << bufname << msg;
   putCmd("PRIVMSG", params);
   QStringList params;
   params << bufname << msg;
   putCmd("PRIVMSG", params);
-  emit displayMsg(Message::plain(params[0], msg, ownNick, Message::Self));
+  if(isChannelName(bufname)) {
+    emit displayMsg(Message::plain(params[0], msg, ownNick, Message::Self));
+  } else {
+    emit displayMsg(Message::plain(params[0], msg, ownNick, Message::Self|Message::PrivMsg));
+  }
 }
 
 void Server::handleUserTopic(QString bufname, QString msg) {
 }
 
 void Server::handleUserTopic(QString bufname, QString msg) {
@@ -532,8 +541,16 @@ void Server::handleServerPing(QString prefix, QStringList params) {
 
 void Server::handleServerPrivmsg(QString prefix, QStringList params) {
   updateNickFromMask(prefix);
 
 void Server::handleServerPrivmsg(QString prefix, QStringList params) {
   updateNickFromMask(prefix);
+  Q_ASSERT(params.count() >= 2);
   if(params.count()<2) emit displayMsg(Message::plain(params[0], "", prefix));
   if(params.count()<2) emit displayMsg(Message::plain(params[0], "", prefix));
-  else emit displayMsg(Message::plain(params[0], params[1], prefix));
+  else {
+    if(params[0] == ownNick) {
+      emit displayMsg(Message::plain("", params[1], prefix, Message::PrivMsg));
+    } else {
+      Q_ASSERT(isChannelName(params[0]));  // should be channel!
+      emit displayMsg(Message::plain(params[0], params[1], prefix));
+    }
+  }
 }
 
 void Server::handleServerQuit(QString prefix, QStringList params) {
 }
 
 void Server::handleServerQuit(QString prefix, QStringList params) {
@@ -568,14 +585,15 @@ void Server::handleServer001(QString prefix, QStringList params) {
   // send performlist
   QStringList performList = networkSettings["Perform"].toString().split( "\n" );
   int count = performList.count();
   // send performlist
   QStringList performList = networkSettings["Perform"].toString().split( "\n" );
   int count = performList.count();
-  for ( int a = 0; a < count; a++ ) {
-       if ( !performList[a].isEmpty() ) {
-               userInput( network, "", performList[a] ); 
-       }
+  for(int a = 0; a < count; a++) {
+    if(!performList[a].isEmpty() ) {
+      userInput(network, "", performList[a]);
+    }
   }
 }
 
 /* RPL_ISUPPORT */
   }
 }
 
 /* RPL_ISUPPORT */
+// TODO Complete 005 handling, also use sensible defaults for non-sent stuff
 void Server::handleServer005(QString prefix, QStringList params) {
   foreach(QString p, params) {
     QString key = p.section("=", 0, 0);
 void Server::handleServer005(QString prefix, QStringList params) {
   foreach(QString p, params) {
     QString key = p.section("=", 0, 0);
@@ -617,7 +635,7 @@ void Server::handleServer332(QString prefix, QStringList params) {
 /* Topic set by... */
 void Server::handleServer333(QString prefix, QStringList params) {
   emit displayMsg(Message::server(params[0],
 /* 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())));
+                    tr("Topic set by %1 on %2").arg(params[1]).arg(QDateTime::fromTime_t(params[2].toUInt()).toString())));
 }
 
 /* RPL_NAMREPLY */
 }
 
 /* RPL_NAMREPLY */
index ba333d3..90763ac 100644 (file)
@@ -76,6 +76,7 @@ class Server : public QThread {
     void topicSet(QString network, QString buffer, QString topic);
     void setNicks(QString network, QString buffer, QStringList nicks);
     void ownNickSet(QString network, QString newNick);
     void topicSet(QString network, QString buffer, QString topic);
     void setNicks(QString network, QString buffer, QStringList nicks);
     void ownNickSet(QString network, QString newNick);
+    void queryRequested(QString network, QString nick);
 
 
   private slots:
 
 
   private slots:
@@ -101,6 +102,7 @@ class Server : public QThread {
     void handleUserNick(QString, QString);
     void handleUserOp(QString, QString);
     void handleUserPart(QString, QString);
     void handleUserNick(QString, QString);
     void handleUserOp(QString, QString);
     void handleUserPart(QString, QString);
+    void handleUserQuery(QString, QString);
     void handleUserQuit(QString, QString);
     void handleUserQuote(QString, QString);
     void handleUserSay(QString, QString);
     void handleUserQuit(QString, QString);
     void handleUserQuote(QString, QString);
     void handleUserSay(QString, QString);
index a9b27aa..fc375b7 100644 (file)
@@ -1,11 +1,12 @@
-SET(gui_SRCS chatwidget.cpp channelwidgetinput.cpp mainwin.cpp serverlist.cpp buffer.cpp
-             identities.cpp coreconnectdlg.cpp guiproxy.cpp networkview.cpp style.cpp settings.cpp)
+SET(gui_SRCS chatwidget.cpp channelwidgetinput.cpp mainwin.cpp serverlist.cpp buffer.cpp bufferwidget.cpp
+             identities.cpp coreconnectdlg.cpp guiproxy.cpp networkview.cpp style.cpp settingsdlg.cpp settingspages.cpp)
 SET(gui_HDRS style.h)
 SET(gui_MOCS chatwidget.h channelwidgetinput.h mainwin.h serverlist.h identities.h coreconnectdlg.h
 SET(gui_HDRS style.h)
 SET(gui_MOCS chatwidget.h channelwidgetinput.h mainwin.h serverlist.h identities.h coreconnectdlg.h
-             guiproxy.h networkview.h buffer.h settings.h)
+             guiproxy.h networkview.h buffer.h bufferwidget.h settingsdlg.h settingspages.h)
 SET(gui_UICS identitiesdlg.ui identitieseditdlg.ui networkeditdlg.ui mainwin.ui
              nickeditdlg.ui serverlistdlg.ui servereditdlg.ui coreconnectdlg.ui ircwidget.ui
 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 settingsdlg.ui)
+             networkview.ui bufferwidget.ui settingsdlg.ui
+             buffermgmntsettingspage.ui connectionsettingspage.ui)
 
 # This prepends ui/ to the UIC names, so we don't have to do this ourselves.
 FOREACH(ui ${gui_UICS})
 
 # This prepends ui/ to the UIC names, so we don't have to do this ourselves.
 FOREACH(ui ${gui_UICS})
index 751da80..7c92ac6 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
 /***************************************************************************
- *   Copyright (C) 2005/06 by The Quassel Team                             *
+ *   Copyright (C) 2005-07 by The Quassel Team                             *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
 #include "buffer.h"
 #include "util.h"
 #include "chatwidget.h"
 #include "buffer.h"
 #include "util.h"
 #include "chatwidget.h"
+#include "bufferwidget.h"
 
 Buffer::Buffer(QString netname, QString bufname) {
 
 Buffer::Buffer(QString netname, QString bufname) {
-  networkName = netname;
-  bufferName = bufname;
+  _networkName = netname;
+  _bufferName = bufname;
+
+  if(bufname.isEmpty()) type = ServerBuffer;
+  else if(isChannelName(bufname)) type = ChannelBuffer;
+  else type = QueryBuffer;
 
 
-  widget = 0;
-  chatWidget = 0;
-  contentsWidget = 0;
   active = false;
   active = false;
+/*
+  QSettings s;
+  s.beginGroup(QString("GUI/BufferStates/%1/%2").arg(netname).arg(bufname));
+  state->splitterState = s.value("Splitter").toByteArray();
+  s.endGroup();
+  */
+  emit bufferUpdated(this);
 }
 
 Buffer::~Buffer() {
 }
 
 Buffer::~Buffer() {
-  delete widget;
-  delete chatWidget;
+  //delete widget;
+  /*
+  QSettings s;
+  s.beginGroup(QString("GUI/BufferStates/%1/%2").arg(networkName).arg(bufferName));
+  s.setValue("Splitter", state->splitterState);
+  s.endGroup();
+*/
+  //delete state;
+  emit bufferDestroyed(this);
 }
 
 }
 
-void Buffer::setActive(bool a) {
-  if(a != active) {
-    active = a;
-    if(widget) widget->setActive(a);
-  }
-}
+void Buffer::init() {
 
 
-void Buffer::displayMsg(Message msg) {
-  contents.append(msg);
-  if(widget) widget->displayMsg(msg);
-}
 
 
-void Buffer::userInput(QString msg) {
-  emit userInput(networkName, bufferName, msg);
 }
 
 }
 
-/* FIXME do we need this? */
-void Buffer::scrollToEnd() {
-  if(!widget) return;
-  //widget->scrollToEnd();
-}
-
-QWidget * Buffer::showWidget(QWidget *parent) {
-
-  if(widget) {
-    return qobject_cast<QWidget*>(widget);
-  }
-
-  if(!contentsWidget) {
-    contentsWidget = new ChatWidgetContents(networkName, bufferName, 0);
-    contentsWidget->hide();
-    /* FIXME do we need this? */
-    for(int i = 0; i < contents.count(); i++) {
-      contentsWidget->appendMsg(contents[i]);
-    }
+void Buffer::setActive(bool a) {
+  if(a != active) {
+    active = a;
+    emit bufferUpdated(this);
   }
   }
-  contentsWidget->hide();
-  widget = new BufferWidget(networkName, bufferName, isActive(), ownNick, contentsWidget, this, parent);
-  widget->setTopic(topic);
-  widget->updateNickList(nicks);
-  connect(widget, SIGNAL(userInput(QString)), this, SLOT(userInput(QString)));
-  return qobject_cast<QWidget*>(widget);
 }
 
 }
 
-void Buffer::hideWidget() {
-  delete widget;
-  widget = 0;
+void Buffer::displayMsg(Message msg) {
+  contents()->append(msg);
+  emit msgDisplayed(msg);
 }
 
 }
 
-void Buffer::deleteWidget() {
-  widget = 0;
+void Buffer::prependMessages(QList<Message> msgs) {
+  _contents = msgs + _contents;
 }
 
 }
 
-QWidget * Buffer::getWidget() {
-  return qobject_cast<QWidget*>(widget);
+void Buffer::processUserInput(QString msg) {
+  // TODO User Input processing (plugins) -> well, this goes through MainWin into Core for processing... so...
+  emit userInput(networkName(), bufferName(), msg);
 }
 
 void Buffer::setTopic(QString t) {
 }
 
 void Buffer::setTopic(QString t) {
-  topic = t;
-  if(widget) widget->setTopic(t);
+  _topic = t;
+  emit topicSet(t);
+  emit bufferUpdated(this);
 }
 
 void Buffer::addNick(QString nick, VarMap props) {
 }
 
 void Buffer::addNick(QString nick, VarMap props) {
-  if(nick == ownNick) setActive(true);
+  if(nick == ownNick()) setActive(true);
   nicks[nick] = props;
   nicks[nick] = props;
-  if(widget) widget->updateNickList(nicks);
+  emit nickListChanged(nicks);
 }
 
 void Buffer::updateNick(QString nick, VarMap props) {
   nicks[nick] = props;
 }
 
 void Buffer::updateNick(QString nick, VarMap props) {
   nicks[nick] = props;
-  if(widget) widget->updateNickList(nicks);
+  emit nickListChanged(nicks);
 }
 
 void Buffer::renameNick(QString oldnick, QString newnick) {
   QVariant v = nicks.take(oldnick);
   nicks[newnick] = v;
 }
 
 void Buffer::renameNick(QString oldnick, QString newnick) {
   QVariant v = nicks.take(oldnick);
   nicks[newnick] = v;
-  if(widget) widget->updateNickList(nicks);
+  emit nickListChanged(nicks);
 }
 
 void Buffer::removeNick(QString nick) {
 }
 
 void Buffer::removeNick(QString nick) {
-  if(nick == ownNick) setActive(false);
+  if(nick == ownNick()) setActive(false);
   nicks.remove(nick);
   nicks.remove(nick);
-  if(widget) widget->updateNickList(nicks);
+  emit nickListChanged(nicks);
 }
 
 void Buffer::setOwnNick(QString nick) {
 }
 
 void Buffer::setOwnNick(QString nick) {
-  ownNick = nick;
-  if(widget) widget->setOwnNick(nick);
+  _ownNick = nick;
+  emit ownNickSet(nick);
 }
 
 /****************************************************************************************/
 
 
 }
 
 /****************************************************************************************/
 
 
-
 /****************************************************************************************/
 
 /****************************************************************************************/
 
-BufferWidget::BufferWidget(QString netname, QString bufname, bool act, QString own, ChatWidgetContents *contents, Buffer *pBuf, QWidget *parent) : QWidget(parent) {
-  ui.setupUi(this);
-  networkName = netname;
-  bufferName = bufname;
-  active = act;
-  parentBuffer = pBuf;
-
-  ui.chatWidget->init(netname, bufname, contents);
-
-  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()));
-
-  opsExpanded = voicedExpanded = usersExpanded = true;
-
-  ui.chatWidget->setFocusProxy(ui.inputEdit);
-  updateTitle();
-}
-
-BufferWidget::~BufferWidget() {
-  ui.chatWidget->takeWidget();   /* remove ownership so the chatwidget contents does not get destroyed */
-  parentBuffer->deleteWidget();  /* make sure the parent buffer knows we are gone */
-}
-
-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();
-    //scrollToEnd();
-  }
-}
-
-void BufferWidget::displayMsg(Message msg) {
-  ui.chatWidget->appendMsg(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();
-}
-
index 4ef1112..09f7e02 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
 /***************************************************************************
- *   Copyright (C) 2005/06 by The Quassel Team                             *
+ *   Copyright (C) 2005-07 by The Quassel Team                             *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
 #include <QtCore>
 #include <QtGui>
 
 #include <QtCore>
 #include <QtGui>
 
-#include "ui_bufferwidget.h"
-
 #include "global.h"
 #include "message.h"
 
 class ChatWidget;
 class ChatWidgetContents;
 class BufferWidget;
 #include "global.h"
 #include "message.h"
 
 class ChatWidget;
 class ChatWidgetContents;
 class BufferWidget;
+struct BufferState;
 
 //!\brief Encapsulates the contents of a single channel, query or server status context.
 /** A Buffer maintains a list of existing nicks and their status. New messages can be appended using
 
 //!\brief Encapsulates the contents of a single channel, query or server status context.
 /** A Buffer maintains a list of existing nicks and their status. New messages can be appended using
@@ -44,18 +43,32 @@ class Buffer : public QObject {
   public:
     Buffer(QString network, QString buffer);
     ~Buffer();
   public:
     Buffer(QString network, QString buffer);
     ~Buffer();
+    static void init();
 
 
+    enum Type { ServerBuffer, ChannelBuffer, QueryBuffer };
+    Type bufferType() { return type; }
     bool isActive() { return active; }
     bool isActive() { return active; }
-  public:
-    QWidget *getWidget();
+
+    QString networkName() { return _networkName; }
+    QString bufferName() { return _bufferName; }
+    QList<Message> *contents() { return &_contents; }
+    VarMap nickList() { return nicks; }
+    QString topic() { return _topic; }
+    QString ownNick() { return _ownNick; }
 
   signals:
     void userInput(QString, QString, QString);
 
   signals:
     void userInput(QString, QString, QString);
-    void nickListChanged(QStringList);
+    void msgDisplayed(Message);
+    void nickListChanged(VarMap nicks);
+    void topicSet(QString topic);
+    void ownNickSet(QString ownNick);
+    void bufferUpdated(Buffer *);
+    void bufferDestroyed(Buffer *);
 
   public slots:
     void setActive(bool active = true);
     void displayMsg(Message);
 
   public slots:
     void setActive(bool active = true);
     void displayMsg(Message);
+    void prependMessages(QList<Message>); // for backlog
     //void recvStatusMsg(QString msg);
     void setTopic(QString);
     //void setNicks(QStringList);
     //void recvStatusMsg(QString msg);
     void setTopic(QString);
     //void setNicks(QStringList);
@@ -65,67 +78,19 @@ class Buffer : public QObject {
     void updateNick(QString nick, VarMap props);
     void setOwnNick(QString nick);
 
     void updateNick(QString nick, VarMap props);
     void setOwnNick(QString nick);
 
-    QWidget * showWidget(QWidget *parent = 0);
-    void hideWidget();
-    void deleteWidget();
-
-    void scrollToEnd();
-
-  private slots:
-    void userInput(QString);
+    void processUserInput(QString);
 
   private:
     bool active;
 
   private:
     bool active;
-    BufferWidget *widget;
-    ChatWidget *chatWidget;
-    ChatWidgetContents *contentsWidget;
-    VarMap nicks;
-    QString topic;
-    QString ownNick;
-    QString networkName, bufferName;
-
-    QList<Message> contents;
-};
-
-//!\brief Displays the contents of a Buffer.
-/** A BufferWidget usually includes a topic line, a nicklist, the chat itself, and an input line.
- * For server buffers or queries, there is of course no nicklist.
- * The contents of the chat is rendered by a ChatWidget.
- */
-class BufferWidget : public QWidget {
-  Q_OBJECT
-
-  public:
-    BufferWidget(QString netname, QString bufname, bool active, QString ownNick, ChatWidgetContents *contents, Buffer *parentBuffer, QWidget *parent = 0);
-    ~BufferWidget();
+    Type type;
 
 
-    void setActive(bool act = true);
-
-  signals:
-    void userInput(QString);
-
-  protected:
-
-  public slots:
-    void displayMsg(Message);
-    void updateNickList(VarMap nicks);
-    void setOwnNick(QString ownNick);
-    void setTopic(QString topic);
-
-  private slots:
-    void enterPressed();
-    void itemExpansionChanged(QTreeWidgetItem *);
-    void updateTitle();
-
-  private:
-    Ui::BufferWidget ui;
-    Buffer *parentBuffer;
-    bool active;
-
-    QString networkName;
-    QString bufferName;
+    VarMap nicks;
+    QString _topic;
+    QString _ownNick;
+    QString _networkName, _bufferName;
+    BufferState *state;
 
 
-    bool opsExpanded, voicedExpanded, usersExpanded;
+    QList<Message> _contents;
 
 };
 
 
 };
 
diff --git a/gui/bufferwidget.cpp b/gui/bufferwidget.cpp
new file mode 100644 (file)
index 0000000..98f8dd8
--- /dev/null
@@ -0,0 +1,259 @@
+/***************************************************************************
+ *   Copyright (C) 2005-07 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 "bufferwidget.h"
+#include "buffer.h"
+#include "chatwidget.h"
+#include "settings.h"
+#include "mainwin.h"
+
+BufferWidget::BufferWidget(QWidget *parent) : QWidget(parent) {
+  ui.setupUi(this);
+
+  //layoutThread->start();
+  //connect(this, SIGNAL(aboutToClose()), layoutThread, SLOT(quit()));
+  //connect(this, SIGNAL(layoutMessages(LayoutTask)), layoutThread, SLOT(processTask(LayoutTask)), Qt::QueuedConnection);
+  //layoutThread->start();
+
+  curBuf = 0;
+  //setBaseSize(QSize(600,400));
+  //setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+  connect(ui.inputEdit, SIGNAL(returnPressed()), this, SLOT(enterPressed()));
+
+}
+
+void BufferWidget::init() {
+  //layoutThread = new LayoutThread();
+  layoutThread = ::layoutThread;
+  connect(layoutThread, SIGNAL(taskProcessed(LayoutTask)), this, SLOT(messagesLayouted(LayoutTask)));
+  //layoutThread->start();
+  //while(!layoutThread->isRunning()) {};
+}
+
+BufferWidget::~BufferWidget() {
+  //emit aboutToClose();
+  //layoutThread->wait(10000);
+  delete layoutThread;
+  foreach(BufferState *s, states.values()) {
+    delete s;
+  }
+}
+
+void BufferWidget::setBuffer(Buffer *buf) {
+  BufferState *state;
+  curBuf = buf;
+  if(states.contains(buf)) {
+    state = states[buf];
+  } else {
+    BufferState *s = new BufferState;
+    s->currentLine = Settings::guiValue(QString("BufferStates/%1/%2/currentLine").arg(buf->networkName()).arg(buf->bufferName()), -1).toInt();
+    if(buf->bufferType() == Buffer::ChannelBuffer) {
+      s->splitterState = Settings::guiValue(QString("BufferStates/%1/%2/splitter").arg(buf->networkName()).arg(buf->bufferName())).toByteArray();
+      s->splitter = new QSplitter(this);
+      s->chatWidget = new ChatWidget(s->splitter);
+      s->nickTree = new QTreeWidget(s->splitter);
+      s->nickTree->headerItem()->setHidden(true);
+      s->nickTree->setRootIsDecorated(false);
+      s->page = s->splitter;
+      updateNickList(s, buf->nickList());
+      s->splitter->restoreState(s->splitterState);
+      connect(buf, SIGNAL(nickListChanged(VarMap)), this, SLOT(updateNickList(VarMap)));
+      connect(s->nickTree, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SLOT(itemExpansionChanged(QTreeWidgetItem*)));
+      connect(s->nickTree, SIGNAL(itemCollapsed(QTreeWidgetItem *)), this, SLOT(itemExpansionChanged(QTreeWidgetItem*)));
+    } else {
+      s->splitter = 0; s->nickTree = 0;
+      s->chatWidget = new ChatWidget(this);
+      s->page = s->chatWidget;
+    }
+    s->opsExpanded = Settings::guiValue(QString("BufferStates/%1/%2/opsExpanded").arg(buf->networkName()).arg(buf->bufferName()), true).toBool();
+    s->voicedExpanded = Settings::guiValue(QString("BufferStates/%1/%2/voicedExpanded").arg(buf->networkName()).arg(buf->bufferName()), true).toBool();
+    s->usersExpanded = Settings::guiValue(QString("BufferStates/%1/%2/usersExpanded").arg(buf->networkName()).arg(buf->bufferName()), true).toBool();
+    states[buf] = s;
+    state = s;
+    state->chatWidget->init(networkName, bufferName);
+    // FIXME: layout and cache all incoming messages... maybe do this in buffer?
+    QList<Message> *l = buf->contents();
+    state->chatWidget->appendMsgList(l);
+    if(chatLineCache.contains(buf)) {
+      state->chatWidget->prependChatLines(chatLineCache[buf]);
+      buf->prependMessages(msgCache[buf]);
+    }
+    connect(buf, SIGNAL(msgDisplayed(Message)), state->chatWidget, SLOT(appendMsg(Message)));
+    connect(buf, SIGNAL(topicSet(QString)), this, SLOT(setTopic(QString)));
+    connect(buf, SIGNAL(ownNickSet(QString)), this, SLOT(setOwnNick(QString)));
+    ui.stackedWidget->addWidget(s->page);
+  }
+  ui.stackedWidget->setCurrentWidget(state->page);
+  ui.topicEdit->setText(buf->topic());
+  chatWidget = state->chatWidget;
+  nickTree = state->nickTree;
+  splitter = state->splitter;
+  //ui.ownNick->set
+  disconnect(this, SIGNAL(userInput(QString)), 0, 0);
+  connect(this, SIGNAL(userInput(QString)), buf, SLOT(processUserInput(QString)));
+  state->chatWidget->setFocusProxy(ui.inputEdit);
+  ui.inputEdit->setFocus();
+  ui.topicEdit->setText(state->topic);
+  ui.ownNick->clear();
+  ui.ownNick->addItem(state->ownNick);
+  updateTitle();
+}
+
+void BufferWidget::prependMessages(Buffer *buf, QList<Message> messages) {
+  LayoutTask task;
+  task.messages = messages;
+  task.buffer = buf;
+  task.net = buf->networkName();
+  task.buf = buf->bufferName();
+  //emit layoutMessages(task);
+  layoutThread->processTask(task);
+}
+
+void BufferWidget::messagesLayouted(LayoutTask task) {
+  if(states.contains(task.buffer)) {
+    states[task.buffer]->chatWidget->prependChatLines(task.lines);
+    task.buffer->prependMessages(task.messages);
+  } else {
+    msgCache[task.buffer] = task.messages + msgCache[task.buffer];
+    chatLineCache[task.buffer] = task.lines + chatLineCache[task.buffer];
+  }
+}
+
+void BufferWidget::saveState() {
+  foreach(Buffer *buf, states.keys()) {
+    BufferState *s = states[buf];
+    if(s->splitter) Settings::setGuiValue(QString("BufferStates/%1/%2/splitter").arg(buf->networkName()).arg(buf->bufferName()), s->splitter->saveState());
+    Settings::setGuiValue(QString("BufferStates/%1/%2/currentLine").arg(buf->networkName()).arg(buf->bufferName()), s->currentLine);
+    Settings::setGuiValue(QString("BufferStates/%1/%2/opsExpanded").arg(buf->networkName()).arg(buf->bufferName()), s->opsExpanded);
+    Settings::setGuiValue(QString("BufferStates/%1/%2/voicedExpanded").arg(buf->networkName()).arg(buf->bufferName()), s->voicedExpanded);
+    Settings::setGuiValue(QString("BufferStates/%1/%2/usersExpanded").arg(buf->networkName()).arg(buf->bufferName()), s->usersExpanded);
+  }
+}
+
+QSize BufferWidget::sizeHint() const {
+  return QSize(800,400);
+}
+
+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();
+    //scrollToEnd();
+  }
+}
+
+void BufferWidget::resizeEvent ( QResizeEvent * event ) {
+  //qDebug() << "resizing:" << bufferName << event->size();
+  QWidget::resizeEvent(event);
+
+}
+
+/*
+void BufferWidget::displayMsg(Message msg) {
+  chatWidget->appendMsg(msg);
+}
+*/
+
+void BufferWidget::setOwnNick(QString nick) {
+  Buffer *buf = qobject_cast<Buffer*>(sender());
+  Q_ASSERT(buf);
+  states[buf]->ownNick = nick;
+  if(buf == curBuf) {
+    ui.ownNick->clear();
+    ui.ownNick->addItem(nick);
+    updateTitle();
+  }
+}
+
+void BufferWidget::setTopic(QString topic) {
+  Buffer *buf = qobject_cast<Buffer*>(sender());
+  Q_ASSERT(buf);
+  states[buf]->topic = topic;
+  if(buf == curBuf) {
+    ui.topicEdit->setText(topic);
+    updateTitle();
+  }
+}
+
+
+void BufferWidget::updateNickList(VarMap nicks) {
+  Buffer *buf = qobject_cast<Buffer*>(sender());
+  Q_ASSERT(buf);
+  updateNickList(states[buf], nicks);
+}
+
+// TODO Use 005
+void BufferWidget::updateNickList(BufferState *state, VarMap nicks) {
+  QTreeWidget *tree = state->nickTree;
+  if(!tree) return;
+  tree->clear();
+  if(nicks.count() != 1) tree->setHeaderLabel(tr("%1 Users").arg(nicks.count()));
+  else tree->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()));
+    tree->addTopLevelItem(ops);
+    ops->setExpanded(state->opsExpanded);
+  } else delete ops;
+  if(voiced->childCount()) {
+    voiced->setText(0, tr("%1 Voiced").arg(voiced->childCount()));
+    tree->addTopLevelItem(voiced);
+    voiced->setExpanded(state->voicedExpanded);
+  } else delete voiced;
+  if(users->childCount()) {
+    users->setText(0, tr("%1 Users").arg(users->childCount()));
+    tree->addTopLevelItem(users);
+    users->setExpanded(state->usersExpanded);
+  } else delete users;
+}
+
+// TODO Use 005 and additional user modes
+void BufferWidget::itemExpansionChanged(QTreeWidgetItem *item) {
+  if(item->child(0)->text(0).startsWith('@')) states[curBuf]->opsExpanded = item->isExpanded();
+  else if(item->child(0)->text(0).startsWith('+')) states[curBuf]->voicedExpanded = item->isExpanded();
+  else states[curBuf]->usersExpanded = item->isExpanded();
+}
+
diff --git a/gui/bufferwidget.h b/gui/bufferwidget.h
new file mode 100644 (file)
index 0000000..e72ebb8
--- /dev/null
@@ -0,0 +1,113 @@
+/***************************************************************************
+ *   Copyright (C) 2005-07 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 _BUFFERWIDGET_H_
+#define _BUFFERWIDGET_H_
+
+#include "gui/ui_bufferwidget.h"
+
+#include "global.h"
+#include "message.h"
+#include "chatwidget.h"
+
+class Buffer;
+struct BufferState;
+class ChatWidget;
+class LayoutThread;
+
+//!\brief Displays the contents of a Buffer.
+/** A BufferWidget usually includes a topic line, a nicklist, the chat itself, and an input line.
+ * For server buffers or queries, there is of course no nicklist.
+ * The contents of the chat is rendered by a ChatWidget.
+ */
+class BufferWidget : public QWidget {
+  Q_OBJECT
+
+  public:
+    BufferWidget(QWidget *parent = 0);
+    ~BufferWidget();
+    void init();
+
+    QSize sizeHint() const;
+
+  signals:
+    void userInput(QString msg);
+    void aboutToClose();
+
+    void layoutMessages(LayoutTask);
+
+  protected:
+
+  public slots:
+    void setBuffer(Buffer *);
+    void saveState();
+    void prependMessages(Buffer *, QList<Message>);  // for backlog processing
+
+  protected:
+    void resizeEvent ( QResizeEvent * event );
+
+  private slots:
+    void enterPressed();
+    void itemExpansionChanged(QTreeWidgetItem *);
+    void updateTitle();
+
+    //void displayMsg(Message);
+    void updateNickList(BufferState *state, VarMap nicks);
+    void updateNickList(VarMap nicks);
+    void setOwnNick(QString ownNick);
+    void setTopic(QString topic);
+    void setActive(bool act = true);
+
+    void messagesLayouted(LayoutTask);
+
+
+  private:
+    Ui::BufferWidget ui;
+    Buffer *curBuf;
+    QHash<Buffer *, BufferState *> states;
+    bool active;
+
+    ChatWidget *chatWidget;
+    QSplitter *splitter;
+    QTreeWidget *nickTree;
+
+    QString networkName;
+    QString bufferName;
+
+    LayoutThread *layoutThread;
+    QHash<Buffer *, QList<ChatLine*> > chatLineCache;
+    QHash<Buffer *, QList<Message> > msgCache;
+};
+
+struct BufferState {
+  ChatWidget *chatWidget;
+  QTreeWidget *nickTree;
+  QSplitter *splitter;
+  QWidget *page;
+  Buffer *buffer;
+  QByteArray splitterState;
+  QString topic, ownNick;
+  QString inputLine;
+  int currentLine;
+  int lineOffset;
+  bool opsExpanded, voicedExpanded, usersExpanded;
+};
+
+#endif
index c029bdd..893623d 100644 (file)
@@ -36,7 +36,7 @@ void ChannelWidgetInput::keyPressEvent(QKeyEvent * event) {
     event->accept();
   } else if(event->key() == Qt::Key_Tab) {
     // Tabcomplete
     event->accept();
   } else if(event->key() == Qt::Key_Tab) {
     // Tabcomplete
-    if(cursorPosition() == text().length()) {
+      if(cursorPosition() == text().length()) {
       
 
     }
       
 
     }
index 03a9d1f..331fdb0 100644 (file)
 #include <QtGui>
 #include <QtCore>
 
 #include <QtGui>
 #include <QtCore>
 
-ChatWidget::ChatWidget(QWidget *parent) : QScrollArea(parent) {
+ChatWidget::ChatWidget(QWidget *parent) : QAbstractScrollArea(parent) {
+  scrollTimer = new QTimer(this);
+  scrollTimer->setSingleShot(false);
+  scrollTimer->setInterval(100);
+  setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+  setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+  setMinimumSize(QSize(400,400));
   setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
   setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
   setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
   setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
-  setAlignment(Qt::AlignLeft | Qt::AlignTop);
-
+  bottomLine = -1;
+  height = 0;
+  ycoords.append(0);
+  pointerPosition = QPoint(0,0);
+  connect(verticalScrollBar(), SIGNAL(actionTriggered(int)), this, SLOT(scrollBarAction(int)));
+  connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrollBarValChanged(int)));
 }
 
 }
 
-void ChatWidget::init(QString netname, QString bufname, ChatWidgetContents *contentsWidget) {
+void ChatWidget::init(QString netname, QString bufname) {
   networkName = netname;
   bufferName = bufname;
   networkName = netname;
   bufferName = bufname;
-  //setAlignment(Qt::AlignBottom);
-  contents = contentsWidget;
-  setWidget(contents);
-  //setWidgetResizable(true);
-  //contents->setWidth(contents->sizeHint().width());
-  contents->setFocusProxy(this);
-  //contents->show();
-  //setAlignment(Qt::AlignBottom);
+  setBackgroundRole(QPalette::Base);
+  setFont(QFont("Fixed"));
+  tsWidth = 90;
+  senderWidth = 100;
+  computePositions();
+  adjustScrollBar();
+  verticalScrollBar()->setValue(verticalScrollBar()->maximum());
+  //verticalScrollBar()->setPageStep(viewport()->height());
+  //verticalScrollBar()->setSingleStep(20);
+  //verticalScrollBar()->setMinimum(0);
+  //verticalScrollBar()->setMaximum((int)height - verticalScrollBar()->pageStep());
+
+  setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+  setMouseTracking(true);
+  mouseMode = Normal;
+  selectionMode = NoSelection;
+  connect(scrollTimer, SIGNAL(timeout()), this, SLOT(handleScrollTimer()));
 }
 
 ChatWidget::~ChatWidget() {
 }
 
 ChatWidget::~ChatWidget() {
-
+  //qDebug() << "destroying chatwidget" << bufferName;
+  foreach(ChatLine *l, lines) {
+    delete l;
+  }
 }
 
 }
 
-void ChatWidget::clear() {
-  //contents->clear();
+QSize ChatWidget::sizeHint() const {
+  //qDebug() << size();
+  return size();
 }
 
 }
 
-void ChatWidget::appendMsg(Message msg) {
-  contents->appendMsg(msg);
-  //qDebug() << "appending" << msg.text;
-
+void ChatWidget::adjustScrollBar() {
+  verticalScrollBar()->setPageStep(viewport()->height());
+  verticalScrollBar()->setSingleStep(20);
+  verticalScrollBar()->setMinimum(0);
+  verticalScrollBar()->setMaximum((int)height - verticalScrollBar()->pageStep());
+  //qDebug() << height << viewport()->height() << verticalScrollBar()->pageStep();
+  //if(bottomLine < 0) {
+  //  verticalScrollBar()->setValue(verticalScrollBar()->maximum());
+  //} else {
+    //int bot = verticalScrollBar()->value() + viewport()->height(); //qDebug() << bottomLine;
+    //verticalScrollBar()->setValue(qMax(0, (int)ycoords[bottomLine+1] - viewport()->height()));
+  //}
 }
 
 }
 
-void ChatWidget::resizeEvent(QResizeEvent *event) {
-  //qDebug() << bufferName << isVisible() << event->size();
-  contents->setWidth(event->size().width());
-  //setAlignment(Qt::AlignBottom);
-  QScrollArea::resizeEvent(event);
+void ChatWidget::scrollBarValChanged(int val) {
+  return;
+  if(val >= verticalScrollBar()->maximum()) bottomLine = -1;
+  else {
+    int bot = val + viewport()->height();
+    int line = yToLineIdx(bot);
+    //bottomLine = line;
+  }
 }
 
 }
 
-/*************************************************************************************/
-
-ChatWidgetContents::ChatWidgetContents(QString net, QString buf, QWidget *parent) : QWidget(parent) {
-  networkName = net;
-  bufferName = buf;
-  layoutTimer = new QTimer(this);
-  layoutTimer->setSingleShot(true);
-  connect(layoutTimer, SIGNAL(timeout()), this, SLOT(triggerLayout()));
+void ChatWidget::scrollBarAction(int action) {
+  switch(action) {
+    case QScrollBar::SliderSingleStepAdd:
+      // More elaborate. But what with loooong lines?
+      // verticalScrollBar()->setValue((int)ycoords[yToLineIdx(verticalScrollBar()->value() + viewport()->height()) + 1] - viewport()->height());
+      break;
+    case QScrollBar::SliderSingleStepSub:
+      //verticalScrollBar()->setValue((int)ycoords[yToLineIdx(verticalScrollBar()->value())]);
+      break;
 
 
-  setBackgroundRole(QPalette::Base);
-  setFont(QFont("Fixed"));
+  }
 
 
-  ycoords.append(0);
-  tsWidth = 90;
-  senderWidth = 100;
-  //textWidth = 400;
-  computePositions();
-  //setFixedWidth((int)(tsWidth + senderWidth + textWidth + 20));
-  setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-  setMouseTracking(true);
-  mouseMode = Normal;
-  selectionMode = NoSelection;
+}
 
 
-  doLayout = false;
+void ChatWidget::handleScrollTimer() {
+  if(mouseMode == MarkText || mouseMode == MarkLines) {
+    if(pointerPosition.y() > viewport()->height()) {
+      verticalScrollBar()->setValue(verticalScrollBar()->value() + pointerPosition.y() - viewport()->height());
+      handleMouseMoveEvent(QPoint(pointerPosition.x(), viewport()->height()));
+    } else if(pointerPosition.y() < 0) {
+      verticalScrollBar()->setValue(verticalScrollBar()->value() + pointerPosition.y());
+      handleMouseMoveEvent(QPoint(pointerPosition.x(), 0));
+    }
+  }
 }
 
 }
 
-ChatWidgetContents::~ChatWidgetContents() {
-  delete layoutTimer;
-  foreach(ChatLine *l, lines) {
-    delete l;
+void ChatWidget::ensureVisible(int line) {
+  int top = verticalScrollBar()->value();
+  int bot = top + viewport()->height();
+  if(ycoords[line+1] > bot) {
+    verticalScrollBar()->setValue(qMax(0, (int)ycoords[line+1] - viewport()->height()));
+  } else if(ycoords[line] < top) {
+    verticalScrollBar()->setValue((int)ycoords[line]);
   }
   }
+
 }
 
 }
 
-QSize ChatWidgetContents::sizeHint() const {
-  //qDebug() << size();
-  return size();
+void ChatWidget::clear() {
+  //contents->clear();
 }
 
 }
 
-void ChatWidgetContents::paintEvent(QPaintEvent *event) {
-  QPainter painter(this);
-  qreal top = event->rect().top();
-  qreal bot = top + event->rect().height();
-  int idx = yToLineIdx(top);
-  if(idx < 0) return;
-  for(int i = idx; i < lines.count() ; i++) {
-    lines[i]->draw(&painter, QPointF(0, ycoords[i]));
-    if(ycoords[i+1] > bot) return;
+void ChatWidget::prependChatLines(QList<ChatLine *> clist) {
+  QList<qreal> tmpy; tmpy.append(0);
+  qreal h = 0;
+  foreach(ChatLine *l, clist) {
+    h += l->layout(tsWidth, senderWidth, textWidth);
+    tmpy.append(h);
   }
   }
+  ycoords.removeFirst();
+  for(int i = 0; i < ycoords.count(); i++) ycoords[i] += h;
+  ycoords = tmpy + ycoords;
+  lines = clist + lines;
+  height += h;
+  /* Fix all variables containing line numbers */
+  int i = clist.count();
+  dragStartLine += i;
+  curLine += i;
+  selectionStart += i; selectionEnd += i; selectionEnd += i;
+  //if(bottomLine >= 0) bottomLine += i;
+  adjustScrollBar();
+  //verticalScrollBar()->setPageStep(viewport()->height());
+  //verticalScrollBar()->setSingleStep(20);
+  //verticalScrollBar()->setMaximum((int)height - verticalScrollBar()->pageStep());
+  verticalScrollBar()->setValue(verticalScrollBar()->value() + (int)h);
+  viewport()->update();
 }
 
 }
 
-void ChatWidgetContents::appendMsg(Message msg) {
+void ChatWidget::appendMsg(Message msg) {
   ChatLine *line = new ChatLine(msg, networkName, bufferName);
   qreal h = line->layout(tsWidth, senderWidth, textWidth);
   ycoords.append(h + ycoords[ycoords.count() - 1]);
   ChatLine *line = new ChatLine(msg, networkName, bufferName);
   qreal h = line->layout(tsWidth, senderWidth, textWidth);
   ycoords.append(h + ycoords[ycoords.count() - 1]);
-  setFixedHeight((int)ycoords[ycoords.count()-1]);
+  height += h;
+  bool flg = (verticalScrollBar()->value() == verticalScrollBar()->maximum());
+  adjustScrollBar();
+  if(flg) verticalScrollBar()->setValue(verticalScrollBar()->maximum());
   lines.append(line);
   lines.append(line);
-  update();
-  return;
-
+  viewport()->update();
 }
 
 }
 
-void ChatWidgetContents::clear() {
-
-
+void ChatWidget::appendMsgList(QList<Message> *list) {
+  foreach(Message msg, *list) {
+   ChatLine *line = new ChatLine(msg, networkName, bufferName);
+    qreal h = line->layout(tsWidth, senderWidth, textWidth);
+    ycoords.append(h + ycoords[ycoords.count() - 1]);
+    height += h;
+    lines.append(line);
+  }
+  bool flg = (verticalScrollBar()->value() == verticalScrollBar()->maximum());
+  adjustScrollBar();
+  if(flg) verticalScrollBar()->setValue(verticalScrollBar()->maximum());
+  viewport()->update();
 }
 
 //!\brief Computes the different x position vars for given tsWidth and senderWidth.
 }
 
 //!\brief Computes the different x position vars for given tsWidth and senderWidth.
-void ChatWidgetContents::computePositions() {
+void ChatWidget::computePositions() {
   senderX = tsWidth + Style::sepTsSender();
   textX = senderX + senderWidth + Style::sepSenderText();
   tsGrabPos = tsWidth + (int)Style::sepTsSender()/2;
   senderGrabPos = senderX + senderWidth + (int)Style::sepSenderText()/2;
   senderX = tsWidth + Style::sepTsSender();
   textX = senderX + senderWidth + Style::sepSenderText();
   tsGrabPos = tsWidth + (int)Style::sepTsSender()/2;
   senderGrabPos = senderX + senderWidth + (int)Style::sepSenderText()/2;
-  textWidth = size().width() - textX;
+  textWidth = viewport()->size().width() - textX;
 }
 
 }
 
-void ChatWidgetContents::setWidth(qreal w) {
-  textWidth = (int)w - (Style::sepTsSender() + Style::sepSenderText()) - tsWidth - senderWidth;
-  setFixedWidth((int)w);
-  layout();
+void ChatWidget::resizeEvent(QResizeEvent *event) {
+  //qDebug() << bufferName << isVisible() << event->size() << event->oldSize();
+  /*if(event->oldSize().isValid())*/
+  //contents->setWidth(event->size().width());
+  //setAlignment(Qt::AlignBottom);
+  if(event->size().width() != event->oldSize().width()) {
+    computePositions();
+    layout();
+  }
+  //adjustScrollBar();
+  //qDebug() << viewport()->size() << viewport()->height();
+  //QAbstractScrollArea::resizeEvent(event);
+  //qDebug() << viewport()->size() << viewport()->geometry();
 }
 
 }
 
-//!\brief Trigger layout (used by layoutTimer only).
-/** This method is triggered by the layoutTimer. Layout the widget if it has been postponed earlier.
- */
-void ChatWidgetContents::triggerLayout() {
-  layout(true);
+void ChatWidget::paintEvent(QPaintEvent *event) {
+  QPainter painter(viewport());
+
+  //qDebug() <<  verticalScrollBar()->value();
+  painter.translate(0, -verticalScrollBar()->value());
+  int top = event->rect().top() + verticalScrollBar()->value();
+  int bot = top + event->rect().height();
+  int idx = yToLineIdx(top);
+  if(idx < 0) return;
+  for(int i = idx; i < lines.count() ; i++) {
+    lines[i]->draw(&painter, QPointF(0, ycoords[i]));
+    if(ycoords[i+1] > bot) return;
+  }
 }
 
 //!\brief Layout the widget.
 }
 
 //!\brief Layout the widget.
-/** The contents of the widget is re-layouted completely. Since this could take a while if the widget
- * is huge, we don't want to trigger the layout procedure too often (causing layout calls to pile up).
- * We use a timer that ensures layouting is only done if the last one has finished.
- */
-void ChatWidgetContents::layout(bool timer) {
-  if(layoutTimer->isActive()) {
-    // Layouting too fast. We set a flag though, so that a final layout is done when the timer runs out.
-    doLayout = true;
-    return;
-  }
-  if(timer && !doLayout) return; // Only check doLayout if we have been triggered by the timer!
+void ChatWidget::layout() {
+  // TODO fix scrollbars
+  //int botLine = yToLineIdx(verticalScrollBar()->value() + 
   qreal y = 0;
   for(int i = 0; i < lines.count(); i++) {
     qreal h = lines[i]->layout(tsWidth, senderWidth, textWidth);
     ycoords[i+1] = h + ycoords[i];
   }
   qreal y = 0;
   for(int i = 0; i < lines.count(); i++) {
     qreal h = lines[i]->layout(tsWidth, senderWidth, textWidth);
     ycoords[i+1] = h + ycoords[i];
   }
-  setFixedHeight((int)ycoords[ycoords.count()-1]);
-  update();
-  doLayout = false; // Clear previous layout requests
-  layoutTimer->start(50); // Minimum time until we start the next layout
+  height = ycoords[ycoords.count()-1];
+  adjustScrollBar();
+  verticalScrollBar()->setValue(verticalScrollBar()->maximum());
+  viewport()->update();
 }
 
 }
 
-int ChatWidgetContents::yToLineIdx(qreal y) {
+int ChatWidget::yToLineIdx(qreal y) {
   if(y >= ycoords[ycoords.count()-1]) ycoords.count()-1;
   if(ycoords.count() <= 1) return 0;
   int uidx = 0;
   if(y >= ycoords[ycoords.count()-1]) ycoords.count()-1;
   if(ycoords.count() <= 1) return 0;
   int uidx = 0;
@@ -190,9 +260,11 @@ int ChatWidgetContents::yToLineIdx(qreal y) {
   }
 }
 
   }
 }
 
-void ChatWidgetContents::mousePressEvent(QMouseEvent *event) {
+void ChatWidget::mousePressEvent(QMouseEvent *event) {
+  if(lines.isEmpty()) return;
+  QPoint pos = event->pos() + QPoint(0, verticalScrollBar()->value());
   if(event->button() == Qt::LeftButton) {
   if(event->button() == Qt::LeftButton) {
-    dragStartPos = event->pos();
+    dragStartPos = pos;
     dragStartMode = Normal;
     switch(mouseMode) {
       case Normal:
     dragStartMode = Normal;
     switch(mouseMode) {
       case Normal:
@@ -203,8 +275,8 @@ void ChatWidgetContents::mousePressEvent(QMouseEvent *event) {
           dragStartMode = DragTextSep;
           setCursor(Qt::ClosedHandCursor);
         } else {
           dragStartMode = DragTextSep;
           setCursor(Qt::ClosedHandCursor);
         } else {
-          dragStartLine = yToLineIdx(event->pos().y());
-          dragStartCursor = lines[dragStartLine]->posToCursor(QPointF(event->pos().x(), event->pos().y()-ycoords[dragStartLine]));
+          dragStartLine = yToLineIdx(pos.y());
+          dragStartCursor = lines[dragStartLine]->posToCursor(QPointF(pos.x(), pos.y()-ycoords[dragStartLine]));
         }
         mouseMode = Pressed;
         break;
         }
         mouseMode = Pressed;
         break;
@@ -212,7 +284,15 @@ void ChatWidgetContents::mousePressEvent(QMouseEvent *event) {
   }
 }
 
   }
 }
 
-void ChatWidgetContents::mouseReleaseEvent(QMouseEvent *event) {
+void ChatWidget::mouseDoubleClickEvent(QMouseEvent *event) {
+
+
+
+}
+
+void ChatWidget::mouseReleaseEvent(QMouseEvent *event) {
+  //QPoint pos = event->pos() + QPoint(0, verticalScrollBar()->value());
+
   if(event->button() == Qt::LeftButton) {
     dragStartPos = QPoint();
     if(mousePos == OverTsSep || mousePos == OverTextSep) setCursor(Qt::OpenHandCursor);
   if(event->button() == Qt::LeftButton) {
     dragStartPos = QPoint();
     if(mousePos == OverTsSep || mousePos == OverTextSep) setCursor(Qt::OpenHandCursor);
@@ -250,10 +330,28 @@ void ChatWidgetContents::mouseReleaseEvent(QMouseEvent *event) {
 /** This is called by Qt whenever the mouse moves. Here we have to do most of the mouse handling,
  * such as changing column widths, marking text or initiating drag & drop.
  */
 /** This is called by Qt whenever the mouse moves. Here we have to do most of the mouse handling,
  * such as changing column widths, marking text or initiating drag & drop.
  */
-void ChatWidgetContents::mouseMoveEvent(QMouseEvent *event) {
+void ChatWidget::mouseMoveEvent(QMouseEvent *event) {
+  QPoint pos = event->pos(); pointerPosition = pos;
+  // Scroll if mouse pointer leaves widget while dragging
+  if((mouseMode == MarkText || mouseMode == MarkLines) && (pos.y() > viewport()->height() || pos.y() < 0)) {
+    if(!scrollTimer->isActive()) {
+      scrollTimer->start();
+    }
+  } else {
+    if(scrollTimer->isActive()) {
+      scrollTimer->stop();
+    }
+  }
+  handleMouseMoveEvent(pos);
+}
+
+void ChatWidget::handleMouseMoveEvent(const QPoint &_pos) {
+  // FIXME
+  if(lines.count() <= 0) return;
   // Set some basic properties of the current position
   // Set some basic properties of the current position
-  int x = event->pos().x();
-  int y = event->pos().y();
+  QPoint pos = _pos + QPoint(0, verticalScrollBar()->value());
+  int x = pos.x();
+  int y = pos.y();
   MousePos oldpos = mousePos;
   if(x >= tsGrabPos - 3 && x <= tsGrabPos + 3) mousePos = OverTsSep;
   else if(x >= senderGrabPos - 3 && x <= senderGrabPos + 3) mousePos = OverTextSep;
   MousePos oldpos = mousePos;
   if(x >= tsGrabPos - 3 && x <= tsGrabPos + 3) mousePos = OverTsSep;
   else if(x >= senderGrabPos - 3 && x <= senderGrabPos + 3) mousePos = OverTextSep;
@@ -263,14 +361,23 @@ void ChatWidgetContents::mouseMoveEvent(QMouseEvent *event) {
   switch(mouseMode) {
     // No special mode. Set mouse cursor if appropriate.
     case Normal:
   switch(mouseMode) {
     // No special mode. Set mouse cursor if appropriate.
     case Normal:
-      if(oldpos != mousePos) {
-        if(mousePos == OverTsSep || mousePos == OverTextSep) setCursor(Qt::OpenHandCursor);
-        else setCursor(Qt::ArrowCursor);
+    {
+      //if(oldpos != mousePos) {
+      if(mousePos == OverTsSep || mousePos == OverTextSep) setCursor(Qt::OpenHandCursor);
+      else {
+        int l = yToLineIdx(y);
+        int c = lines[l]->posToCursor(QPointF(x, y - ycoords[l]));
+        if(c >= 0 && lines[l]->isUrl(c)) {
+          setCursor(Qt::PointingHandCursor);
+        } else {
+          setCursor(Qt::ArrowCursor);
+        }
       }
       }
+    }
       break;
     // Left button pressed. Might initiate marking or drag & drop if we moved past the drag distance.
     case Pressed:
       break;
     // Left button pressed. Might initiate marking or drag & drop if we moved past the drag distance.
     case Pressed:
-      if(!dragStartPos.isNull() && (dragStartPos - event->pos()).manhattanLength() >= QApplication::startDragDistance()) {
+      if(!dragStartPos.isNull() && (dragStartPos - pos).manhattanLength() >= QApplication::startDragDistance()) {
         // Moving a column separator?
         if(dragStartMode == DragTsSep) mouseMode = DragTsSep;
         else if(dragStartMode == DragTextSep) mouseMode = DragTextSep;
         // Moving a column separator?
         if(dragStartMode == DragTsSep) mouseMode = DragTsSep;
         else if(dragStartMode == DragTextSep) mouseMode = DragTextSep;
@@ -297,6 +404,7 @@ void ChatWidgetContents::mouseMoveEvent(QMouseEvent *event) {
             mouseMode = Normal;
           // Otherwise, clear the selection and start text marking!
           } else {
             mouseMode = Normal;
           // Otherwise, clear the selection and start text marking!
           } else {
+            setCursor(Qt::ArrowCursor);
             clearSelection();
             if(dragStartCursor < 0) { mouseMode = MarkLines; curLine = -1; }
             else mouseMode = MarkText;
             clearSelection();
             if(dragStartCursor < 0) { mouseMode = MarkLines; curLine = -1; }
             else mouseMode = MarkText;
@@ -330,36 +438,42 @@ void ChatWidgetContents::mouseMoveEvent(QMouseEvent *event) {
       if(c != curCursor) {
         curCursor = c;
         lines[curLine]->setSelection(ChatLine::Partial, dragStartCursor, c);
       if(c != curCursor) {
         curCursor = c;
         lines[curLine]->setSelection(ChatLine::Partial, dragStartCursor, c);
-        update();
+        viewport()->update();
       }
     } else {
       mouseMode = MarkLines;
       selectionStart = qMin(curLine, dragStartLine); selectionEnd = qMax(curLine, dragStartLine);
       for(int i = selectionStart; i <= selectionEnd; i++) lines[i]->setSelection(ChatLine::Full);
       }
     } else {
       mouseMode = MarkLines;
       selectionStart = qMin(curLine, dragStartLine); selectionEnd = qMax(curLine, dragStartLine);
       for(int i = selectionStart; i <= selectionEnd; i++) lines[i]->setSelection(ChatLine::Full);
-      update();
+      viewport()->update();
     }
   } else if(mouseMode == MarkLines) {
     // Line marking
     int l = yToLineIdx(y);
     if(l != curLine) {
       selectionStart = qMin(l, dragStartLine); selectionEnd = qMax(l, dragStartLine);
     }
   } else if(mouseMode == MarkLines) {
     // Line marking
     int l = yToLineIdx(y);
     if(l != curLine) {
       selectionStart = qMin(l, dragStartLine); selectionEnd = qMax(l, dragStartLine);
-      if(curLine >= 0 && curLine < selectionStart) {
-        for(int i = curLine; i < selectionStart; i++) lines[i]->setSelection(ChatLine::None);
-      } else if(curLine > selectionEnd) {
-        for(int i = selectionEnd+1; i <= curLine; i++) lines[i]->setSelection(ChatLine::None);
-      } else if(selectionStart < curLine && l < curLine) {
+      if(curLine < 0) {
+        Q_ASSERT(selectionStart == selectionEnd);
+        lines[l]->setSelection(ChatLine::Full);
+      } else {
+        if(curLine < selectionStart) {
+          for(int i = curLine; i < selectionStart; i++) lines[i]->setSelection(ChatLine::None);
+        } else if(curLine > selectionEnd) {
+          for(int i = selectionEnd+1; i <= curLine; i++) lines[i]->setSelection(ChatLine::None);
+        } else if(selectionStart < curLine && l < curLine) {
           for(int i = selectionStart; i < curLine; i++) lines[i]->setSelection(ChatLine::Full);
           for(int i = selectionStart; i < curLine; i++) lines[i]->setSelection(ChatLine::Full);
-      } else if(curLine < selectionEnd && l > curLine) {
-        for(int i = curLine+1; i <= selectionEnd; i++) lines[i]->setSelection(ChatLine::Full);
+        } else if(curLine < selectionEnd && l > curLine) {
+          for(int i = curLine+1; i <= selectionEnd; i++) lines[i]->setSelection(ChatLine::Full);
+        }
       }
       curLine = l;
       }
       curLine = l;
-      update();
+      //ensureVisible(l);
+      viewport()->update();
     }
   }
 }
 
 //!\brief Clear current text selection.
     }
   }
 }
 
 //!\brief Clear current text selection.
-void ChatWidgetContents::clearSelection() {
+void ChatWidget::clearSelection() {
   if(selectionMode == TextSelected) {
     lines[selectionLine]->setSelection(ChatLine::None);
   } else if(selectionMode == LinesSelected) {
   if(selectionMode == TextSelected) {
     lines[selectionLine]->setSelection(ChatLine::None);
   } else if(selectionMode == LinesSelected) {
@@ -368,11 +482,11 @@ void ChatWidgetContents::clearSelection() {
     }
   }
   selectionMode = NoSelection;
     }
   }
   selectionMode = NoSelection;
-  update();
+  viewport()->update();
 }
 
 //!\brief Convert current selection to human-readable string.
 }
 
 //!\brief Convert current selection to human-readable string.
-QString ChatWidgetContents::selectionToString() {
+QString ChatWidget::selectionToString() {
   //TODO Make selection format configurable!
   if(selectionMode == NoSelection) return "";
   if(selectionMode == LinesSelected) {
   //TODO Make selection format configurable!
   if(selectionMode == NoSelection) return "";
   if(selectionMode == LinesSelected) {
@@ -391,7 +505,7 @@ QString ChatWidgetContents::selectionToString() {
 
 //!\brief Construct a ChatLine object from a message.
 /**
 
 //!\brief Construct a ChatLine object from a message.
 /**
- * \param m The message to be layouted and rendered
+ * \param m   The message to be layouted and rendered
  * \param net The network name
  * \param buf The buffer name
  */ 
  * \param net The network name
  * \param buf The buffer name
  */ 
@@ -425,13 +539,13 @@ void ChatLine::formatMsg(Message msg) {
     case Message::Error:
       s = tr("%De*"); t = tr("%De%1").arg(text); break;
     case Message::Join:
     case Message::Error:
       s = tr("%De*"); t = tr("%De%1").arg(text); break;
     case Message::Join:
-      s = tr("%Dj-->"); t = tr("%Dj%DN%1%DN %DH(%2@%3)%DH has joined %DC%4%DC").arg(nick, user, host, bufferName); break;
+      s = tr("%Dj-->"); t = tr("%Dj%DN%DU%1%DU%DN %DH(%2@%3)%DH has joined %DC%DU%4%DU%DC").arg(nick, user, host, bufferName); break;
     case Message::Part:
     case Message::Part:
-      s = tr("%Dp<--"); t = tr("%Dp%DN%1%DN %DH(%2@%3)%DH has left %DC%4%DC").arg(nick, user, host, bufferName);
+      s = tr("%Dp<--"); t = tr("%Dp%DN%DU%1%DU%DN %DH(%2@%3)%DH has left %DC%DU%4%DU%DC").arg(nick, user, host, bufferName);
       if(!text.isEmpty()) t = QString("%1 (%2)").arg(t).arg(text);
       break;
     case Message::Quit:
       if(!text.isEmpty()) t = QString("%1 (%2)").arg(t).arg(text);
       break;
     case Message::Quit:
-      s = tr("%Dq<--"); t = tr("%Dq%DN%1%DN %DH(%2@%3)%DH has quit").arg(nick, user, host);
+      s = tr("%Dq<--"); t = tr("%Dq%DN%DU%1%DU%DN %DH(%2@%3)%DH has quit").arg(nick, user, host);
       if(!text.isEmpty()) t = QString("%1 (%2)").arg(t).arg(text);
       break;
     case Message::Kick:
       if(!text.isEmpty()) t = QString("%1 (%2)").arg(t).arg(text);
       break;
     case Message::Kick:
@@ -439,19 +553,19 @@ void ChatLine::formatMsg(Message msg) {
         QString victim = text.section(" ", 0, 0);
         //if(victim == ui.ownNick->currentText()) victim = tr("you");
         QString kickmsg = text.section(" ", 1);
         QString victim = text.section(" ", 0, 0);
         //if(victim == ui.ownNick->currentText()) victim = tr("you");
         QString kickmsg = text.section(" ", 1);
-        t = tr("%Dk%DN%1%DN has kicked %DN%2%DN from %DC%3%DC").arg(nick).arg(victim).arg(bufferName);
+        t = tr("%Dk%DN%DU%1%DU%DN has kicked %DN%DU%2%DU%DN from %DC%DU%3%DU%DC").arg(nick).arg(victim).arg(bufferName);
         if(!kickmsg.isEmpty()) t = QString("%1 (%2)").arg(t).arg(kickmsg);
       }
       break;
     case Message::Nick:
       s = tr("%Dr<->");
         if(!kickmsg.isEmpty()) t = QString("%1 (%2)").arg(t).arg(kickmsg);
       }
       break;
     case Message::Nick:
       s = tr("%Dr<->");
-      if(nick == msg.text) t = tr("You are now known as %DN%1%DN").arg(msg.text);
-      else t = tr("%DN%1%DN is now known as %DN%2%DN").arg(nick, msg.text);
+      if(nick == msg.text) t = tr("%DrYou are now known as %DN%1%DN").arg(msg.text);
+      else t = tr("%Dr%DN%1%DN is now known as %DN%DU%2%DU%DN").arg(nick, msg.text);
       break;
     case Message::Mode:
       s = tr("%Dm***");
       break;
     case Message::Mode:
       s = tr("%Dm***");
-      if(nick.isEmpty()) t = tr("User mode: %DM%1%DM").arg(msg.text);
-      else t = tr("Mode %DM%1%DM by %DN%2%DN").arg(msg.text, nick);
+      if(nick.isEmpty()) t = tr("%DmUser mode: %DM%1%DM").arg(msg.text);
+      else t = tr("%DmMode %DM%1%DM by %DN%DU%2%DU%DN").arg(msg.text, nick);
       break;
     default:
       s = tr("%De%1").arg(msg.sender);
       break;
     default:
       s = tr("%De%1").arg(msg.sender);
@@ -461,42 +575,52 @@ void ChatLine::formatMsg(Message msg) {
   tsFormatted = Style::internalToFormatted(c);
   senderFormatted = Style::internalToFormatted(s);
   textFormatted = Style::internalToFormatted(t);
   tsFormatted = Style::internalToFormatted(c);
   senderFormatted = Style::internalToFormatted(s);
   textFormatted = Style::internalToFormatted(t);
-  tsLayout.setText(tsFormatted.text); tsLayout.setAdditionalFormats(tsFormatted.formats);
-  tsOption.setWrapMode(QTextOption::NoWrap);
-  tsLayout.setTextOption(tsOption);
-  senderLayout.setText(senderFormatted.text); senderLayout.setAdditionalFormats(senderFormatted.formats);
-  senderOption.setAlignment(Qt::AlignRight); senderOption.setWrapMode(QTextOption::ManualWrap);
-  senderLayout.setTextOption(senderOption);
-  textLayout.setText(textFormatted.text); textLayout.setAdditionalFormats(textFormatted.formats);
-  textOption.setWrapMode(QTextOption::WrapAnywhere); // seems to do what we want, apparently
-  textLayout.setTextOption(textOption);
-}
-
-//!\brief Return the cursor position for the given coordinate pos.
-/**
- * \param pos The position relative to the ChatLine
- * \return The cursor position, or -2 for timestamp, or -1 for sender
- */
-int ChatLine::posToCursor(QPointF pos) {
-  if(pos.x() < tsWidth + (int)Style::sepTsSender()/2) return -2;
-  qreal textStart = tsWidth + Style::sepTsSender() + senderWidth + Style::sepSenderText();
-  if(pos.x() < textStart) return -1;
-  for(int l = textLayout.lineCount() - 1; l >=0; l--) {
-    QTextLine line = textLayout.lineAt(l);
-    if(pos.y() >= line.position().y()) {
-      int p = line.xToCursor(pos.x() - textStart, QTextLine::CursorOnCharacter);
-      return p;
+  precomputeLine();
+}
+
+QList<ChatLine::FormatRange> ChatLine::calcFormatRanges(const Style::FormattedString &fs, QTextLayout::FormatRange additional) {
+  QList<FormatRange> ranges;
+  QList<QTextLayout::FormatRange> formats = fs.formats;
+  formats.append(additional);
+  int cur = -1;
+  FormatRange range, lastrange;
+  for(int i = 0; i < fs.text.length(); i++) {
+    QTextCharFormat format;
+    foreach(QTextLayout::FormatRange f, formats) {
+      if(i >= f.start && i < f.start + f.length) format.merge(f.format);
     }
     }
+    if(cur < 0) {
+      range.start = 0; range.length = 1; range.format= format;
+      cur = 0;
+    } else {
+      if(format == range.format) range.length++;
+      else {
+        QFontMetrics metrics(range.format.font());
+        range.height = metrics.lineSpacing();
+        ranges.append(range);
+        range.start = i; range.length = 1; range.format = format;
+        cur++;
+      }
+    }
+  }
+  if(cur >= 0) {
+    QFontMetrics metrics(range.format.font());
+    range.height = metrics.lineSpacing();
+    ranges.append(range);
   }
   }
+  return ranges;
 }
 
 void ChatLine::setSelection(SelectionMode mode, int start, int end) {
   selectionMode = mode;
 }
 
 void ChatLine::setSelection(SelectionMode mode, int start, int end) {
   selectionMode = mode;
-  tsFormat.clear(); senderFormat.clear(); textFormat.clear();
+  //tsFormat.clear(); senderFormat.clear(); textFormat.clear();
   QPalette pal = QApplication::palette();
   QTextLayout::FormatRange tsSel, senderSel, textSel;
   switch (mode) {
     case None:
   QPalette pal = QApplication::palette();
   QTextLayout::FormatRange tsSel, senderSel, textSel;
   switch (mode) {
     case None:
+      tsFormat = calcFormatRanges(tsFormatted);
+      senderFormat = calcFormatRanges(senderFormatted);
+      textFormat = calcFormatRanges(textFormatted);
       break;
     case Partial:
       selectionStart = qMin(start, end); selectionEnd = qMax(start, end);
       break;
     case Partial:
       selectionStart = qMin(start, end); selectionEnd = qMax(start, end);
@@ -504,15 +628,23 @@ void ChatLine::setSelection(SelectionMode mode, int start, int end) {
       textSel.format.setBackground(pal.brush(QPalette::Highlight));
       textSel.start = selectionStart;
       textSel.length = selectionEnd - selectionStart;
       textSel.format.setBackground(pal.brush(QPalette::Highlight));
       textSel.start = selectionStart;
       textSel.length = selectionEnd - selectionStart;
-      textFormat.append(textSel);
+      //textFormat.append(textSel);
+      textFormat = calcFormatRanges(textFormatted, textSel);
+      foreach(FormatRange fr, textFormat);
       break;
     case Full:
       tsSel.format.setForeground(pal.brush(QPalette::HighlightedText));
       break;
     case Full:
       tsSel.format.setForeground(pal.brush(QPalette::HighlightedText));
-      tsSel.start = 0; tsSel.length = tsLayout.text().length(); tsFormat.append(tsSel);
+      tsSel.format.setBackground(pal.brush(QPalette::Highlight));
+      tsSel.start = 0; tsSel.length = tsFormatted.text.length();
+      tsFormat = calcFormatRanges(tsFormatted, tsSel);
       senderSel.format.setForeground(pal.brush(QPalette::HighlightedText));
       senderSel.format.setForeground(pal.brush(QPalette::HighlightedText));
-      senderSel.start = 0; senderSel.length = senderLayout.text().length(); senderFormat.append(senderSel);
+      senderSel.format.setBackground(pal.brush(QPalette::Highlight));
+      senderSel.start = 0; senderSel.length = senderFormatted.text.length();
+      senderFormat = calcFormatRanges(senderFormatted, senderSel);
       textSel.format.setForeground(pal.brush(QPalette::HighlightedText));
       textSel.format.setForeground(pal.brush(QPalette::HighlightedText));
-      textSel.start = 0; textSel.length = textLayout.text().length(); textFormat.append(textSel);
+      textSel.format.setBackground(pal.brush(QPalette::Highlight));
+      textSel.start = 0; textSel.length = textFormatted.text.length();
+      textFormat = calcFormatRanges(textFormatted, textSel);
       break;
   }
 }
       break;
   }
 }
@@ -522,56 +654,273 @@ QDateTime ChatLine::getTimeStamp() {
 }
 
 QString ChatLine::getSender() {
 }
 
 QString ChatLine::getSender() {
-  return senderLayout.text();
+  return senderFormatted.text;
 }
 
 QString ChatLine::getText() {
 }
 
 QString ChatLine::getText() {
-  return textLayout.text();
+  return textFormatted.text;
+}
+
+bool ChatLine::isUrl(int c) {
+  if(c < 0 || c >= charUrlIdx.count()) return false;;
+  return charUrlIdx[c] >= 0;
+}
+
+QUrl ChatLine::getUrl(int c) {
+  if(c < 0 || c >= charUrlIdx.count()) return QUrl();
+  int i = charUrlIdx[c];
+  if(i >= 0) return textFormatted.urls[i].url;
+  else return QUrl();
+}
+
+//!\brief Return the cursor position for the given coordinate pos.
+/**
+ * \param pos The position relative to the ChatLine
+ * \return The cursor position, [or -3 for invalid,] or -2 for timestamp, or -1 for sender
+ */
+int ChatLine::posToCursor(QPointF pos) {
+  if(pos.x() < tsWidth + (int)Style::sepTsSender()/2) return -2;
+  qreal textStart = tsWidth + Style::sepTsSender() + senderWidth + Style::sepSenderText();
+  if(pos.x() < textStart) return -1;
+  int x = (int)(pos.x() - textStart);
+  for(int l = lineLayouts.count() - 1; l >=0; l--) {
+    LineLayout line = lineLayouts[l];
+    if(pos.y() >= line.y) {
+      int offset = charPos[line.start]; x += offset;
+      for(int i = line.start + line.length - 1; i >= line.start; i--) {
+        if((charPos[i] + charPos[i+1])/2 <= x) return i+1; // FIXME: Optimize this!
+      }
+      return line.start;
+    }
+  }
+}
+
+void ChatLine::precomputeLine() {
+  tsFormat = calcFormatRanges(tsFormatted);
+  senderFormat = calcFormatRanges(senderFormatted);
+  textFormat = calcFormatRanges(textFormatted);
+
+  minHeight = 0;
+  foreach(FormatRange fr, tsFormat) minHeight = qMax(minHeight, fr.height);
+  foreach(FormatRange fr, senderFormat) minHeight = qMax(minHeight, fr.height);
+
+  words.clear();
+  charPos.resize(textFormatted.text.length() + 1);
+  charHeights.resize(textFormatted.text.length());
+  charUrlIdx.fill(-1, textFormatted.text.length());
+  for(int i = 0; i < textFormatted.urls.count(); i++) {
+    Style::UrlInfo url = textFormatted.urls[i];
+    for(int j = url.start; j < url.end; j++) charUrlIdx[j] = i;
+  }
+  if(!textFormat.count()) return;
+  int idx = 0; int cnt = 0; int w = 0; int h = 0;
+  QFontMetrics metrics(textFormat[0].format.font());
+  Word wr;
+  wr.start = -1; wr.trailing = -1;
+  for(int i = 0; i < textFormatted.text.length(); ) {
+    charPos[i] = w; charHeights[i] = textFormat[idx].height;
+    w += metrics.charWidth(textFormatted.text, i);
+    if(!textFormatted.text[i].isSpace()) {
+      if(wr.trailing >= 0) {
+        // new word after space
+        words.append(wr);
+        wr.start = -1;
+      }
+      if(wr.start < 0) {
+        wr.start = i; wr.length = 1; wr.trailing = -1; wr.height = textFormat[idx].height;
+      } else {
+        wr.length++; wr.height = qMax(wr.height, textFormat[idx].height);
+      }
+    } else {
+      if(wr.start < 0) {
+        wr.start = i; wr.length = 0; wr.trailing = 1; wr.height = 0;
+      } else {
+        wr.trailing++;
+      }
+    }
+    if(++i < textFormatted.text.length() && ++cnt >= textFormat[idx].length) {
+      cnt = 0; idx++;
+      Q_ASSERT(idx < textFormat.count());
+      metrics = QFontMetrics(textFormat[idx].format.font());
+    }
+  }
+  charPos[textFormatted.text.length()] = w;
+  if(wr.start >= 0) words.append(wr);
 }
 
 qreal ChatLine::layout(qreal tsw, qreal senderw, qreal textw) {
   tsWidth = tsw; senderWidth = senderw; textWidth = textw;
 }
 
 qreal ChatLine::layout(qreal tsw, qreal senderw, qreal textw) {
   tsWidth = tsw; senderWidth = senderw; textWidth = textw;
-  QTextLine tl;
-  tsLayout.beginLayout();
-  tl = tsLayout.createLine();
-  tl.setLineWidth(tsWidth);
-  tl.setPosition(QPointF(0, 0));
-  tsLayout.endLayout();
-
-  senderLayout.beginLayout();
-  tl = senderLayout.createLine();
-  tl.setLineWidth(senderWidth);
-  tl.setPosition(QPointF(0, 0));
-  senderLayout.endLayout();
-
-  qreal h = 0;
-  textLayout.beginLayout();
-  while(1) {
-    tl = textLayout.createLine();
-    if(!tl.isValid()) break;
-    tl.setLineWidth(textWidth);
-    tl.setPosition(QPointF(0, h));
-    h += tl.height();
+  if(textw <= 0) return minHeight;
+  lineLayouts.clear(); LineLayout line;
+  int h = 0;
+  int offset = 0; int numWords = 0;
+  line.y = 0;
+  line.start = 0;
+  line.height = minHeight;  // first line needs room for ts and sender
+  for(int i = 0; i < words.count(); i++) {
+    int lastpos = charPos[words[i].start + words[i].length]; // We use charPos[lastchar + 1], 'coz last char needs to fit
+    if(lastpos - offset <= textw) {
+      line.height = qMax(line.height, words[i].height);
+      line.length = words[i].start + words[i].length - line.start;
+      numWords++;
+    } else {
+      // we need to wrap!
+      if(numWords > 0) {
+        // ok, we had some words before, so store the layout and start a new line
+        h += line.height;
+        line.length = words[i-1].start + words[i-1].length - line.start;
+        lineLayouts.append(line);
+        line.y += line.height;
+        line.start = words[i].start;
+        line.height = words[i].height;
+        offset = charPos[words[i].start];
+      }
+      numWords = 1;
+      // check if the word fits into the current line
+      if(lastpos - offset <= textw) {
+        line.length = words[i].length;
+      } else {
+        // we need to break a word in the middle
+        int border = (int)textw + offset; // save some additions
+        line.start = words[i].start;
+        line.length = 1;
+        line.height = charHeights[line.start];
+        int j = line.start + 1;
+        for(int l = 1; l < words[i].length; j++, l++) {
+          if(charPos[j+1] < border) {
+            line.length++;
+            line.height = qMax(line.height, charHeights[j]);
+            continue;
+          } else {
+            h += line.height;
+            lineLayouts.append(line);
+            line.y += line.height;
+            line.start = j;
+            line.height = charHeights[j];
+            line.length = 1;
+            offset = charPos[j];
+            border = (int)textw + offset;
+          }
+        }
+      }
+    }
+  }
+  h += line.height;
+  if(numWords > 0) {
+    lineLayouts.append(line);
   }
   }
-  textLayout.endLayout();
   hght = h;
   hght = h;
-  return h;
+  return hght;
 }
 
 //!\brief Draw ChatLine on the given QPainter at the given position.
 void ChatLine::draw(QPainter *p, const QPointF &pos) {
   QPalette pal = QApplication::palette();
 }
 
 //!\brief Draw ChatLine on the given QPainter at the given position.
 void ChatLine::draw(QPainter *p, const QPointF &pos) {
   QPalette pal = QApplication::palette();
+
   if(selectionMode == Full) {
     p->setPen(Qt::NoPen);
     p->setBrush(pal.brush(QPalette::Highlight));
     p->drawRect(QRectF(pos, QSizeF(tsWidth + Style::sepTsSender() + senderWidth + Style::sepSenderText() + textWidth, height())));
   } else if(selectionMode == Partial) {
 
   if(selectionMode == Full) {
     p->setPen(Qt::NoPen);
     p->setBrush(pal.brush(QPalette::Highlight));
     p->drawRect(QRectF(pos, QSizeF(tsWidth + Style::sepTsSender() + senderWidth + Style::sepSenderText() + textWidth, height())));
   } else if(selectionMode == Partial) {
 
-  }
+  } /*
   p->setClipRect(QRectF(pos, QSizeF(tsWidth, height())));
   tsLayout.draw(p, pos, tsFormat);
   p->setClipRect(QRectF(pos + QPointF(tsWidth + Style::sepTsSender(), 0), QSizeF(senderWidth, height())));
   senderLayout.draw(p, pos + QPointF(tsWidth + Style::sepTsSender(), 0), senderFormat);
   p->setClipping(false);
   textLayout.draw(p, pos + QPointF(tsWidth + Style::sepTsSender() + senderWidth + Style::sepSenderText(), 0), textFormat);
   p->setClipRect(QRectF(pos, QSizeF(tsWidth, height())));
   tsLayout.draw(p, pos, tsFormat);
   p->setClipRect(QRectF(pos + QPointF(tsWidth + Style::sepTsSender(), 0), QSizeF(senderWidth, height())));
   senderLayout.draw(p, pos + QPointF(tsWidth + Style::sepTsSender(), 0), senderFormat);
   p->setClipping(false);
   textLayout.draw(p, pos + QPointF(tsWidth + Style::sepTsSender() + senderWidth + Style::sepSenderText(), 0), textFormat);
+  */
+  //p->setClipRect(QRectF(pos, QSizeF(tsWidth, 15)));
+  //p->drawRect(QRectF(pos, QSizeF(tsWidth, minHeight)));
+  p->setBackgroundMode(Qt::OpaqueMode);
+  QPointF tp = pos;
+  QRectF rect(pos, QSizeF(tsWidth, minHeight));
+  QRectF brect;
+  foreach(FormatRange fr, tsFormat) {
+    p->setFont(fr.format.font());
+    p->setPen(QPen(fr.format.foreground(), 0)); p->setBackground(fr.format.background());
+    p->drawText(rect, Qt::AlignLeft|Qt::TextSingleLine, tsFormatted.text.mid(fr.start, fr.length), &brect);
+    rect.setLeft(brect.right());
+  }
+  rect = QRectF(pos + QPointF(tsWidth + Style::sepTsSender(), 0), QSizeF(senderWidth, minHeight));
+  for(int i = senderFormat.count() - 1; i >= 0; i--) {
+    FormatRange fr = senderFormat[i];
+    p->setFont(fr.format.font()); p->setPen(QPen(fr.format.foreground(), 0)); p->setBackground(fr.format.background());
+    p->drawText(rect, Qt::AlignRight|Qt::TextSingleLine, senderFormatted.text.mid(fr.start, fr.length), &brect);
+    rect.setRight(brect.left());
+  }
+  QPointF tpos = pos + QPointF(tsWidth + Style::sepTsSender() + senderWidth + Style::sepSenderText(), 0);
+  qreal h = 0; int l = 0;
+  rect = QRectF(tpos + QPointF(0, h), QSizeF(textWidth, lineLayouts[l].height));
+  int offset = 0;
+  foreach(FormatRange fr, textFormat) {
+    if(l >= lineLayouts.count()) break;
+    p->setFont(fr.format.font()); p->setPen(QPen(fr.format.foreground(), 0)); p->setBackground(fr.format.background());
+    int start, end, frend, llend;
+    do {
+      frend = fr.start + fr.length;
+      if(frend <= lineLayouts[l].start) break;
+      llend = lineLayouts[l].start + lineLayouts[l].length;
+      start = qMax(fr.start, lineLayouts[l].start); end = qMin(frend, llend);
+      rect.setLeft(tpos.x() + charPos[start] - offset);
+      p->drawText(rect, Qt::AlignLeft|Qt::TextSingleLine, textFormatted.text.mid(start, end - start), &brect);
+      if(llend <= end) {
+        h += lineLayouts[l].height;
+        l++;
+        if(l < lineLayouts.count()) {
+          rect = QRectF(tpos + QPointF(0, h), QSizeF(textWidth, lineLayouts[l].height));
+          offset = charPos[lineLayouts[l].start];
+        }
+      }
+    } while(end < frend && l < lineLayouts.count());
+  }
+}
+
+/******************************************************************************************************************/
+
+LayoutThread::LayoutThread() : QThread() {
+  mutex.lock();
+  abort = false;
+  mutex.unlock();
+
+}
+
+LayoutThread::~LayoutThread() {
+  mutex.lock();
+  abort = true;
+  mutex.unlock();
+  condition.wakeOne();
+  wait();
+}
+
+void LayoutThread::processTask(LayoutTask task) {
+  if(!isRunning()) start();
+  Q_ASSERT(isRunning());
+  mutex.lock();
+  queue.append(task);
+  condition.wakeOne();
+  mutex.unlock();
+}
+
+void LayoutThread::run() {
+  forever {
+    mutex.lock();
+    if(!queue.count()) {
+      condition.wait(&mutex);
+    }
+    if(abort) {
+      mutex.unlock(); return;
+    }
+    Q_ASSERT(queue.count()); //qDebug() << "process";
+    LayoutTask task = queue.takeFirst();
+    mutex.unlock();
+    foreach(Message msg, task.messages) {
+      //qDebug() << msg.text;
+      ChatLine *line = new ChatLine(msg, task.net, task.buf);
+      task.lines.append(line);
+    }
+    emit taskProcessed(task);
+    //msleep(500);
+  }
 }
 }
index 93ee117..1641824 100644 (file)
 
 #include "style.h"
 #include "message.h"
 
 #include "style.h"
 #include "message.h"
+#include "buffer.h"
+#include <QtCore>
 #include <QtGui>
 
 #include <QtGui>
 
-class ChatWidgetContents;
+class ChatLine;
 
 //!\brief Scroll area showing part of the chat messages for a given buffer.
 /** The contents of the scroll area, i.e. a widget of type ChatWidgetContents,
 
 //!\brief Scroll area showing part of the chat messages for a given buffer.
 /** The contents of the scroll area, i.e. a widget of type ChatWidgetContents,
@@ -38,62 +40,39 @@ class ChatWidgetContents;
  * Because we use this as a custom widget in Qt Designer, we cannot use a constructor that takes custom
  * parameters. Instead, it is mandatory to call init() before using this widget.
  */
  * Because we use this as a custom widget in Qt Designer, we cannot use a constructor that takes custom
  * parameters. Instead, it is mandatory to call init() before using this widget.
  */
-class ChatWidget : public QScrollArea {
+class ChatWidget : public QAbstractScrollArea {
   Q_OBJECT
 
   public:
     ChatWidget(QWidget *parent = 0);
     ~ChatWidget();
   Q_OBJECT
 
   public:
     ChatWidget(QWidget *parent = 0);
     ~ChatWidget();
-    void init(QString net, QString buf, ChatWidgetContents *contents);
-
-  public slots:
-    void clear();
-    void appendMsg(Message);
-
-  protected:
-    void resizeEvent(QResizeEvent *event);
-
-  private:
-    QString networkName, bufferName;
-    ChatWidgetContents *contents;
-};
-
-class ChatLine;
-
-//!\brief Renders the complete contents of a Buffer.
-/** Usually, this widget is used within a scroll
- * area. Because Qt's rich-text rendering engine is much too slow for our purposes, we do everything
- * except for the actual glyph painting ourselves. While this makes managing text quite cumbersome
- * (we cannot, for example, use any of Qt's text editing, selecting or layouting features), it is
- * also orders of magnitudes faster than any of the usual methods.
- */
-class ChatWidgetContents : public QWidget {
-  Q_OBJECT
+    void init(QString net, QString buf);
 
 
-  public:
-    ChatWidgetContents(QString net, QString buf, QWidget *parent = 0);
-    ~ChatWidgetContents();
-    //int heightForWidth(int w) const;
     virtual QSize sizeHint() const;
 
   public slots:
     void clear();
     virtual QSize sizeHint() const;
 
   public slots:
     void clear();
+    void prependChatLines(QList<ChatLine *>);
     void appendMsg(Message);
     void appendMsg(Message);
-    void setWidth(qreal);
+    void appendMsgList(QList<Message> *);
 
   protected:
 
   protected:
-    virtual void paintEvent(QPaintEvent *event);
-
-  protected slots:
+    virtual void resizeEvent(QResizeEvent *event);
+    virtual void paintEvent(QPaintEvent * event);
     virtual void mousePressEvent(QMouseEvent *event);
     virtual void mouseReleaseEvent(QMouseEvent *event);
     virtual void mouseMoveEvent(QMouseEvent *event);
     virtual void mousePressEvent(QMouseEvent *event);
     virtual void mouseReleaseEvent(QMouseEvent *event);
     virtual void mouseMoveEvent(QMouseEvent *event);
+    virtual void mouseDoubleClickEvent(QMouseEvent *event);
 
   private slots:
 
   private slots:
-    void layout(bool timer = false);
-    void triggerLayout();
+    void layout();
+    void scrollBarAction(int);
+    void scrollBarValChanged(int);
+    void ensureVisible(int line);
+    void handleScrollTimer();
 
   private:
 
   private:
+    QString networkName, bufferName;
     enum SelectionMode { NoSelection, TextSelected, LinesSelected };
     enum MouseMode { Normal, Pressed, DragTsSep, DragTextSep, MarkText, MarkLines };
     enum MousePos { None, OverTsSep, OverTextSep, OverUrl };
     enum SelectionMode { NoSelection, TextSelected, LinesSelected };
     enum MouseMode { Normal, Pressed, DragTsSep, DragTextSep, MarkText, MarkLines };
     enum MousePos { None, OverTsSep, OverTextSep, OverUrl };
@@ -107,12 +86,13 @@ class ChatWidgetContents : public QWidget {
     int curLine;
     SelectionMode selectionMode;
     int selectionStart, selectionEnd, selectionLine;
     int curLine;
     SelectionMode selectionMode;
     int selectionStart, selectionEnd, selectionLine;
-    QString networkName, bufferName;
-    QTimer *layoutTimer;
-    bool doLayout;
+
+    int bottomLine, bottomLineOffset;
 
     QList<ChatLine *> lines;
     QList<qreal> ycoords;
 
     QList<ChatLine *> lines;
     QList<qreal> ycoords;
+    QTimer *scrollTimer;
+    QPoint pointerPosition;
 
     int senderX;
     int textX;
 
     int senderX;
     int textX;
@@ -123,15 +103,20 @@ class ChatWidgetContents : public QWidget {
     int senderGrabPos;
     void computePositions();
 
     int senderGrabPos;
     void computePositions();
 
-    qreal width;
+    int width;
     qreal height;
     qreal y;
 
     qreal height;
     qreal y;
 
+    void adjustScrollBar();
+
     int yToLineIdx(qreal y);
     void clearSelection();
     QString selectionToString();
     int yToLineIdx(qreal y);
     void clearSelection();
     QString selectionToString();
+    void handleMouseMoveEvent(const QPoint &pos);
+
 };
 
 };
 
+//FIXME: chatline doku
 //!\brief Containing the layout and providing the rendering of a single message.
 /** A ChatLine takes a Message object,
  * formats it (by turning the various message types into a human-readable form and afterwards pumping it through
 //!\brief Containing the layout and providing the rendering of a single message.
 /** A ChatLine takes a Message object,
  * formats it (by turning the various message types into a human-readable form and afterwards pumping it through
@@ -161,22 +146,82 @@ class ChatLine : public QObject {
     QDateTime getTimeStamp();
     QString getSender();
     QString getText();
     QDateTime getTimeStamp();
     QString getSender();
     QString getText();
+
+    bool isUrl(int pos);
+    QUrl getUrl(int pos);
+
+  public slots:
+
   private:
     qreal hght;
     Message msg;
     QString networkName, bufferName;
   private:
     qreal hght;
     Message msg;
     QString networkName, bufferName;
-    //QString ts, nick, text;
     qreal tsWidth, senderWidth, textWidth;
     qreal tsWidth, senderWidth, textWidth;
-    QTextLayout tsLayout;
-    QTextLayout senderLayout;
-    QTextLayout textLayout;
-    QVector<QTextLayout::FormatRange> tsFormat, senderFormat, textFormat;
-    Style::StringFormats tsFormatted, senderFormatted, textFormatted;
+    Style::FormattedString tsFormatted, senderFormatted, textFormatted;
+
+    struct FormatRange {
+      int start;
+      int length;
+      int height;
+      QTextCharFormat format;
+    };
+    struct Word {
+      int start;
+      int length;
+      int trailing;
+      int height;
+    };
+    struct LineLayout {
+      int y;
+      int height;
+      int start;
+      int length;
+    };
+    QVector<int> charPos;
+    QVector<int> charWidths;
+    QVector<int> charHeights;
+    QVector<int> charUrlIdx;
+    QList<FormatRange> tsFormat, senderFormat, textFormat;
+    QList<Word> words;
+    QList<LineLayout> lineLayouts;
+    int minHeight;
 
     SelectionMode selectionMode;
     int selectionStart, selectionEnd;
     void formatMsg(Message);
 
     SelectionMode selectionMode;
     int selectionStart, selectionEnd;
     void formatMsg(Message);
+    void precomputeLine();
+    QList<FormatRange> calcFormatRanges(const Style::FormattedString &, QTextLayout::FormatRange additional = QTextLayout::FormatRange());
 };
 
 };
 
+struct LayoutTask {
+  QList<Message> messages;
+  Buffer *buffer;
+  QString net, buf;
+  QList<ChatLine *> lines;
+};
+
+Q_DECLARE_METATYPE(LayoutTask);
+
+class LayoutThread : public QThread {
+  Q_OBJECT
+
+  public:
+    LayoutThread();
+    virtual ~LayoutThread();
+    virtual void run();
+
+  public:
+    void processTask(LayoutTask task);
+
+  signals:
+    void taskProcessed(LayoutTask task);
+
+  private:
+    QList<LayoutTask> queue;
+    QMutex mutex;
+    QWaitCondition condition;
+    bool abort;
+
+};
 
 #endif
 
 #endif
index 3657623..3c3c0b3 100644 (file)
@@ -43,6 +43,7 @@ void GUIProxy::recv(CoreSignal sig, QVariant arg1, QVariant arg2, QVariant arg3)
     case CS_NICK_RENAMED: emit csNickRenamed(arg1.toString(), arg2.toString(), arg3.toString()); break;
     case CS_NICK_UPDATED: emit csNickUpdated(arg1.toString(), arg2.toString(), arg3.toMap()); break;
     case CS_OWN_NICK_SET: emit csOwnNickSet(arg1.toString(), arg2.toString()); break;
     case CS_NICK_RENAMED: emit csNickRenamed(arg1.toString(), arg2.toString(), arg3.toString()); break;
     case CS_NICK_UPDATED: emit csNickUpdated(arg1.toString(), arg2.toString(), arg3.toMap()); break;
     case CS_OWN_NICK_SET: emit csOwnNickSet(arg1.toString(), arg2.toString()); break;
+    case CS_QUERY_REQUESTED: emit csQueryRequested(arg1.toString(), arg2.toString()); break;
 
     default: qWarning() << "Unknown signal in GUIProxy::recv: " << sig;
   }
 
     default: qWarning() << "Unknown signal in GUIProxy::recv: " << sig;
   }
index c8ac87e..e07599b 100644 (file)
@@ -64,6 +64,7 @@ class GUIProxy : public QObject {
     void csNickRenamed(QString, QString, QString);
     void csNickUpdated(QString, QString, VarMap);
     void csOwnNickSet(QString, QString);
     void csNickRenamed(QString, QString, QString);
     void csNickUpdated(QString, QString, VarMap);
     void csOwnNickSet(QString, QString);
+    void csQueryRequested(QString, QString);
 
     void coreConnected();
     void coreDisconnected();
 
     void coreConnected();
     void coreDisconnected();
index e9fe9e4..5beefdf 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
 /***************************************************************************
- *   Copyright (C) 2005 by The Quassel Team                                *
+ *   Copyright (C) 2005-07 by The Quassel Team                             *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -20,7 +20,9 @@
 
 #include <QtGui>
 #include <QtCore>
 
 #include <QtGui>
 #include <QtCore>
+#include <QSqlDatabase>
 
 
+#include "util.h"
 #include "global.h"
 #include "message.h"
 #include "guiproxy.h"
 #include "global.h"
 #include "message.h"
 #include "guiproxy.h"
 #include "networkview.h"
 #include "serverlist.h"
 #include "coreconnectdlg.h"
 #include "networkview.h"
 #include "serverlist.h"
 #include "coreconnectdlg.h"
-#include "settings.h"
+#include "settingsdlg.h"
+#include "settingspages.h"
+
+LayoutThread *layoutThread;
 
 MainWin::MainWin() : QMainWindow() {
   ui.setupUi(this);
 
 MainWin::MainWin() : QMainWindow() {
   ui.setupUi(this);
-  widget = 0;
-
+  //widget = 0;
+  qDebug() << "Available DB drivers: " << QSqlDatabase::drivers ();
   setWindowTitle("Quassel IRC");
   setWindowIcon(QIcon(":/qirc-icon.png"));
   setWindowIconText("Quassel IRC");
 
   setWindowTitle("Quassel IRC");
   setWindowIcon(QIcon(":/qirc-icon.png"));
   setWindowIconText("Quassel IRC");
 
-  QSettings s;
-  s.beginGroup("Geometry");
-  resize(s.value("MainWinSize", QSize(500, 400)).toSize());
-  move(s.value("MainWinPos", QPoint(50, 50)).toPoint());
-  s.endGroup();
-
   //workspace = new QWorkspace(this);
   //setCentralWidget(workspace);
   statusBar()->showMessage(tr("Waiting for core..."));
   //workspace = new QWorkspace(this);
   //setCentralWidget(workspace);
   statusBar()->showMessage(tr("Waiting for core..."));
+}
 
 
-  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)));
+void MainWin::init() {
 
   connect(guiProxy, SIGNAL(csServerState(QString, QVariant)), this, SLOT(recvNetworkState(QString, QVariant)));
   connect(guiProxy, SIGNAL(csServerConnected(QString)), this, SLOT(networkConnected(QString)));
 
   connect(guiProxy, SIGNAL(csServerState(QString, QVariant)), this, SLOT(recvNetworkState(QString, QVariant)));
   connect(guiProxy, SIGNAL(csServerConnected(QString)), this, SLOT(networkConnected(QString)));
@@ -70,25 +66,64 @@ MainWin::MainWin() : QMainWindow() {
   connect(guiProxy, SIGNAL(csOwnNickSet(QString, QString)), this, SLOT(setOwnNick(QString, QString)));
   connect(this, SIGNAL(sendInput( QString, QString, QString )), guiProxy, SLOT(gsUserInput(QString, QString, QString)));
 
   connect(guiProxy, SIGNAL(csOwnNickSet(QString, QString)), this, SLOT(setOwnNick(QString, QString)));
   connect(this, SIGNAL(sendInput( QString, QString, QString )), guiProxy, SLOT(gsUserInput(QString, QString, QString)));
 
+  layoutThread = new LayoutThread();
+  layoutThread->start();
+  while(!layoutThread->isRunning()) {};
+  ui.bufferWidget->init();
+
   show();
   syncToCore();
   statusBar()->showMessage(tr("Ready."));
   show();
   syncToCore();
   statusBar()->showMessage(tr("Ready."));
-
-  buffersUpdated();
+  systray = new QSystemTrayIcon(this);
+  systray->setIcon(QIcon(":/qirc-icon.png"));
+  systray->show();
 
   serverListDlg = new ServerListDlg(this);
   serverListDlg->setVisible(serverListDlg->showOnStartup());
 
   serverListDlg = new ServerListDlg(this);
   serverListDlg->setVisible(serverListDlg->showOnStartup());
-  settingsDlg = new SettingsDlg(this);
-  settingsDlg->setVisible(false);
+
+  setupSettingsDlg();
+
+  //Buffer::init();
   setupMenus();
   setupMenus();
+  setupViews();
+
+  //bufferWidget = 0;
+
+  QSettings s;
+  s.beginGroup("Geometry");
+  //resize(s.value("MainWinSize", QSize(500, 400)).toSize());
+  //move(s.value("MainWinPos", QPoint(50, 50)).toPoint());
+  if(s.contains("MainWinState")) restoreState(s.value("MainWinState").toByteArray());
+  s.endGroup();
 
   // replay backlog
 
   // replay backlog
+  // FIXME do this right
+  QHash<Buffer *, QList<Message> > hash;
+  Buffer *b;
+
   foreach(QString net, coreBackLog.keys()) {
   foreach(QString net, coreBackLog.keys()) {
+    //if(net != "MoepNet") continue;
     while(coreBackLog[net].count()) {
     while(coreBackLog[net].count()) {
-      recvMessage(net, coreBackLog[net].takeFirst());
+      //recvMessage(net, coreBackLog[net].takeFirst());
+      Message msg = coreBackLog[net].takeLast();
+      if(msg.flags & Message::PrivMsg) {
+      // query
+        if(msg.flags & Message::Self) b = getBuffer(net, msg.target);
+        else b = getBuffer(net, nickFromMask(msg.sender));
+      } else {
+        b = getBuffer(net, msg.target);
+      }
+      hash[b].prepend(msg);
+      if(hash[b].count() >= 5) {
+        ui.bufferWidget->prependMessages(b, hash.take(b));
+      }
     }
   }
     }
   }
-  /*
+  foreach(Buffer *buf, hash.keys()) {
+    ui.bufferWidget->prependMessages(buf, hash.take(buf));
+  }
+
+/*
   foreach(QString key, buffers.keys()) {
     foreach(Buffer *b, buffers[key].values()) {
       QWidget *widget = b->showWidget(this);
   foreach(QString key, buffers.keys()) {
     foreach(Buffer *b, buffers[key].values()) {
       QWidget *widget = b->showWidget(this);
@@ -96,7 +131,8 @@ MainWin::MainWin() : QMainWindow() {
       widget->show();
     }
   }
       widget->show();
     }
   }
-  */
+*/
+
   s.beginGroup("Buffers");
   QString net = s.value("CurrentNetwork", "").toString();
   QString buf = s.value("CurrentBuffer", "").toString();
   s.beginGroup("Buffers");
   QString net = s.value("CurrentNetwork", "").toString();
   QString buf = s.value("CurrentBuffer", "").toString();
@@ -112,11 +148,62 @@ MainWin::MainWin() : QMainWindow() {
   }
 }
 
   }
 }
 
+MainWin::~MainWin() {
+  typedef QHash<QString, Buffer*> BufHash;
+  foreach(BufHash h, buffers.values()) {
+    foreach(Buffer *b, h.values()) {
+      delete b;
+    }
+  }
+}
+
+/* This is implemented in settingspages.cpp */
+/*
+void MainWin::setupSettingsDlg() {
+
+}
+*/
+
 void MainWin::setupMenus() {
   connect(ui.actionNetworkList, SIGNAL(triggered()), this, SLOT(showServerList()));
   connect(ui.actionEditIdentities, SIGNAL(triggered()), serverListDlg, SLOT(editIdentities()));
   connect(ui.actionSettingsDlg, SIGNAL(triggered()), this, SLOT(showSettingsDlg()));
 void MainWin::setupMenus() {
   connect(ui.actionNetworkList, SIGNAL(triggered()), this, SLOT(showServerList()));
   connect(ui.actionEditIdentities, SIGNAL(triggered()), serverListDlg, SLOT(editIdentities()));
   connect(ui.actionSettingsDlg, SIGNAL(triggered()), this, SLOT(showSettingsDlg()));
-  ui.actionSettingsDlg->setEnabled(false);
+  //ui.actionSettingsDlg->setEnabled(false);
+  connect(ui.actionAboutQt, SIGNAL(triggered()), QApplication::instance(), SLOT(aboutQt()));
+}
+
+void MainWin::setupViews() {
+  NetworkView *all = new NetworkView(tr("All Buffers"), NetworkView::AllNets);
+  registerNetView(all);
+  addDockWidget(Qt::LeftDockWidgetArea, all);
+  NetworkView *allchans = new NetworkView(tr("All Channels"), NetworkView::AllNets|NetworkView::NoQueries|NetworkView::NoServers);
+  registerNetView(allchans);
+  addDockWidget(Qt::LeftDockWidgetArea, allchans);
+  NetworkView *allqrys = new NetworkView(tr("All Queries"), NetworkView::AllNets|NetworkView::NoChannels|NetworkView::NoServers);
+  registerNetView(allqrys);
+  addDockWidget(Qt::RightDockWidgetArea, allqrys);
+  NetworkView *allnets = new NetworkView(tr("All Networks"), NetworkView::AllNets|NetworkView::NoChannels|NetworkView::NoQueries);
+  registerNetView(allnets);
+  addDockWidget(Qt::RightDockWidgetArea, allnets);
+
+  ui.menuViews->addSeparator();
+}
+
+void MainWin::registerNetView(NetworkView *view) {
+  connect(this, SIGNAL(bufferSelected(Buffer *)), view, SLOT(selectBuffer(Buffer *)));
+  connect(this, SIGNAL(bufferUpdated(Buffer *)), view, SLOT(bufferUpdated(Buffer *)));
+  connect(this, SIGNAL(bufferDestroyed(Buffer *)), view, SLOT(bufferDestroyed(Buffer *)));
+  connect(view, SIGNAL(bufferSelected(Buffer *)), this, SLOT(showBuffer(Buffer *)));
+  QList<Buffer *> bufs;
+  typedef QHash<QString, Buffer *> bufhash;
+  QList<bufhash> foo = buffers.values();
+  foreach(bufhash h, foo) {
+    bufs += h.values();
+  }
+  view->setBuffers(bufs);
+  view->setAllowedAreas(Qt::RightDockWidgetArea|Qt::LeftDockWidgetArea);
+  netViews.append(view);
+  ui.menuViews->addAction(view->toggleViewAction());
 }
 
 void MainWin::showServerList() {
 }
 
 void MainWin::showServerList() {
@@ -133,15 +220,18 @@ void MainWin::showSettingsDlg() {
 void MainWin::closeEvent(QCloseEvent *event)
 {
   //if (userReallyWantsToQuit()) {
 void MainWin::closeEvent(QCloseEvent *event)
 {
   //if (userReallyWantsToQuit()) {
+    ui.bufferWidget->saveState();
     QSettings s;
     s.beginGroup("Geometry");
     s.setValue("MainWinSize", size());
     s.setValue("MainWinPos", pos());
     QSettings s;
     s.beginGroup("Geometry");
     s.setValue("MainWinSize", size());
     s.setValue("MainWinPos", pos());
+    s.setValue("MainWinState", saveState());
     s.endGroup();
     s.beginGroup("Buffers");
     s.setValue("CurrentNetwork", currentNetwork);
     s.setValue("CurrentBuffer", currentBuffer);
     s.endGroup();
     s.endGroup();
     s.beginGroup("Buffers");
     s.setValue("CurrentNetwork", currentNetwork);
     s.setValue("CurrentBuffer", currentBuffer);
     s.endGroup();
+    delete systray;
     event->accept();
   //} else {
     //event->ignore();
     event->accept();
   //} else {
     //event->ignore();
@@ -149,23 +239,15 @@ void MainWin::closeEvent(QCloseEvent *event)
 }
 
 void MainWin::showBuffer(QString net, QString buf) {
 }
 
 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->show();
-  setCentralWidget(widget);
-  widget->show();
-  //workspace->setActiveWindow(widget);
-  widget->setFocus();
-  //workspace->setFocus();
-  widget->activateWindow();
-  widget->setFocus(Qt::MouseFocusReason);
-  focusNextChild();
-  //workspace->tile();
-  emit bufferSelected(net, buf);
+  showBuffer(getBuffer(net, buf));
+}
+
+void MainWin::showBuffer(Buffer *b) {
+  currentBuffer = b->bufferName(); currentNetwork = b->networkName();
+  //emit bufferSelected(b);
+  //qApp->processEvents();
+  ui.bufferWidget->setBuffer(b);
+  emit bufferSelected(b);
 }
 
 void MainWin::networkConnected(QString net) {
 }
 
 void MainWin::networkConnected(QString net) {
@@ -173,7 +255,7 @@ void MainWin::networkConnected(QString net) {
   Buffer *b = getBuffer(net, "");
   b->setActive(true);
   b->displayMsg(Message::server("", tr("Connected.")));
   Buffer *b = getBuffer(net, "");
   b->setActive(true);
   b->displayMsg(Message::server("", tr("Connected.")));
-  buffersUpdated();
+  // TODO buffersUpdated();
 }
 
 void MainWin::networkDisconnected(QString net) {
 }
 
 void MainWin::networkDisconnected(QString net) {
@@ -192,16 +274,14 @@ Buffer * MainWin::getBuffer(QString net, QString buf) {
     Buffer *b = new Buffer(net, buf);
     b->setOwnNick(ownNick[net]);
     connect(b, SIGNAL(userInput(QString, QString, QString)), this, SLOT(userInput(QString, QString, QString)));
     Buffer *b = new Buffer(net, buf);
     b->setOwnNick(ownNick[net]);
     connect(b, SIGNAL(userInput(QString, QString, QString)), this, SLOT(userInput(QString, QString, QString)));
+    connect(b, SIGNAL(bufferUpdated(Buffer *)), this, SIGNAL(bufferUpdated(Buffer *)));
+    connect(b, SIGNAL(bufferDestroyed(Buffer *)), this, SIGNAL(bufferDestroyed(Buffer *)));
     buffers[net][buf] = b;
     buffers[net][buf] = b;
-    buffersUpdated();
+    emit bufferUpdated(b);
   }
   return buffers[net][buf];
 }
 
   }
   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());
 void MainWin::recvNetworkState(QString net, QVariant state) {
   connected[net] = true;
   setOwnNick(net, state.toMap()["OwnNick"].toString());
@@ -215,11 +295,17 @@ void MainWin::recvNetworkState(QString net, QVariant state) {
   foreach(QString nick, n.keys()) {
     addNick(net, nick, n[nick].toMap());
   }
   foreach(QString nick, n.keys()) {
     addNick(net, nick, n[nick].toMap());
   }
-  buffersUpdated();
 }
 
 void MainWin::recvMessage(QString net, Message msg) {
 }
 
 void MainWin::recvMessage(QString net, Message msg) {
-  Buffer *b = getBuffer(net, msg.target);
+  Buffer *b;
+  if(msg.flags & Message::PrivMsg) {
+  // query
+    if(msg.flags & Message::Self) b = getBuffer(net, msg.target);
+    else b = getBuffer(net, nickFromMask(msg.sender));
+  } else {
+    b = getBuffer(net, msg.target);
+  }
   b->displayMsg(msg);
 }
 
   b->displayMsg(msg);
 }
 
@@ -236,7 +322,6 @@ void MainWin::setTopic(QString net, QString buf, QString topic) {
   if(!connected[net]) return;
   Buffer *b = getBuffer(net, buf);
   b->setTopic(topic);
   if(!connected[net]) return;
   Buffer *b = getBuffer(net, buf);
   b->setTopic(topic);
-  buffersUpdated();
   //if(!b->isActive()) {
   //  b->setActive(true);
   //  buffersUpdated();
   //if(!b->isActive()) {
   //  b->setActive(true);
   //  buffersUpdated();
@@ -255,7 +340,6 @@ void MainWin::addNick(QString net, QString nick, VarMap props) {
   foreach(QString bufname, c) {
     getBuffer(net, bufname)->addNick(nick, props);
   }
   foreach(QString bufname, c) {
     getBuffer(net, bufname)->addNick(nick, props);
   }
-  buffersUpdated();
 }
 
 void MainWin::renameNick(QString net, QString oldnick, QString newnick) {
 }
 
 void MainWin::renameNick(QString net, QString oldnick, QString newnick) {
@@ -279,7 +363,6 @@ void MainWin::updateNick(QString net, QString nick, VarMap props) {
     if(!newchans.contains(c)) getBuffer(net, c)->removeNick(nick);
   }
   nicks[net][nick] = props;
     if(!newchans.contains(c)) getBuffer(net, c)->removeNick(nick);
   }
   nicks[net][nick] = props;
-  buffersUpdated();
 }
 
 void MainWin::removeNick(QString net, QString nick) {
 }
 
 void MainWin::removeNick(QString net, QString nick) {
@@ -289,7 +372,6 @@ void MainWin::removeNick(QString net, QString nick) {
     getBuffer(net, bufname)->removeNick(nick);
   }
   nicks[net].remove(nick);
     getBuffer(net, bufname)->removeNick(nick);
   }
   nicks[net].remove(nick);
-  buffersUpdated();
 }
 
 void MainWin::setOwnNick(QString net, QString nick) {
 }
 
 void MainWin::setOwnNick(QString net, QString nick) {
index 2d2516a..ff3596e 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
 /***************************************************************************
- *   Copyright (C) 2005 by The Quassel Team                                *
+ *   Copyright (C) 2005-07 by The Quassel Team                             *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
 
 #include "global.h"
 #include "message.h"
 
 #include "global.h"
 #include "message.h"
+#include "chatwidget.h"
 
 class ServerListDlg;
 class CoreConnectDlg;
 class NetworkView;
 class Buffer;
 
 class ServerListDlg;
 class CoreConnectDlg;
 class NetworkView;
 class Buffer;
+class BufferWidget;
 class SettingsDlg;
 
 class SettingsDlg;
 
+extern LayoutThread *layoutThread;
+
 //!\brief The main window and central object of Quassel GUI.
 /** In addition to displaying the main window including standard stuff like a menubar,
  * dockwidgets and of course the chat window, this class also stores all data it
 //!\brief The main window and central object of Quassel GUI.
 /** In addition to displaying the main window including standard stuff like a menubar,
  * dockwidgets and of course the chat window, this class also stores all data it
@@ -43,13 +47,20 @@ class MainWin : public QMainWindow {
 
   public:
     MainWin();
 
   public:
     MainWin();
+    ~MainWin();
+
+    void init();
+    void registerNetView(NetworkView *);
 
   protected:
     void closeEvent(QCloseEvent *event);
 
   signals:
     void sendInput(QString network, QString buffer, QString message);
 
   protected:
     void closeEvent(QCloseEvent *event);
 
   signals:
     void sendInput(QString network, QString buffer, QString message);
-    void bufferSelected(QString net, QString buffer);
+    void bufferSelected(Buffer *);
+    void bufferUpdated(Buffer *);
+    void bufferDestroyed(Buffer *);
+    void backlogReceived(Buffer *, QList<Message>);
 
   private slots:
     void userInput(QString, QString, QString);
 
   private slots:
     void userInput(QString, QString, QString);
@@ -70,17 +81,22 @@ class MainWin : public QMainWindow {
     void showSettingsDlg();
 
     void showBuffer(QString net, QString buf);
     void showSettingsDlg();
 
     void showBuffer(QString net, QString buf);
+    void showBuffer(Buffer *);
 
   private:
     Ui::MainWin ui;
 
     void setupMenus();
 
   private:
     Ui::MainWin ui;
 
     void setupMenus();
+    void setupViews();
+    void setupSettingsDlg();
     void syncToCore();  // implemented in main_mono.cpp or main_gui.cpp
     Buffer * getBuffer(QString net, QString buf);
     void syncToCore();  // implemented in main_mono.cpp or main_gui.cpp
     Buffer * getBuffer(QString net, QString buf);
-    void buffersUpdated();
+    //void buffersUpdated();
 
 
-    QWorkspace *workspace;
-    QWidget *widget;
+    QSystemTrayIcon *systray;
+    //QWorkspace *workspace;
+    //QWidget *widget;
+    //BufferWidget *bufferWidget;
 
     ServerListDlg *serverListDlg;
     CoreConnectDlg *coreConnectDlg;
 
     ServerListDlg *serverListDlg;
     CoreConnectDlg *coreConnectDlg;
@@ -93,7 +109,7 @@ class MainWin : public QMainWindow {
     QHash<QString, QString> ownNick;
     QHash<QString, QList<Message> > coreBackLog;
 
     QHash<QString, QString> ownNick;
     QHash<QString, QList<Message> > coreBackLog;
 
-    NetworkView *netView;
+    QList<NetworkView *> netViews;
 };
 
 #endif
 };
 
 #endif
index be08b3b..c97e288 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
 /***************************************************************************
- *   Copyright (C) 2005/06 by The Quassel Team                             *
+ *   Copyright (C) 2005-07 by The Quassel Team                             *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
 NetworkViewWidget::NetworkViewWidget(QWidget *parent) : QWidget(parent) {
   ui.setupUi(this);
 
 NetworkViewWidget::NetworkViewWidget(QWidget *parent) : QWidget(parent) {
   ui.setupUi(this);
 
+  //setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
 }
 
 }
 
+
 QSize NetworkViewWidget::sizeHint() const {
   return QSize(150,100);
 
 QSize NetworkViewWidget::sizeHint() const {
   return QSize(150,100);
 
@@ -33,59 +35,107 @@ QSize NetworkViewWidget::sizeHint() const {
 
 /**************************************************************************/
 
 
 /**************************************************************************/
 
-NetworkView::NetworkView(QString net, QWidget *parent) : network(net), QDockWidget(parent) {
-  if(!net.isEmpty()) setWindowTitle(net);
-  else setWindowTitle(tr("All Buffers"));
+NetworkView::NetworkView(QString n, int m, QStringList nets, QWidget *parent) : QDockWidget(parent) {
+  setObjectName(QString("View-"+n)); // should be unique for mainwindow state!
+  name = n; mode = m;
+  setWindowTitle(name);
+  networks = nets;
+  currentBuffer = 0;
   setWidget(new NetworkViewWidget(this));
   tree = qobject_cast<NetworkViewWidget*>(widget())->tree();
   setWidget(new NetworkViewWidget(this));
   tree = qobject_cast<NetworkViewWidget*>(widget())->tree();
-  tree->setSortingEnabled(false);
+  tree->header()->hide();
+  tree->setSortingEnabled(true);
   connect(tree, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(itemClicked(QTreeWidgetItem*)));
 }
 
   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"));
-        }
+void NetworkView::setBuffers(QList<Buffer *> buffers) {
+  tree->clear(); bufitems.clear(); netitems.clear();
+  foreach(Buffer *b, buffers) {
+    bufferUpdated(b);
+  }
+}
+
+void NetworkView::bufferUpdated(Buffer *b) {
+  if(bufitems.contains(b)) {
+    // FIXME this looks ugly
+    QTreeWidgetItem *item = bufitems[b]->parent()->takeChild(bufitems[b]->parent()->indexOfChild(bufitems[b]));
+    delete item;
+    bufitems.remove(b);
+  }
+  if(shouldShow(b)) {
+    QString net = b->networkName();
+    QString buf = b->bufferName();
+    QTreeWidgetItem *item;
+    QStringList label;
+    if(b->bufferType() == Buffer::ServerBuffer) label << tr("Status");
+    else label << buf;
+    if((mode & SomeNets) || ( mode & AllNets)) {
+      if(!netitems.contains(net)) {
+        netitems[net] = new QTreeWidgetItem(tree, QStringList(net));
+        netitems[net]->setFlags(Qt::ItemIsEnabled);
       }
       }
-      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);
+      QTreeWidgetItem *ni = netitems[net];
+      ni->setExpanded(true);
+      ni->setFlags(Qt::ItemIsEnabled);
+      item = new QTreeWidgetItem(ni, label);
+    } else {
+      item = new QTreeWidgetItem(label);
+    }
+    //item->setFlags(Qt::ItemIsEnabled);
+    bufitems[b] = item;
+    // TODO Use style engine!
+    if(!b->isActive()) {
+      item->setForeground(0, QColor("grey"));
+    }
+    if(b == currentBuffer) {
+      item->setSelected(true);
+    }
+  }
+  foreach(QString key, netitems.keys()) {
+    if(!netitems[key]->childCount()) {
+      delete netitems[key];
+      QTreeWidgetItem *item = netitems[key]->parent()->takeChild(netitems[key]->parent()->indexOfChild(netitems[key]));
+      delete item;
+      netitems.remove(key);
     }
     }
-    if(items[currentNetwork][currentBuffer]) items[currentNetwork][currentBuffer]->setSelected(true);
   }
 }
 
   }
 }
 
+bool NetworkView::shouldShow(Buffer *b) {
+  bool f = false;
+  if((mode & NoActive) && b->isActive()) return false;
+  if((mode & NoInactive) && !b->isActive()) return false;
+  if((mode & NoChannels) && b->bufferType() == Buffer::ChannelBuffer) return false;
+  if((mode & NoQueries) && b->bufferType() == Buffer::QueryBuffer) return false;
+  if((mode & NoServers) && b->bufferType() == Buffer::ServerBuffer) return false;
+  if((mode & SomeNets) && !networks.contains(b->networkName())) return false;
+  return true;
+}
+
+void NetworkView::bufferDestroyed(Buffer *b) {
+
+
+}
+
 void NetworkView::itemClicked(QTreeWidgetItem *item) {
 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;
-    }
+  Buffer *b = bufitems.key(item);
+  if(b) emit bufferSelected(b);
+  else {
+    b = currentBuffer;
+    if(bufitems.contains(b)) bufitems[b]->setSelected(true);
+    item->setExpanded(!item->isExpanded());
   }
 }
 
   }
 }
 
-void NetworkView::selectBuffer(QString net, QString buf) {
-  if(items[net][buf]) items[net][buf]->setSelected(true);
-  currentNetwork = net; currentBuffer = buf;
+void NetworkView::selectBuffer(Buffer *b) {
+  QTreeWidgetItem *item = 0;
+  if(bufitems.contains(b)) item = bufitems[b];
+  QList<QTreeWidgetItem *> sel = tree->selectedItems();
+  foreach(QTreeWidgetItem *i, sel) { if(i != item) i->setSelected(false); }
+  if(item) {
+    item->setSelected(true);
+    currentBuffer = b;
+  } else {
+    currentBuffer = 0;
+  }
 }
 }
index ac87077..2ed94b6 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
 /***************************************************************************
- *   Copyright (C) 2005/06 by The Quassel Team                             *
+ *   Copyright (C) 2005-07 by The Quassel Team                             *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -32,13 +32,11 @@ class NetworkViewWidget : public QWidget {
 
   public:
     NetworkViewWidget(QWidget *parent = 0);
 
   public:
     NetworkViewWidget(QWidget *parent = 0);
+
     QTreeWidget *tree() { return ui.tree; }
 
     virtual QSize sizeHint () const;
 
     QTreeWidget *tree() { return ui.tree; }
 
     virtual QSize sizeHint () const;
 
-  public slots:
-
-
   signals:
     void bufferSelected(QString net, QString buf);
 
   signals:
     void bufferSelected(QString net, QString buf);
 
@@ -54,25 +52,41 @@ class NetworkView : public QDockWidget {
   Q_OBJECT
 
   public:
   Q_OBJECT
 
   public:
-    NetworkView(QString network, QWidget *parent = 0);
+    enum Mode {
+      NoActive = 0x01, NoInactive = 0x02,
+      SomeNets = 0x04, AllNets = 0x08,
+      NoChannels = 0x10, NoQueries = 0x20, NoServers = 0x40
+    };
+
+    NetworkView(QString name, int mode, QStringList nets = QStringList(), QWidget *parent = 0);
+    void setMode(int mode, QStringList nets = QStringList());
+    void setName(QString name);
+
 
   public slots:
 
   public slots:
-    void buffersUpdated(BufferHash);
-    void selectBuffer(QString net, QString buf);
+    void bufferUpdated(Buffer *);
+    void bufferDestroyed(Buffer *);
+    void setBuffers(QList<Buffer *>);
+    void selectBuffer(Buffer *);
 
   signals:
 
   signals:
-    void bufferSelected(QString net, QString buf);
+    void bufferSelected(Buffer *);
 
   private slots:
     void itemClicked(QTreeWidgetItem *item);
 
   private:
 
   private slots:
     void itemClicked(QTreeWidgetItem *item);
 
   private:
-    QString network;
-    QString currentNetwork, currentBuffer;
-    QHash<QString, QHash<QString, Buffer*> > buffers;
-    QHash<QString, QHash<QString, QTreeWidgetItem *> > items;
+    int mode;
+    QString name;
+    QStringList networks;
+    Buffer *currentBuffer;
+    //QHash<QString, QHash<QString, Buffer*> > buffers;
+    QHash<Buffer *, QTreeWidgetItem *> bufitems;
+    QHash<QString, QTreeWidgetItem *> netitems;
+    //QHash<QString, QHash<QString, QTreeWidgetItem *> > items;
     QTreeWidget *tree;
 
     QTreeWidget *tree;
 
+    bool shouldShow(Buffer *);
 };
 
 
 };
 
 
index 55200e8..f49708c 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
 /***************************************************************************
- *   Copyright (C) 2005 by The Quassel Team                                *
+ *   Copyright (C) 2005-07 by The Quassel Team                                *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
diff --git a/gui/settingsdlg.cpp b/gui/settingsdlg.cpp
new file mode 100644 (file)
index 0000000..06aeb0b
--- /dev/null
@@ -0,0 +1,46 @@
+/***************************************************************************
+ *   Copyright (C) 2005-07 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 "settingsdlg.h"
+
+SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) {
+  ui.setupUi(this);
+
+  currentWidget = 0;
+
+  ui.settingsFrame->setWidgetResizable(true);
+  ui.settingsTree->setRootIsDecorated(false);
+}
+
+void SettingsDlg::registerSettingsPage(SettingsInterface *sp) {
+  QWidget *w = sp->settingsWidget();
+  w->setParent(this);
+  ui.settingsFrame->setWidget(w);
+
+  QTreeWidgetItem *cat;
+  QList<QTreeWidgetItem *> cats = ui.settingsTree->findItems(sp->category(), Qt::MatchExactly);
+  if(!cats.count()) {
+    cat = new QTreeWidgetItem(ui.settingsTree, QStringList(sp->category()));
+    cat->setExpanded(true);
+    cat->setFlags(Qt::ItemIsEnabled);
+  } else cat = cats[0];
+  new QTreeWidgetItem(cat, QStringList(sp->title()));
+
+}
similarity index 89%
rename from gui/settings.h
rename to gui/settingsdlg.h
index ed9be0e..12fac46 100644 (file)
@@ -18,8 +18,8 @@
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
-#ifndef _SETTINGS_H_
-#define _SETTINGS_H_
+#ifndef _SETTINGSDLG_H_
+#define _SETTINGSDLG_H_
 
 #include <QtGui>
 #include "ui_settingsdlg.h"
 
 #include <QtGui>
 #include "ui_settingsdlg.h"
@@ -37,7 +37,21 @@ class SettingsDlg : public QDialog {
   private:
     Ui::SettingsDlg ui;
 
   private:
     Ui::SettingsDlg ui;
 
+    QWidget *currentWidget;
 
 };
 
 
 };
 
+/*
+class CoreSettingsPage : public QWidget, SettingsInterface {
+  Q_OBJECT
+
+  public:
+
+
+  private:
+    Ui::CoreSettingsPage ui;
+
+};
+*/
+
 #endif
 #endif
similarity index 74%
rename from gui/settings.cpp
rename to gui/settingspages.cpp
index a4efaea..b67fd3a 100644 (file)
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
-#include "settings.h"
+#include "mainwin.h"
+#include "settingspages.h"
 
 
-SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) {
+void MainWin::setupSettingsDlg() {
+  settingsDlg = new SettingsDlg(this);
+  settingsDlg->setVisible(false);
+
+  settingsDlg->registerSettingsPage(new BufferManagementSettingsPage());
+  //settingsDlg->registerSettingsPage(new ConnectionSettingsPage());
+
+}
+
+
+BufferManagementSettingsPage::BufferManagementSettingsPage() {
   ui.setupUi(this);
 
   ui.setupUi(this);
 
+  ui.tree->headerItem()->setText(0, tr("Buffers"));
+  setEnabled(false);
 }
 }
+
+ConnectionSettingsPage::ConnectionSettingsPage() {
+  ui.setupUi(this);
+
+}
+
diff --git a/gui/settingspages.h b/gui/settingspages.h
new file mode 100644 (file)
index 0000000..f7d0f85
--- /dev/null
@@ -0,0 +1,68 @@
+/***************************************************************************
+ *   Copyright (C) 2005-07 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 _SETTINGSPAGES_H_
+#define _SETTINGSPAGES_H_
+
+#include <QtCore>
+#include <QtGui>
+
+#include "plugin.h"
+#include "settingsdlg.h"
+#include "ui_buffermgmntsettingspage.h"
+#include "ui_connectionsettingspage.h"
+
+class BufferManagementSettingsPage : public QWidget, public SettingsInterface {
+  Q_OBJECT
+
+  public:
+    QString category() { return tr("Buffers"); }
+    QString title() { return tr("Buffer Management"); }
+    QWidget *settingsWidget() { return this; }
+
+    BufferManagementSettingsPage();
+
+
+  private:
+    Ui::BufferManagementSettingsPage ui;
+
+};
+
+class ConnectionSettingsPage : public QWidget, public SettingsInterface {
+  Q_OBJECT
+
+  public:
+    QString category() { return tr("Behavior"); }
+    QString title() { return tr("Connection"); }
+    QWidget *settingsWidget() { return this; }
+
+    ConnectionSettingsPage();
+
+
+  private:
+    Ui::ConnectionSettingsPage ui;
+
+};
+
+
+
+
+
+#endif
index 8837872..6210552 100644 (file)
@@ -40,7 +40,7 @@ void Style::init() {
   colors["15"] = QColor("silver");
 
   QTextCharFormat def;
   colors["15"] = QColor("silver");
 
   QTextCharFormat def;
-  //def.setFont(QFont("Lucida Mono"));
+  def.setFont(QFont("Verdana",9));
   formats["default"] = def;
 
   // %B - 0x02 - bold
   formats["default"] = def;
 
   // %B - 0x02 - bold
@@ -97,15 +97,15 @@ void Style::init() {
   formats["%Dj"] = join;
   // %Dp - part
   QTextCharFormat part;
   formats["%Dj"] = join;
   // %Dp - part
   QTextCharFormat part;
-  part.setForeground(QBrush("firebrick"));
+  part.setForeground(QBrush("indianred"));
   formats["%Dp"] = part;
   // %Dq - quit
   QTextCharFormat quit;
   formats["%Dp"] = part;
   // %Dq - quit
   QTextCharFormat quit;
-  quit.setForeground(QBrush("firebrick"));
+  quit.setForeground(QBrush("indianred"));
   formats["%Dq"] = quit;
   // %Dk - kick
   QTextCharFormat kick;
   formats["%Dq"] = quit;
   // %Dk - kick
   QTextCharFormat kick;
-  kick.setForeground(QBrush("firebrick"));
+  kick.setForeground(QBrush("indianred"));
   formats["%Dk"] = kick;
   // %Dr - nick rename
   QTextCharFormat nren;
   formats["%Dk"] = kick;
   // %Dr - nick rename
   QTextCharFormat nren;
@@ -143,7 +143,11 @@ void Style::init() {
   QTextCharFormat flags;
   flags.setFontWeight(QFont::Bold);
   formats["%DM"] = flags;
   QTextCharFormat flags;
   flags.setFontWeight(QFont::Bold);
   formats["%DM"] = flags;
-
+  // %DU - clickable URL
+  QTextCharFormat url;
+  url.setFontUnderline(true);
+  url.setAnchor(true);
+  formats["%DU"] = url;
 }
 
 QString Style::mircToInternal(QString mirc) {
 }
 
 QString Style::mircToInternal(QString mirc) {
@@ -162,10 +166,10 @@ QString Style::mircToInternal(QString mirc) {
  *  describing the formats of the string.
  * \param s string in internal format (% style format codes)
  */ 
  *  describing the formats of the string.
  * \param s string in internal format (% style format codes)
  */ 
-Style::StringFormats Style::internalToFormatted(QString s) {
+Style::FormattedString Style::internalToFormatted(QString s) {
   QHash<QString, int> toggles;
   QString p;
   QHash<QString, int> toggles;
   QString p;
-  StringFormats sf;
+  FormattedString sf;
   QTextLayout::FormatRange rng;
   rng.format = formats["default"]; rng.start = 0; rng.length = -1; sf.formats.append(rng);
   toggles["default"] = sf.formats.count() - 1;
   QTextLayout::FormatRange rng;
   rng.format = formats["default"]; rng.start = 0; rng.length = -1; sf.formats.append(rng);
   toggles["default"] = sf.formats.count() - 1;
@@ -231,6 +235,16 @@ Style::StringFormats Style::internalToFormatted(QString s) {
         if(s[i] == 'D') i++;
         if(toggles.contains(key)) {
           sf.formats[toggles[key]].length = j - sf.formats[toggles[key]].start;
         if(s[i] == 'D') i++;
         if(toggles.contains(key)) {
           sf.formats[toggles[key]].length = j - sf.formats[toggles[key]].start;
+          if(key == "%DU") {
+            // URL handling
+            // FIXME check for and handle format codes within URLs
+            QString u = s.mid(i - sf.formats[toggles[key]].length - 2, sf.formats[toggles[key]].length);
+            UrlInfo url;
+            url.start = sf.formats[toggles[key]].start;
+            url.end   = j;
+            url.url = QUrl(u);
+            sf.urls.append(url);
+          }
           toggles.remove(key);
         } else {
           QTextLayout::FormatRange range;
           toggles.remove(key);
         } else {
           QTextLayout::FormatRange range;
index 19c3be6..861d5c5 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
 /***************************************************************************
- *   Copyright (C) 2005/06 by The Quassel Team                             *
+ *   Copyright (C) 2005-07 by The Quassel Team                             *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -29,14 +29,20 @@ class Style {
   public:
     static void init();
 
   public:
     static void init();
 
-    struct StringFormats {
+    struct UrlInfo {
+      int start, end;
+      QUrl url;
+    };
+
+    struct FormattedString {
       QString text;
       QList<QTextLayout::FormatRange> formats;
       QString text;
       QList<QTextLayout::FormatRange> formats;
+      QList<UrlInfo> urls;
     };
 
     static QString mircToInternal(QString);
     //static QString internalToMirc(QString);
     };
 
     static QString mircToInternal(QString);
     //static QString internalToMirc(QString);
-    static StringFormats internalToFormatted(QString);
+    static FormattedString internalToFormatted(QString);
     static int sepTsSender() { return 10; }
     static int sepSenderText() { return 10; }
 
     static int sepTsSender() { return 10; }
     static int sepSenderText() { return 10; }
 
diff --git a/gui/ui/aboutdlg.ui b/gui/ui/aboutdlg.ui
new file mode 100644 (file)
index 0000000..9740c85
--- /dev/null
@@ -0,0 +1,124 @@
+<ui version="4.0" >
+ <class>AboutDlg</class>
+ <widget class="QDialog" name="AboutDlg" >
+  <property name="windowModality" >
+   <enum>Qt::ApplicationModal</enum>
+  </property>
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>618</width>
+    <height>415</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Dialog</string>
+  </property>
+  <widget class="QLabel" name="label" >
+   <property name="geometry" >
+    <rect>
+     <x>10</x>
+     <y>10</y>
+     <width>351</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="text" >
+    <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'; font-size:11pt; font-weight:400; font-style:normal; text-decoration:none;">
+&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;span style=" font-weight:600;">Quassel IRC &lt;/span>(pre-release)&lt;/p>&lt;/body>&lt;/html></string>
+   </property>
+  </widget>
+  <widget class="QDialogButtonBox" name="buttonBox" >
+   <property name="geometry" >
+    <rect>
+     <x>200</x>
+     <y>360</y>
+     <width>341</width>
+     <height>32</height>
+    </rect>
+   </property>
+   <property name="orientation" >
+    <enum>Qt::Horizontal</enum>
+   </property>
+   <property name="standardButtons" >
+    <set>QDialogButtonBox::Close</set>
+   </property>
+  </widget>
+  <widget class="QTabWidget" name="tabWidget" >
+   <property name="geometry" >
+    <rect>
+     <x>20</x>
+     <y>40</y>
+     <width>581</width>
+     <height>301</height>
+    </rect>
+   </property>
+   <property name="currentIndex" >
+    <number>0</number>
+   </property>
+   <widget class="QWidget" name="aboutTab" >
+    <attribute name="title" >
+     <string>About</string>
+    </attribute>
+   </widget>
+   <widget class="QWidget" name="authorsTab" >
+    <attribute name="title" >
+     <string>Authors</string>
+    </attribute>
+   </widget>
+   <widget class="QWidget" name="licenceTab" >
+    <attribute name="title" >
+     <string>Licence Agreement</string>
+    </attribute>
+    <widget class="QTextBrowser" name="textBrowser" >
+     <property name="geometry" >
+      <rect>
+       <x>10</x>
+       <y>10</y>
+       <width>561</width>
+       <height>251</height>
+      </rect>
+     </property>
+    </widget>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>AboutDlg</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel" >
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel" >
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>AboutDlg</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel" >
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel" >
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/gui/ui/buffermgmntsettingspage.ui b/gui/ui/buffermgmntsettingspage.ui
new file mode 100644 (file)
index 0000000..34505a8
--- /dev/null
@@ -0,0 +1,585 @@
+<ui version="4.0" >
+ <class>BufferManagementSettingsPage</class>
+ <widget class="QWidget" name="BufferManagementSettingsPage" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>672</width>
+    <height>488</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" >
+   <property name="margin" >
+    <number>9</number>
+   </property>
+   <property name="spacing" >
+    <number>6</number>
+   </property>
+   <item>
+    <widget class="QLabel" name="label" >
+     <property name="frameShape" >
+      <enum>QFrame::NoFrame</enum>
+     </property>
+     <property name="text" >
+      <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'; font-size:11pt; font-weight:400; font-style:normal; text-decoration:none;">
+&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;span style=" font-weight:600;">Buffer Management&lt;/span>&lt;/p>&lt;/body>&lt;/html></string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="Line" name="line" >
+     <property name="orientation" >
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" >
+     <property name="margin" >
+      <number>0</number>
+     </property>
+     <property name="spacing" >
+      <number>6</number>
+     </property>
+     <item>
+      <widget class="QTreeWidget" name="tree" >
+       <property name="rootIsDecorated" >
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" >
+       <property name="margin" >
+        <number>0</number>
+       </property>
+       <property name="spacing" >
+        <number>6</number>
+       </property>
+       <item>
+        <widget class="QGroupBox" name="groupBox" >
+         <property name="title" >
+          <string>Statistics</string>
+         </property>
+         <property name="flat" >
+          <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="label_2" >
+            <property name="text" >
+             <string>Created: 01.04.2007
+Lines:     1234
+</string>
+            </property>
+            <property name="alignment" >
+             <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QTabWidget" name="tabWidget" >
+         <property name="currentIndex" >
+          <number>1</number>
+         </property>
+         <widget class="QWidget" name="tab" >
+          <property name="whatsThis" >
+           <string>Defines how many messages the local Quassel client shows for this buffer. This is independent from the central backlog storage in the core.</string>
+          </property>
+          <attribute name="title" >
+           <string>Local Display</string>
+          </attribute>
+          <layout class="QVBoxLayout" >
+           <property name="margin" >
+            <number>9</number>
+           </property>
+           <property name="spacing" >
+            <number>6</number>
+           </property>
+           <item>
+            <widget class="QGroupBox" name="groupBox_2" >
+             <property name="title" >
+              <string>Override default display options</string>
+             </property>
+             <property name="checkable" >
+              <bool>true</bool>
+             </property>
+             <property name="checked" >
+              <bool>false</bool>
+             </property>
+             <layout class="QVBoxLayout" >
+              <property name="margin" >
+               <number>9</number>
+              </property>
+              <property name="spacing" >
+               <number>6</number>
+              </property>
+              <item>
+               <widget class="QRadioButton" name="radioButton" >
+                <property name="text" >
+                 <string>Show all messages</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="QRadioButton" name="radioButton_2" >
+                  <property name="sizePolicy" >
+                   <sizepolicy>
+                    <hsizetype>5</hsizetype>
+                    <vsizetype>0</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text" >
+                   <string>Show last</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QSpinBox" name="spinBox" >
+                  <property name="sizePolicy" >
+                   <sizepolicy>
+                    <hsizetype>5</hsizetype>
+                    <vsizetype>0</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="maximum" >
+                   <number>99999</number>
+                  </property>
+                  <property name="minimum" >
+                   <number>1</number>
+                  </property>
+                  <property name="value" >
+                   <number>1000</number>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QLabel" name="label_3" >
+                  <property name="text" >
+                   <string>messages</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <spacer>
+                  <property name="orientation" >
+                   <enum>Qt::Horizontal</enum>
+                  </property>
+                  <property name="sizeHint" >
+                   <size>
+                    <width>40</width>
+                    <height>20</height>
+                   </size>
+                  </property>
+                 </spacer>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" >
+                <property name="margin" >
+                 <number>0</number>
+                </property>
+                <property name="spacing" >
+                 <number>6</number>
+                </property>
+                <item>
+                 <widget class="QRadioButton" name="radioButton_3" >
+                  <property name="text" >
+                   <string>Show messages from the last</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QSpinBox" name="spinBox_2" >
+                  <property name="maximum" >
+                   <number>1000</number>
+                  </property>
+                  <property name="minimum" >
+                   <number>1</number>
+                  </property>
+                  <property name="value" >
+                   <number>30</number>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QLabel" name="label_4" >
+                  <property name="enabled" >
+                   <bool>false</bool>
+                  </property>
+                  <property name="text" >
+                   <string>days</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <spacer>
+                  <property name="orientation" >
+                   <enum>Qt::Horizontal</enum>
+                  </property>
+                  <property name="sizeHint" >
+                   <size>
+                    <width>40</width>
+                    <height>20</height>
+                   </size>
+                  </property>
+                 </spacer>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <widget class="QRadioButton" name="radioButton_4" >
+                <property name="text" >
+                 <string>Hide buffer locally</string>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <spacer>
+                <property name="orientation" >
+                 <enum>Qt::Vertical</enum>
+                </property>
+                <property name="sizeHint" >
+                 <size>
+                  <width>20</width>
+                  <height>40</height>
+                 </size>
+                </property>
+               </spacer>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" >
+                <property name="margin" >
+                 <number>0</number>
+                </property>
+                <property name="spacing" >
+                 <number>6</number>
+                </property>
+                <item>
+                 <spacer>
+                  <property name="orientation" >
+                   <enum>Qt::Horizontal</enum>
+                  </property>
+                  <property name="sizeHint" >
+                   <size>
+                    <width>40</width>
+                    <height>20</height>
+                   </size>
+                  </property>
+                 </spacer>
+                </item>
+                <item>
+                 <widget class="QPushButton" name="pushButton" >
+                  <property name="text" >
+                   <string>Save as default</string>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+             </layout>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+         <widget class="QWidget" name="tab_2" >
+          <attribute name="title" >
+           <string>Backlog Storage</string>
+          </attribute>
+          <layout class="QVBoxLayout" >
+           <property name="margin" >
+            <number>9</number>
+           </property>
+           <property name="spacing" >
+            <number>6</number>
+           </property>
+           <item>
+            <widget class="QLabel" name="label_7" >
+             <property name="text" >
+              <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'; font-size:11pt; font-weight:400; font-style:normal; text-decoration:none;">
+&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;span style=" font-weight:600;">Warning:&lt;/span> This affects the global message storage (backlog) for the selected buffer!&lt;/p>&lt;/body>&lt;/html></string>
+             </property>
+             <property name="alignment" >
+              <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+             </property>
+             <property name="wordWrap" >
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QGroupBox" name="groupBox_3" >
+             <property name="title" >
+              <string>Override default storage settings</string>
+             </property>
+             <property name="checkable" >
+              <bool>true</bool>
+             </property>
+             <property name="checked" >
+              <bool>false</bool>
+             </property>
+             <layout class="QVBoxLayout" >
+              <property name="margin" >
+               <number>9</number>
+              </property>
+              <property name="spacing" >
+               <number>6</number>
+              </property>
+              <item>
+               <widget class="QRadioButton" name="radioButton_5" >
+                <property name="text" >
+                 <string>Keep all messages</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="QRadioButton" name="radioButton_6" >
+                  <property name="sizePolicy" >
+                   <sizepolicy>
+                    <hsizetype>5</hsizetype>
+                    <vsizetype>0</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text" >
+                   <string>Keep last</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QSpinBox" name="spinBox_3" >
+                  <property name="sizePolicy" >
+                   <sizepolicy>
+                    <hsizetype>5</hsizetype>
+                    <vsizetype>0</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="maximum" >
+                   <number>99999</number>
+                  </property>
+                  <property name="minimum" >
+                   <number>1</number>
+                  </property>
+                  <property name="value" >
+                   <number>1000</number>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QLabel" name="label_5" >
+                  <property name="text" >
+                   <string>messages</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <spacer>
+                  <property name="orientation" >
+                   <enum>Qt::Horizontal</enum>
+                  </property>
+                  <property name="sizeHint" >
+                   <size>
+                    <width>40</width>
+                    <height>20</height>
+                   </size>
+                  </property>
+                 </spacer>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" >
+                <property name="margin" >
+                 <number>0</number>
+                </property>
+                <property name="spacing" >
+                 <number>6</number>
+                </property>
+                <item>
+                 <widget class="QRadioButton" name="radioButton_7" >
+                  <property name="text" >
+                   <string>Keep messages from the last</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QSpinBox" name="spinBox_4" >
+                  <property name="maximum" >
+                   <number>1000</number>
+                  </property>
+                  <property name="minimum" >
+                   <number>1</number>
+                  </property>
+                  <property name="value" >
+                   <number>30</number>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QLabel" name="label_6" >
+                  <property name="enabled" >
+                   <bool>false</bool>
+                  </property>
+                  <property name="text" >
+                   <string>days</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <spacer>
+                  <property name="orientation" >
+                   <enum>Qt::Horizontal</enum>
+                  </property>
+                  <property name="sizeHint" >
+                   <size>
+                    <width>40</width>
+                    <height>20</height>
+                   </size>
+                  </property>
+                 </spacer>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <spacer>
+                <property name="orientation" >
+                 <enum>Qt::Vertical</enum>
+                </property>
+                <property name="sizeHint" >
+                 <size>
+                  <width>20</width>
+                  <height>40</height>
+                 </size>
+                </property>
+               </spacer>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" >
+                <property name="margin" >
+                 <number>0</number>
+                </property>
+                <property name="spacing" >
+                 <number>6</number>
+                </property>
+                <item>
+                 <spacer>
+                  <property name="orientation" >
+                   <enum>Qt::Horizontal</enum>
+                  </property>
+                  <property name="sizeHint" >
+                   <size>
+                    <width>40</width>
+                    <height>20</height>
+                   </size>
+                  </property>
+                 </spacer>
+                </item>
+                <item>
+                 <widget class="QPushButton" name="pushButton_2" >
+                  <property name="text" >
+                   <string>Save as default</string>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+             </layout>
+            </widget>
+           </item>
+           <item>
+            <layout class="QHBoxLayout" >
+             <property name="margin" >
+              <number>0</number>
+             </property>
+             <property name="spacing" >
+              <number>6</number>
+             </property>
+             <item>
+              <spacer>
+               <property name="orientation" >
+                <enum>Qt::Horizontal</enum>
+               </property>
+               <property name="sizeHint" >
+                <size>
+                 <width>40</width>
+                 <height>20</height>
+                </size>
+               </property>
+              </spacer>
+             </item>
+             <item>
+              <widget class="QPushButton" name="pushButton_3" >
+               <property name="text" >
+                <string>Delete permanently</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <spacer>
+               <property name="orientation" >
+                <enum>Qt::Horizontal</enum>
+               </property>
+               <property name="sizeHint" >
+                <size>
+                 <width>40</width>
+                 <height>20</height>
+                </size>
+               </property>
+              </spacer>
+             </item>
+            </layout>
+           </item>
+          </layout>
+         </widget>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
index 854d491..9cb2da0 100644 (file)
@@ -5,15 +5,15 @@
    <rect>
     <x>0</x>
     <y>0</y>
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>713</width>
-    <height>519</height>
+    <width>787</width>
+    <height>551</height>
    </rect>
   </property>
   <property name="sizePolicy" >
    <sizepolicy>
    </rect>
   </property>
   <property name="sizePolicy" >
    <sizepolicy>
-    <hsizetype>3</hsizetype>
-    <vsizetype>3</vsizetype>
-    <horstretch>0</horstretch>
+    <hsizetype>7</hsizetype>
+    <vsizetype>7</vsizetype>
+    <horstretch>5</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
@@ -31,7 +31,7 @@
   </property>
   <layout class="QVBoxLayout" >
    <property name="margin" >
   </property>
   <layout class="QVBoxLayout" >
    <property name="margin" >
-    <number>9</number>
+    <number>0</number>
    </property>
    <property name="spacing" >
     <number>6</number>
    </property>
    <property name="spacing" >
     <number>6</number>
@@ -46,6 +46,9 @@
      </property>
      <item>
       <widget class="QLineEdit" name="topicEdit" >
      </property>
      <item>
       <widget class="QLineEdit" name="topicEdit" >
+       <property name="enabled" >
+        <bool>false</bool>
+       </property>
        <property name="focusPolicy" >
         <enum>Qt::ClickFocus</enum>
        </property>
        <property name="focusPolicy" >
         <enum>Qt::ClickFocus</enum>
        </property>
     </layout>
    </item>
    <item>
     </layout>
    </item>
    <item>
-    <widget class="QSplitter" name="splitter" >
-     <property name="orientation" >
-      <enum>Qt::Horizontal</enum>
+    <widget class="QStackedWidget" name="stackedWidget" >
+     <property name="sizePolicy" >
+      <sizepolicy>
+       <hsizetype>7</hsizetype>
+       <vsizetype>5</vsizetype>
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
      </property>
      </property>
-     <widget class="ChatWidget" native="1" name="chatWidget" >
-      <property name="sizePolicy" >
-       <sizepolicy>
-        <hsizetype>5</hsizetype>
-        <vsizetype>5</vsizetype>
-        <horstretch>6</horstretch>
-        <verstretch>0</verstretch>
-       </sizepolicy>
-      </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 name="currentIndex" >
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="page" >
+      <layout class="QVBoxLayout" >
+       <property name="margin" >
+        <number>0</number>
        </property>
        </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 name="spacing" >
+        <number>6</number>
        </property>
        <item>
        </property>
        <item>
-        <property name="text" >
-         <string>New Sub Item</string>
-        </property>
+        <widget class="QFrame" name="frame" >
+         <property name="frameShape" >
+          <enum>QFrame::StyledPanel</enum>
+         </property>
+         <property name="frameShadow" >
+          <enum>QFrame::Plain</enum>
+         </property>
+         <layout class="QVBoxLayout" >
+          <property name="margin" >
+           <number>9</number>
+          </property>
+          <property name="spacing" >
+           <number>6</number>
+          </property>
+          <item>
+           <widget class="QLabel" name="label" >
+            <property name="font" >
+             <font>
+              <family>Trebuchet MS</family>
+             </font>
+            </property>
+            <property name="text" >
+             <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:'Trebuchet MS'; font-size:11pt; font-weight:400; font-style:normal; text-decoration:none;">
+&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;span style=" font-size:60pt; color:#000055">Quassel IRC&lt;/span>&lt;/p>&lt;/body>&lt;/html></string>
+            </property>
+            <property name="scaledContents" >
+             <bool>true</bool>
+            </property>
+            <property name="alignment" >
+             <set>Qt::AlignCenter</set>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
        </item>
        </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>
+      </layout>
      </widget>
     </widget>
    </item>
      </widget>
     </widget>
    </item>
   </layout>
  </widget>
  <customwidgets>
   </layout>
  </widget>
  <customwidgets>
-  <customwidget>
-   <class>ChatWidget</class>
-   <extends>QWidget</extends>
-   <header>chatwidget.h</header>
-  </customwidget>
   <customwidget>
    <class>ChannelWidgetInput</class>
    <extends>QLineEdit</extends>
   <customwidget>
    <class>ChannelWidgetInput</class>
    <extends>QLineEdit</extends>
  <tabstops>
   <tabstop>inputEdit</tabstop>
   <tabstop>ownNick</tabstop>
  <tabstops>
   <tabstop>inputEdit</tabstop>
   <tabstop>ownNick</tabstop>
-  <tabstop>nickTree</tabstop>
   <tabstop>topicEdit</tabstop>
   <tabstop>chanSettingsButton</tabstop>
  </tabstops>
   <tabstop>topicEdit</tabstop>
   <tabstop>chanSettingsButton</tabstop>
  </tabstops>
diff --git a/gui/ui/connectionsettingspage.ui b/gui/ui/connectionsettingspage.ui
new file mode 100644 (file)
index 0000000..82fff87
--- /dev/null
@@ -0,0 +1,18 @@
+<ui version="4.0" >
+ <class>ConnectionSettingsPage</class>
+ <widget class="QWidget" name="ConnectionSettingsPage" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>666</width>
+    <height>457</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Form</string>
+  </property>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/ui/coresettingspage.ui b/gui/ui/coresettingspage.ui
new file mode 100644 (file)
index 0000000..2e63270
--- /dev/null
@@ -0,0 +1,18 @@
+<ui version="4.0" >
+ <class>CoreSettingsPage</class>
+ <widget class="QWidget" name="CoreSettingsPage" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>693</width>
+    <height>482</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Form</string>
+  </property>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
index 32f9fa8..9e25be8 100644 (file)
   <property name="windowTitle" >
    <string>MainWindow</string>
   </property>
   <property name="windowTitle" >
    <string>MainWindow</string>
   </property>
-  <widget class="QWidget" name="centralwidget" />
+  <widget class="QWidget" name="centralwidget" >
+   <property name="sizePolicy" >
+    <sizepolicy>
+     <hsizetype>7</hsizetype>
+     <vsizetype>7</vsizetype>
+     <horstretch>0</horstretch>
+     <verstretch>0</verstretch>
+    </sizepolicy>
+   </property>
+   <layout class="QVBoxLayout" >
+    <property name="margin" >
+     <number>9</number>
+    </property>
+    <property name="spacing" >
+     <number>6</number>
+    </property>
+    <item>
+     <widget class="BufferWidget" native="1" name="bufferWidget" />
+    </item>
+   </layout>
+  </widget>
   <widget class="QMenuBar" name="menubar" >
    <property name="geometry" >
     <rect>
      <x>0</x>
      <y>0</y>
      <width>800</width>
   <widget class="QMenuBar" name="menubar" >
    <property name="geometry" >
     <rect>
      <x>0</x>
      <y>0</y>
      <width>800</width>
-     <height>32</height>
+     <height>28</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuConnection" >
     </rect>
    </property>
    <widget class="QMenu" name="menuConnection" >
     <addaction name="separator" />
     <addaction name="actionQuit" />
    </widget>
     <addaction name="separator" />
     <addaction name="actionQuit" />
    </widget>
-   <widget class="QMenu" name="menuViews" >
-    <property name="title" >
-     <string>Views</string>
-    </property>
-   </widget>
    <widget class="QMenu" name="menuSettings" >
     <property name="title" >
      <string>Settings</string>
    <widget class="QMenu" name="menuSettings" >
     <property name="title" >
      <string>Settings</string>
     <addaction name="separator" />
     <addaction name="actionSettingsDlg" />
    </widget>
     <addaction name="separator" />
     <addaction name="actionSettingsDlg" />
    </widget>
+   <widget class="QMenu" name="menuViews" >
+    <property name="title" >
+     <string>Views</string>
+    </property>
+    <addaction name="actionManageViews" />
+    <addaction name="separator" />
+   </widget>
    <widget class="QMenu" name="menuHelp" >
     <property name="title" >
      <string>Help</string>
     </property>
    <widget class="QMenu" name="menuHelp" >
     <property name="title" >
      <string>Help</string>
     </property>
+    <addaction name="actionAboutQuassel" />
+    <addaction name="actionAboutQt" />
    </widget>
    <addaction name="menuConnection" />
    <addaction name="menuViews" />
    </widget>
    <addaction name="menuConnection" />
    <addaction name="menuViews" />
     <string>F7</string>
    </property>
   </action>
     <string>F7</string>
    </property>
   </action>
+  <action name="actionManageViews" >
+   <property name="enabled" >
+    <bool>false</bool>
+   </property>
+   <property name="text" >
+    <string>Manage Views...</string>
+   </property>
+  </action>
+  <action name="actionAboutQt" >
+   <property name="text" >
+    <string>About Qt...</string>
+   </property>
+  </action>
+  <action name="actionAboutQuassel" >
+   <property name="text" >
+    <string>About Quassel IRC...</string>
+   </property>
+  </action>
  </widget>
  </widget>
+ <customwidgets>
+  <customwidget>
+   <class>BufferWidget</class>
+   <extends>QWidget</extends>
+   <header>bufferwidget.h</header>
+  </customwidget>
+ </customwidgets>
  <resources/>
  <connections>
   <connection>
  <resources/>
  <connections>
   <connection>
index a30e868..205afc8 100644 (file)
@@ -5,34 +5,40 @@
    <rect>
     <x>0</x>
     <y>0</y>
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>151</width>
-    <height>294</height>
+    <width>116</width>
+    <height>183</height>
    </rect>
   </property>
   <property name="sizePolicy" >
    <sizepolicy>
     <hsizetype>5</hsizetype>
    </rect>
   </property>
   <property name="sizePolicy" >
    <sizepolicy>
     <hsizetype>5</hsizetype>
-    <vsizetype>1</vsizetype>
+    <vsizetype>5</vsizetype>
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
+  <property name="minimumSize" >
+   <size>
+    <width>100</width>
+    <height>100</height>
+   </size>
+  </property>
   <property name="windowTitle" >
   <property name="windowTitle" >
-   <string>Form</string>
+   <string>NetView</string>
   </property>
   </property>
-  <layout class="QHBoxLayout" >
+  <layout class="QVBoxLayout" >
    <property name="margin" >
    <property name="margin" >
-    <number>9</number>
+    <number>4</number>
    </property>
    <property name="spacing" >
    </property>
    <property name="spacing" >
-    <number>6</number>
+    <number>0</number>
    </property>
    <item>
     <widget class="QTreeWidget" name="tree" >
      <property name="sizePolicy" >
       <sizepolicy>
        <hsizetype>5</hsizetype>
    </property>
    <item>
     <widget class="QTreeWidget" name="tree" >
      <property name="sizePolicy" >
       <sizepolicy>
        <hsizetype>5</hsizetype>
-       <vsizetype>1</vsizetype>
+       <vsizetype>5</vsizetype>
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
@@ -43,6 +49,9 @@
        <height>16777215</height>
       </size>
      </property>
        <height>16777215</height>
       </size>
      </property>
+     <property name="rootIsDecorated" >
+      <bool>false</bool>
+     </property>
     </widget>
    </item>
   </layout>
     </widget>
    </item>
   </layout>
index 771012a..a2097a4 100644 (file)
@@ -5,8 +5,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>848</width>
-    <height>514</height>
+    <width>808</width>
+    <height>568</height>
    </rect>
   </property>
   <property name="windowTitle" >
    </rect>
   </property>
   <property name="windowTitle" >
     <number>6</number>
    </property>
    <item>
     <number>6</number>
    </property>
    <item>
-    <layout class="QHBoxLayout" >
-     <property name="margin" >
-      <number>0</number>
-     </property>
-     <property name="spacing" >
-      <number>6</number>
+    <widget class="QSplitter" name="splitter" >
+     <property name="orientation" >
+      <enum>Qt::Horizontal</enum>
      </property>
      </property>
-     <item>
-      <widget class="QTreeWidget" name="settingsTree" >
-       <property name="sizePolicy" >
-        <sizepolicy>
-         <hsizetype>5</hsizetype>
-         <vsizetype>7</vsizetype>
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
-       </property>
-       <column>
-        <property name="text" >
-         <string>Settings</string>
-        </property>
-       </column>
-      </widget>
-     </item>
-     <item>
-      <widget class="QFrame" name="settingsFrame" >
-       <property name="sizePolicy" >
-        <sizepolicy>
-         <hsizetype>3</hsizetype>
-         <vsizetype>5</vsizetype>
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
+     <widget class="QTreeWidget" name="settingsTree" >
+      <property name="sizePolicy" >
+       <sizepolicy>
+        <hsizetype>5</hsizetype>
+        <vsizetype>7</vsizetype>
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <column>
+       <property name="text" >
+        <string>Settings</string>
        </property>
        </property>
-       <property name="frameShape" >
-        <enum>QFrame::StyledPanel</enum>
-       </property>
-       <property name="frameShadow" >
-        <enum>QFrame::Raised</enum>
-       </property>
-      </widget>
-     </item>
-    </layout>
+      </column>
+     </widget>
+     <widget class="QScrollArea" name="settingsFrame" >
+      <property name="sizePolicy" >
+       <sizepolicy>
+        <hsizetype>5</hsizetype>
+        <vsizetype>5</vsizetype>
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="frameShape" >
+       <enum>QFrame::StyledPanel</enum>
+      </property>
+      <property name="frameShadow" >
+       <enum>QFrame::Raised</enum>
+      </property>
+     </widget>
+    </widget>
    </item>
    <item>
     <widget class="QDialogButtonBox" name="buttonBox" >
    </item>
    <item>
     <widget class="QDialogButtonBox" name="buttonBox" >
    </item>
   </layout>
  </widget>
    </item>
   </layout>
  </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QScrollArea</class>
+   <extends>QFrame</extends>
+   <header>QScrollArea</header>
+  </customwidget>
+ </customwidgets>
  <resources/>
  <connections>
   <connection>
  <resources/>
  <connections>
   <connection>
index bfdb7af..32b82ba 100644 (file)
@@ -1,5 +1,5 @@
-SET(main_SRCS global.cpp logger.cpp util.cpp message.cpp)
-SET(main_HDRS util.h message.h)
+SET(main_SRCS global.cpp logger.cpp util.cpp message.cpp settings.cpp)
+SET(main_HDRS util.h message.h settings.h)
 SET(main_MOCS global.h logger.h)
 
 QT4_WRAP_CPP(_MOC ${main_MOCS})
 SET(main_MOCS global.h logger.h)
 
 QT4_WRAP_CPP(_MOC ${main_MOCS})
index 3ee9f9f..2d9c4e7 100644 (file)
@@ -28,6 +28,7 @@
 #include "guiproxy.h"
 #include "coreconnectdlg.h"
 #include "util.h"
 #include "guiproxy.h"
 #include "coreconnectdlg.h"
 #include "util.h"
+#include "chatwidget.h"
 
 #include "mainwin.h"
 
 
 #include "mainwin.h"
 
@@ -37,6 +38,8 @@ int main(int argc, char **argv) {
   QApplication::setApplicationName("Quassel IRC");
   QApplication::setOrganizationName("The Quassel Team");
 
   QApplication::setApplicationName("Quassel IRC");
   QApplication::setOrganizationName("The Quassel Team");
 
+  qRegisterMetaType<LayoutTask>("LayoutTask");
+
   Global::runMode = Global::GUIOnly;
   Global::quasselDir = QDir::homePath() + "/.quassel";
 
   Global::runMode = Global::GUIOnly;
   Global::quasselDir = QDir::homePath() + "/.quassel";
 
@@ -46,9 +49,11 @@ int main(int argc, char **argv) {
   Style::init();
 
   MainWin mainWin;
   Style::init();
 
   MainWin mainWin;
+  mainWin.init();
   int exitCode = app.exec();
   delete guiProxy;
   delete global;
   int exitCode = app.exec();
   delete guiProxy;
   delete global;
+  return exitCode;
 }
 
 void MainWin::syncToCore() {
 }
 
 void MainWin::syncToCore() {
index 268f3c1..584c220 100644 (file)
@@ -27,6 +27,8 @@
 #include "global.h"
 #include "guiproxy.h"
 #include "coreproxy.h"
 #include "global.h"
 #include "guiproxy.h"
 #include "coreproxy.h"
+#include "settings.h"
+#include "chatwidget.h"
 
 #include "mainwin.h"
 
 
 #include "mainwin.h"
 
@@ -36,22 +38,29 @@ int main(int argc, char **argv) {
   QApplication::setApplicationName("Quassel IRC");
   QApplication::setOrganizationName("The Quassel Team");
 
   QApplication::setApplicationName("Quassel IRC");
   QApplication::setOrganizationName("The Quassel Team");
 
+  qRegisterMetaType<LayoutTask>("LayoutTask");
+
   Global::runMode = Global::Monolithic;
   Global::quasselDir = QDir::homePath() + "/.quassel";
 
   Global::runMode = Global::Monolithic;
   Global::quasselDir = QDir::homePath() + "/.quassel";
 
+  //settings = new Settings();
   global = new Global();
   guiProxy = new GUIProxy();
   coreProxy = new CoreProxy();
 
   global = new Global();
   guiProxy = new GUIProxy();
   coreProxy = new CoreProxy();
 
+  Settings::init();
   Style::init();
 
   Style::init();
 
-  MainWin mainWin;
-  mainWin.show();
+  MainWin *mainWin = new MainWin();
+  mainWin->show();
+  mainWin->init();
   int exitCode = app.exec();
   delete core;
   delete guiProxy;
   delete coreProxy;
   delete global;
   int exitCode = app.exec();
   delete core;
   delete guiProxy;
   delete coreProxy;
   delete global;
+  delete mainWin;
+  //delete settings;
   return exitCode;
 }
 
   return exitCode;
 }
 
index 160c828..6d1e7a8 100644 (file)
 #include "message.h"
 #include <QDataStream>
 
 #include "message.h"
 #include <QDataStream>
 
-Message Message::plain(QString _target, QString _text, QString _sender, Flags _flags) {
+Message Message::plain(QString _target, QString _text, QString _sender, quint8 _flags) {
   return Message(_target, Plain, _text, _sender, _flags);
 }
 
   return Message(_target, Plain, _text, _sender, _flags);
 }
 
-Message Message::notice(QString _target, QString _text, QString _sender, Flags _flags) {
+Message Message::notice(QString _target, QString _text, QString _sender, quint8 _flags) {
   return Message(_target, Notice, _text, _sender, _flags);
 }
 
   return Message(_target, Notice, _text, _sender, _flags);
 }
 
-Message Message::action(QString _target, QString _text, QString _sender, Flags _flags) {
+Message Message::action(QString _target, QString _text, QString _sender, quint8 _flags) {
   return Message(_target, Action, _text, _sender, _flags);
 }
 
   return Message(_target, Action, _text, _sender, _flags);
 }
 
-Message Message::kick(QString _target, QString _text, QString _sender, Flags _flags) {
+Message Message::kick(QString _target, QString _text, QString _sender, quint8 _flags) {
   return Message(_target, Kick, _text, _sender, _flags);
 }
 
   return Message(_target, Kick, _text, _sender, _flags);
 }
 
-Message Message::join(QString _target, QString _text, QString _sender, Flags _flags) {
+Message Message::join(QString _target, QString _text, QString _sender, quint8 _flags) {
   return Message(_target, Join, _text, _sender, _flags);
 }
 
   return Message(_target, Join, _text, _sender, _flags);
 }
 
-Message Message::part(QString _target, QString _text, QString _sender, Flags _flags) {
+Message Message::part(QString _target, QString _text, QString _sender, quint8 _flags) {
   return Message(_target, Part, _text, _sender, _flags);
 }
 
   return Message(_target, Part, _text, _sender, _flags);
 }
 
-Message Message::nick(QString _target, QString _text, QString _sender, Flags _flags) {
+Message Message::nick(QString _target, QString _text, QString _sender, quint8 _flags) {
   return Message(_target, Nick, _text, _sender, _flags);
 }
 
   return Message(_target, Nick, _text, _sender, _flags);
 }
 
-Message Message::mode(QString _target, QString _text, QString _sender, Flags _flags) {
+Message Message::mode(QString _target, QString _text, QString _sender, quint8 _flags) {
   return Message(_target, Mode, _text, _sender, _flags);
 }
 
   return Message(_target, Mode, _text, _sender, _flags);
 }
 
-Message Message::quit(QString _target, QString _text, QString _sender, Flags _flags) {
+Message Message::quit(QString _target, QString _text, QString _sender, quint8 _flags) {
   return Message(_target, Quit, _text, _sender, _flags);
 }
 
   return Message(_target, Quit, _text, _sender, _flags);
 }
 
-Message Message::kill(QString _target, QString _text, QString _sender, Flags _flags) {
+Message Message::kill(QString _target, QString _text, QString _sender, quint8 _flags) {
   return Message(_target, Kill, _text, _sender, _flags);
 }
 
   return Message(_target, Kill, _text, _sender, _flags);
 }
 
-Message Message::server(QString _target, QString _text, QString _sender, Flags _flags) {
+Message Message::server(QString _target, QString _text, QString _sender, quint8 _flags) {
   return Message(_target, Server, _text, _sender, _flags);
 }
 
   return Message(_target, Server, _text, _sender, _flags);
 }
 
-Message Message::info(QString _target, QString _text, QString _sender, Flags _flags) {
+Message Message::info(QString _target, QString _text, QString _sender, quint8 _flags) {
   return Message(_target, Info, _text, _sender, _flags);
 }
 
   return Message(_target, Info, _text, _sender, _flags);
 }
 
-Message Message::error(QString _target, QString _text, QString _sender, Flags _flags) {
+Message Message::error(QString _target, QString _text, QString _sender, quint8 _flags) {
   return Message(_target, Error, _text, _sender, _flags);
 }
 
   return Message(_target, Error, _text, _sender, _flags);
 }
 
@@ -85,10 +85,11 @@ QDataStream &operator>>(QDataStream &in, Message &msg) {
   QByteArray s, m, targ;
   in >> ts >> t >> f >> targ >> s >> m;
   msg.type = (Message::Type)t;
   QByteArray s, m, targ;
   in >> ts >> t >> f >> targ >> s >> m;
   msg.type = (Message::Type)t;
-  msg.flags = (Message::Flags)f;
+  msg.flags = (quint8)f;
   msg.timeStamp = QDateTime::fromTime_t(ts);
   msg.target = QString::fromUtf8(targ);
   msg.sender = QString::fromUtf8(s);
   msg.text = QString::fromUtf8(m);
   return in;
 }
   msg.timeStamp = QDateTime::fromTime_t(ts);
   msg.target = QString::fromUtf8(targ);
   msg.sender = QString::fromUtf8(s);
   msg.text = QString::fromUtf8(m);
   return in;
 }
+
index 711d7ad..2426a0c 100644 (file)
@@ -27,31 +27,31 @@ struct Message {
 
   /** The different types a message can have for display */
   enum Type { Plain, Notice, Action, Nick, Mode, Join, Part, Quit, Kick, Kill, Server, Info, Error };
 
   /** The different types a message can have for display */
   enum Type { Plain, Notice, Action, Nick, Mode, Join, Part, Quit, Kick, Kill, Server, Info, Error };
-  enum Flags { None = 0, Self = 1, Highlight = 2 };
+  enum Flags { None = 0, Self = 1, PrivMsg = 2, Highlight = 4 };
 
   Type type;
 
   Type type;
-  Flags flags;
+  quint8 flags;
   QString target;
   QString sender;
   QString text;
   QDateTime timeStamp;
 
   QString target;
   QString sender;
   QString text;
   QDateTime timeStamp;
 
-  Message(QString _target = "", Type _type = Plain, QString _text = "", QString _sender = "", Flags _flags = None)
+  Message(QString _target = "", Type _type = Plain, QString _text = "", QString _sender = "", quint8 _flags = None)
   : target(_target), text(_text), sender(_sender), type(_type), flags(_flags) { timeStamp = QDateTime::currentDateTime().toUTC(); }
 
   : 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);
+  static Message plain(QString _target, QString _text, QString _sender = "", quint8 _flags = None);
+  static Message notice(QString _target, QString _text, QString _sender = "", quint8 _flags = None);
+  static Message action(QString _target, QString _text, QString _sender = "", quint8 _flags = None);
+  static Message nick(QString _target, QString _text, QString _sender = "", quint8 _flags = None);
+  static Message mode(QString _target, QString _text, QString _sender = "", quint8 _flags = None);
+  static Message join(QString _target, QString _text, QString _sender = "", quint8 _flags = None);
+  static Message part(QString _target, QString _text, QString _sender = "", quint8 _flags = None);
+  static Message quit(QString _target, QString _text, QString _sender = "", quint8 _flags = None);
+  static Message kick(QString _target, QString _text, QString _sender = "", quint8 _flags = None);
+  static Message kill(QString _target, QString _text, QString _sender = "", quint8 _flags = None);
+  static Message server(QString _target, QString _text, QString _sender = "", quint8 _flags = None);
+  static Message info(QString _target, QString _text, QString _sender = "", quint8 _flags = None);
+  static Message error(QString _target, QString _text, QString _sender = "", quint8 _flags = None);
 };
 
 QDataStream &operator<<(QDataStream &out, const Message &msg);
 };
 
 QDataStream &operator<<(QDataStream &out, const Message &msg);
index 5fefd56..8ca9afb 100644 (file)
@@ -28,7 +28,7 @@ enum GUISignal { GS_CLIENT_INIT, GS_USER_INPUT, GS_REQUEST_CONNECT, GS_UPDATE_GL
 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,
 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,
+  CS_OWN_NICK_SET, CS_QUERY_REQUESTED
 
 };
 
 
 };
 
diff --git a/main/settings.cpp b/main/settings.cpp
new file mode 100644 (file)
index 0000000..6f2ba0f
--- /dev/null
@@ -0,0 +1,63 @@
+/***************************************************************************
+ *   Copyright (C) 2005-07 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 "settings.h"
+
+
+Settings *settings;
+
+
+void Settings::init() {
+  curProfile = QObject::tr("Default");
+}
+/*
+Settings::~Settings() {
+  qDebug() << "destructing";
+
+}
+*/
+
+void Settings::setProfile(const QString &profile) {
+  curProfile = profile;
+}
+
+void Settings::setGuiValue(const QString &key, const QVariant &value) {
+  QSettings s;
+  //s.setValue("GUI/Default/BufferStates/QuakeNet/#quassel/voicedExpanded", true);
+  //QString k = QString("GUI/%1/%2").arg(curProfile).arg(key);
+  s.setValue(QString("GUI/%1/%2").arg(curProfile).arg(key), value);
+}
+
+QVariant Settings::guiValue(const QString &key, const QVariant &defaultValue) {
+  QSettings s;
+  return s.value(QString("GUI/%1/%2").arg(curProfile).arg(key), defaultValue);
+}
+
+void Settings::setCoreValue(const QString &user, const QString &key, const QVariant &value) {
+  QSettings s;
+  s.setValue(QString("Core/%1/%2").arg(user).arg(key), value);
+}
+
+QVariant Settings::coreValue(const QString &user, const QString &key, const QVariant &defaultValue) {
+  QSettings s;
+  return s.value(QString("Core/%1/%2").arg(user).arg(key), defaultValue);
+}
+
+QString Settings::curProfile;
diff --git a/main/settings.h b/main/settings.h
new file mode 100644 (file)
index 0000000..f2e0d38
--- /dev/null
@@ -0,0 +1,47 @@
+/***************************************************************************
+ *   Copyright (C) 2005-07 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 _SETTINGS_H_
+#define _SETTINGS_H_
+
+#include <QtCore>
+
+class Settings {
+
+  public:
+    //Settings();
+    //~Settings();
+    static void init();
+    static void setProfile(const QString &string);
+    static QString profile();
+
+    static void setGuiValue(const QString &key, const QVariant &value);
+    static QVariant guiValue (const QString &key, const QVariant &defaultValue = QVariant());
+    static void setCoreValue(const QString &user, const QString &key, const QVariant &value);
+    static QVariant coreValue (const QString &user, const QString& key, const QVariant &defaultValue = QVariant());
+
+  private:
+    static QString curProfile;
+
+};
+
+//extern Settings *settings;
+
+#endif
index 5fa7052..6c4ef44 100644 (file)
@@ -63,7 +63,9 @@ Q_DECLARE_INTERFACE(CorePluginInterface,
  */
 class SettingsInterface {
   public:
  */
 class SettingsInterface {
   public:
-    virtual QWidget *settingsWidget(QWidget *parent = 0) = 0;
+    virtual QString category() = 0;
+    virtual QString title() = 0; 
+    virtual QWidget *settingsWidget() = 0;
 
 };
 
 
 };