koIconChooser.cpp
1/* This file is part of the KDE project
2 Copyright (c) 1999 Carsten Pfeiffer (pfeiffer@kde.org)
3 Copyright (c) 2002 Igor Jansen (rm@kde.org)
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include <kdebug.h>
22#include <koIconChooser.h>
23#include <tdeglobal.h>
24
25#include <tqpainter.h>
26#include <tqcursor.h>
27#include <tqapplication.h>
28#include <tqhbox.h>
29#include <tqlayout.h>
30#include <kdebug.h>
31
32KoPixmapWidget::KoPixmapWidget(const TQPixmap &aPixmap, TQWidget *parent, const char *name):
33TQFrame(parent, name, WType_Popup)
34{
35 kdDebug() << "Popup created: " << name << "\n";
36 setFrameStyle(TQFrame::WinPanel | TQFrame::Raised);
37 mPixmap = aPixmap;
38 int w = mPixmap.width() + 2 * lineWidth();
39 int h = mPixmap.height() + 2 * lineWidth();
40 resize(w, h);
41
42}
43
44KoPixmapWidget::~KoPixmapWidget()
45{
46}
47
48// paint the centered pixmap; don't overpaint the frame
49void KoPixmapWidget::paintEvent(TQPaintEvent *e)
50{
51 TQFrame::paintEvent(e);
52 TQPainter p(this);
53 p.setClipRect(e->rect());
54 p.drawPixmap(lineWidth(), lineWidth(), mPixmap);
55}
56
57
58void KoPixmapWidget::mouseReleaseEvent(TQMouseEvent *)
59{
60 hide();
61}
62
63
64KoIconChooser::KoIconChooser(TQSize aIconSize, TQWidget *parent, const char *name, bool sort):
65TQGridView(parent, name)
66{
67 TQGridView::setBackgroundColor(TQt::white);
68
69 mMargin = 2;
70 setCellWidth(aIconSize.width() + 2 * mMargin);
71 setCellHeight(aIconSize.height() + 2 * mMargin);
72
73 mIconList.clear();
74 mPixmapWidget = 0L;
75 mNCols = 0;
76 mCurRow = 0;
77 mCurCol = 0;
78 mItemCount = 0;
79 mItemWidth = aIconSize.width();
80 mItemHeight = aIconSize.height();
81 mMouseButtonDown = false;
82 mDragEnabled = false;
83 mSort = sort;
84}
85
86KoIconChooser::~KoIconChooser()
87{
88 mIconList.clear();
89 if(mPixmapWidget)
90 delete mPixmapWidget;
91}
92
93void KoIconChooser::addItem(KoIconItem *item)
94{
95 TQ_INT32 n = mItemCount;
96 KoIconItem *current = currentItem();
97
98 Q_ASSERT(item);
99
100 int i;
101
102 if (mSort)
103 {
104 i = sortInsertionIndex(item);
105 }
106 else
107 {
108 i = mItemCount;
109 }
110
111 mIconList.insert(i, item);
112 mItemCount++;
113 calculateCells();
114
115 if (mSort)
116 {
117 setCurrentItem(current);
118 updateContents();
119 }
120 else
121 {
122 updateCell(n / numCols(), n - (n / numCols()) * numCols());
123 }
124}
125
126bool KoIconChooser::removeItem(KoIconItem *item)
127{
128 int index = mIconList.find(item);
129 bool ok = mIconList.remove(item);
130 if( ok )
131 {
132 mItemCount--;
133 // select same cell as deleted cell, or new last cell if we deleted
134 // last cell.
135 setCurrentItem(itemAt(kMin(index, mItemCount - 1)));
136 calculateCells();
137 }
138 return ok;
139}
140
141void KoIconChooser::clear()
142{
143 mItemCount = 0;
144 mIconList.clear();
145 calculateCells();
146}
147
148// return 0L if there is no current item
149KoIconItem *KoIconChooser::currentItem()
150{
151 return itemAt(mCurRow, mCurCol);
152}
153
154// sets the current item to item
155// does NOT emit selected() (should it?)
156void KoIconChooser::setCurrentItem(KoIconItem *item)
157{
158 int index = mIconList.find(item);
159
160 // item is available
161 if(index != -1 && mNCols > 0)
162 {
163 int oldRow = mCurRow;
164 int oldCol = mCurCol;
165
166 mCurRow = index / mNCols;
167 mCurCol = index % mNCols;
168
169 // repaint the old and the new item
170 repaintCell(oldRow, oldCol);
171 repaintCell(mCurRow, mCurCol);
172
173 ensureCellVisible(mCurRow, mCurCol);
174 }
175}
176
177// eventually select the item, clicked on
178void KoIconChooser::mousePressEvent(TQMouseEvent *e)
179{
180 TQGridView::mousePressEvent(e);
181}
182
183void KoIconChooser::mouseMoveEvent(TQMouseEvent *e)
184{
185 if(mMouseButtonDown && mDragEnabled )
186 {
187#if 0
188 if(mPixmapWidget)
189 {
190 delete mPixmapWidget;
191 mPixmapWidget = 0L;
192 }
193#endif
194 if( ( mDragStartPos - e->pos() ).manhattanLength() > TQApplication::startDragDistance() )
195 startDrag();
196 }
197}
198
199void
200KoIconChooser::startDrag()
201{
202 mMouseButtonDown = false;
203}
204
205void KoIconChooser::mouseReleaseEvent(TQMouseEvent * e)
206{
207 mMouseButtonDown = true;
208 if(e->button() == TQt::LeftButton)
209 {
210 TQPoint p = e->pos();
211 mDragStartPos = p;
212 int x = contentsX() + p.x();
213 int y = contentsY() + p.y();
214 TQSize gridExtent = gridSize();
215
216 if (x < gridExtent.width() && y < gridExtent.height())
217 {
218 int row = rowAt(y);
219 int col = columnAt(x);
220
221 KoIconItem *item = itemAt(row, col);
222 if(item)
223 {
224 const TQPixmap &pix = item->pixmap();
225 if(pix.width() > mItemWidth || pix.height() > mItemHeight)
226 showFullPixmap(pix, p);
227
228 setCurrentItem(item);
229 emit selected( item );
230 }
231 }
232 }
233}
234
235// FIXME: implement keyboard navigation
236void KoIconChooser::keyPressEvent(TQKeyEvent *e)
237{
238 TQGridView::keyPressEvent(e);
239}
240
241// recalculate the number of items that fit into one row
242// set the current item again after calculating the new grid
243void KoIconChooser::resizeEvent(TQResizeEvent *e)
244{
245 TQGridView::resizeEvent(e);
246
247 KoIconItem *item = currentItem();
248 int oldNCols = mNCols;
249
250 if (cellWidth() != 0)
251 {
252 mNCols = e -> size().width() / cellWidth();
253 }
254
255 if(mNCols != oldNCols)
256 {
257 setNumCols(mNCols);
258 calculateCells();
259 setCurrentItem(item);
260 updateContents();
261 }
262}
263
264// paint one cell
265// mark the current item and center items smaller than the cellSize
266// TODO: scale down big pixmaps and paint the size as text into the pixmap
267void KoIconChooser::paintCell(TQPainter *p, int row, int col)
268{
269 KoIconItem *item = itemAt(row, col);
270
271 if(item)
272 {
273 const TQPixmap &pix = item->pixmap();
274
275 int x = mMargin;
276 int y = mMargin;
277 int pw = pix.width();
278 int ph = pix.height();
279 int cw = cellWidth();
280 int ch = cellHeight();
281
282 // center small pixmaps
283 if(pw < mItemWidth)
284 x = (cw - pw) / 2;
285 if(ph < mItemHeight)
286 y = (cw - ph) / 2;
287
288 if((!item->hasValidThumb()) || (pw <= mItemWidth && ph <= mItemHeight))
289 p->drawPixmap(x, y, pix, 0, 0, mItemWidth, mItemHeight);
290 else
291 {
292 const TQPixmap &thumbpix = item->thumbPixmap();
293 x = mMargin;
294 y = mMargin;
295 pw = thumbpix.width();
296 ph = thumbpix.height();
297 cw = cellWidth();
298 ch = cellHeight();
299
300 // center small pixmaps
301 if(pw < mItemWidth)
302 x = (cw - pw) / 2;
303 if(ph < mItemHeight)
304 y = (cw - ph) / 2;
305 p->drawPixmap(x, y, thumbpix, 0, 0, mItemWidth, mItemHeight);
306 }
307
308 // highlight current item
309 if(row == mCurRow && col == mCurCol)
310 {
311 p->setPen(blue);
312 p->drawRect(0, 0, cw, ch);
313 }
314 else
315 {
316 p->setPen(gray);
317 //p->drawRect(0, 0, cw, ch);
318 p->drawLine(cw-1, 0, cw-1, ch-1);
319 p->drawLine(0, ch-1, cw-1, ch-1);
320 }
321 }
322 else
323 {
324 // empty cell
325 p->fillRect(0, 0, cellWidth(), cellHeight(), TQBrush(TQt::white));
326 }
327}
328
329// return the pointer of the item at (row,col) - beware, resizing disturbs
330// rows and cols!
331// return 0L if item is not found
332KoIconItem *KoIconChooser::itemAt(int row, int col)
333{
334 return itemAt(cellIndex(row, col));
335}
336
337// return the pointer of the item at position index
338// return 0L if item is not found
339KoIconItem *KoIconChooser::itemAt(int index)
340{
341 if(index < 0)
342 return 0L;
343 return mIconList.count() > uint(index) ? mIconList.at(index) : 0;
344}
345
346// return the index of a cell, given row and column position
347// maps directly to the position in the itemlist
348// return -1 on failure
349int KoIconChooser::cellIndex(int row, int col)
350{
351 if(row < 0 || col < 0)
352 return -1;
353 else
354 return((row * mNCols) + col);
355}
356
357// calculate the grid and set the number of rows and columns
358// reorder all items approrpriately
359void KoIconChooser::calculateCells()
360{
361 if(mNCols == 0)
362 return;
363 bool update = isUpdatesEnabled();
364 int rows = mItemCount / mNCols;
365 setUpdatesEnabled(false);
366 if((rows * mNCols) < mItemCount)
367 rows++;
368 setNumRows(rows);
369 setUpdatesEnabled(update);
370 updateContents();
371}
372
373// show the full pixmap of a large item in an extra widget
374void KoIconChooser::showFullPixmap(const TQPixmap &pix, const TQPoint &/*p*/)
375{
376 //delete mPixmapWidget;
377 mPixmapWidget = new KoPixmapWidget(pix, this);
378
379 // center widget under mouse cursor
380 TQPoint p = TQCursor::pos();
381 int w = mPixmapWidget->width();
382 int h = mPixmapWidget->height();
383 mPixmapWidget->move(p.x() - w / 2, p.y() - h / 2);
384 mPixmapWidget->show();
385}
386
387int KoIconChooser::sortInsertionIndex(const KoIconItem *item)
388{
389 int index = 0;
390
391 if (!mIconList.isEmpty())
392 {
393 // Binary insertion
394 int first = 0;
395 int last = mIconList.count() - 1;
396
397 while (first != last)
398 {
399 int middle = (first + last) / 2;
400
401 if (item -> compare(mIconList.at(middle)) < 0)
402 {
403 last = middle - 1;
404
405 if (last < first)
406 {
407 last = first;
408 }
409 }
410 else
411 {
412 first = middle + 1;
413
414 if (first > last)
415 {
416 first = last;
417 }
418 }
419 }
420
421 if (item -> compare(mIconList.at(first)) < 0)
422 {
423 index = first;
424 }
425 else
426 {
427 index = first + 1;
428 }
429 }
430
431 return index;
432}
433
434KoPatternChooser::KoPatternChooser( const TQPtrList<KoIconItem> &list, TQWidget *parent, const char *name )
435 : TQWidget( parent, name )
436{
437 // only serves as beautifier for the iconchooser
438 //frame = new TQHBox( this );
439 //frame->setFrameStyle( TQFrame::Panel | TQFrame::Sunken );
440 chooser = new KoIconChooser( TQSize(30,30), this, "pattern chooser" );
441
442 TQObject::connect( chooser, TQ_SIGNAL(selected( KoIconItem * ) ),
443 this, TQ_SIGNAL( selected( KoIconItem * )));
444
445 TQPtrListIterator<KoIconItem> itr( list );
446 for( itr.toFirst(); itr.current(); ++itr )
447 chooser->addItem( itr.current() );
448
449 TQVBoxLayout *mainLayout = new TQVBoxLayout( this, 1, -1, "main layout" );
450 mainLayout->addWidget( chooser, 10 );
451}
452
453
454KoPatternChooser::~KoPatternChooser()
455{
456 delete chooser;
457 //delete frame;
458}
459
460// set the active pattern in the chooser - does NOT emit selected() (should it?)
461void KoPatternChooser::setCurrentPattern( KoIconItem *pattern )
462{
463 chooser->setCurrentItem( pattern );
464}
465
466void KoPatternChooser::addPattern( KoIconItem *pattern )
467{
468 chooser->addItem( pattern );
469}
470
471// return the active pattern
472KoIconItem *KoPatternChooser::currentPattern()
473{
474 return chooser->currentItem();
475}
476
477void KoPatternChooser::removePattern( KoIconItem *pattern )
478{
479 chooser->removeItem( pattern );
480}
481
482#include "koIconChooser.moc"
const char * name(StdAction id)