[klipper] Use HistoryModel as backend for History

Complete functionality is preserved, but History API needs to be adjusted
to shared pointers.
wilder-5.14
Martin Gräßlin 12 years ago
parent 55b74b2bfa
commit 2e47d84772
  1. 1
      klipper/autotests/CMakeLists.txt
  2. 94
      klipper/autotests/historymodeltest.cpp
  3. 15
      klipper/autotests/historytest.cpp
  4. 282
      klipper/history.cpp
  5. 38
      klipper/history.h
  6. 57
      klipper/historyitem.cpp
  7. 28
      klipper/historyitem.h
  8. 19
      klipper/historymodel.cpp
  9. 2
      klipper/historymodel.h

@ -10,6 +10,7 @@ set(testHistory_SRCS
../historyitem.cpp
../historystringitem.cpp
../historyurlitem.cpp
../historymodel.cpp
)
add_executable(testHistory ${testHistory_SRCS})
target_link_libraries(testHistory

@ -21,7 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "../historystringitem.h"
#include <QtTest>
Q_DECLARE_METATYPE(HistoryItem*)
class HistoryModelTest : public QObject
{
@ -87,69 +86,86 @@ void HistoryModelTest::testInsertRemove()
// let's insert a few items
history->insert(QSharedPointer<HistoryItem>(new HistoryStringItem(fooText)));
QCOMPARE(history->data(history->index(0, 0)).toString(), fooText);
// QCOMPARE(history->first()->next_uuid(), history->first()->uuid());
// QCOMPARE(history->first()->previous_uuid(), history->first()->uuid());
QModelIndex index = history->index(0, 0);
QCOMPARE(index.data().toString(), fooText);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), index.data(Qt::UserRole+1).toByteArray());
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), index.data(Qt::UserRole+1).toByteArray());
history->insert(QSharedPointer<HistoryItem>(new HistoryStringItem(barText)));
QCOMPARE(history->data(history->index(0, 0)).toString(), barText);
// QCOMPARE(history->first()->next_uuid(), fooUuid);
// QCOMPARE(history->first()->previous_uuid(), fooUuid);
// QCOMPARE(history->find(fooUuid)->next_uuid(), barUuid);
// QCOMPARE(history->find(fooUuid)->previous_uuid(), barUuid);
QCOMPARE(index.data().toString(), barText);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), fooUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), fooUuid);
index = history->indexOf(fooUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), barUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), barUuid);
history->insert(QSharedPointer<HistoryItem>(new HistoryStringItem(fooBarText)));
QCOMPARE(history->data(history->index(0, 0)).toString(), fooBarText);
// QCOMPARE(history->first()->next_uuid(), barUuid);
// QCOMPARE(history->first()->previous_uuid(), fooUuid);
// QCOMPARE(history->find(fooUuid)->next_uuid(), foobarUuid);
// QCOMPARE(history->find(fooUuid)->previous_uuid(), barUuid);
// QCOMPARE(history->find(barUuid)->next_uuid(), fooUuid);
// QCOMPARE(history->find(barUuid)->previous_uuid(), foobarUuid);
index = history->index(0, 0);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), barUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), fooUuid);
index = history->indexOf(fooUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), foobarUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), barUuid);
index = history->indexOf(barUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), fooUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), foobarUuid);
// insert one again - it should be moved to top
history->insert(QSharedPointer<HistoryItem>(new HistoryStringItem(barText)));
QCOMPARE(history->data(history->index(0, 0)).toString(), barText);
// QCOMPARE(history->first()->next_uuid(), foobarUuid);
// QCOMPARE(history->first()->previous_uuid(), fooUuid);
// QCOMPARE(history->find(fooUuid)->next_uuid(), barUuid);
// QCOMPARE(history->find(fooUuid)->previous_uuid(), foobarUuid);
// QCOMPARE(history->find(foobarUuid)->next_uuid(), fooUuid);
// QCOMPARE(history->find(foobarUuid)->previous_uuid(), barUuid);
index = history->index(0, 0);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), foobarUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), fooUuid);
index = history->indexOf(fooUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), barUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), foobarUuid);
index = history->indexOf(foobarUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), fooUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), barUuid);
// move one to top using the slot
// already on top, shouldn't change anything
history->moveToTop(barUuid);
QCOMPARE(history->data(history->index(0, 0)).toString(), barText);
// QCOMPARE(history->first()->next_uuid(), foobarUuid);
// QCOMPARE(history->first()->previous_uuid(), fooUuid);
// QCOMPARE(history->find(fooUuid)->next_uuid(), barUuid);
// QCOMPARE(history->find(fooUuid)->previous_uuid(), foobarUuid);
// QCOMPARE(history->find(foobarUuid)->next_uuid(), fooUuid);
// QCOMPARE(history->find(foobarUuid)->previous_uuid(), barUuid);
index = history->index(0, 0);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), foobarUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), fooUuid);
index = history->indexOf(fooUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), barUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), foobarUuid);
index = history->indexOf(foobarUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), fooUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), barUuid);
// another one should change, though
history->moveToTop(foobarUuid);
QCOMPARE(history->data(history->index(0, 0)).toString(), fooBarText);
// QCOMPARE(history->first()->next_uuid(), barUuid);
// QCOMPARE(history->first()->previous_uuid(), fooUuid);
// QCOMPARE(history->find(fooUuid)->next_uuid(), foobarUuid);
// QCOMPARE(history->find(fooUuid)->previous_uuid(), barUuid);
// QCOMPARE(history->find(barUuid)->next_uuid(), fooUuid);
// QCOMPARE(history->find(barUuid)->previous_uuid(), foobarUuid);
index = history->index(0, 0);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), barUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), fooUuid);
index = history->indexOf(fooUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), foobarUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), barUuid);
index = history->indexOf(barUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), fooUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), foobarUuid);
// remove them again
QVERIFY(history->remove(foobarUuid));
QCOMPARE(history->data(history->index(0, 0)).toString(), barText);
// QCOMPARE(history->first()->next_uuid(), fooUuid);
// QCOMPARE(history->first()->previous_uuid(), fooUuid);
// QCOMPARE(history->find(fooUuid)->next_uuid(), barUuid);
// QCOMPARE(history->find(fooUuid)->previous_uuid(), barUuid);
index = history->index(0, 0);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), fooUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), fooUuid);
index = history->indexOf(fooUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), barUuid);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), barUuid);
QVERIFY(history->remove(barUuid));
QCOMPARE(history->data(history->index(0, 0)).toString(), fooText);
// QCOMPARE(history->first()->next_uuid(), history->first()->uuid());
// QCOMPARE(history->first()->previous_uuid(), history->first()->uuid());
index = history->index(0, 0);
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->next_uuid(), index.data(Qt::UserRole+1).toByteArray());
QCOMPARE(index.data(Qt::UserRole).value<HistoryItem*>()->previous_uuid(), index.data(Qt::UserRole+1).toByteArray());
QVERIFY(history->remove(fooUuid));
QCOMPARE(history->rowCount(), 0);

@ -47,12 +47,8 @@ void HistoryTest::testSetMaxSize()
// insert an item - should still be empty
history->insert(new HistoryStringItem(QStringLiteral("foo")));
QVERIFY(history->empty());
QEXPECT_FAIL("", "Item is first inserted, then removed, changed signal gets emitted, although not changed", Continue);
QVERIFY(changedSpy.isEmpty());
changedSpy.clear();
QEXPECT_FAIL("", "Item is first inserted, then removed, changed signal gets emitted, although not changed", Continue);
QVERIFY(topSpy.isEmpty());
topSpy.clear();
// now it should insert again
history->setMaxSize(1);
@ -70,6 +66,7 @@ void HistoryTest::testSetMaxSize()
history->insert(new HistoryStringItem(QStringLiteral("bar")));
QCOMPARE(history->first()->text(), QLatin1String("bar"));
QCOMPARE(history->first()->next_uuid(), history->first()->uuid());
QEXPECT_FAIL("", "Item is first inserted, then removed, changed signal gets emitted, although not changed", Continue);
QCOMPARE(topSpy.size(), 1);
topSpy.clear();
QVERIFY(topSpy.isEmpty());
@ -83,7 +80,6 @@ void HistoryTest::testSetMaxSize()
QCOMPARE(history->maxSize(), 0u);
QVERIFY(history->empty());
QCOMPARE(changedSpy.size(), 1);
QEXPECT_FAIL("", "Clearing history doesn't emit top changed", Continue);
QCOMPARE(topSpy.size(), 1);
}
@ -146,7 +142,6 @@ void HistoryTest::testInsertRemove()
QCOMPARE(history->find(fooUuid)->previous_uuid(), foobarUuid);
QCOMPARE(history->find(foobarUuid)->next_uuid(), fooUuid);
QCOMPARE(history->find(foobarUuid)->previous_uuid(), barUuid);
QEXPECT_FAIL("", "TopChanged is emitted twice when inserting an existing item", Continue);
QCOMPARE(topSpy.size(), 1);
topSpy.clear();
QVERIFY(topSpy.isEmpty());
@ -154,7 +149,7 @@ void HistoryTest::testInsertRemove()
// move one to top using the slot
// already on top, shouldn't change anything
history->slotMoveToTop(barUuid);
QVERIFY(!history->topIsUserSelected());
QVERIFY(history->topIsUserSelected());
QCOMPARE(history->first()->text(), barText);
QCOMPARE(history->first()->next_uuid(), foobarUuid);
QCOMPARE(history->first()->previous_uuid(), fooUuid);
@ -162,8 +157,6 @@ void HistoryTest::testInsertRemove()
QCOMPARE(history->find(fooUuid)->previous_uuid(), foobarUuid);
QCOMPARE(history->find(foobarUuid)->next_uuid(), fooUuid);
QCOMPARE(history->find(foobarUuid)->previous_uuid(), barUuid);
QCOMPARE(topSpy.size(), 1);
topSpy.clear();
QVERIFY(topSpy.isEmpty());
// another one should change, though
@ -188,7 +181,6 @@ void HistoryTest::testInsertRemove()
QCOMPARE(history->first()->previous_uuid(), fooUuid);
QCOMPARE(history->find(fooUuid)->next_uuid(), barUuid);
QCOMPARE(history->find(fooUuid)->previous_uuid(), barUuid);
QEXPECT_FAIL("", "topChanged not emitted when removing top item", Continue);
QCOMPARE(topSpy.size(), 1);
topSpy.clear();
QVERIFY(topSpy.isEmpty());
@ -198,7 +190,6 @@ void HistoryTest::testInsertRemove()
QCOMPARE(history->first()->text(), fooText);
QCOMPARE(history->first()->next_uuid(), history->first()->uuid());
QCOMPARE(history->first()->previous_uuid(), history->first()->uuid());
QEXPECT_FAIL("", "topChanged not emitted when removing top item", Continue);
QCOMPARE(topSpy.size(), 1);
topSpy.clear();
QVERIFY(topSpy.isEmpty());
@ -207,7 +198,6 @@ void HistoryTest::testInsertRemove()
QVERIFY(!history->topIsUserSelected());
QVERIFY(!history->first());
QVERIFY(history->empty());
QEXPECT_FAIL("", "topChanged not emitted when removing top item", Continue);
QCOMPARE(topSpy.size(), 1);
}
@ -237,7 +227,6 @@ void HistoryTest::testClear()
QVERIFY(history->empty());
QVERIFY(!history->first());
QVERIFY(!history->topIsUserSelected());
QEXPECT_FAIL("", "topChanged not emitted on clearing", Continue);
QVERIFY(!topSpy.isEmpty());
}

@ -2,6 +2,7 @@
Copyright (C) 2004 Esben Mose Hansen <kde@mosehansen.dk>
Copyright (C) by Andrew Stanley-Jones <asj@cban.com>
Copyright (C) 2000 by Carsten Pfeiffer <pfeiffer@kde.org>
Copyright (C) 2014 by Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
@ -24,107 +25,110 @@
#include "historyitem.h"
#include "historystringitem.h"
#include "historymodel.h"
class CycleBlocker
{
public:
CycleBlocker();
~CycleBlocker();
static bool isBlocked();
private:
static int s_blocker;
};
int CycleBlocker::s_blocker = 0;
CycleBlocker::CycleBlocker()
{
s_blocker++;
}
CycleBlocker::~CycleBlocker()
{
s_blocker--;
}
bool CycleBlocker::isBlocked()
{
return s_blocker;
}
History::History( QObject* parent )
: QObject( parent ),
m_top(0L),
m_maxSize(0),
m_topIsUserSelected( false ),
m_nextCycle(0L)
m_model(new HistoryModel(this))
{
connect(m_model, &HistoryModel::rowsInserted, this,
[this](const QModelIndex &parent, int start) {
Q_UNUSED(parent)
if (start == 0) {
emit topChanged();
}
emit changed();
}
);
connect(m_model, &HistoryModel::rowsMoved, this,
[this](const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) {
Q_UNUSED(sourceParent)
Q_UNUSED(sourceEnd)
Q_UNUSED(destinationParent)
if (sourceStart == 0 || destinationRow == 0) {
emit topChanged();
}
emit changed();
}
);
connect(m_model, &HistoryModel::rowsRemoved, this,
[this](const QModelIndex &parent, int start) {
Q_UNUSED(parent)
if (start == 0) {
emit topChanged();
}
emit changed();
}
);
connect(m_model, &HistoryModel::modelReset, this, &History::changed);
connect(m_model, &HistoryModel::modelReset, this, &History::topChanged);
connect(this, &History::topChanged,
[this]() {
m_topIsUserSelected = false;
if (!CycleBlocker::isBlocked()) {
m_cycleStartUuid = QByteArray();
}
}
);
}
History::~History() {
qDeleteAll(m_items);
}
void History::insert( HistoryItem* item ) {
if ( !item )
return;
const HistoryItem* existingItem = this->find(item->uuid());
if ( existingItem ) {
if ( existingItem == m_top) {
return;
}
slotMoveToTop( existingItem->uuid() );
} else {
forceInsert( item );
}
m_topIsUserSelected = false;
emit topChanged();
m_model->insert(QSharedPointer<HistoryItem>(item));
}
void History::forceInsert( HistoryItem* item ) {
if ( !item )
return;
if (m_items.find(item->uuid()) != m_items.end()) {
return; // Don't insert duplicates
}
m_nextCycle = m_top;
item->insertBetweeen(m_top ? m_items[m_top->previous_uuid()] : 0L, m_top);
m_items.insert( item->uuid(), item );
m_top = item;
emit changed();
trim();
}
void History::trim() {
int i = m_items.count() - maxSize();
if ( i <= 0 || !m_top )
return;
items_t::iterator bottom = m_items.find(m_top->previous_uuid());
while ( i-- ) {
items_t::iterator it = bottom;
bottom = m_items.find((*bottom)->previous_uuid());
// FIXME: managing memory manually is tedious; use smart pointer instead
delete *it;
m_items.erase(it);
}
(*bottom)->chain(m_top);
if (m_items.size()<=1) {
m_nextCycle = 0L;
}
if (m_items.isEmpty()) {
// force top to nullptr
m_top = nullptr;
}
emit changed();
// TODO: do we need a force insert in HistoryModel
m_model->insert(QSharedPointer<HistoryItem>(item));
}
void History::remove( const HistoryItem* newItem ) {
if ( !newItem )
return;
items_t::iterator it = m_items.find(newItem->uuid());
if (it == m_items.end()) {
return;
}
if (*it == m_top) {
m_top = m_items[m_top->next_uuid()];
if (m_top == *it) {
// last element in chain
m_top = nullptr;
}
m_topIsUserSelected = false;
}
m_items[(*it)->previous_uuid()]->chain(m_items[(*it)->next_uuid()]);
m_items.erase(it);
m_model->remove(newItem->uuid());
}
void History::slotClear() {
// FIXME: managing memory manually is tedious; use smart pointer instead
qDeleteAll(m_items);
m_items.clear();
m_top = 0L;
m_topIsUserSelected = false;
emit changed();
m_model->clear();
}
void History::slotMoveToTop(QAction* action) {
@ -136,114 +140,90 @@ void History::slotMoveToTop(QAction* action) {
}
void History::slotMoveToTop(const QByteArray& uuid) {
items_t::iterator it = m_items.find(uuid);
if (it == m_items.end()) {
return;
}
if (*it == m_top) {
emit topChanged();
return;
}
m_model->moveToTop(uuid);
m_topIsUserSelected = true;
m_nextCycle = m_top;
m_items[(*it)->previous_uuid()]->chain(m_items[(*it)->next_uuid()]);
(*it)->insertBetweeen(m_items[m_top->previous_uuid()], m_top);
m_top = *it;
emit changed();
emit topChanged();
}
void History::setMaxSize( unsigned max_size ) {
m_maxSize = max_size;
trim();
m_model->setMaxSize(max_size);
}
void History::cycleNext() {
if (m_top && m_nextCycle && m_nextCycle != m_top) {
HistoryItem* prev = m_items[m_nextCycle->previous_uuid()];
HistoryItem* next = m_items[m_nextCycle->next_uuid()];
//if we have only two items in clipboard
if (prev == next) {
m_top=m_nextCycle;
}
else {
HistoryItem* endofhist = m_items[m_top->previous_uuid()];
HistoryItem* aftertop = m_items[m_top->next_uuid()];
if (prev == m_top) {
prev = m_nextCycle;
aftertop = m_top;
}
else if (next == m_top) {
next = m_nextCycle;
endofhist = m_top;
}
m_top->insertBetweeen(prev, next);
m_nextCycle->insertBetweeen(endofhist, aftertop);
m_top = m_nextCycle;
m_nextCycle = next;
}
emit changed();
emit topChanged();
if (m_model->rowCount() < 2) {
return;
}
if (m_cycleStartUuid.isEmpty()) {
m_cycleStartUuid = m_model->index(0).data(Qt::UserRole+1).toByteArray();
} else if (m_cycleStartUuid == m_model->index(1).data(Qt::UserRole+1).toByteArray()) {
// end of cycle
return;
}
CycleBlocker blocker;
m_model->moveTopToBack();
}
void History::cyclePrev() {
if (m_top && m_nextCycle) {
HistoryItem* prev = m_items[m_nextCycle->previous_uuid()];
if (prev == m_top) {
return;
}
HistoryItem* prevprev = m_items[prev->previous_uuid()];
HistoryItem* aftertop = m_items[m_top->next_uuid()];
//if we have only two items in clipboard
if (m_nextCycle == prevprev) {
m_top=aftertop;
}
else {
HistoryItem* endofhist = m_items[m_top->previous_uuid()];
if (prevprev == m_top) {
prevprev = prev;
aftertop = m_top;
}
else if (m_nextCycle == m_top) {
m_nextCycle = aftertop;
endofhist = m_top;
}
m_top->insertBetweeen(prevprev,m_nextCycle);
prev->insertBetweeen(endofhist, aftertop);
m_nextCycle = m_top;
m_top = prev;
}
emit changed();
emit topChanged();
if (m_cycleStartUuid.isEmpty()) {
return;
}
CycleBlocker blocker;
m_model->moveBackToTop();
if (m_cycleStartUuid == m_model->index(0).data(Qt::UserRole+1).toByteArray()) {
m_cycleStartUuid = QByteArray();
}
}
const HistoryItem* History::nextInCycle() const
{
return m_nextCycle != m_top ? m_nextCycle : 0L; // pointing to top=no more items
if (m_model->hasIndex(1, 0)) {
if (!m_cycleStartUuid.isEmpty()) {
// check whether we are not at the end
if (m_cycleStartUuid == m_model->index(1).data(Qt::UserRole+1).toByteArray()) {
return nullptr;
}
}
return m_model->index(1).data(Qt::UserRole).value<HistoryItem*>();
}
return nullptr;
}
const HistoryItem* History::prevInCycle() const
{
if (m_nextCycle) {
const HistoryItem* prev = m_items[m_nextCycle->previous_uuid()];
if (prev != m_top) {
return prev;
}
if (m_cycleStartUuid.isEmpty()) {
return nullptr;
}
return 0L;
return m_model->index(m_model->rowCount() - 1).data(Qt::UserRole).value<HistoryItem*>();
}
const HistoryItem* History::find(const QByteArray& uuid) const
{
items_t::const_iterator it = m_items.find(uuid);
return (it == m_items.end()) ? 0L : *it;
const QModelIndex index = m_model->indexOf(uuid);
if (!index.isValid()) {
return nullptr;
}
return index.data(Qt::UserRole).value<HistoryItem*>();
}
bool History::empty() const
{
return m_model->rowCount() == 0;
}
unsigned int History::maxSize() const
{
return m_model->maxSize();
}
const HistoryItem *History::first() const
{
const QModelIndex index = m_model->index(0);
if (!index.isValid()) {
return nullptr;
}
return index.data(Qt::UserRole).value<HistoryItem*>();
}
#include "history.moc"

@ -25,6 +25,7 @@
#include <QByteArray>
class HistoryItem;
class HistoryModel;
class QAction;
class History : public QObject
@ -78,7 +79,7 @@ public:
/**
* True if no history items
*/
bool empty() const { return m_items.isEmpty(); }
bool empty() const;
/**
* Set maximum history size
@ -88,7 +89,7 @@ public:
/**
* Get the maximum history size
*/
unsigned maxSize() const { return m_maxSize; }
unsigned maxSize() const;
/**
* returns true if the user has selected the top item
@ -131,43 +132,16 @@ Q_SIGNALS:
*/
void topChanged();
private:
/**
* ensure that the number of items does not exceed max_size()
* Deletes items from the end as necessary.
*/
void trim();
private:
typedef QHash<QByteArray, HistoryItem*> items_t;
/**
* The history
*/
items_t m_items;
/**
* First item
*/
HistoryItem* m_top;
/**
* The number of clipboard items stored.
*/
unsigned m_maxSize;
/**
* True if the top is selected by the user
*/
bool m_topIsUserSelected;
/**
* The "next" when cycling through the
* history. May be 0, if history is empty
*/
HistoryItem* m_nextCycle;
};
HistoryModel *m_model;
inline const HistoryItem* History::first() const { return m_top; }
QByteArray m_cycleStartUuid;
};
#endif

@ -27,9 +27,12 @@
#include "historystringitem.h"
#include "historyimageitem.h"
#include "historyurlitem.h"
#include "historymodel.h"
HistoryItem::HistoryItem(const QByteArray& uuid) : m_uuid(uuid) {
HistoryItem::HistoryItem(const QByteArray& uuid)
: m_uuid(uuid)
, m_model(nullptr)
{
}
HistoryItem::~HistoryItem() {
@ -94,32 +97,38 @@ HistoryItem* HistoryItem::create( QDataStream& dataStream ) {
return 0;
}
void HistoryItem::chain(HistoryItem* next)
QByteArray HistoryItem::next_uuid() const
{
m_next_uuid = next->uuid();
next->m_previous_uuid = uuid();
if (!m_model) {
return m_uuid;
}
// go via the model to the next
const QModelIndex ownIndex = m_model->indexOf(m_uuid);
if (!ownIndex.isValid()) {
// that was wrong, model doesn't contain our item, so there is no chain
return m_uuid;
}
const int nextRow = (ownIndex.row() +1) % m_model->rowCount();
return m_model->index(nextRow, 0).data(Qt::UserRole+1).toByteArray();
}
void HistoryItem::insertBetweeen(HistoryItem* prev, HistoryItem* next)
QByteArray HistoryItem::previous_uuid() const
{
if (prev && next) {
prev->chain(this);
chain(next);
} else {
Q_ASSERT(!prev && !next);
// First item in chain
m_next_uuid = m_uuid;
m_previous_uuid = m_uuid;
if (!m_model) {
return m_uuid;
}
#if 0 // Extra checks, if anyone ever needs them
Q_ASSERT(prev->uuid() == m_previous_uuid);
Q_ASSERT(prev->next_uuid() == m_uuid);
Q_ASSERT(next->previous_uuid() == m_uuid);
Q_ASSERT(next->uuid() == m_next_uuid);
Q_ASSERT(prev->uuid() != uuid());
Q_ASSERT(next->uuid() != uuid());
#endif
// go via the model to the next
const QModelIndex ownIndex = m_model->indexOf(m_uuid);
if (!ownIndex.isValid()) {
// that was wrong, model doesn't contain our item, so there is no chain
return m_uuid;
}
const int nextRow = ((ownIndex.row() == 0) ? m_model->rowCount() : ownIndex.row()) - 1;
return m_model->index(nextRow, 0).data(Qt::UserRole+1).toByteArray();
}
void HistoryItem::setModel(HistoryModel *model)
{
m_model = model;
}

@ -21,6 +21,7 @@
#include <QPixmap>
class HistoryModel;
class QString;
class QMimeData;
class QDataStream;
@ -83,33 +84,22 @@ public:
*/
static HistoryItem* create( QDataStream& dataStream );
/**
* Inserts this item between prev and next
*/
void insertBetweeen(HistoryItem* prev, HistoryItem* next);
/**
* Chain this with next
*/
void chain(HistoryItem* next);
/**
* previous item's uuid
* TODO: drop, only used in unit test now
*/
const QByteArray& previous_uuid() const {
return m_previous_uuid;
}
QByteArray previous_uuid() const;
/**
* next item's uuid
* TODO: drop, only used in unit test now
*/
const QByteArray& next_uuid() const {
return m_next_uuid;
}
QByteArray next_uuid() const;
void setModel(HistoryModel *model);
private:
QByteArray m_previous_uuid;
QByteArray m_uuid;
QByteArray m_next_uuid;
HistoryModel *m_model;
};
inline
@ -127,4 +117,6 @@ QDataStream& operator<<( QDataStream& lhs, HistoryItem const * const rhs ) {
}
Q_DECLARE_METATYPE(HistoryItem*)
#endif

@ -21,8 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QDebug>
Q_DECLARE_METATYPE(HistoryItem*)
HistoryModel::HistoryModel(QObject *parent)
: QAbstractListModel(parent)
, m_maxSize(0)
@ -145,6 +143,7 @@ void HistoryModel::insert(QSharedPointer<HistoryItem> item)
}
beginInsertRows(QModelIndex(), 0, 0);
item->setModel(this);
m_items.prepend(item);
endInsertRows();
}
@ -167,3 +166,19 @@ void HistoryModel::moveToTop(int row)
m_items.move(row, 0);
endMoveRows();
}
void HistoryModel::moveTopToBack()
{
if (m_items.count() < 2) {
return;
}
beginMoveRows(QModelIndex(), 0, 0, QModelIndex(), m_items.count());
auto item = m_items.takeFirst();
m_items.append(item);
endMoveRows();
}
void HistoryModel::moveBackToTop()
{
moveToTop(m_items.count() - 1);
}

@ -39,6 +39,8 @@ public:
void clear();
void moveToTop(const QByteArray &uuid);
void moveTopToBack();
void moveBackToTop();
QModelIndex indexOf(const QByteArray &uuid) const;
QModelIndex indexOf(const HistoryItem *item) const;

Loading…
Cancel
Save