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 | |
30 | History::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 | |
42 | History::~History() { |
43 | qDeleteAll(m_items); |
44 | } |
45 | |
46 | void 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 | |
65 | void 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 | |
79 | void 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 | |
99 | void 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 | |
116 | void 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 | |
124 | void 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 | |
132 | void 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 | |
152 | void History::setMaxSize( unsigned max_size ) { |
153 | m_maxSize = max_size; |
154 | trim(); |
155 | } |
156 | |
157 | KlipperPopup* History::() { |
158 | return m_popup; |
159 | } |
160 | |
161 | void 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 | |
190 | void 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 | |
223 | const HistoryItem* History::nextInCycle() const |
224 | { |
225 | return m_nextCycle != m_top ? m_nextCycle : 0L; // pointing to top=no more items |
226 | |
227 | } |
228 | |
229 | const 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 | |
241 | const 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 | |