1 /*
2     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "historymodel.h"
8 #include "historyimageitem.h"
9 #include "historystringitem.h"
10 #include "historyurlitem.h"
11 
HistoryModel(QObject * parent)12 HistoryModel::HistoryModel(QObject *parent)
13     : QAbstractListModel(parent)
14     , m_maxSize(0)
15     , m_displayImages(true)
16     , m_mutex(QMutex::Recursive)
17 {
18 }
19 
~HistoryModel()20 HistoryModel::~HistoryModel()
21 {
22     clear();
23 }
24 
clear()25 void HistoryModel::clear()
26 {
27     QMutexLocker lock(&m_mutex);
28     beginResetModel();
29     m_items.clear();
30     endResetModel();
31 }
32 
setMaxSize(int size)33 void HistoryModel::setMaxSize(int size)
34 {
35     if (m_maxSize == size) {
36         return;
37     }
38     QMutexLocker lock(&m_mutex);
39     m_maxSize = size;
40     if (m_items.count() > m_maxSize) {
41         removeRows(m_maxSize, m_items.count() - m_maxSize);
42     }
43 }
44 
rowCount(const QModelIndex & parent) const45 int HistoryModel::rowCount(const QModelIndex &parent) const
46 {
47     if (parent.isValid()) {
48         return 0;
49     }
50     return m_items.count();
51 }
52 
data(const QModelIndex & index,int role) const53 QVariant HistoryModel::data(const QModelIndex &index, int role) const
54 {
55     if (!index.isValid() || index.row() >= m_items.count() || index.column() != 0) {
56         return QVariant();
57     }
58 
59     QSharedPointer<HistoryItem> item = m_items.at(index.row());
60     HistoryItemType type = HistoryItemType::Text;
61     if (dynamic_cast<HistoryStringItem *>(item.data())) {
62         type = HistoryItemType::Text;
63     } else if (dynamic_cast<HistoryImageItem *>(item.data())) {
64         type = HistoryItemType::Image;
65     } else if (dynamic_cast<HistoryURLItem *>(item.data())) {
66         type = HistoryItemType::Url;
67     }
68 
69     switch (role) {
70     case Qt::DisplayRole:
71         return item->text();
72     case Qt::DecorationRole:
73         return item->image();
74     case Qt::UserRole:
75         return QVariant::fromValue<HistoryItemConstPtr>(qSharedPointerConstCast<const HistoryItem>(item));
76     case Qt::UserRole + 1:
77         return item->uuid();
78     case Qt::UserRole + 2:
79         return QVariant::fromValue<HistoryItemType>(type);
80     case Qt::UserRole + 3:
81         return item->uuid().toBase64();
82     case Qt::UserRole + 4:
83         return int(type);
84     }
85     return QVariant();
86 }
87 
removeRows(int row,int count,const QModelIndex & parent)88 bool HistoryModel::removeRows(int row, int count, const QModelIndex &parent)
89 {
90     if (parent.isValid()) {
91         return false;
92     }
93     if ((row + count) > m_items.count()) {
94         return false;
95     }
96     QMutexLocker lock(&m_mutex);
97     beginRemoveRows(QModelIndex(), row, row + count - 1);
98     for (int i = 0; i < count; ++i) {
99         m_items.removeAt(row);
100     }
101     endRemoveRows();
102     return true;
103 }
104 
remove(const QByteArray & uuid)105 bool HistoryModel::remove(const QByteArray &uuid)
106 {
107     QModelIndex index = indexOf(uuid);
108     if (!index.isValid()) {
109         return false;
110     }
111     return removeRow(index.row(), QModelIndex());
112 }
113 
indexOf(const QByteArray & uuid) const114 QModelIndex HistoryModel::indexOf(const QByteArray &uuid) const
115 {
116     for (int i = 0; i < m_items.count(); ++i) {
117         if (m_items.at(i)->uuid() == uuid) {
118             return index(i);
119         }
120     }
121     return QModelIndex();
122 }
123 
indexOf(const HistoryItem * item) const124 QModelIndex HistoryModel::indexOf(const HistoryItem *item) const
125 {
126     if (!item) {
127         return QModelIndex();
128     }
129     return indexOf(item->uuid());
130 }
131 
insert(QSharedPointer<HistoryItem> item)132 void HistoryModel::insert(QSharedPointer<HistoryItem> item)
133 {
134     if (item.isNull()) {
135         return;
136     }
137     const QModelIndex existingItem = indexOf(item.data());
138     if (existingItem.isValid()) {
139         // move to top
140         moveToTop(existingItem.row());
141         return;
142     }
143 
144     QMutexLocker lock(&m_mutex);
145     if (m_items.count() == m_maxSize) {
146         // remove last item
147         if (m_maxSize == 0) {
148             // special case - cannot insert any items
149             return;
150         }
151         beginRemoveRows(QModelIndex(), m_items.count() - 1, m_items.count() - 1);
152         m_items.removeLast();
153         endRemoveRows();
154     }
155 
156     beginInsertRows(QModelIndex(), 0, 0);
157     item->setModel(this);
158     m_items.prepend(item);
159     endInsertRows();
160 }
161 
moveToTop(const QByteArray & uuid)162 void HistoryModel::moveToTop(const QByteArray &uuid)
163 {
164     const QModelIndex existingItem = indexOf(uuid);
165     if (!existingItem.isValid()) {
166         return;
167     }
168     moveToTop(existingItem.row());
169 }
170 
moveToTop(int row)171 void HistoryModel::moveToTop(int row)
172 {
173     if (row == 0 || row >= m_items.count()) {
174         return;
175     }
176     QMutexLocker lock(&m_mutex);
177     beginMoveRows(QModelIndex(), row, row, QModelIndex(), 0);
178     m_items.move(row, 0);
179     endMoveRows();
180 }
181 
moveTopToBack()182 void HistoryModel::moveTopToBack()
183 {
184     if (m_items.count() < 2) {
185         return;
186     }
187     QMutexLocker lock(&m_mutex);
188     beginMoveRows(QModelIndex(), 0, 0, QModelIndex(), m_items.count());
189     auto item = m_items.takeFirst();
190     m_items.append(item);
191     endMoveRows();
192 }
193 
moveBackToTop()194 void HistoryModel::moveBackToTop()
195 {
196     moveToTop(m_items.count() - 1);
197 }
198 
roleNames() const199 QHash<int, QByteArray> HistoryModel::roleNames() const
200 {
201     QHash<int, QByteArray> hash;
202     hash.insert(Qt::DisplayRole, QByteArrayLiteral("DisplayRole"));
203     hash.insert(Qt::DecorationRole, QByteArrayLiteral("DecorationRole"));
204     hash.insert(Qt::UserRole + 3, QByteArrayLiteral("UuidRole"));
205     hash.insert(Qt::UserRole + 4, QByteArrayLiteral("TypeRole"));
206     return hash;
207 }
208