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")
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.
// 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<Server*>(sender())->getNetwork();
- emit displayMsg(net, buf, msg);
+ Server *s = qobject_cast<Server*>(sender());
+ Q_ASSERT(s);
+ logMessage(msg);
+ emit displayMsg(s->getNetwork(), buf, msg);
}
void Core::recvStatusMsgFromServer(QString msg) {
- Q_ASSERT(sender());
- QString net = qobject_cast<Server*>(sender())->getNetwork();
- emit displayStatusMsg(net, msg);
+ Server *s = qobject_cast<Server*>(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;
private:
QHash<QString, Server *> servers;
+ QList<Message> backLog;
+ bool backLogEnabled;
+ QDir backLogDir;
+ QFile currentLogFile;
+ QDataStream logStream;
+ QDate currentLogFileDate;
+
+ void initBackLog();
+ void logMessage(Message);
};
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})
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;
if(!n.isEmpty())
html += QString("<td width=150><div align=right style=\"white-space:pre;margin-left:6px;color:%2;\">%1</div></td>")
.arg(n).arg("royalblue");
- html += QString("<td><div style=\"margin-left:6px;color:%2;\">%1</div></td>""</tr></table>").arg(s).arg(c);
- ui.chatWidget->append(html);
+ html += QString("<td><div style=\"white-space:pre-wrap;margin-left:6px;color:%2;\">%1</div></td>""</tr></table>").arg(s).arg(c);
+ ui.chatWidget->append(html); // qDebug() << html;
//ui.chatWidget->append(QString("<table border=1 cellspacing=0 cellpadding=0><tr><td>%1</td><td width=100 style=border-right-width:1px;><div style=margin-left:8px; margin-right:8px;>%2</div></td><td style=color:firebrick> %3</td></tr></table>")
//.arg(msg.timeStamp.toLocalTime().toString("hh:mm:ss")).arg(nick).arg(s));
ui.chatWidget->ensureCursorVisible();
#include "coreconnectdlg.h"
MainWin::MainWin() : QMainWindow() {
+ ui.setupUi(this);
setWindowTitle("Quassel IRC");
setWindowIcon(QIcon(":/qirc-icon.png"));
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
}
}
-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);
#ifndef _MAINWIN_H_
#define _MAINWIN_H_
-#include <QMainWindow>
-
-class QMenu;
-class QWorkspace;
+#include <QtGui>
+#include "gui/ui_mainwin.h"
class ServerListDlg;
class CoreConnectDlg;
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;
--- /dev/null
+<ui version="4.0" >
+ <class>MainWin</class>
+ <widget class="QMainWindow" name="MainWin" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralwidget" />
+ <widget class="QMenuBar" name="menubar" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>29</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menuViews" >
+ <property name="title" >
+ <string>Views</string>
+ </property>
+ </widget>
+ <widget class="QMenu" name="menuHelp" >
+ <property name="title" >
+ <string>Help</string>
+ </property>
+ </widget>
+ <widget class="QMenu" name="menuConnection" >
+ <property name="title" >
+ <string>Connection</string>
+ </property>
+ <addaction name="actionNetworkList" />
+ <addaction name="actionQuickConnect" />
+ <addaction name="separator" />
+ <addaction name="actionDisconnect" />
+ <addaction name="actionReconnect" />
+ <addaction name="separator" />
+ <addaction name="actionJoinChannel" />
+ <addaction name="separator" />
+ <addaction name="actionSetAwayGlobally" />
+ <addaction name="separator" />
+ <addaction name="actionQuit" />
+ </widget>
+ <widget class="QMenu" name="menuSettings" >
+ <property name="title" >
+ <string>Settings</string>
+ </property>
+ <addaction name="actionEditIdentities" />
+ </widget>
+ <addaction name="menuConnection" />
+ <addaction name="menuSettings" />
+ <addaction name="menuViews" />
+ <addaction name="menuHelp" />
+ </widget>
+ <widget class="QStatusBar" name="statusbar" />
+ <action name="actionNetworkList" >
+ <property name="text" >
+ <string>&Network List...</string>
+ </property>
+ <property name="shortcut" >
+ <string>F2</string>
+ </property>
+ </action>
+ <action name="actionQuickConnect" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Quick &Connect...</string>
+ </property>
+ </action>
+ <action name="actionDisconnect" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Disconnect</string>
+ </property>
+ </action>
+ <action name="actionReconnect" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Reconnect</string>
+ </property>
+ </action>
+ <action name="actionJoinChannel" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Join Channel...</string>
+ </property>
+ </action>
+ <action name="actionSetAwayGlobally" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Set Away globally</string>
+ </property>
+ </action>
+ <action name="actionQuit" >
+ <property name="text" >
+ <string>Quit...</string>
+ </property>
+ </action>
+ <action name="actionEditIdentities" >
+ <property name="text" >
+ <string>Edit Identities...</string>
+ </property>
+ </action>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>actionQuit</sender>
+ <signal>activated()</signal>
+ <receiver>MainWin</receiver>
+ <slot>close()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>-1</x>
+ <y>-1</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>399</x>
+ <y>299</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
--- /dev/null
+<ui version="4.0" >
+ <class>NetworkWidget</class>
+ <widget class="QWidget" name="NetworkWidget" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>236</width>
+ <height>358</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTreeWidget" name="tree" />
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
list << net;
}
}
- qDebug() << "Autoconnect:"<<list;
if(!list.isEmpty()) emit requestConnect(list);
}
--- /dev/null
+<ui version="4.0" >
+ <class>SettingsDialog</class>
+ <widget class="QDialog" name="SettingsDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>700</width>
+ <height>577</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Settings</string>
+ </property>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTreeWidget" name="treeWidget" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <column>
+ <property name="text" >
+ <string>Settings</string>
+ </property>
+ </column>
+ <item>
+ <property name="text" >
+ <string>General</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Appearance</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Connection</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Plugins</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QStackedWidget" name="stackedWidget" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>4</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <widget class="QWidget" name="page" />
+ <widget class="QWidget" name="page_2" />
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>SettingsDialog</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>SettingsDialog</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>
Global *global = 0;
Global::RunMode Global::runMode;
+QString Global::quasselDir;
/** 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 <QtCore>
public:
enum RunMode { Monolithic, GUIOnly, CoreOnly };
static RunMode runMode;
+ static QString quasselDir;
private:
static void initIconMap();
QCoreApplication::setOrganizationName("The Quassel Team");
Global::runMode = Global::CoreOnly;
+ Global::quasselDir = QDir::homePath() + "/.quassel";
+
global = new Global();
coreProxy = new CoreProxy();
core = new Core();
QApplication::setOrganizationName("The Quassel Team");
Global::runMode = Global::GUIOnly;
+ Global::quasselDir = QDir::homePath() + "/.quassel";
+
global = new Global();
guiProxy = new GUIProxy();
QApplication::setOrganizationName("The Quassel Team");
Global::runMode = Global::Monolithic;
+ Global::quasselDir = QDir::homePath() + "/.quassel";
+
global = new Global();
guiProxy = new GUIProxy();
coreProxy = new CoreProxy();
#include <QDataStream>
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;
}
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;
m += " " + params[i];
}
if(!params.isEmpty()) m += " :" + params.last();
- qDebug() << "SentCmd: " << m;
+ qDebug() << "Sent: " << m;
m += "\r\n";
socket.write(m.toAscii());
}
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;
}
*/
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;
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;
/* 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 */