X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fclient%2Fbuffertreemodel.cpp;h=865c4a45a41906573997ef47a8fe3cbbfe0cd0a9;hp=54ef7a876cec79d383380178714ecbc5c0134e59;hb=788fd0058595c815dc42597e9956c02aea45261f;hpb=0555b01cbb4789122de7bc0a5325c664fbed87cd diff --git a/src/client/buffertreemodel.cpp b/src/client/buffertreemodel.cpp index 54ef7a87..865c4a45 100644 --- a/src/client/buffertreemodel.cpp +++ b/src/client/buffertreemodel.cpp @@ -1,11 +1,11 @@ /*************************************************************************** - * Copyright (C) 2005-07 by The Quassel Team * + * Copyright (C) 2005-07 by the Quassel IRC 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. * + * (at your option) version 3. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * @@ -20,37 +20,42 @@ #include // FIXME Dependency on QtGui! -#include "clientproxy.h" #include "buffertreemodel.h" +#include "mappedselectionmodel.h" +#include + +#include "bufferinfo.h" +#include "client.h" +#include "signalproxy.h" +#include "networkinfo.h" +#include "ircchannel.h" +#include "ircuser.h" + /***************************************** * Fancy Buffer Items *****************************************/ -BufferTreeItem::BufferTreeItem(Buffer *buffer, TreeItem *parent) : TreeItem(parent) { - buf = buffer; - activity = Buffer::NoActivity; +BufferItem::BufferItem(Buffer *buffer, AbstractTreeItem *parent) + : PropertyMapItem(QStringList() << "bufferName" << "topic" << "nickCount", parent), + buf(buffer), + activity(Buffer::NoActivity) +{ + Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; + if(buf->bufferType() == Buffer::QueryType) + flags |= Qt::ItemIsDropEnabled; + setFlags(flags); } -uint BufferTreeItem::id() const { - return buf->bufferId().uid(); +quint64 BufferItem::id() const { + return buf->bufferInfo().uid(); } -void BufferTreeItem::setActivity(const Buffer::ActivityLevel &level) { +void BufferItem::setActivity(const Buffer::ActivityLevel &level) { activity = level; } -QString BufferTreeItem::text(int column) const { - switch(column) { - case 0: - return buf->displayName(); - case 1: - return buf->networkName(); - default: - return QString(); - } -} - -QColor BufferTreeItem::foreground(int /*column*/) const { +QColor BufferItem::foreground(int column) const { + Q_UNUSED(column) // for the time beeing we ignore the column :) if(activity & Buffer::Highlight) { return QColor(Qt::red); @@ -66,197 +71,436 @@ QColor BufferTreeItem::foreground(int /*column*/) const { } } - -QVariant BufferTreeItem::data(int column, int role) const { +QVariant BufferItem::data(int column, int role) const { switch(role) { - case Qt::DisplayRole: - return text(column); - case Qt::ForegroundRole: - return foreground(column); - case BufferTreeModel::BufferNameRole: - return buf->bufferName(); - case BufferTreeModel::BufferTypeRole: - return buf->bufferType(); - case BufferTreeModel::BufferActiveRole: - return buf->isActive(); - case BufferTreeModel::BufferIdRole: - return buf->bufferId().uid(); - default: - return QVariant(); + case NetworkModel::ItemTypeRole: + return NetworkModel::BufferItemType; + case NetworkModel::BufferUidRole: + return buf->bufferInfo().uid(); + case NetworkModel::NetworkIdRole: + return buf->bufferInfo().networkId(); + case NetworkModel::BufferTypeRole: + return int(buf->bufferType()); + case NetworkModel::BufferActiveRole: + return buf->isActive(); + case Qt::ForegroundRole: + return foreground(column); + default: + return PropertyMapItem::data(column, role); } } -Qt::ItemFlags BufferTreeItem::flags() const { - Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; - if(buf->bufferType() == Buffer::QueryBuffer) - flags |= Qt::ItemIsDropEnabled; +void BufferItem::attachIrcChannel(IrcChannel *ircChannel) { + if(!ircChannel) + return; + + _ircChannel = ircChannel; + + connect(ircChannel, SIGNAL(topicSet(QString)), + this, SLOT(setTopic(QString))); + connect(ircChannel, SIGNAL(ircUserJoined(IrcUser *)), + this, SLOT(join(IrcUser *))); + connect(ircChannel, SIGNAL(ircUserParted(IrcUser *)), + this, SLOT(part(IrcUser *))); +} - return flags; +QString BufferItem::bufferName() const { + return buf->name(); +} + +QString BufferItem::topic() const { + if(_ircChannel) + return _ircChannel->topic(); + else + return QString(); +} + +int BufferItem::nickCount() const { + if(_ircChannel) + return _ircChannel->ircUsers().count(); + else + return 0; +} + +void BufferItem::setTopic(const QString &topic) { + Q_UNUSED(topic); + emit dataChanged(1); +} + +void BufferItem::join(IrcUser *ircUser) { +// emit newChild(new IrcUserItem(ircUser, this)); +// emit dataChanged(2); +} + +void BufferItem::part(IrcUser *ircUser) { + Q_UNUSED(ircUser); + emit dataChanged(2); } /***************************************** * Network Items *****************************************/ -NetworkTreeItem::NetworkTreeItem(const QString &network, TreeItem *parent) : TreeItem(parent) { - net = network; - itemData << net << ""; +NetworkItem::NetworkItem(const uint &netid, const QString &network, AbstractTreeItem *parent) + : PropertyMapItem(QList() << "networkName" << "currentServer" << "nickCount", parent), + _networkId(netid), + _networkName(network) +{ + setFlags(Qt::ItemIsEnabled); +} + +QVariant NetworkItem::data(int column, int role) const { + switch(role) { + case NetworkModel::NetworkIdRole: + return _networkId; + case NetworkModel::ItemTypeRole: + return NetworkModel::NetworkItemType; + default: + return PropertyMapItem::data(column, role); + } +} + +quint64 NetworkItem::id() const { + return _networkId; +} + +QString NetworkItem::networkName() const { + if(_networkInfo) + return _networkInfo->networkName(); + else + return _networkName; +} + +QString NetworkItem::currentServer() const { + if(_networkInfo) + return _networkInfo->currentServer(); + else + return QString(); +} + +int NetworkItem::nickCount() const { + BufferItem *bufferItem; + int count = 0; + for(int i = 0; i < childCount(); i++) { + bufferItem = qobject_cast(child(i)); + if(!bufferItem) + continue; + count += bufferItem->nickCount(); + } + return count; +} + +void NetworkItem::attachNetworkInfo(NetworkInfo *networkInfo) { + if(!networkInfo) + return; + + _networkInfo = networkInfo; + + connect(networkInfo, SIGNAL(networkNameSet(QString)), + this, SLOT(setNetworkName(QString))); + connect(networkInfo, SIGNAL(currentServerSet(QString)), + this, SLOT(setCurrentServer(QString))); + connect(networkInfo, SIGNAL(ircChannelAdded(QString)), + this, SLOT(attachIrcChannel(QString))); + // FIXME: connect this and that... +} + +void NetworkItem::attachIrcChannel(const QString &channelName) { + IrcChannel *ircChannel = _networkInfo->ircChannel(channelName); + if(!ircChannel) { + qWarning() << "NetworkItem::attachIrcChannel(): unkown Channel" << channelName; + return; + } + + BufferItem *bufferItem; + for(int i = 0; i < childCount(); i++) { + bufferItem = qobject_cast(child(i)); + if(bufferItem->bufferName() == ircChannel->name()) { + bufferItem->attachIrcChannel(ircChannel); + break; + } + } +} + +void NetworkItem::setNetworkName(const QString &networkName) { + Q_UNUSED(networkName); + emit dataChanged(0); +} + +void NetworkItem::setCurrentServer(const QString &serverName) { + Q_UNUSED(serverName); + emit dataChanged(1); +} + +/***************************************** +* Irc User Items +*****************************************/ +IrcUserItem::IrcUserItem(IrcUser *ircUser, AbstractTreeItem *parent) + : PropertyMapItem(QStringList() << "nickName", parent), + _ircUser(ircUser) +{ + connect(ircUser, SIGNAL(destroyed()), + this, SLOT(ircUserDestroyed())); + + connect(ircUser, SIGNAL(nickSet(QString)), + this, SLOT(setNick(QString))); } -uint NetworkTreeItem::id() const { - return qHash(net); +QString IrcUserItem::nickName() { + return _ircUser->nick(); } -Qt::ItemFlags NetworkTreeItem::flags() const { - return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; +void IrcUserItem::setNick(QString newNick) { + Q_UNUSED(newNick); + emit dataChanged(0); } +void IrcUserItem::ircUserDestroyed() { + deleteLater(); +} + /***************************************** - * BufferTreeModel + * NetworkModel *****************************************/ -BufferTreeModel::BufferTreeModel(QObject *parent) - : TreeModel(BufferTreeModel::defaultHeader(), parent) +NetworkModel::NetworkModel(QObject *parent) + : TreeModel(NetworkModel::defaultHeader(), parent), + _selectionModelSynchronizer(new SelectionModelSynchronizer(this)), + _propertyMapper(new ModelPropertyMapper(this)) { - connect(this, SIGNAL(fakeUserInput(BufferId, QString)), - ClientProxy::instance(), SLOT(gsUserInput(BufferId, QString))); + // initialize the Property Mapper + _propertyMapper->setModel(this); + delete _propertyMapper->selectionModel(); + MappedSelectionModel *mappedSelectionModel = new MappedSelectionModel(this); + _propertyMapper->setSelectionModel(mappedSelectionModel); + synchronizeSelectionModel(mappedSelectionModel); + + connect(_selectionModelSynchronizer, SIGNAL(setCurrentIndex(QModelIndex, QItemSelectionModel::SelectionFlags)), + this, SLOT(setCurrentIndex(QModelIndex, QItemSelectionModel::SelectionFlags))); } -QListBufferTreeModel::defaultHeader() { +QListNetworkModel::defaultHeader() { QList data; - data << tr("Buffer") << tr("Network"); + data << tr("Buffer") << tr("Topic") << tr("Nick Count"); return data; } -bool BufferTreeModel::isBufferIndex(const QModelIndex &index) const { - // not so purdy... - return parent(index) != QModelIndex(); +void NetworkModel::synchronizeSelectionModel(MappedSelectionModel *selectionModel) { + selectionModelSynchronizer()->addSelectionModel(selectionModel); +} + +void NetworkModel::synchronizeView(QAbstractItemView *view) { + MappedSelectionModel *mappedSelectionModel = new MappedSelectionModel(view->model()); + selectionModelSynchronizer()->addSelectionModel(mappedSelectionModel); + Q_ASSERT(mappedSelectionModel); + delete view->selectionModel(); + view->setSelectionModel(mappedSelectionModel); } -Buffer *BufferTreeModel::getBufferByIndex(const QModelIndex &index) const { - BufferTreeItem *item = static_cast(index.internalPointer()); +void NetworkModel::mapProperty(int column, int role, QObject *target, const QByteArray &property) { + propertyMapper()->addMapping(column, role, target, property); +} + +bool NetworkModel::isBufferIndex(const QModelIndex &index) const { + return index.data(NetworkModel::ItemTypeRole) == NetworkModel::BufferItemType; +} + +Buffer *NetworkModel::getBufferByIndex(const QModelIndex &index) const { + BufferItem *item = static_cast(index.internalPointer()); + // FIXME get rid of this + Q_ASSERT(item->buffer() == Client::instance()->buffer(item->id())); return item->buffer(); } -QModelIndex BufferTreeModel::getOrCreateNetworkItemIndex(Buffer *buffer) { - QString net = buffer->networkName(); - TreeItem *networkItem; - if(not(networkItem = rootItem->childById(qHash(net)))) { - int nextRow = rootItem->childCount(); - networkItem = new NetworkTreeItem(net, rootItem); - - beginInsertRows(QModelIndex(), nextRow, nextRow); - rootItem->appendChild(networkItem); - endInsertRows(); +// experimental stuff :) +QModelIndex NetworkModel::networkIndex(uint networkId) { + return indexById(networkId); +} + +NetworkItem *NetworkModel::network(uint networkId) { + return qobject_cast(rootItem->childById(networkId)); +} + +NetworkItem *NetworkModel::newNetwork(uint networkId, const QString &networkName) { + NetworkItem *networkItem = network(networkId); + + if(networkItem == 0) { + networkItem = new NetworkItem(networkId, networkName, rootItem); + appendChild(rootItem, networkItem); } Q_ASSERT(networkItem); - return index(networkItem->row(), 0); + return networkItem; } -QModelIndex BufferTreeModel::getOrCreateBufferItemIndex(Buffer *buffer) { - QModelIndex networkItemIndex = getOrCreateNetworkItemIndex(buffer); - NetworkTreeItem *networkItem = static_cast(networkItemIndex.internalPointer()); - TreeItem *bufferItem; - - if(not(bufferItem = networkItem->childById(buffer->bufferId().uid()))) { - int nextRow = networkItem->childCount(); - bufferItem = new BufferTreeItem(buffer, networkItem); - - beginInsertRows(networkItemIndex, nextRow, nextRow); - networkItem->appendChild(bufferItem); - endInsertRows(); +QModelIndex NetworkModel::bufferIndex(BufferInfo bufferInfo) { + QModelIndex networkIdx = networkIndex(bufferInfo.networkId()); + if(!networkIdx.isValid()) + return QModelIndex(); + else + return indexById(bufferInfo.uid(), networkIdx); +} + +BufferItem *NetworkModel::buffer(BufferInfo bufferInfo) { + QModelIndex bufferIdx = bufferIndex(bufferInfo); + if(bufferIdx.isValid()) + return static_cast(bufferIdx.internalPointer()); + else + return 0; +} + +BufferItem *NetworkModel::newBuffer(BufferInfo bufferInfo) { + BufferItem *bufferItem = buffer(bufferInfo); + if(bufferItem == 0) { + NetworkItem *networkItem = newNetwork(bufferInfo.networkId(), bufferInfo.network()); + + // FIXME: get rid of the buffer pointer + Buffer *buffer = Client::instance()->buffer(bufferInfo.uid()); + bufferItem = new BufferItem(buffer, networkItem); + appendChild(networkItem, bufferItem); } Q_ASSERT(bufferItem); - return index(bufferItem->row(), 0, networkItemIndex); + return bufferItem; } -QStringList BufferTreeModel::mimeTypes() const { +QStringList NetworkModel::mimeTypes() const { + // mimetypes we accept for drops QStringList types; - types << "application/Quassel/BufferItem/row" - << "application/Quassel/BufferItem/network" - << "application/Quassel/BufferItem/bufferId"; + // comma separated list of colon separated pairs of networkid and bufferid + // example: 0:1,0:2,1:4 + types << "application/Quassel/BufferItemList"; return types; } -QMimeData *BufferTreeModel::mimeData(const QModelIndexList &indexes) const { +bool NetworkModel::mimeContainsBufferList(const QMimeData *mimeData) { + return mimeData->hasFormat("application/Quassel/BufferItemList"); +} + +QList< QPair > NetworkModel::mimeDataToBufferList(const QMimeData *mimeData) { + QList< QPair > bufferList; + + if(!mimeContainsBufferList(mimeData)) + return bufferList; + + QStringList rawBufferList = QString::fromAscii(mimeData->data("application/Quassel/BufferItemList")).split(","); + uint networkId, bufferUid; + foreach(QString rawBuffer, rawBufferList) { + if(!rawBuffer.contains(":")) + continue; + networkId = rawBuffer.section(":", 0, 0).toUInt(); + bufferUid = rawBuffer.section(":", 1, 1).toUInt(); + bufferList.append(qMakePair(networkId, bufferUid)); + } + return bufferList; +} + + +QMimeData *NetworkModel::mimeData(const QModelIndexList &indexes) const { QMimeData *mimeData = new QMimeData(); - QModelIndex index = indexes.first(); - - mimeData->setData("application/Quassel/BufferItem/row", QByteArray::number(index.row())); - mimeData->setData("application/Quassel/BufferItem/network", getBufferByIndex(index)->networkName().toUtf8()); - mimeData->setData("application/Quassel/BufferItem/bufferId", QByteArray::number(getBufferByIndex(index)->bufferId().uid())); + QStringList bufferlist; + QString netid, uid, bufferid; + foreach(QModelIndex index, indexes) { + netid = QString::number(index.data(NetworkIdRole).toUInt()); + uid = QString::number(index.data(BufferUidRole).toUInt()); + bufferid = QString("%1:%2").arg(netid).arg(uid); + if(!bufferlist.contains(bufferid)) + bufferlist << bufferid; + } + + mimeData->setData("application/Quassel/BufferItemList", bufferlist.join(",").toAscii()); + return mimeData; } -bool BufferTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex &parent) { - foreach(QString mimeType, mimeTypes()) { - if(!(data->hasFormat(mimeType))) - return false; // whatever the drop is... it's not a buffer... - } - - int sourcerow = data->data("application/Quassel/BufferItem/row").toInt(); - QString network = QString::fromUtf8(data->data("application/Quassel/BufferItem/network")); - - Q_ASSERT(rootItem->childById(qHash(network))); +bool NetworkModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { + Q_UNUSED(action) + Q_UNUSED(row) + Q_UNUSED(column) - if(parent == QModelIndex()) // can't be a query... + if(!mimeContainsBufferList(data)) return false; - - Buffer *sourceBuffer = static_cast(rootItem->childById(qHash(network))->child(sourcerow))->buffer(); - Buffer *targetBuffer = getBufferByIndex(parent); - if(!(sourceBuffer->bufferType() & targetBuffer->bufferType() & Buffer::QueryBuffer)) // only queries can be merged + // target must be a query + Buffer::Type targetType = (Buffer::Type)parent.data(NetworkModel::BufferTypeRole).toInt(); + if(targetType != Buffer::QueryType) + return false; + + QList< QPair > bufferList = mimeDataToBufferList(data); + + // exactly one buffer has to be dropped + if(bufferList.count() != 1) return false; + + uint netId = bufferList.first().first; + uint bufferId = bufferList.first().second; + + // no self merges (would kill us) + if(bufferId == parent.data(BufferUidRole).toUInt()) + return false; - if(sourceBuffer == targetBuffer) // we won't merge with ourself :) + Q_ASSERT(rootItem->childById(netId)); + Q_ASSERT(rootItem->childById(netId)->childById(bufferId)); + + // source must be a query too + Buffer::Type sourceType = (Buffer::Type)rootItem->childById(netId)->childById(bufferId)->data(0, BufferTypeRole).toInt(); + if(sourceType != Buffer::QueryType) return false; // TODO: warn user about buffermerge! - qDebug() << "merging" << sourceBuffer->bufferName() << "with" << targetBuffer->bufferName(); - removeRow(parent.row(), BufferTreeModel::parent(parent)); + qDebug() << "merging" << bufferId << parent.data(BufferUidRole).toInt(); + removeRow(parent.row(), parent.parent()); return true; } -void BufferTreeModel::bufferUpdated(Buffer *buffer) { - QModelIndex itemindex = getOrCreateBufferItemIndex(buffer); - emit invalidateFilter(); +void NetworkModel::attachNetworkInfo(NetworkInfo *networkInfo) { + NetworkItem *networkItem = network(networkInfo->networkId()); + if(!networkItem) { + qWarning() << "NetworkModel::attachNetworkInfo(): network is unknown!"; + return; + } + networkItem->attachNetworkInfo(networkInfo); +} + +void NetworkModel::bufferUpdated(Buffer *buffer) { + BufferItem *bufferItem = newBuffer(buffer->bufferInfo()); + QModelIndex itemindex = indexByItem(bufferItem); emit dataChanged(itemindex, itemindex); } // This Slot indicates that the user has selected a different buffer in the gui -void BufferTreeModel::changeCurrent(const QModelIndex ¤t, const QModelIndex &/*previous*/) { - if(isBufferIndex(current)) { - currentBuffer = getBufferByIndex(current); +void NetworkModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) { + Q_UNUSED(command) + Buffer *newCurrentBuffer; + if(isBufferIndex(index) && currentBuffer != (newCurrentBuffer = getBufferByIndex(index))) { + currentBuffer = newCurrentBuffer; bufferActivity(Buffer::NoActivity, currentBuffer); emit bufferSelected(currentBuffer); - emit selectionChanged(current); + emit selectionChanged(index); } } -// we received a double click on a buffer, so we're going to join it -void BufferTreeModel::doubleClickReceived(const QModelIndex &clicked) { - if(isBufferIndex(clicked)) { - Buffer *buffer = getBufferByIndex(clicked); - if(!buffer->isStatusBuffer()) - emit fakeUserInput(buffer->bufferId(), QString("/join " + buffer->bufferName())); +void NetworkModel::bufferActivity(Buffer::ActivityLevel level, Buffer *buf) { + BufferItem *bufferItem = buffer(buf->bufferInfo()); + if(!bufferItem) { + qWarning() << "NetworkModel::bufferActivity(): received Activity Info for uknown Buffer"; + return; } -} - -void BufferTreeModel::bufferActivity(Buffer::ActivityLevel level, Buffer *buffer) { - BufferTreeItem *bufferItem = static_cast(getOrCreateBufferItemIndex(buffer).internalPointer()); - if(buffer != currentBuffer) + + if(buf != currentBuffer) bufferItem->setActivity(level); else bufferItem->setActivity(Buffer::NoActivity); - bufferUpdated(buffer); + bufferUpdated(buf); } -void BufferTreeModel::selectBuffer(Buffer *buffer) { - QModelIndex index = getOrCreateBufferItemIndex(buffer); - emit selectionChanged(index); +void NetworkModel::selectBuffer(Buffer *buffer) { + QModelIndex index = bufferIndex(buffer->bufferInfo()); + if(!index.isValid()) { + qWarning() << "NetworkModel::selectBuffer(): unknown Buffer has been selected."; + return; + } + // SUPER UGLY! + setCurrentIndex(index, 0); } - -