File indexing completed on 2024-04-28 16:54:24
0001 /* 0002 SPDX-FileCopyrightText: 2004 Esben Mose Hansen <kde@mosehansen.dk> 0003 SPDX-FileCopyrightText: Andrew Stanley-Jones <asj@cban.com> 0004 SPDX-FileCopyrightText: 2000 Carsten Pfeiffer <pfeiffer@kde.org> 0005 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "history.h" 0010 0011 #include <QAction> 0012 0013 #include "historyitem.h" 0014 #include "historymodel.h" 0015 #include "historystringitem.h" 0016 0017 class CycleBlocker 0018 { 0019 public: 0020 CycleBlocker(); 0021 ~CycleBlocker(); 0022 0023 static bool isBlocked(); 0024 0025 private: 0026 static int s_blocker; 0027 }; 0028 0029 int CycleBlocker::s_blocker = 0; 0030 CycleBlocker::CycleBlocker() 0031 { 0032 s_blocker++; 0033 } 0034 0035 CycleBlocker::~CycleBlocker() 0036 { 0037 s_blocker--; 0038 } 0039 0040 bool CycleBlocker::isBlocked() 0041 { 0042 return s_blocker; 0043 } 0044 0045 History::History(QObject *parent) 0046 : QObject(parent) 0047 , m_topIsUserSelected(false) 0048 , m_model(new HistoryModel(this)) 0049 { 0050 connect(m_model, &HistoryModel::rowsInserted, this, [this](const QModelIndex &parent, int start) { 0051 Q_UNUSED(parent) 0052 if (start == 0) { 0053 Q_EMIT topChanged(); 0054 } 0055 Q_EMIT changed(); 0056 }); 0057 connect(m_model, 0058 &HistoryModel::rowsMoved, 0059 this, 0060 [this](const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) { 0061 Q_UNUSED(sourceParent) 0062 Q_UNUSED(sourceEnd) 0063 Q_UNUSED(destinationParent) 0064 if (sourceStart == 0 || destinationRow == 0) { 0065 Q_EMIT topChanged(); 0066 } 0067 Q_EMIT changed(); 0068 }); 0069 connect(m_model, &HistoryModel::rowsRemoved, this, [this](const QModelIndex &parent, int start) { 0070 Q_UNUSED(parent) 0071 if (start == 0) { 0072 Q_EMIT topChanged(); 0073 } 0074 Q_EMIT changed(); 0075 }); 0076 connect(m_model, &HistoryModel::modelReset, this, &History::changed); 0077 connect(m_model, &HistoryModel::modelReset, this, &History::topChanged); 0078 connect(this, &History::topChanged, [this]() { 0079 m_topIsUserSelected = false; 0080 if (!CycleBlocker::isBlocked()) { 0081 m_cycleStartUuid = QByteArray(); 0082 } 0083 }); 0084 } 0085 0086 History::~History() 0087 { 0088 } 0089 0090 void History::insert(HistoryItemPtr item) 0091 { 0092 if (!item) 0093 return; 0094 0095 m_model->insert(item); 0096 } 0097 0098 void History::clearAndBatchInsert(const QVector<HistoryItemPtr> &items) 0099 { 0100 m_model->clearAndBatchInsert(items); 0101 } 0102 0103 void History::remove(const HistoryItemConstPtr &newItem) 0104 { 0105 if (!newItem) 0106 return; 0107 0108 m_model->remove(newItem->uuid()); 0109 } 0110 0111 void History::slotClear() 0112 { 0113 m_model->clear(); 0114 } 0115 0116 void History::slotMoveToTop(QAction *action) 0117 { 0118 QByteArray uuid = action->data().toByteArray(); 0119 if (uuid.isNull()) // not an action from popupproxy 0120 return; 0121 0122 slotMoveToTop(uuid); 0123 } 0124 0125 void History::slotMoveToTop(const QByteArray &uuid) 0126 { 0127 const QModelIndex item = m_model->indexOf(uuid); 0128 if (item.isValid() && item.row() == 0) { 0129 // The item is already at the top, but it still may be not be set as the actual clipboard 0130 // contents, normally this happens if the item is only in the X11 mouse selection but 0131 // not in the Ctrl+V clipboard. 0132 Q_EMIT topChanged(); 0133 m_topIsUserSelected = true; 0134 Q_EMIT topIsUserSelectedSet(); 0135 return; 0136 } 0137 m_model->moveToTop(uuid); 0138 m_topIsUserSelected = true; 0139 Q_EMIT topIsUserSelectedSet(); 0140 } 0141 0142 void History::setMaxSize(unsigned max_size) 0143 { 0144 m_model->setMaxSize(max_size); 0145 } 0146 0147 void History::cycleNext() 0148 { 0149 if (m_model->rowCount() < 2) { 0150 return; 0151 } 0152 0153 if (m_cycleStartUuid.isEmpty()) { 0154 m_cycleStartUuid = m_model->index(0).data(HistoryModel::UuidRole).toByteArray(); 0155 } else if (m_cycleStartUuid == m_model->index(1).data(HistoryModel::UuidRole).toByteArray()) { 0156 // end of cycle 0157 return; 0158 } 0159 CycleBlocker blocker; 0160 m_model->moveTopToBack(); 0161 } 0162 0163 void History::cyclePrev() 0164 { 0165 if (m_cycleStartUuid.isEmpty()) { 0166 return; 0167 } 0168 CycleBlocker blocker; 0169 m_model->moveBackToTop(); 0170 if (m_cycleStartUuid == m_model->index(0).data(HistoryModel::UuidRole).toByteArray()) { 0171 m_cycleStartUuid = QByteArray(); 0172 } 0173 } 0174 0175 HistoryItemConstPtr History::nextInCycle() const 0176 { 0177 if (m_model->hasIndex(1, 0)) { 0178 if (!m_cycleStartUuid.isEmpty()) { 0179 // check whether we are not at the end 0180 if (m_cycleStartUuid == m_model->index(1).data(HistoryModel::UuidRole).toByteArray()) { 0181 return HistoryItemConstPtr(); 0182 } 0183 } 0184 return m_model->index(1).data(HistoryModel::HistoryItemConstPtrRole).value<HistoryItemConstPtr>(); 0185 } 0186 return HistoryItemConstPtr(); 0187 } 0188 0189 HistoryItemConstPtr History::prevInCycle() const 0190 { 0191 if (m_cycleStartUuid.isEmpty()) { 0192 return HistoryItemConstPtr(); 0193 } 0194 return m_model->index(m_model->rowCount() - 1).data(HistoryModel::HistoryItemConstPtrRole).value<HistoryItemConstPtr>(); 0195 } 0196 0197 HistoryItemConstPtr History::find(const QByteArray &uuid) const 0198 { 0199 const QModelIndex index = m_model->indexOf(uuid); 0200 if (!index.isValid()) { 0201 return HistoryItemConstPtr(); 0202 } 0203 return index.data(HistoryModel::HistoryItemConstPtrRole).value<HistoryItemConstPtr>(); 0204 } 0205 0206 bool History::empty() const 0207 { 0208 return m_model->rowCount() == 0; 0209 } 0210 0211 unsigned int History::maxSize() const 0212 { 0213 return m_model->maxSize(); 0214 } 0215 0216 HistoryItemConstPtr History::first() const 0217 { 0218 const QModelIndex index = m_model->index(0); 0219 if (!index.isValid()) { 0220 return HistoryItemConstPtr(); 0221 } 0222 return index.data(HistoryModel::HistoryItemConstPtrRole).value<HistoryItemConstPtr>(); 0223 }