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;