From 117a8a4d7ced61a3e374f20c74bea1834386a1d7 Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Sun, 29 Oct 2006 21:17:38 +0000 Subject: [PATCH] OK, another update. This is just prior to redoing the MainWin completely. I have already switched to a designer-generated .ui rather and thrown out the hand-written stuff. Next couple of days will hopefully show the first few of Haui's ideas functional. In other news: backlog generation is implemented now. The GUI does not sync the backlog yet, but coreside files are being generated and saved in $HOME/.quassel/backlog. I expect the format not to change in the foreseeable future, so that at some point in the future, you'll be able to scroll up your buffers and find everything you did starting from now ;-) NOTE: Core/GUI mode has not been tested in a while. Though I don't think I have broken stuff already, I might be wrong there. Run cmake with -DBUILD="mono" to build the monolithic version, which should work quite fine. --- CMakeLists.txt | 1 + core/core.cpp | 86 ++++++++++++++++++++++++-- core/core.h | 9 +++ gui/CMakeLists.txt | 2 +- gui/channelwidget.cpp | 8 +-- gui/mainwin.cpp | 40 ++---------- gui/mainwin.h | 13 ++-- gui/mainwin.ui | 141 ++++++++++++++++++++++++++++++++++++++++++ gui/networkwidget.ui | 29 +++++++++ gui/serverlist.cpp | 1 - gui/settingsdlg.ui | 130 ++++++++++++++++++++++++++++++++++++++ main/global.cpp | 1 + main/global.h | 4 ++ main/main_core.cpp | 2 + main/main_gui.cpp | 2 + main/main_mono.cpp | 2 + main/message.cpp | 7 ++- network/server.cpp | 23 +++++-- 18 files changed, 439 insertions(+), 62 deletions(-) create mode 100644 gui/mainwin.ui create mode 100644 gui/networkwidget.ui create mode 100644 gui/settingsdlg.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 49bea2de..589e657d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ FOREACH(dir ${quassel_DIRS}) SET(SDIRS ${SDIRS} "${CMAKE_CURRENT_SOURCE_DIR}/${dir}") ENDFOREACH(dir) INCLUDE_DIRECTORIES(${SDIRS}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) # We need Qt4 support. SET(QT_MIN_VERSION "4.2.0") diff --git a/core/core.cpp b/core/core.cpp index 9ccbd211..f9f4f23a 100644 --- a/core/core.cpp +++ b/core/core.cpp @@ -40,6 +40,7 @@ Core::Core() { foreach(key, s.childKeys()) { global->updateData(key, s.value(key)); } + initBackLog(); 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. @@ -85,16 +86,89 @@ void Core::connectToIrc(QStringList networks) { // ALL messages coming pass through these functions before going to the GUI. // So this is the perfect place for storing the backlog and log stuff. void Core::recvMessageFromServer(QString buf, Message msg) { - Q_ASSERT(sender()); - QString net = qobject_cast(sender())->getNetwork(); - emit displayMsg(net, buf, msg); + Server *s = qobject_cast(sender()); + Q_ASSERT(s); + logMessage(msg); + emit displayMsg(s->getNetwork(), buf, msg); } void Core::recvStatusMsgFromServer(QString msg) { - Q_ASSERT(sender()); - QString net = qobject_cast(sender())->getNetwork(); - emit displayStatusMsg(net, msg); + Server *s = qobject_cast(sender()); + Q_ASSERT(s); + emit displayStatusMsg(s->getNetwork(), msg); } +// file name scheme: quassel-backlog-2006-29-10.bin +void Core::initBackLog() { + backLogDir = QDir(Global::quasselDir + "/backlog"); + 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; + } + } + backLogDir.refresh(); + //if(!backLogDir.isReadable()) { + // qWarning(QString("Cannot read directory \"%1\". Disabling logging...").arg(backLogDir.absolutePath()).toAscii()); + // backLogEnabled = false; + // return; + //} + QStringList logs = backLogDir.entryList(QStringList("quassel-backlog-*.bin"), QDir::Files|QDir::Readable, QDir::Name); + foreach(QString name, logs) { + QFile f(backLogDir.absolutePath() + "/" + name); + if(!f.open(QIODevice::ReadOnly)) { + qWarning(QString("Could not open \"%1\" for reading!").arg(f.fileName()).toAscii()); + continue; + } + QDataStream in(&f); + in.setVersion(QDataStream::Qt_4_2); + QByteArray verstring; quint8 vernum; in >> verstring >> vernum; + if(verstring != BACKLOG_STRING) { + qWarning(QString("\"%1\" is not a Quassel backlog file!").arg(f.fileName()).toAscii()); + f.close(); continue; + } + if(vernum != BACKLOG_FORMAT) { + qWarning(QString("\"%1\": Version mismatch!").arg(f.fileName()).toAscii()); + f.close(); continue; + } + qDebug() << "Reading backlog from" << f.fileName(); + currentLogFileDate = QDate::fromString(f.fileName(), QString("'%1/quassel-backlog-'yyyy-MM-dd'.bin'").arg(backLogDir.absolutePath())); + if(!currentLogFileDate.isValid()) { + qWarning(QString("\"%1\" has an invalid file name!").arg(f.fileName()).toAscii()); + } + while(!in.atEnd()) { + Message m; + in >> m; + backLog.append(m); + } + f.close(); + } + backLogEnabled = true; +} + +/** 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(Message msg) { + backLog.append(msg); + if(!currentLogFileDate.isValid() || currentLogFileDate < QDate::currentDate()) { + if(currentLogFile.isOpen()) currentLogFile.close(); + currentLogFileDate = QDate::currentDate(); + } + if(!currentLogFile.isOpen()) { + currentLogFile.setFileName(backLogDir.absolutePath() + "/" + currentLogFileDate.toString("'quassel-backlog-'yyyy-MM-dd'.bin'")); + if(!currentLogFile.open(QIODevice::WriteOnly|QIODevice::Append)) { + qWarning(QString("Could not open \"%1\" for writing: %2").arg(currentLogFile.fileName()).arg(currentLogFile.errorString()).toAscii()); + return; + } + logStream.setDevice(¤tLogFile); logStream.setVersion(QDataStream::Qt_4_2); + if(!currentLogFile.size()) logStream << BACKLOG_STRING << (quint8)BACKLOG_FORMAT; + } + logStream << msg; +} Core *core = 0; diff --git a/core/core.h b/core/core.h index 6b7a0aec..f5532c3b 100644 --- a/core/core.h +++ b/core/core.h @@ -53,6 +53,15 @@ class Core : public QObject { private: QHash servers; + QList backLog; + bool backLogEnabled; + QDir backLogDir; + QFile currentLogFile; + QDataStream logStream; + QDate currentLogFileDate; + + void initBackLog(); + void logMessage(Message); }; diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index abe989b2..997c656b 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -2,7 +2,7 @@ SET(gui_SRCS channelwidget.cpp channelwidgetinput.cpp mainwin.cpp serverlist.cpp identities.cpp coreconnectdlg.cpp guiproxy.cpp) SET(gui_HDRS ) SET(gui_MOCS channelwidget.h channelwidgetinput.h mainwin.h serverlist.h identities.h coreconnectdlg.h guiproxy.h) -SET(gui_UICS channelwidget.ui identitiesdlg.ui identitieseditdlg.ui networkeditdlg.ui +SET(gui_UICS channelwidget.ui identitiesdlg.ui identitieseditdlg.ui networkeditdlg.ui mainwin.ui nickeditdlg.ui serverlistdlg.ui servereditdlg.ui coreconnectdlg.ui ircwidget.ui) QT4_WRAP_UI(_UIC ${gui_UICS}) diff --git a/gui/channelwidget.cpp b/gui/channelwidget.cpp index 84e81b1d..8aeb5926 100644 --- a/gui/channelwidget.cpp +++ b/gui/channelwidget.cpp @@ -115,8 +115,8 @@ void ChannelWidget::recvMessage(Message msg) { break; case Message::Mode: c = serverCol; - if(nick.isEmpty()) s = tr("* User mode: %1").arg(msg.msg); - else s = tr("* Mode %1 by %2").arg(msg.msg).arg(nick); + if(nick.isEmpty()) s = tr("*** User mode: %1").arg(msg.msg); + else s = tr("*** Mode %1 by %2").arg(msg.msg).arg(nick); break; default: c = stdCol; n = QString("[%1]").arg(msg.sender); s = msg.msg; @@ -129,8 +129,8 @@ void ChannelWidget::recvMessage(Message msg) { if(!n.isEmpty()) html += QString("
%1
") .arg(n).arg("royalblue"); - html += QString("
%1
""").arg(s).arg(c); - ui.chatWidget->append(html); + html += QString("
%1
""").arg(s).arg(c); + ui.chatWidget->append(html); // qDebug() << html; //ui.chatWidget->append(QString("
%1
%2
 %3
") //.arg(msg.timeStamp.toLocalTime().toString("hh:mm:ss")).arg(nick).arg(s)); ui.chatWidget->ensureCursorVisible(); diff --git a/gui/mainwin.cpp b/gui/mainwin.cpp index 3fefe4d7..8a21a9d3 100644 --- a/gui/mainwin.cpp +++ b/gui/mainwin.cpp @@ -29,6 +29,7 @@ #include "coreconnectdlg.h" MainWin::MainWin() : QMainWindow() { + ui.setupUi(this); setWindowTitle("Quassel IRC"); setWindowIcon(QIcon(":/qirc-icon.png")); @@ -60,6 +61,11 @@ MainWin::MainWin() : QMainWindow() { cw->setFocus(); } +void MainWin::setupMenus() { + connect(ui.actionNetworkList, SIGNAL(activated()), this, SLOT(showServerList())); + connect(ui.actionEditIdentities, SIGNAL(activated()), serverListDlg, SLOT(editIdentities())); +} + void MainWin::syncToCore() { if(global->getData("CoreReady").toBool()) return; // ok, apparently we are running as standalone GUI @@ -81,40 +87,6 @@ void MainWin::syncToCore() { } } -void MainWin::setupMenus() { - fileMenu = menuBar()->addMenu(tr("&File")); - serverListAct = fileMenu->addAction(QIcon(":/default/server.png"), tr("&Server List..."), this, SLOT(showServerList()), tr("F7")); - fileMenu->addSeparator(); - quitAct = fileMenu->addAction(QIcon(":/default/exit.png"), tr("&Quit"), qApp, SLOT(quit()), tr("CTRL+Q")); - - editMenu = menuBar()->addMenu(tr("&Edit")); - editMenu->setEnabled(0); - - ircMenu = menuBar()->addMenu(tr("&IRC")); - ircMenu->setEnabled(0); - - serverMenu = menuBar()->addMenu(tr("Ser&ver")); - serverMenu->setEnabled(0); - - windowMenu = menuBar()->addMenu(tr("&Window")); - windowMenu->setEnabled(0); - - settingsMenu = menuBar()->addMenu(tr("&Settings")); - identitiesAct = settingsMenu->addAction(QIcon(":/default/identity.png"), tr("&Identities..."), serverListDlg, SLOT(editIdentities())); - settingsMenu->addSeparator(); - configAct = settingsMenu->addAction(QIcon(":/default/configure.png"), tr("&Configure Quassel...")); - configAct->setEnabled(0); - - helpMenu = menuBar()->addMenu(tr("&Help")); - aboutAct = helpMenu->addAction(tr("&About")); - aboutAct->setEnabled(0); - aboutQtAct = helpMenu->addAction(tr("About &Qt"), qApp, SLOT(aboutQt())); - - //toolBar = new QToolBar("Test", this); - //toolBar->addAction(identitiesAct); - //addToolBar(Qt::TopToolBarArea, toolBar); -} - void MainWin::showServerList() { // if(!serverListDlg) { // serverListDlg = new ServerListDlg(this); diff --git a/gui/mainwin.h b/gui/mainwin.h index 9d7770f3..a6d1e1ec 100644 --- a/gui/mainwin.h +++ b/gui/mainwin.h @@ -21,10 +21,8 @@ #ifndef _MAINWIN_H_ #define _MAINWIN_H_ -#include - -class QMenu; -class QWorkspace; +#include +#include "gui/ui_mainwin.h" class ServerListDlg; class CoreConnectDlg; @@ -42,15 +40,12 @@ class MainWin : public QMainWindow { void showServerList(); private: + Ui::MainWin ui; + void setupMenus(); void syncToCore(); QWorkspace *workspace; - QToolBar *toolBar; - QMenu *fileMenu, *editMenu, *ircMenu, *serverMenu, *windowMenu, *helpMenu, *settingsMenu; - QAction *quitAct, *serverListAct; - QAction *aboutAct, *aboutQtAct; - QAction *identitiesAct, *configAct; ServerListDlg *serverListDlg; CoreConnectDlg *coreConnectDlg; diff --git a/gui/mainwin.ui b/gui/mainwin.ui new file mode 100644 index 00000000..169d454d --- /dev/null +++ b/gui/mainwin.ui @@ -0,0 +1,141 @@ + + MainWin + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + 0 + 0 + 800 + 29 + + + + + Views + + + + + Help + + + + + Connection + + + + + + + + + + + + + + + + Settings + + + + + + + + + + + + &Network List... + + + F2 + + + + + false + + + Quick &Connect... + + + + + false + + + Disconnect + + + + + false + + + Reconnect + + + + + false + + + Join Channel... + + + + + false + + + Set Away globally + + + + + Quit... + + + + + Edit Identities... + + + + + + + actionQuit + activated() + MainWin + close() + + + -1 + -1 + + + 399 + 299 + + + + + diff --git a/gui/networkwidget.ui b/gui/networkwidget.ui new file mode 100644 index 00000000..65f20287 --- /dev/null +++ b/gui/networkwidget.ui @@ -0,0 +1,29 @@ + + NetworkWidget + + + + 0 + 0 + 236 + 358 + + + + Form + + + + 9 + + + 6 + + + + + + + + + diff --git a/gui/serverlist.cpp b/gui/serverlist.cpp index 39901cc5..146203eb 100644 --- a/gui/serverlist.cpp +++ b/gui/serverlist.cpp @@ -50,7 +50,6 @@ ServerListDlg::ServerListDlg(QWidget *parent) : QDialog(parent) { list << net; } } - qDebug() << "Autoconnect:"< + SettingsDialog + + + + 0 + 0 + 700 + 577 + + + + Settings + + + + 9 + + + 6 + + + + + + 4 + 7 + 1 + 0 + + + + + Settings + + + + + General + + + + + Appearance + + + + + Connection + + + + + Plugins + + + + + + + + 0 + + + 6 + + + + + + 5 + 5 + 4 + 0 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults + + + + + + + + + + + buttonBox + accepted() + SettingsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SettingsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/main/global.cpp b/main/global.cpp index 962ac41c..4121806f 100644 --- a/main/global.cpp +++ b/main/global.cpp @@ -110,3 +110,4 @@ void Global::initIconMap() { Global *global = 0; Global::RunMode Global::runMode; +QString Global::quasselDir; diff --git a/main/global.h b/main/global.h index ad4cb1db..1faf4d18 100644 --- a/main/global.h +++ b/main/global.h @@ -24,6 +24,9 @@ /** The protocol version we use fo the communication between core and GUI */ #define GUI_PROTOCOL 1 +#define BACKLOG_FORMAT 1 +#define BACKLOG_STRING "QuasselIRC Backlog File" + class Global; #include @@ -67,6 +70,7 @@ class Global : public QObject { public: enum RunMode { Monolithic, GUIOnly, CoreOnly }; static RunMode runMode; + static QString quasselDir; private: static void initIconMap(); diff --git a/main/main_core.cpp b/main/main_core.cpp index 740482b5..b7f0a8e6 100644 --- a/main/main_core.cpp +++ b/main/main_core.cpp @@ -36,6 +36,8 @@ int main(int argc, char **argv) { QCoreApplication::setOrganizationName("The Quassel Team"); Global::runMode = Global::CoreOnly; + Global::quasselDir = QDir::homePath() + "/.quassel"; + global = new Global(); coreProxy = new CoreProxy(); core = new Core(); diff --git a/main/main_gui.cpp b/main/main_gui.cpp index 31f30e65..7dc2d53f 100644 --- a/main/main_gui.cpp +++ b/main/main_gui.cpp @@ -35,6 +35,8 @@ int main(int argc, char **argv) { QApplication::setOrganizationName("The Quassel Team"); Global::runMode = Global::GUIOnly; + Global::quasselDir = QDir::homePath() + "/.quassel"; + global = new Global(); guiProxy = new GUIProxy(); diff --git a/main/main_mono.cpp b/main/main_mono.cpp index 6bd0f336..7700b27f 100644 --- a/main/main_mono.cpp +++ b/main/main_mono.cpp @@ -36,6 +36,8 @@ int main(int argc, char **argv) { QApplication::setOrganizationName("The Quassel Team"); Global::runMode = Global::Monolithic; + Global::quasselDir = QDir::homePath() + "/.quassel"; + global = new Global(); guiProxy = new GUIProxy(); coreProxy = new CoreProxy(); diff --git a/main/message.cpp b/main/message.cpp index 53b81702..e19e74c5 100644 --- a/main/message.cpp +++ b/main/message.cpp @@ -22,16 +22,19 @@ #include QDataStream &operator<<(QDataStream &out, const Message &msg) { - out << (quint32)msg.timeStamp.toTime_t() << (quint8)msg.type << (quint8)msg.flags << msg.sender << msg.msg; + out << (quint32)msg.timeStamp.toTime_t() << (quint8)msg.type << (quint8)msg.flags << msg.sender.toUtf8() << msg.msg.toUtf8(); return out; } QDataStream &operator>>(QDataStream &in, Message &msg) { quint8 t, f; quint32 ts; - in >> ts >> t >> f >> msg.sender >> msg.msg; + QByteArray s, m; + in >> ts >> t >> f >> s >> m; msg.type = (Message::Type)t; msg.flags = (Message::Flags)f; msg.timeStamp = QDateTime::fromTime_t(ts); + msg.sender = QString::fromUtf8(s); + msg.msg = QString::fromUtf8(m); return in; } diff --git a/network/server.cpp b/network/server.cpp index 7e0d7490..c27117e8 100644 --- a/network/server.cpp +++ b/network/server.cpp @@ -109,7 +109,7 @@ QString Server::updateNickFromMask(QString mask) { void Server::userInput(QString net, QString buf, QString msg) { if(net != network) return; // not me! - msg = msg.trimmed(); // remove whitespace from start and end + //msg = msg.trimmed(); // remove whitespace from start and end if(msg.isEmpty()) return; if(!msg.startsWith('/')) { msg = QString("/SAY ") + msg; @@ -131,7 +131,7 @@ void Server::putCmd(QString cmd, QStringList params, QString prefix) { m += " " + params[i]; } if(!params.isEmpty()) m += " :" + params.last(); - qDebug() << "SentCmd: " << m; + qDebug() << "Sent: " << m; m += "\r\n"; socket.write(m.toAscii()); } @@ -198,12 +198,24 @@ void Server::defaultServerHandler(QString cmd, QString prefix, QStringList param case 2: case 3: case 4: case 5: case 251: case 252: case 253: case 254: case 255: case 372: case 375: emit displayMsg("", Message(Message::Server, params.join(" "), prefix)); break; + // Server error messages without param, just display them + case 409: case 411: case 412: case 422: case 424: case 431: case 445: case 446: case 451: case 462: + case 463: case 464: case 465: case 466: case 472: case 481: case 483: case 485: case 491: case 501: case 502: + emit displayMsg("", Message(Message::Error, params.join(" "), prefix)); + break; // Server error messages, display them in red. First param will be appended. - case 401: case 402: case 403: case 404: + case 401: case 402: case 403: case 404: case 406: case 408: case 415: case 421: case 432: case 442: { QString p = params.takeFirst(); emit displayMsg("", Message(Message::Error, params.join(" ") + " " + p, prefix)); break; } + // Server error messages which will be displayed with a colon between the first param and the rest + case 413: case 414: case 423: case 433: case 436: case 441: case 444: case 461: + case 467: case 471: case 473: case 474: case 475: case 476: case 477: case 478: case 482: + { QString p = params.takeFirst(); + emit displayMsg("", Message(Message::Error, p + ": " + params.join(" "))); + break; + } // Ignore these commands. case 366: case 376: break; @@ -229,7 +241,7 @@ void Server::handleUserInput(QString bufname, QString usrMsg) { } */ QString cmd = usrMsg.section(' ', 0, 0).remove(0, 1).toUpper(); - QString msg = usrMsg.section(' ', 1).trimmed(); + QString msg = usrMsg.section(' ', 1); QString hname = cmd.toLower(); hname[0] = hname[0].toUpper(); hname = "handleUser" + hname; @@ -302,7 +314,7 @@ void Server::handleUserMode(QString bufname, QString msg) { void Server::handleUserMsg(QString bufname, QString msg) { QString nick = msg.section(" ", 0, 0); - msg = msg.section(" ", 1).trimmed(); + msg = msg.section(" ", 1); if(nick.isEmpty() || msg.isEmpty()) return; QStringList params; params << nick << msg; @@ -543,6 +555,7 @@ void Server::handleServer005(QString prefix, QStringList params) { /* RPL_NOTOPIC */ void Server::handleServer331(QString prefix, QStringList params) { emit topicSet(network, params[0], ""); + emit displayMsg(params[0], Message(Message::Server, tr("No topic is set for %1.").arg(params[0]))); } /* RPL_TOPIC */ -- 2.20.1