diff --git a/klipper/autotests/CMakeLists.txt b/klipper/autotests/CMakeLists.txt index e167c6e8d..f5d9ade6a 100644 --- a/klipper/autotests/CMakeLists.txt +++ b/klipper/autotests/CMakeLists.txt @@ -10,6 +10,7 @@ set(testHistory_SRCS ../historyitem.cpp ../historystringitem.cpp ../historyurlitem.cpp + ../historymodel.cpp ) add_executable(testHistory ${testHistory_SRCS}) target_link_libraries(testHistory diff --git a/klipper/autotests/historymodeltest.cpp b/klipper/autotests/historymodeltest.cpp index 12081fa39..d2462ec91 100644 --- a/klipper/autotests/historymodeltest.cpp +++ b/klipper/autotests/historymodeltest.cpp @@ -21,7 +21,6 @@ along with this program. If not, see . #include "../historystringitem.h" #include -Q_DECLARE_METATYPE(HistoryItem*) class HistoryModelTest : public QObject { @@ -87,69 +86,86 @@ void HistoryModelTest::testInsertRemove() // let's insert a few items history->insert(QSharedPointer(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()->next_uuid(), index.data(Qt::UserRole+1).toByteArray()); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), index.data(Qt::UserRole+1).toByteArray()); history->insert(QSharedPointer(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()->next_uuid(), fooUuid); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), fooUuid); + index = history->indexOf(fooUuid); + QCOMPARE(index.data(Qt::UserRole).value()->next_uuid(), barUuid); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), barUuid); history->insert(QSharedPointer(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()->next_uuid(), barUuid); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), fooUuid); + index = history->indexOf(fooUuid); + QCOMPARE(index.data(Qt::UserRole).value()->next_uuid(), foobarUuid); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), barUuid); + index = history->indexOf(barUuid); + QCOMPARE(index.data(Qt::UserRole).value()->next_uuid(), fooUuid); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), foobarUuid); // insert one again - it should be moved to top history->insert(QSharedPointer(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()->next_uuid(), foobarUuid); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), fooUuid); + index = history->indexOf(fooUuid); + QCOMPARE(index.data(Qt::UserRole).value()->next_uuid(), barUuid); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), foobarUuid); + index = history->indexOf(foobarUuid); + QCOMPARE(index.data(Qt::UserRole).value()->next_uuid(), fooUuid); + QCOMPARE(index.data(Qt::UserRole).value()->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()->next_uuid(), foobarUuid); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), fooUuid); + index = history->indexOf(fooUuid); + QCOMPARE(index.data(Qt::UserRole).value()->next_uuid(), barUuid); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), foobarUuid); + index = history->indexOf(foobarUuid); + QCOMPARE(index.data(Qt::UserRole).value()->next_uuid(), fooUuid); + QCOMPARE(index.data(Qt::UserRole).value()->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()->next_uuid(), barUuid); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), fooUuid); + index = history->indexOf(fooUuid); + QCOMPARE(index.data(Qt::UserRole).value()->next_uuid(), foobarUuid); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), barUuid); + index = history->indexOf(barUuid); + QCOMPARE(index.data(Qt::UserRole).value()->next_uuid(), fooUuid); + QCOMPARE(index.data(Qt::UserRole).value()->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()->next_uuid(), fooUuid); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), fooUuid); + index = history->indexOf(fooUuid); + QCOMPARE(index.data(Qt::UserRole).value()->next_uuid(), barUuid); + QCOMPARE(index.data(Qt::UserRole).value()->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()->next_uuid(), index.data(Qt::UserRole+1).toByteArray()); + QCOMPARE(index.data(Qt::UserRole).value()->previous_uuid(), index.data(Qt::UserRole+1).toByteArray()); QVERIFY(history->remove(fooUuid)); QCOMPARE(history->rowCount(), 0); diff --git a/klipper/autotests/historytest.cpp b/klipper/autotests/historytest.cpp index d00708040..631979781 100644 --- a/klipper/autotests/historytest.cpp +++ b/klipper/autotests/historytest.cpp @@ -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()); } diff --git a/klipper/history.cpp b/klipper/history.cpp index 56e48f7ac..963dc4a23 100644 --- a/klipper/history.cpp +++ b/klipper/history.cpp @@ -2,6 +2,7 @@ Copyright (C) 2004 Esben Mose Hansen Copyright (C) by Andrew Stanley-Jones Copyright (C) 2000 by Carsten Pfeiffer + Copyright (C) 2014 by Martin Gräßlin 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(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(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(); + } + 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(); } 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(); +} + +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(); } #include "history.moc" diff --git a/klipper/history.h b/klipper/history.h index 747aa673f..d2ccbd9eb 100644 --- a/klipper/history.h +++ b/klipper/history.h @@ -25,6 +25,7 @@ #include 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 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 diff --git a/klipper/historyitem.cpp b/klipper/historyitem.cpp index 1205dd339..eead6b377 100644 --- a/klipper/historyitem.cpp +++ b/klipper/historyitem.cpp @@ -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; } diff --git a/klipper/historyitem.h b/klipper/historyitem.h index 97d301c04..30166786b 100644 --- a/klipper/historyitem.h +++ b/klipper/historyitem.h @@ -21,6 +21,7 @@ #include +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 diff --git a/klipper/historymodel.cpp b/klipper/historymodel.cpp index 12c6fb7d4..222e3aa81 100644 --- a/klipper/historymodel.cpp +++ b/klipper/historymodel.cpp @@ -21,8 +21,6 @@ along with this program. If not, see . #include -Q_DECLARE_METATYPE(HistoryItem*) - HistoryModel::HistoryModel(QObject *parent) : QAbstractListModel(parent) , m_maxSize(0) @@ -145,6 +143,7 @@ void HistoryModel::insert(QSharedPointer 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); +} diff --git a/klipper/historymodel.h b/klipper/historymodel.h index b4fdc67ea..16695b71b 100644 --- a/klipper/historymodel.h +++ b/klipper/historymodel.h @@ -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;