connect(bufferSyncer(), SIGNAL(lastSeenMsgSet(BufferId, MsgId)), _networkModel, SLOT(setLastSeenMsgId(BufferId, MsgId)));
connect(bufferSyncer(), SIGNAL(bufferRemoved(BufferId)), this, SLOT(bufferRemoved(BufferId)));
connect(bufferSyncer(), SIGNAL(bufferRenamed(BufferId, QString)), this, SLOT(bufferRenamed(BufferId, QString)));
+ connect(bufferSyncer(), SIGNAL(buffersPermanentlyMerged(BufferId, BufferId)), this, SLOT(buffersPermanentlyMerged(BufferId, BufferId)));
+ connect(bufferSyncer(), SIGNAL(buffersPermanentlyMerged(BufferId, BufferId)), _messageModel, SLOT(buffersPermanentlyMerged(BufferId, BufferId)));
connect(bufferSyncer(), SIGNAL(initDone()), this, SLOT(requestInitialBacklog()));
connect(networkModel(), SIGNAL(setLastSeenMsg(BufferId, MsgId)), bufferSyncer(), SLOT(requestSetLastSeenMsg(BufferId, const MsgId &)));
signalProxy()->synchronize(bufferSyncer());
bufferSyncer()->requestRenameBuffer(bufferId, newName);
}
+void Client::mergeBuffersPermanently(BufferId bufferId1, BufferId bufferId2) {
+ if(!bufferSyncer())
+ return;
+ bufferSyncer()->requestMergeBuffersPermanently(bufferId1, bufferId2);
+}
+
void Client::bufferRemoved(BufferId bufferId) {
// select a sane buffer (status buffer)
/* we have to manually select a buffer because otherwise inconsitent changes
}
}
+void Client::buffersPermanentlyMerged(BufferId bufferId1, BufferId bufferId2) {
+ QModelIndex idx = networkModel()->bufferIndex(bufferId1);
+ bufferModel()->setCurrentIndex(bufferModel()->mapFromSource(idx));
+ networkModel()->removeBuffer(bufferId2);
+}
+
void Client::logMessage(QtMsgType type, const char *msg) {
fprintf(stderr, "%s\n", msg);
fflush(stderr);
static void setBufferLastSeenMsg(BufferId id, const MsgId &msgId); // this is synced to core and other clients
static void removeBuffer(BufferId id);
static void renameBuffer(BufferId bufferId, const QString &newName);
+ static void mergeBuffersPermanently(BufferId bufferId1, BufferId bufferId2);
static void logMessage(QtMsgType type, const char *msg);
static inline const QString &debugLog() { return instance()->_debugLogBuffer; }
void bufferRemoved(BufferId bufferId);
void bufferRenamed(BufferId bufferId, const QString &newName);
+ void buffersPermanentlyMerged(BufferId bufferId1, BufferId bufferId2);
private slots:
void disconnectedFromCore();
}
void MessageFilter::init() {
+ setDynamicSortFilter(true);
+
BufferSettings defaultSettings;
_messageTypeFilter = defaultSettings.messageFilter();
defaultSettings.notify("MessageTypeFilter", this, SLOT(messageTypeFilterChanged()));
_messagesWaiting.remove(bufferId);
}
+void MessageModel::buffersPermanentlyMerged(BufferId bufferId1, BufferId bufferId2) {
+ for(int i = 0; i < _messageList.count(); i++) {
+ if(_messageList[i]->bufferId() == bufferId2) {
+ _messageList[i]->setBufferId(bufferId1);
+ QModelIndex idx = index(i, 0);
+ emit dataChanged(idx, idx);
+ }
+ }
+}
+
// ========================================
// MessageModelItem
// ========================================
}
}
-
// Stuff for later
bool MessageModelItem::lessThan(const MessageModelItem *m1, const MessageModelItem *m2){
return (*m1) < (*m2);
public slots:
void requestBacklog(BufferId bufferId);
void messagesReceived(BufferId bufferId, int count);
+ void buffersPermanentlyMerged(BufferId bufferId1, BufferId bufferId2);
protected:
virtual MessageModelItem *createMessageModelItem(const Message &) = 0;
inline const QDateTime &timeStamp() const { return _timestamp; }
inline MsgId msgId() const { return _msgId; }
inline BufferId bufferId() const { return _bufferId; }
+ inline void setBufferId(BufferId bufferId) { _bufferId = bufferId; }
inline Message::Type msgType() const { return _type; }
inline Message::Flags msgFlags() const { return _flags; }
return mimeData;
}
-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(!mimeContainsBufferList(data))
- return false;
-
- // target must be a query
- BufferInfo::Type targetType = (BufferInfo::Type)parent.data(NetworkModel::BufferTypeRole).toInt();
- if(targetType != BufferInfo::QueryBuffer)
- return false;
-
- QList< QPair<NetworkId, BufferId> > bufferList = mimeDataToBufferList(data);
-
- // exactly one buffer has to be dropped
- if(bufferList.count() != 1)
- return false;
-
- NetworkId netId = bufferList.first().first;
- BufferId bufferId = bufferList.first().second;
-
- // no self merges (would kill us)
- if(bufferId == parent.data(BufferIdRole).value<BufferId>())
- return false;
-
- NetworkItem *netItem = findNetworkItem(netId);
- Q_ASSERT(netItem);
-
- BufferItem *bufferItem = netItem->findBufferItem(bufferId);
- Q_ASSERT(bufferItem);
-
- // source must be a query too
- if(bufferItem->bufferType() != BufferInfo::QueryBuffer)
- return false;
-
- // TODO: warn user about buffermerge!
- qDebug() << "merging" << bufferId << parent.data(BufferIdRole).value<BufferId>();
- removeRow(parent.row(), parent.parent());
-
- return true;
-}
-
void NetworkModel::attachNetwork(Network *net) {
NetworkItem *netItem = networkItem(net->networkId());
netItem->attachNetwork(net);
virtual QStringList mimeTypes() const;
virtual QMimeData *mimeData(const QModelIndexList &) const;
- virtual bool dropMimeData(const QMimeData *, Qt::DropAction, int, int, const QModelIndex &);
void attachNetwork(Network *network);
}
void BufferSyncer::removeBuffer(BufferId buffer) {
- if(_lastSeenMsg.contains(buffer)) {
+ if(_lastSeenMsg.contains(buffer))
_lastSeenMsg.remove(buffer);
- emit bufferRemoved(buffer);
- }
+ emit bufferRemoved(buffer);
}
-// void BufferSyncer::renameBuffer(BufferId buffer, QString newName) {
-// emit bufferRenamed(buffer, newName);
-// }
+
+void BufferSyncer::mergeBuffersPermanently(BufferId buffer1, BufferId buffer2) {
+ if(_lastSeenMsg.contains(buffer2))
+ _lastSeenMsg.remove(buffer2);
+ emit buffersPermanentlyMerged(buffer1, buffer2);
+}
void initSetLastSeenMsg(const QVariantList &);
virtual inline void requestSetLastSeenMsg(BufferId buffer, const MsgId &msgId) { emit setLastSeenMsgRequested(buffer, msgId); }
+
virtual inline void requestRemoveBuffer(BufferId buffer) { emit removeBufferRequested(buffer); }
virtual void removeBuffer(BufferId buffer);
+
virtual inline void requestRenameBuffer(BufferId buffer, QString newName) { emit renameBufferRequested(buffer, newName); }
virtual inline void renameBuffer(BufferId buffer, QString newName) { emit bufferRenamed(buffer, newName); }
+ virtual inline void requestMergeBuffersPermanently(BufferId buffer1, BufferId buffer2) { emit mergeBuffersPermanentlyRequested(buffer1, buffer2); }
+ virtual void mergeBuffersPermanently(BufferId buffer1, BufferId buffer2);
+
signals:
void lastSeenMsgSet(BufferId buffer, const MsgId &msgId);
void setLastSeenMsgRequested(BufferId buffer, const MsgId &msgId);
+
void removeBufferRequested(BufferId buffer);
void bufferRemoved(BufferId buffer);
+
void renameBufferRequested(BufferId buffer, QString newName);
void bufferRenamed(BufferId buffer, QString newName);
+ void mergeBuffersPermanentlyRequested(BufferId buffer1, BufferId buffer2);
+ void buffersPermanentlyMerged(BufferId buffer1, BufferId buffer2);
+
protected slots:
bool setLastSeenMsg(BufferId buffer, const MsgId &msgId);
--- /dev/null
+UPDATE backlog
+SET bufferid = :newbufferid
+WHERE bufferid = :oldbufferid
exit(1); // TODO make this less brutal (especially for mono client -> popup)
}
connect(&_storageSyncTimer, SIGNAL(timeout()), this, SLOT(syncStorage()));
- _storageSyncTimer.start(10 * 60 * 1000); // in msecs
+ _storageSyncTimer.start(10 * 60 * 1000); // 10 minutes
}
void Core::init() {
return instance()->storage->renameBuffer(user, bufferId, newName);
}
+ //! Merge the content of two Buffers permanently. This cannot be reversed!
+ /** \note This method is threadsafe.
+ * \param user The id of the buffer owner
+ * \param bufferId1 The bufferId of the remaining buffer
+ * \param bufferId2 The buffer that is about to be removed
+ * \return true if successfulln
+ */
+ static inline bool mergeBuffersPermanently(const UserId &user, const BufferId &bufferId1, const BufferId &bufferId2) {
+ return instance()->storage->mergeBuffersPermanently(user, bufferId1, bufferId2);
+ }
+
//! Update the LastSeenDate for a Buffer
/** This Method is used to make the LastSeenDate of a Buffer persistent
* \note This method is threadsafe.
const QDateTime &startTime() const { return _startTime; }
+ static inline QTimer &syncTimer() { return instance()->_storageSyncTimer; }
+
public slots:
//! Make storage data persistent
/** \note This method is threadsafe.
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-08 by the Quassel Project *
+ * 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) version 3. *
+ * *
+ * 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 "corebuffersyncer.h"
+
+#include "core.h"
+#include "coresession.h"
+#include "corenetwork.h"
+#include "ircchannel.h"
+
+CoreBufferSyncer::CoreBufferSyncer(CoreSession *parent)
+ : BufferSyncer(parent),
+ _coreSession(parent)
+{
+}
+
+void CoreBufferSyncer::requestSetLastSeenMsg(BufferId buffer, const MsgId &msgId) {
+ if(setLastSeenMsg(buffer, msgId))
+ dirtyBuffers << buffer;
+}
+
+void CoreBufferSyncer::storeDirtyIds() {
+ UserId userId = _coreSession->user();
+ MsgId msgId;
+ foreach(BufferId bufferId, dirtyBuffers) {
+ msgId = lastSeenMsg(bufferId);
+ if(msgId.isValid())
+ Core::setBufferLastSeenMsg(userId, bufferId, msgId);
+ }
+ dirtyBuffers.clear();
+}
+
+void CoreBufferSyncer::removeBuffer(BufferId bufferId) {
+ BufferInfo bufferInfo = Core::getBufferInfo(_coreSession->user(), bufferId);
+ if(!bufferInfo.isValid()) {
+ qWarning() << "CoreBufferSyncer::removeBuffer(): invalid BufferId:" << bufferId << "for User:" << _coreSession->user();
+ return;
+ }
+
+ if(bufferInfo.type() == BufferInfo::StatusBuffer) {
+ qWarning() << "CoreBufferSyncer::removeBuffer(): Status Buffers cannot be removed!";
+ return;
+ }
+
+ if(bufferInfo.type() == BufferInfo::ChannelBuffer) {
+ CoreNetwork *net = _coreSession->network(bufferInfo.networkId());
+ if(!net) {
+ qWarning() << "CoreBufferSyncer::removeBuffer(): Received BufferInfo with unknown networkId!";
+ return;
+ }
+ IrcChannel *chan = net->ircChannel(bufferInfo.bufferName());
+ if(chan) {
+ qWarning() << "CoreBufferSyncer::removeBuffer(): Unable to remove Buffer for joined Channel:" << bufferInfo.bufferName();
+ return;
+ }
+ }
+ if(Core::removeBuffer(_coreSession->user(), bufferId))
+ BufferSyncer::removeBuffer(bufferId);
+}
+
+void CoreBufferSyncer::renameBuffer(BufferId bufferId, QString newName) {
+ BufferInfo bufferInfo = Core::getBufferInfo(_coreSession->user(), bufferId);
+ if(!bufferInfo.isValid()) {
+ qWarning() << "CoreBufferSyncer::renameBuffer(): invalid BufferId:" << bufferId << "for User:" << _coreSession->user();
+ return;
+ }
+
+ if(bufferInfo.type() != BufferInfo::QueryBuffer) {
+ qWarning() << "CoreBufferSyncer::renameBuffer(): only QueryBuffers can be renamed" << bufferId;
+ return;
+ }
+
+ if(Core::renameBuffer(_coreSession->user(), bufferId, newName))
+ BufferSyncer::renameBuffer(bufferId, newName);
+}
+
+void CoreBufferSyncer::mergeBuffersPermanently(BufferId bufferId1, BufferId bufferId2) {
+ BufferInfo bufferInfo1 = Core::getBufferInfo(_coreSession->user(), bufferId1);
+ BufferInfo bufferInfo2 = Core::getBufferInfo(_coreSession->user(), bufferId2);
+ qDebug() << Q_FUNC_INFO << bufferInfo1 << bufferInfo2;
+ if(!bufferInfo1.isValid() || !bufferInfo2.isValid()) {
+ qWarning() << "CoreBufferSyncer::mergeBufferPermanently(): invalid BufferIds:" << bufferId1 << bufferId2 << "for User:" << _coreSession->user();
+ return;
+ }
+
+ if(bufferInfo1.type() != BufferInfo::QueryBuffer || bufferInfo2.type() != BufferInfo::QueryBuffer) {
+ qWarning() << "CoreBufferSyncer::mergeBufferPermanently(): only QueryBuffers can be merged!" << bufferId1 << bufferId2;
+ return;
+ }
+
+ if(Core::mergeBuffersPermanently(_coreSession->user(), bufferId1, bufferId2)) {
+ qDebug () << "HUUUUP";
+ BufferSyncer::mergeBuffersPermanently(bufferId1, bufferId2);
+ }
+}
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-08 by the Quassel Project *
+ * 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) version 3. *
+ * *
+ * 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 COREBUFFERSYNCER_H
+#define COREBUFFERSYNCER_H
+
+#include "buffersyncer.h"
+
+class CoreSession;
+
+class CoreBufferSyncer : public BufferSyncer {
+ Q_OBJECT
+
+public:
+ CoreBufferSyncer(CoreSession *parent);
+
+public slots:
+ virtual void requestSetLastSeenMsg(BufferId buffer, const MsgId &msgId);
+
+ virtual inline void requestRemoveBuffer(BufferId buffer) { removeBuffer(buffer); }
+ virtual void removeBuffer(BufferId bufferId);
+
+ virtual inline void requestRenameBuffer(BufferId buffer, QString newName) { renameBuffer(buffer, newName); }
+ virtual void renameBuffer(BufferId buffer, QString newName);
+
+ virtual inline void requestMergeBuffersPermanently(BufferId buffer1, BufferId buffer2) { mergeBuffersPermanently(buffer1, buffer2); }
+ virtual void mergeBuffersPermanently(BufferId buffer1, BufferId buffer2);
+
+ void storeDirtyIds();
+
+private:
+ CoreSession *_coreSession;
+
+ QSet<BufferId> dirtyBuffers;
+};
+
+#endif //COREBUFFERSYNCER_H
foreach(BufferId id, lastSeenHash.keys())
_bufferSyncer->requestSetLastSeenMsg(id, lastSeenHash[id]);
- connect(_bufferSyncer, SIGNAL(lastSeenMsgSet(BufferId, MsgId)), this, SLOT(storeBufferLastSeenMsg(BufferId, MsgId)));
+ // connect(_bufferSyncer, SIGNAL(lastSeenMsgSet(BufferId, MsgId)), this, SLOT(storeBufferLastSeenMsg(BufferId, MsgId)));
+ connect(&(Core::instance()->syncTimer()), SIGNAL(timeout()), _bufferSyncer, SLOT(storeDirtyIds()));
p->synchronize(_bufferSyncer);
}
void CoreSession::saveSessionState() const {
+ _bufferSyncer->storeDirtyIds();
}
void CoreSession::restoreSessionState() {
<file>./SQL/SQLite/13/setup_130_identity.sql</file>
<file>./SQL/SQLite/13/setup_140_identity_nick.sql</file>
<file>./SQL/SQLite/13/setup_999_version.sql</file>
+ <file>./SQL/SQLite/13/update_backlog_bufferid.sql</file>
<file>./SQL/SQLite/13/update_buffer_lastseen.sql</file>
<file>./SQL/SQLite/13/update_buffer_name.sql</file>
<file>./SQL/SQLite/13/update_buffer_persistent_channel.sql</file>
return true;
}
+bool SqliteStorage::mergeBuffersPermanently(const UserId &user, const BufferId &bufferId1, const BufferId &bufferId2) {
+ if(!isValidBuffer(user, bufferId1) || !isValidBuffer(user, bufferId2))
+ return false;
+
+ QSqlQuery query(logDb());
+ query.prepare(queryString("update_backlog_bufferid"));
+ query.bindValue(":oldbufferid", bufferId2.toInt());
+ query.bindValue(":newbufferid", bufferId1.toInt());
+ safeExec(query);
+ if(!watchQuery(query))
+ return false;
+
+ QSqlQuery delBufferQuery(logDb());
+ delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
+ delBufferQuery.bindValue(":bufferid", bufferId2.toInt());
+ safeExec(delBufferQuery);
+ watchQuery(delBufferQuery);
+
+ return true;
+}
+
void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId, const MsgId &msgId) {
QSqlQuery query(logDb());
query.prepare(queryString("update_buffer_lastseen"));
virtual QList<BufferId> requestBufferIdsForNetwork(UserId user, NetworkId networkId);
virtual bool removeBuffer(const UserId &user, const BufferId &bufferId);
virtual bool renameBuffer(const UserId &user, const BufferId &bufferId, const QString &newName);
+ virtual bool mergeBuffersPermanently(const UserId &user, const BufferId &bufferId1, const BufferId &bufferId2);
virtual void setBufferLastSeenMsg(UserId user, const BufferId &bufferId, const MsgId &msgId);
virtual QHash<BufferId, MsgId> bufferLastSeenMsgIds(UserId user);
* \return true if successfull
*/
virtual bool renameBuffer(const UserId &user, const BufferId &bufferId, const QString &newName) = 0;
-
+
+ //! Merge the content of two Buffers permanently. This cannot be reversed!
+ /** \note This method is threadsafe.
+ * \param user The id of the buffer owner
+ * \param bufferId1 The bufferId of the remaining buffer
+ * \param bufferId2 The buffer that is about to be removed
+ * \return true if successfull
+ */
+ virtual bool mergeBuffersPermanently(const UserId &user, const BufferId &bufferId1, const BufferId &bufferId2) = 0;
+
//! Update the LastSeenDate for a Buffer
/** This Method is used to make the LastSeenDate of a Buffer persistent
* \param user The Owner of that Buffer
QTreeView::keyPressEvent(event);
}
+void BufferView::dropEvent(QDropEvent *event) {
+ QList< QPair<NetworkId, BufferId> > bufferList = Client::networkModel()->mimeDataToBufferList(event->mimeData());
+
+ if(bufferList.count() != 1)
+ return QTreeView::dropEvent(event);
+
+ NetworkId networkId = bufferList[0].first;
+ BufferId bufferId2 = bufferList[0].second;
+
+ QModelIndex index = indexAt(event->pos());
+ if(index.data(NetworkModel::ItemTypeRole) != NetworkModel::BufferItemType)
+ return;
+
+ if(index.data(NetworkModel::BufferTypeRole) != BufferInfo::QueryBuffer)
+ return;
+
+ if(index.data(NetworkModel::NetworkIdRole).value<NetworkId>() != networkId)
+ return;
+
+ BufferId bufferId1 = index.data(NetworkModel::BufferIdRole).value<BufferId>();
+ if(bufferId1 == bufferId2)
+ return;
+
+ int res = QMessageBox::question(0, tr("Merge buffers permanently?"),
+ tr("Do you want to merge the buffer \"%1\" permanently into buffer \"%2\"?\n This cannot be reversed!").arg(Client::networkModel()->bufferName(bufferId2)).arg(Client::networkModel()->bufferName(bufferId1)),
+ QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
+ if(res == QMessageBox::Yes) {
+ Client::mergeBuffersPermanently(bufferId1, bufferId2);
+ }
+}
+
void BufferView::removeSelectedBuffers(bool permanently) {
if(!config())
return;
protected:
virtual void keyPressEvent(QKeyEvent *);
+ virtual void dropEvent(QDropEvent *event);
virtual void rowsInserted(const QModelIndex & parent, int start, int end);
virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
virtual void wheelEvent(QWheelEvent *);