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!
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)
# 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})
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
#include "core.h"
#include "server.h"
#include "global.h"
+#include "util.h"
#include "coreproxy.h"
+#include <QtSql>
#include <QSettings>
Core::Core() {
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.
if(f->isOpen()) f->close();
delete f;
}
+ logDb.close();
}
void Core::globalDataUpdated(QString key) {
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)));
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 displayStatusMsg(s->getNetwork(), msg);
}
-// file name scheme: quassel-backlog-2006-29-10.bin
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());
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()) {
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.
*/
-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);
#include <QMap>
#include <QString>
#include <QVariant>
+#include <QSqlDatabase>
#include "server.h"
QHash<QString, QDate> logFileDates;
QHash<QString, QDir> logFileDirs;
+ QSqlDatabase logDb;
+
//uint getNetIdx(QString net);
void initBackLog();
+ void initBackLogOld();
void logMessage(QString, Message);
+ void logMessageOld(QString, Message);
};
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);
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);
}
void Server::putRawLine(QString s) {
- qDebug() << "SentRaw: " << s;
+// qDebug() << "SentRaw: " << s;
s += "\r\n";
socket.write(s.toAscii());
}
m += " " + params[i];
}
if(!params.isEmpty()) m += " :" + params.last();
- qDebug() << "Sent: " << m;
+// qDebug() << "Sent: " << m;
m += "\r\n";
socket.write(m.toAscii());
}
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));
}
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::handleServerPrivmsg(QString prefix, QStringList params) {
updateNickFromMask(prefix);
+ Q_ASSERT(params.count() >= 2);
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) {
// 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 */
+// 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);
/* 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 */
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:
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);
-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
- 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
- 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})
/***************************************************************************
- * 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 *
#include "buffer.h"
#include "util.h"
#include "chatwidget.h"
+#include "bufferwidget.h"
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;
+/*
+ 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() {
- 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) {
- topic = t;
- if(widget) widget->setTopic(t);
+ _topic = t;
+ emit topicSet(t);
+ emit bufferUpdated(this);
}
void Buffer::addNick(QString nick, VarMap props) {
- if(nick == ownNick) setActive(true);
+ if(nick == ownNick()) setActive(true);
nicks[nick] = props;
- if(widget) widget->updateNickList(nicks);
+ emit nickListChanged(nicks);
}
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;
- if(widget) widget->updateNickList(nicks);
+ emit nickListChanged(nicks);
}
void Buffer::removeNick(QString nick) {
- if(nick == ownNick) setActive(false);
+ if(nick == ownNick()) setActive(false);
nicks.remove(nick);
- if(widget) widget->updateNickList(nicks);
+ emit nickListChanged(nicks);
}
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();
-}
-
/***************************************************************************
- * 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 *
#include <QtCore>
#include <QtGui>
-#include "ui_bufferwidget.h"
-
#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
public:
Buffer(QString network, QString buffer);
~Buffer();
+ static void init();
+ enum Type { ServerBuffer, ChannelBuffer, QueryBuffer };
+ Type bufferType() { return type; }
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);
- 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);
+ void prependMessages(QList<Message>); // for backlog
//void recvStatusMsg(QString msg);
void setTopic(QString);
//void setNicks(QStringList);
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;
- 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;
};
--- /dev/null
+/***************************************************************************
+ * 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();
+}
+
--- /dev/null
+/***************************************************************************
+ * 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
event->accept();
} else if(event->key() == Qt::Key_Tab) {
// Tabcomplete
- if(cursorPosition() == text().length()) {
+ if(cursorPosition() == text().length()) {
}
#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);
- 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;
- //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() {
-
+ //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]);
- setFixedHeight((int)ycoords[ycoords.count()-1]);
+ height += h;
+ bool flg = (verticalScrollBar()->value() == verticalScrollBar()->maximum());
+ adjustScrollBar();
+ if(flg) verticalScrollBar()->setValue(verticalScrollBar()->maximum());
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.
-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;
- 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.
-/** 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];
}
- 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;
}
}
-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) {
- dragStartPos = event->pos();
+ dragStartPos = pos;
dragStartMode = Normal;
switch(mouseMode) {
case Normal:
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;
}
}
-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);
/** 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
- 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;
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:
- 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;
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;
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);
- update();
+ viewport()->update();
}
} 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);
- } 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;
- update();
+ //ensureVisible(l);
+ viewport()->update();
}
}
}
//!\brief Clear current text selection.
-void ChatWidgetContents::clearSelection() {
+void ChatWidget::clearSelection() {
if(selectionMode == TextSelected) {
lines[selectionLine]->setSelection(ChatLine::None);
} else if(selectionMode == LinesSelected) {
}
}
selectionMode = NoSelection;
- update();
+ viewport()->update();
}
//!\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) {
//!\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
*/
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:
- 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:
- 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:
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(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***");
- 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);
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;
- 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:
+ tsFormat = calcFormatRanges(tsFormatted);
+ senderFormat = calcFormatRanges(senderFormatted);
+ textFormat = calcFormatRanges(textFormatted);
break;
case Partial:
selectionStart = qMin(start, end); selectionEnd = qMax(start, end);
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));
- 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.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.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;
}
}
}
QString ChatLine::getSender() {
- return senderLayout.text();
+ return senderFormatted.text;
}
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;
- 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;
- 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();
+
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, 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);
+ }
}
#include "style.h"
#include "message.h"
+#include "buffer.h"
+#include <QtCore>
#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,
* 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();
- 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();
+ void prependChatLines(QList<ChatLine *>);
void appendMsg(Message);
- void setWidth(qreal);
+ void appendMsgList(QList<Message> *);
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 mouseDoubleClickEvent(QMouseEvent *event);
private slots:
- void layout(bool timer = false);
- void triggerLayout();
+ void layout();
+ void scrollBarAction(int);
+ void scrollBarValChanged(int);
+ void ensureVisible(int line);
+ void handleScrollTimer();
private:
+ QString networkName, bufferName;
enum SelectionMode { NoSelection, TextSelected, LinesSelected };
enum MouseMode { Normal, Pressed, DragTsSep, DragTextSep, MarkText, MarkLines };
enum MousePos { None, OverTsSep, OverTextSep, OverUrl };
int curLine;
SelectionMode selectionMode;
int selectionStart, selectionEnd, selectionLine;
- QString networkName, bufferName;
- QTimer *layoutTimer;
- bool doLayout;
+
+ int bottomLine, bottomLineOffset;
QList<ChatLine *> lines;
QList<qreal> ycoords;
+ QTimer *scrollTimer;
+ QPoint pointerPosition;
int senderX;
int textX;
int senderGrabPos;
void computePositions();
- qreal width;
+ int width;
qreal height;
qreal y;
+ void adjustScrollBar();
+
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
QDateTime getTimeStamp();
QString getSender();
QString getText();
+
+ bool isUrl(int pos);
+ QUrl getUrl(int pos);
+
+ public slots:
+
private:
qreal hght;
Message msg;
QString networkName, bufferName;
- //QString ts, nick, text;
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);
+ 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
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;
}
void csNickRenamed(QString, QString, QString);
void csNickUpdated(QString, QString, VarMap);
void csOwnNickSet(QString, QString);
+ void csQueryRequested(QString, QString);
void coreConnected();
void coreDisconnected();
/***************************************************************************
- * 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 *
#include <QtGui>
#include <QtCore>
+#include <QSqlDatabase>
+#include "util.h"
#include "global.h"
#include "message.h"
#include "guiproxy.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);
- widget = 0;
-
+ //widget = 0;
+ qDebug() << "Available DB drivers: " << QSqlDatabase::drivers ();
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..."));
+}
- 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(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."));
-
- buffersUpdated();
+ systray = new QSystemTrayIcon(this);
+ systray->setIcon(QIcon(":/qirc-icon.png"));
+ systray->show();
serverListDlg = new ServerListDlg(this);
serverListDlg->setVisible(serverListDlg->showOnStartup());
- settingsDlg = new SettingsDlg(this);
- settingsDlg->setVisible(false);
+
+ setupSettingsDlg();
+
+ //Buffer::init();
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
+ // FIXME do this right
+ QHash<Buffer *, QList<Message> > hash;
+ Buffer *b;
+
foreach(QString net, coreBackLog.keys()) {
+ //if(net != "MoepNet") continue;
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);
widget->show();
}
}
- */
+*/
+
s.beginGroup("Buffers");
QString net = s.value("CurrentNetwork", "").toString();
QString buf = s.value("CurrentBuffer", "").toString();
}
}
+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()));
- 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::closeEvent(QCloseEvent *event)
{
//if (userReallyWantsToQuit()) {
+ ui.bufferWidget->saveState();
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();
+ delete systray;
event->accept();
//} else {
//event->ignore();
}
void MainWin::showBuffer(QString net, QString buf) {
- currentBuffer = buf; currentNetwork = net;
- Buffer *b = getBuffer(net, buf);
- QWidget *old = widget;
- widget = b->showWidget(this);
- if(widget == old) return;
- //workspace->addWindow(widget);
- //widget->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) {
Buffer *b = getBuffer(net, "");
b->setActive(true);
b->displayMsg(Message::server("", tr("Connected.")));
- buffersUpdated();
+ // TODO buffersUpdated();
}
void MainWin::networkDisconnected(QString net) {
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;
- buffersUpdated();
+ emit bufferUpdated(b);
}
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());
foreach(QString nick, n.keys()) {
addNick(net, nick, n[nick].toMap());
}
- buffersUpdated();
}
void MainWin::recvMessage(QString net, Message msg) {
- Buffer *b = getBuffer(net, msg.target);
+ 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);
}
if(!connected[net]) return;
Buffer *b = getBuffer(net, buf);
b->setTopic(topic);
- buffersUpdated();
//if(!b->isActive()) {
// b->setActive(true);
// buffersUpdated();
foreach(QString bufname, c) {
getBuffer(net, bufname)->addNick(nick, props);
}
- buffersUpdated();
}
void MainWin::renameNick(QString net, QString oldnick, QString newnick) {
if(!newchans.contains(c)) getBuffer(net, c)->removeNick(nick);
}
nicks[net][nick] = props;
- buffersUpdated();
}
void MainWin::removeNick(QString net, QString nick) {
getBuffer(net, bufname)->removeNick(nick);
}
nicks[net].remove(nick);
- buffersUpdated();
}
void MainWin::setOwnNick(QString net, QString nick) {
/***************************************************************************
- * 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 *
#include "global.h"
#include "message.h"
+#include "chatwidget.h"
class ServerListDlg;
class CoreConnectDlg;
class NetworkView;
class Buffer;
+class BufferWidget;
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
public:
MainWin();
+ ~MainWin();
+
+ void init();
+ void registerNetView(NetworkView *);
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);
void showSettingsDlg();
void showBuffer(QString net, QString buf);
+ void showBuffer(Buffer *);
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 buffersUpdated();
+ //void buffersUpdated();
- QWorkspace *workspace;
- QWidget *widget;
+ QSystemTrayIcon *systray;
+ //QWorkspace *workspace;
+ //QWidget *widget;
+ //BufferWidget *bufferWidget;
ServerListDlg *serverListDlg;
CoreConnectDlg *coreConnectDlg;
QHash<QString, QString> ownNick;
QHash<QString, QList<Message> > coreBackLog;
- NetworkView *netView;
+ QList<NetworkView *> netViews;
};
#endif
/***************************************************************************
- * 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 *
NetworkViewWidget::NetworkViewWidget(QWidget *parent) : QWidget(parent) {
ui.setupUi(this);
+ //setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
}
+
QSize NetworkViewWidget::sizeHint() const {
return QSize(150,100);
/**************************************************************************/
-NetworkView::NetworkView(QString net, QWidget *parent) : network(net), QDockWidget(parent) {
- if(!net.isEmpty()) setWindowTitle(net);
- else setWindowTitle(tr("All Buffers"));
+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();
- tree->setSortingEnabled(false);
+ tree->header()->hide();
+ tree->setSortingEnabled(true);
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) {
- 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;
+ }
}
/***************************************************************************
- * 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 *
public:
NetworkViewWidget(QWidget *parent = 0);
+
QTreeWidget *tree() { return ui.tree; }
virtual QSize sizeHint () const;
- public slots:
-
-
signals:
void bufferSelected(QString net, QString buf);
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:
- void buffersUpdated(BufferHash);
- void selectBuffer(QString net, QString buf);
+ void bufferUpdated(Buffer *);
+ void bufferDestroyed(Buffer *);
+ void setBuffers(QList<Buffer *>);
+ void selectBuffer(Buffer *);
signals:
- void bufferSelected(QString net, QString buf);
+ void bufferSelected(Buffer *);
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;
+ bool shouldShow(Buffer *);
};
/***************************************************************************
- * 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 *
--- /dev/null
+/***************************************************************************
+ * 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()));
+
+}
* 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"
private:
Ui::SettingsDlg ui;
+ QWidget *currentWidget;
};
+/*
+class CoreSettingsPage : public QWidget, SettingsInterface {
+ Q_OBJECT
+
+ public:
+
+
+ private:
+ Ui::CoreSettingsPage ui;
+
+};
+*/
+
#endif
* 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.tree->headerItem()->setText(0, tr("Buffers"));
+ setEnabled(false);
}
+
+ConnectionSettingsPage::ConnectionSettingsPage() {
+ ui.setupUi(this);
+
+}
+
--- /dev/null
+/***************************************************************************
+ * 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
colors["15"] = QColor("silver");
QTextCharFormat def;
- //def.setFont(QFont("Lucida Mono"));
+ def.setFont(QFont("Verdana",9));
formats["default"] = def;
// %B - 0x02 - bold
formats["%Dj"] = join;
// %Dp - part
QTextCharFormat part;
- part.setForeground(QBrush("firebrick"));
+ part.setForeground(QBrush("indianred"));
formats["%Dp"] = part;
// %Dq - quit
QTextCharFormat quit;
- quit.setForeground(QBrush("firebrick"));
+ quit.setForeground(QBrush("indianred"));
formats["%Dq"] = quit;
// %Dk - kick
QTextCharFormat kick;
- kick.setForeground(QBrush("firebrick"));
+ kick.setForeground(QBrush("indianred"));
formats["%Dk"] = kick;
// %Dr - nick rename
QTextCharFormat nren;
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) {
* 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;
- 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;
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;
/***************************************************************************
- * 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 *
public:
static void init();
- struct StringFormats {
+ struct UrlInfo {
+ int start, end;
+ QUrl url;
+ };
+
+ struct FormattedString {
QString text;
QList<QTextLayout::FormatRange> formats;
+ QList<UrlInfo> urls;
};
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; }
--- /dev/null
+<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><html><head><meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:400; font-style:normal; text-decoration:none;">
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Quassel IRC </span>(pre-release)</p></body></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>
--- /dev/null
+<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><html><head><meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:400; font-style:normal; text-decoration:none;">
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Buffer Management</span></p></body></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><html><head><meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:400; font-style:normal; text-decoration:none;">
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Warning:</span> This affects the global message storage (backlog) for the selected buffer!</p></body></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>
<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>
- <hsizetype>3</hsizetype>
- <vsizetype>3</vsizetype>
- <horstretch>0</horstretch>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>5</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
- <number>9</number>
+ <number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QLineEdit" name="topicEdit" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
<property name="focusPolicy" >
<enum>Qt::ClickFocus</enum>
</property>
</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>
- <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>
- <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 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><html><head><meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Trebuchet MS'; font-size:11pt; font-weight:400; font-style:normal; text-decoration:none;">
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:60pt; color:#000055">Quassel IRC</span></p></body></html></string>
+ </property>
+ <property name="scaledContents" >
+ <bool>true</bool>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</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>
</layout>
</widget>
<customwidgets>
- <customwidget>
- <class>ChatWidget</class>
- <extends>QWidget</extends>
- <header>chatwidget.h</header>
- </customwidget>
<customwidget>
<class>ChannelWidgetInput</class>
<extends>QLineEdit</extends>
<tabstops>
<tabstop>inputEdit</tabstop>
<tabstop>ownNick</tabstop>
- <tabstop>nickTree</tabstop>
<tabstop>topicEdit</tabstop>
<tabstop>chanSettingsButton</tabstop>
</tabstops>
--- /dev/null
+<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>
--- /dev/null
+<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>
<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>
- <height>32</height>
+ <height>28</height>
</rect>
</property>
<widget class="QMenu" name="menuConnection" >
<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>
<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>
+ <addaction name="actionAboutQuassel" />
+ <addaction name="actionAboutQt" />
</widget>
<addaction name="menuConnection" />
<addaction name="menuViews" />
<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>
+ <customwidgets>
+ <customwidget>
+ <class>BufferWidget</class>
+ <extends>QWidget</extends>
+ <header>bufferwidget.h</header>
+ </customwidget>
+ </customwidgets>
<resources/>
<connections>
<connection>
<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>
- <vsizetype>1</vsizetype>
+ <vsizetype>5</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="minimumSize" >
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
<property name="windowTitle" >
- <string>Form</string>
+ <string>NetView</string>
</property>
- <layout class="QHBoxLayout" >
+ <layout class="QVBoxLayout" >
<property name="margin" >
- <number>9</number>
+ <number>4</number>
</property>
<property name="spacing" >
- <number>6</number>
+ <number>0</number>
</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>
<height>16777215</height>
</size>
</property>
+ <property name="rootIsDecorated" >
+ <bool>false</bool>
+ </property>
</widget>
</item>
</layout>
<rect>
<x>0</x>
<y>0</y>
- <width>848</width>
- <height>514</height>
+ <width>808</width>
+ <height>568</height>
</rect>
</property>
<property name="windowTitle" >
<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>
- <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 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>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>QScrollArea</class>
+ <extends>QFrame</extends>
+ <header>QScrollArea</header>
+ </customwidget>
+ </customwidgets>
<resources/>
<connections>
<connection>
-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})
#include "guiproxy.h"
#include "coreconnectdlg.h"
#include "util.h"
+#include "chatwidget.h"
#include "mainwin.h"
QApplication::setApplicationName("Quassel IRC");
QApplication::setOrganizationName("The Quassel Team");
+ qRegisterMetaType<LayoutTask>("LayoutTask");
+
Global::runMode = Global::GUIOnly;
Global::quasselDir = QDir::homePath() + "/.quassel";
Style::init();
MainWin mainWin;
+ mainWin.init();
int exitCode = app.exec();
delete guiProxy;
delete global;
+ return exitCode;
}
void MainWin::syncToCore() {
#include "global.h"
#include "guiproxy.h"
#include "coreproxy.h"
+#include "settings.h"
+#include "chatwidget.h"
#include "mainwin.h"
QApplication::setApplicationName("Quassel IRC");
QApplication::setOrganizationName("The Quassel Team");
+ qRegisterMetaType<LayoutTask>("LayoutTask");
+
Global::runMode = Global::Monolithic;
Global::quasselDir = QDir::homePath() + "/.quassel";
+ //settings = new Settings();
global = new Global();
guiProxy = new GUIProxy();
coreProxy = new CoreProxy();
+ Settings::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;
+ delete mainWin;
+ //delete settings;
return exitCode;
}
#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);
}
-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);
}
-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);
}
-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);
}
-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);
}
-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);
}
-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);
}
-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);
}
-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);
}
-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);
}
-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);
}
-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);
}
-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);
}
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;
}
+
/** 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;
- Flags flags;
+ quint8 flags;
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(); }
- 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);
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
};
--- /dev/null
+/***************************************************************************
+ * 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;
--- /dev/null
+/***************************************************************************
+ * 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
*/
class SettingsInterface {
public:
- virtual QWidget *settingsWidget(QWidget *parent = 0) = 0;
+ virtual QString category() = 0;
+ virtual QString title() = 0;
+ virtual QWidget *settingsWidget() = 0;
};