1/* This file is part of the KDE project
2 Copyright (C) 2004 Esben Mose Hansen <kde@mosehansen.dk>
3 Copyright (C) by Andrew Stanley-Jones <asj@cban.com>
4 Copyright (C) 2000 by Carsten Pfeiffer <pfeiffer@kde.org>
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21#include "history.h"
22
23#include <QtGui/QAction>
24
25#include <KDebug>
26
27#include "historystringitem.h"
28#include "klipperpopup.h"
29
30History::History( QObject* parent )
31 : QObject( parent ),
32 m_top(0L),
33 m_popup( new KlipperPopup( this ) ),
34 m_topIsUserSelected( false ),
35 m_nextCycle(0L)
36{
37 connect( this, SIGNAL(changed()), m_popup, SLOT(slotHistoryChanged()) );
38
39}
40
41
42History::~History() {
43 qDeleteAll(m_items);
44}
45
46void History::insert( HistoryItem* item ) {
47 if ( !item )
48 return;
49
50 m_topIsUserSelected = false;
51 const HistoryItem* existingItem = this->find(item->uuid());
52 if ( existingItem ) {
53 if ( existingItem == m_top) {
54 return;
55 }
56 slotMoveToTop( existingItem->uuid() );
57 } else {
58 forceInsert( item );
59 }
60
61 emit topChanged();
62
63}
64
65void History::forceInsert( HistoryItem* item ) {
66 if ( !item )
67 return;
68 if (m_items.find(item->uuid()) != m_items.end()) {
69 return; // Don't insert duplicates
70 }
71 m_nextCycle = m_top;
72 item->insertBetweeen(m_top ? m_items[m_top->previous_uuid()] : 0L, m_top);
73 m_items.insert( item->uuid(), item );
74 m_top = item;
75 emit changed();
76 trim();
77}
78
79void History::trim() {
80 int i = m_items.count() - maxSize();
81 if ( i <= 0 || !m_top )
82 return;
83
84 items_t::iterator bottom = m_items.find(m_top->previous_uuid());
85 while ( i-- ) {
86 items_t::iterator it = bottom;
87 bottom = m_items.find((*bottom)->previous_uuid());
88 // FIXME: managing memory manually is tedious; use smart pointer instead
89 delete *it;
90 m_items.erase(it);
91 }
92 (*bottom)->chain(m_top);
93 if (m_items.size()<=1) {
94 m_nextCycle = 0L;
95 }
96 emit changed();
97}
98
99void History::remove( const HistoryItem* newItem ) {
100 if ( !newItem )
101 return;
102
103 items_t::iterator it = m_items.find(newItem->uuid());
104 if (it == m_items.end()) {
105 return;
106 }
107
108 if (*it == m_top) {
109 m_top = m_items[m_top->next_uuid()];
110 }
111 m_items[(*it)->previous_uuid()]->chain(m_items[(*it)->next_uuid()]);
112 m_items.erase(it);
113}
114
115
116void History::slotClear() {
117 // FIXME: managing memory manually is tedious; use smart pointer instead
118 qDeleteAll(m_items);
119 m_items.clear();
120 m_top = 0L;
121 emit changed();
122}
123
124void History::slotMoveToTop(QAction* action) {
125 QByteArray uuid = action->data().toByteArray();
126 if (uuid.isNull()) // not an action from popupproxy
127 return;
128
129 slotMoveToTop(uuid);
130}
131
132void History::slotMoveToTop(const QByteArray& uuid) {
133
134 items_t::iterator it = m_items.find(uuid);
135 if (it == m_items.end()) {
136 return;
137 }
138 if (*it == m_top) {
139 emit topChanged();
140 return;
141 }
142 m_topIsUserSelected = true;
143
144 m_nextCycle = m_top;
145 m_items[(*it)->previous_uuid()]->chain(m_items[(*it)->next_uuid()]);
146 (*it)->insertBetweeen(m_items[m_top->previous_uuid()], m_top);
147 m_top = *it;
148 emit changed();
149 emit topChanged();
150}
151
152void History::setMaxSize( unsigned max_size ) {
153 m_maxSize = max_size;
154 trim();
155}
156
157KlipperPopup* History::popup() {
158 return m_popup;
159}
160
161void History::cycleNext() {
162 if (m_top && m_nextCycle && m_nextCycle != m_top) {
163 HistoryItem* prev = m_items[m_nextCycle->previous_uuid()];
164 HistoryItem* next = m_items[m_nextCycle->next_uuid()];
165 //if we have only two items in clipboard
166 if (prev == next) {
167 m_top=m_nextCycle;
168 }
169 else {
170 HistoryItem* endofhist = m_items[m_top->previous_uuid()];
171 HistoryItem* aftertop = m_items[m_top->next_uuid()];
172 if (prev == m_top) {
173 prev = m_nextCycle;
174 aftertop = m_top;
175 }
176 else if (next == m_top) {
177 next = m_nextCycle;
178 endofhist = m_top;
179 }
180 m_top->insertBetweeen(prev, next);
181 m_nextCycle->insertBetweeen(endofhist, aftertop);
182 m_top = m_nextCycle;
183 m_nextCycle = next;
184 }
185 emit changed();
186 emit topChanged();
187 }
188}
189
190void History::cyclePrev() {
191 if (m_top && m_nextCycle) {
192 HistoryItem* prev = m_items[m_nextCycle->previous_uuid()];
193 if (prev == m_top) {
194 return;
195 }
196 HistoryItem* prevprev = m_items[prev->previous_uuid()];
197 HistoryItem* aftertop = m_items[m_top->next_uuid()];
198 //if we have only two items in clipboard
199 if (m_nextCycle == prevprev) {
200 m_top=aftertop;
201 }
202 else {
203 HistoryItem* endofhist = m_items[m_top->previous_uuid()];
204 if (prevprev == m_top) {
205 prevprev = prev;
206 aftertop = m_top;
207 }
208 else if (m_nextCycle == m_top) {
209 m_nextCycle = aftertop;
210 endofhist = m_top;
211 }
212 m_top->insertBetweeen(prevprev,m_nextCycle);
213 prev->insertBetweeen(endofhist, aftertop);
214 m_nextCycle = m_top;
215 m_top = prev;
216 }
217 emit changed();
218 emit topChanged();
219 }
220}
221
222
223const HistoryItem* History::nextInCycle() const
224{
225 return m_nextCycle != m_top ? m_nextCycle : 0L; // pointing to top=no more items
226
227}
228
229const HistoryItem* History::prevInCycle() const
230{
231 if (m_nextCycle) {
232 const HistoryItem* prev = m_items[m_nextCycle->previous_uuid()];
233 if (prev != m_top) {
234 return prev;
235 }
236 }
237 return 0L;
238
239}
240
241const HistoryItem* History::find(const QByteArray& uuid) const
242{
243 items_t::const_iterator it = m_items.find(uuid);
244 return (it == m_items.end()) ? 0L : *it;
245}
246
247#include "history.moc"
248