author | zautrix <zautrix> | 2004-06-26 19:01:18 (UTC) |
---|---|---|
committer | zautrix <zautrix> | 2004-06-26 19:01:18 (UTC) |
commit | b9aad1f15dc600e4dbe4c62d3fcced6363188ba3 (patch) (unidiff) | |
tree | 2c3d4004fb21c72cba65793859f9bcd8ffd3a49c /kaddressbook/views/cardview.cpp | |
download | kdepimpi-b9aad1f15dc600e4dbe4c62d3fcced6363188ba3.zip kdepimpi-b9aad1f15dc600e4dbe4c62d3fcced6363188ba3.tar.gz kdepimpi-b9aad1f15dc600e4dbe4c62d3fcced6363188ba3.tar.bz2 |
Initial revision
Diffstat (limited to 'kaddressbook/views/cardview.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | kaddressbook/views/cardview.cpp | 1719 |
1 files changed, 1719 insertions, 0 deletions
diff --git a/kaddressbook/views/cardview.cpp b/kaddressbook/views/cardview.cpp new file mode 100644 index 0000000..65f793c --- a/dev/null +++ b/kaddressbook/views/cardview.cpp | |||
@@ -0,0 +1,1719 @@ | |||
1 | /* | ||
2 | This file is part of KAddressBook. | ||
3 | Copyright (c) 2002 Mike Pilone <mpilone@slac.com> | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program 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 | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program; if not, write to the Free Software | ||
17 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
18 | |||
19 | As a special exception, permission is given to link this program | ||
20 | with any edition of Qt, and distribute the resulting executable, | ||
21 | without including the source code for Qt in the source distribution. | ||
22 | */ | ||
23 | |||
24 | //BEGIN Includes | ||
25 | #include "cardview.h" | ||
26 | |||
27 | #include <limits.h> | ||
28 | |||
29 | #include <qpainter.h> | ||
30 | #include <qtimer.h> | ||
31 | #include <qdatetime.h> | ||
32 | #include <qlabel.h> | ||
33 | #include <qstyle.h> | ||
34 | #include <qcursor.h> | ||
35 | #include <qtooltip.h> | ||
36 | |||
37 | #include <kdebug.h> | ||
38 | #include <kglobalsettings.h> | ||
39 | //END includes | ||
40 | |||
41 | #define MIN_ITEM_WIDTH 80 | ||
42 | |||
43 | //BEGIN Helpers | ||
44 | ////////////////////////////////////// | ||
45 | // CardViewTip | ||
46 | class CardViewTip : public QLabel { | ||
47 | public: | ||
48 | CardViewTip(QWidget *parent=0, const char *name=0) : QLabel( parent, name ) | ||
49 | { | ||
50 | setPalette( QToolTip::palette() ); | ||
51 | setFrameStyle( Panel|Plain ); | ||
52 | setMidLineWidth(0); | ||
53 | setIndent(1); | ||
54 | } | ||
55 | |||
56 | ~CardViewTip() {}; | ||
57 | protected: | ||
58 | void leaveEvent( QEvent * ) | ||
59 | { | ||
60 | hide(); | ||
61 | } | ||
62 | }; | ||
63 | |||
64 | ////////////////////////////////////// | ||
65 | // CardViewItemList | ||
66 | |||
67 | |||
68 | // | ||
69 | // Warning: make sure you use findRef() instead of find() to find an | ||
70 | // item! Only the pointer value is unique in the list. | ||
71 | // | ||
72 | class CardViewItemList : public QPtrList<CardViewItem> | ||
73 | { | ||
74 | protected: | ||
75 | virtual int compareItems(QPtrCollection::Item item1, | ||
76 | QPtrCollection::Item item2) | ||
77 | { | ||
78 | CardViewItem *cItem1 = (CardViewItem*)item1; | ||
79 | CardViewItem *cItem2 = (CardViewItem*)item2; | ||
80 | |||
81 | if ( cItem1 == cItem2 ) | ||
82 | return 0; | ||
83 | |||
84 | if ((cItem1 == 0) || (cItem2 == 0)) | ||
85 | return cItem1 ? -1 : 1; | ||
86 | |||
87 | if (cItem1->caption() < cItem2->caption()) | ||
88 | return -1; | ||
89 | |||
90 | else if (cItem1->caption() > cItem2->caption()) | ||
91 | return 1; | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | private: | ||
97 | /*int find( const CardViewItem * ) | ||
98 | { | ||
99 | qDebug("DON'T USE CardViewItemList::find( item )! Use findRef( item )!"); | ||
100 | }*/ | ||
101 | }; | ||
102 | |||
103 | ////////////////////////////////////// | ||
104 | // CardViewSeparator | ||
105 | class CardViewSeparator | ||
106 | { | ||
107 | friend class CardView; | ||
108 | |||
109 | public: | ||
110 | CardViewSeparator(CardView *view) | ||
111 | : mView(view) | ||
112 | { | ||
113 | mRect = QRect(0, 0, view->separatorWidth(), 0); | ||
114 | } | ||
115 | |||
116 | ~CardViewSeparator() {} | ||
117 | |||
118 | void paintSeparator(QPainter *p, QColorGroup &cg) | ||
119 | { | ||
120 | p->fillRect(0, 0, mRect.width(), mRect.height(), | ||
121 | cg.brush(QColorGroup::Button)); | ||
122 | } | ||
123 | |||
124 | void repaintSeparator() | ||
125 | { | ||
126 | mView->repaintContents(mRect); | ||
127 | } | ||
128 | |||
129 | private: | ||
130 | CardView *mView; | ||
131 | QRect mRect; | ||
132 | }; | ||
133 | |||
134 | //END Helpers | ||
135 | |||
136 | //BEGIN Private Data | ||
137 | |||
138 | class CardViewPrivate | ||
139 | { | ||
140 | public: | ||
141 | CardViewPrivate() | ||
142 | : mSelectionMode( CardView::Multi ), | ||
143 | mDrawCardBorder( true ), | ||
144 | mDrawFieldLabels( true ), | ||
145 | mDrawSeparators( true), | ||
146 | mSepWidth( 2 ), | ||
147 | mShowEmptyFields( false ), | ||
148 | mLayoutDirty( true ), | ||
149 | mLastClickOnItem( false ), | ||
150 | mItemMargin( 0 ), | ||
151 | mItemSpacing( 10 ), | ||
152 | mItemWidth( 200 ), | ||
153 | mMaxFieldLines( INT_MAX ), | ||
154 | mCurrentItem( 0L ), | ||
155 | mLastClickPos( QPoint(0, 0) ), | ||
156 | mRubberBandAnchor( 0 ), | ||
157 | mCompText( QString::null ), | ||
158 | mResizeAnchor(0) | ||
159 | {}; | ||
160 | |||
161 | CardViewItemList mItemList; | ||
162 | QPtrList<CardViewSeparator> mSeparatorList; | ||
163 | QFontMetrics *mFm; | ||
164 | QFontMetrics *mBFm; // bold font | ||
165 | QFont mHeaderFont; // custom header font | ||
166 | CardView::SelectionMode mSelectionMode; | ||
167 | bool mDrawCardBorder; | ||
168 | bool mDrawFieldLabels; | ||
169 | bool mDrawSeparators; | ||
170 | int mSepWidth; | ||
171 | bool mShowEmptyFields; | ||
172 | bool mLayoutDirty; | ||
173 | bool mLastClickOnItem; | ||
174 | uint mItemMargin; // internal margin in items | ||
175 | uint mItemSpacing; // spacing between items, column seperators and border | ||
176 | int mItemWidth; // width of all items | ||
177 | uint mMaxFieldLines; // Max lines to dispaly pr field | ||
178 | CardViewItem *mCurrentItem; | ||
179 | QPoint mLastClickPos; | ||
180 | QTimer *mTimer; // times out if mouse rests for more than 500 msecs | ||
181 | CardViewTip *mTip; // passed to the item under a resting cursor to display full text | ||
182 | bool mOnSeparator; // set/reset on mouse movement | ||
183 | // for resizing by dragging the separators | ||
184 | int mResizeAnchor; // uint, ulong? the mouse down separator left | ||
185 | int mRubberBandAnchor; // for erasing rubber bands | ||
186 | // data used for resizing. | ||
187 | // as they are beeded by each mouse move while resizing, we store them here, | ||
188 | // saving 8 calculations in each mouse move. | ||
189 | int colspace; // amount of space between items pr column | ||
190 | uint first; // the first col to anchor at for painting rubber bands | ||
191 | int firstX; // X position of first in pixel | ||
192 | int pressed; // the colummn that was pressed on at resizing start | ||
193 | int span; // pressed - first | ||
194 | // key completion | ||
195 | QString mCompText; // current completion string | ||
196 | QDateTime mCompUpdated; // ...was updated at this time | ||
197 | }; | ||
198 | |||
199 | class CardViewItemPrivate | ||
200 | { | ||
201 | public: | ||
202 | CardViewItemPrivate() : | ||
203 | x( 0 ), | ||
204 | y( 0 ), | ||
205 | mSelected( false ){}; | ||
206 | |||
207 | |||
208 | QString mCaption; | ||
209 | QPtrList< CardViewItem::Field > mFieldList; | ||
210 | bool mSelected; | ||
211 | int x; // horizontal position, set by the view | ||
212 | int y; // vertical position, set by the view | ||
213 | int maxLabelWidth; // the width of the widest label, according to the view font. | ||
214 | int hcache; // height cache | ||
215 | }; | ||
216 | //END Private Data | ||
217 | |||
218 | //BEGIN CardViewItem | ||
219 | |||
220 | CardViewItem::CardViewItem(CardView *parent, QString caption) | ||
221 | : d(new CardViewItemPrivate()), mView(parent) | ||
222 | { | ||
223 | d->mCaption = caption; | ||
224 | |||
225 | initialize(); | ||
226 | } | ||
227 | |||
228 | CardViewItem::~CardViewItem() | ||
229 | { | ||
230 | // Remove ourself from the view | ||
231 | if (mView != 0) | ||
232 | mView->takeItem(this); | ||
233 | |||
234 | delete d; | ||
235 | d = 0; | ||
236 | } | ||
237 | |||
238 | void CardViewItem::initialize() | ||
239 | { | ||
240 | d->mSelected = false; | ||
241 | d->mFieldList.setAutoDelete(true); | ||
242 | d->maxLabelWidth = 0; | ||
243 | d->hcache=0; | ||
244 | |||
245 | //calcRect(); | ||
246 | |||
247 | // Add ourself to the view | ||
248 | if (mView != 0) | ||
249 | mView->insertItem(this); | ||
250 | } | ||
251 | |||
252 | void CardViewItem::paintCard(QPainter *p, QColorGroup &cg) | ||
253 | { | ||
254 | |||
255 | if (!mView) | ||
256 | return; | ||
257 | |||
258 | QPen pen; | ||
259 | QBrush brush; | ||
260 | QFontMetrics fm = *(mView->d->mFm); | ||
261 | QFontMetrics bFm = *(mView->d->mBFm); | ||
262 | bool drawLabels = mView->d->mDrawFieldLabels; | ||
263 | bool drawBorder = mView->d->mDrawCardBorder; | ||
264 | int mg = mView->itemMargin(); | ||
265 | int w = mView->itemWidth() - (mg*2); | ||
266 | int h = height() - (mg*2); | ||
267 | const int colonWidth( fm.width(":") ); | ||
268 | int labelXPos = 2 + mg; | ||
269 | int labelWidth = QMIN( w/2 - 4 - mg, d->maxLabelWidth + colonWidth + 4 ); | ||
270 | int valueXPos = labelWidth + 4 + mg; | ||
271 | int valueWidth = w - labelWidth - 4 - mg; | ||
272 | |||
273 | p->setFont( mView->font() ); | ||
274 | labelWidth -= colonWidth; // extra space for the colon | ||
275 | |||
276 | if (!drawLabels) | ||
277 | { | ||
278 | valueXPos = labelXPos; | ||
279 | valueWidth = w - 4; | ||
280 | } | ||
281 | |||
282 | // Draw a simple box | ||
283 | if (isSelected()) | ||
284 | pen = QPen(cg.highlight(), 1); | ||
285 | else | ||
286 | pen = QPen(cg.button(), 1); | ||
287 | p->setPen(pen); | ||
288 | |||
289 | // Draw the border - this is only draw if the user asks for it. | ||
290 | if (drawBorder) | ||
291 | p->drawRect( mg, mg, w, h ); | ||
292 | |||
293 | // set the proper pen color for the caption box | ||
294 | if (isSelected()) | ||
295 | brush = cg.brush(QColorGroup::Highlight); | ||
296 | else | ||
297 | brush = cg.brush(QColorGroup::Button); | ||
298 | |||
299 | p->fillRect(mg, mg, w, 4 + bFm.height(), brush); | ||
300 | |||
301 | // Now paint the caption | ||
302 | p->save(); | ||
303 | QFont bFont = mView->headerFont(); | ||
304 | //bFont.setBold(true); | ||
305 | p->setFont(bFont); | ||
306 | if (isSelected()) | ||
307 | p->setPen(cg.highlightedText()); | ||
308 | else | ||
309 | p->setPen(cg.buttonText()); | ||
310 | p->drawText(2+mg, 2+mg + bFm.ascent()/*bFm.height()*//*-bFm.descent()*//*-bFm.leading()*/, trimString(d->mCaption, w-4, bFm)); | ||
311 | p->restore(); | ||
312 | |||
313 | // Go through the fields and draw them | ||
314 | QPtrListIterator< CardViewItem::Field > iter(d->mFieldList); | ||
315 | QString label, value; | ||
316 | int yPos = mg + 4 + bFm.height()/* + 1*/ + fm.height(); // why the + 1 ??? (anders) | ||
317 | p->setPen(cg.text()); | ||
318 | |||
319 | int fh = fm.height(); | ||
320 | int cln( 0 ); | ||
321 | QString tmp; | ||
322 | int maxLines = mView->maxFieldLines(); | ||
323 | for (iter.toFirst(); iter.current(); ++iter) | ||
324 | { | ||
325 | value = (*iter)->second; | ||
326 | if ( value.isEmpty() && ! mView->d->mShowEmptyFields ) | ||
327 | continue; | ||
328 | |||
329 | if (drawLabels) | ||
330 | { | ||
331 | label = trimString((*iter)->first, labelWidth, fm); | ||
332 | p->drawText(labelXPos, yPos, label + ":"); | ||
333 | } | ||
334 | /* US original | ||
335 | for (cln=0; cln <= maxLines; cln++) | ||
336 | { | ||
337 | tmp = value.section('\n',cln,cln); | ||
338 | if ( !tmp.isEmpty() ) p->drawText( valueXPos, yPos + cln*fh, trimString( tmp, valueWidth, fm ) ); | ||
339 | else break; | ||
340 | } | ||
341 | */ | ||
342 | |||
343 | //US new implementation | ||
344 | QStringList strlst = QStringList::split('\n', value, true); | ||
345 | |||
346 | for (cln=0; cln <= maxLines && cln <= (int)strlst.count(); cln++) | ||
347 | { | ||
348 | tmp = strlst[cln]; | ||
349 | |||
350 | if ( !tmp.isEmpty() ) | ||
351 | p->drawText( valueXPos, yPos + cln*fh, trimString( tmp, valueWidth, fm ) ); | ||
352 | else | ||
353 | break; | ||
354 | |||
355 | } | ||
356 | |||
357 | if ( cln == 0 ) cln = 1; | ||
358 | yPos += cln * fh + 2; | ||
359 | } | ||
360 | |||
361 | // if we are the current item and the view has focus, draw focus rect | ||
362 | if ( mView->currentItem() == this && mView->hasFocus() ) | ||
363 | { | ||
364 | /*US | ||
365 | mView->style().drawPrimitive( QStyle::PE_FocusRect, p, | ||
366 | QRect(0, 0, mView->itemWidth(), h+(2*mg)), cg, | ||
367 | QStyle::Style_FocusAtBorder, | ||
368 | QStyleOption( isSelected() ? cg.highlight() : cg.base() ) ); | ||
369 | */ | ||
370 | |||
371 | const QColor pHighl = isSelected() ? cg.highlight() : cg.base(); | ||
372 | const QRect r(0, 0, mView->itemWidth(), h+(2*mg)); | ||
373 | #ifndef DESKTOP_VERSION | ||
374 | mView->style().drawFocusRect(p, r, cg, &pHighl, true); | ||
375 | #endif | ||
376 | } | ||
377 | } | ||
378 | |||
379 | const QString &CardViewItem::caption() const | ||
380 | { | ||
381 | return d->mCaption; | ||
382 | } | ||
383 | |||
384 | |||
385 | int CardViewItem::height( bool allowCache ) const | ||
386 | { | ||
387 | // use cache | ||
388 | if ( allowCache && d->hcache ) | ||
389 | return d->hcache; | ||
390 | |||
391 | // Base height: | ||
392 | // 2 for line width | ||
393 | // 2 for top caption pad | ||
394 | // 2 for bottom caption pad | ||
395 | // 2 pad for the end | ||
396 | // + 2 times the advised margin | ||
397 | int baseHeight = 8 + ( 2 * mView->itemMargin() ); | ||
398 | |||
399 | // size of font for each field | ||
400 | // 2 pad for each field | ||
401 | |||
402 | // anders: if the view does not show empty fields, check for value | ||
403 | bool sef = mView->showEmptyFields(); | ||
404 | int fh = mView->d->mFm->height();//lineSpacing(); // font height | ||
405 | //int sp = QMAX( 0, 2- mView->d->mFm->leading() ); // field spacing NOTE make a property | ||
406 | int fieldHeight = 0; | ||
407 | int lines; | ||
408 | int maxLines( mView->maxFieldLines() ); | ||
409 | QPtrListIterator< CardViewItem::Field > iter(d->mFieldList); | ||
410 | for (iter.toFirst(); iter.current(); ++iter) | ||
411 | { | ||
412 | if ( !sef && (*iter)->second.isEmpty() ) | ||
413 | continue; | ||
414 | lines = QMIN( (*iter)->second.contains('\n') + 1, maxLines ); | ||
415 | fieldHeight += ( lines * fh ) + 2;//sp; | ||
416 | } | ||
417 | |||
418 | // height of caption font (bold) | ||
419 | fieldHeight += mView->d->mBFm->height(); | ||
420 | d->hcache = baseHeight + fieldHeight; | ||
421 | return d->hcache; | ||
422 | } | ||
423 | |||
424 | bool CardViewItem::isSelected() const | ||
425 | { | ||
426 | return d->mSelected; | ||
427 | } | ||
428 | |||
429 | void CardViewItem::setSelected(bool selected) | ||
430 | { | ||
431 | d->mSelected = selected; | ||
432 | } | ||
433 | |||
434 | void CardViewItem::insertField(const QString &label, const QString &value) | ||
435 | { | ||
436 | CardViewItem::Field *f = new CardViewItem::Field(label, value); | ||
437 | d->mFieldList.append(f); | ||
438 | d->hcache=0; | ||
439 | |||
440 | if (mView) | ||
441 | { | ||
442 | mView->setLayoutDirty(true); | ||
443 | d->maxLabelWidth = QMAX( mView->d->mFm->width( label ), d->maxLabelWidth ); | ||
444 | } | ||
445 | } | ||
446 | |||
447 | void CardViewItem::removeField(const QString &label) | ||
448 | { | ||
449 | CardViewItem::Field *f; | ||
450 | |||
451 | QPtrListIterator< CardViewItem::Field > iter(d->mFieldList); | ||
452 | for (iter.toFirst(); iter.current(); ++iter) | ||
453 | { | ||
454 | f = *iter; | ||
455 | if (f->first == label) | ||
456 | break; | ||
457 | } | ||
458 | |||
459 | if (*iter) | ||
460 | d->mFieldList.remove(*iter); | ||
461 | d->hcache = 0; | ||
462 | |||
463 | if (mView) | ||
464 | mView->setLayoutDirty(true); | ||
465 | } | ||
466 | |||
467 | void CardViewItem::clearFields() | ||
468 | { | ||
469 | d->mFieldList.clear(); | ||
470 | d->hcache = 0; | ||
471 | |||
472 | if (mView) | ||
473 | mView->setLayoutDirty(true); | ||
474 | } | ||
475 | |||
476 | QString CardViewItem::trimString(const QString &text, int width, | ||
477 | QFontMetrics &fm) | ||
478 | { | ||
479 | if (fm.width(text) <= width) | ||
480 | return text; | ||
481 | |||
482 | QString dots = "..."; | ||
483 | int dotWidth = fm.width(dots); | ||
484 | QString trimmed; | ||
485 | int charNum = 0; | ||
486 | |||
487 | while (fm.width(trimmed) + dotWidth < width) | ||
488 | { | ||
489 | trimmed += text[charNum]; | ||
490 | charNum++; | ||
491 | } | ||
492 | |||
493 | // Now trim the last char, since it put the width over the top | ||
494 | trimmed = trimmed.left(trimmed.length()-1); | ||
495 | trimmed += dots; | ||
496 | |||
497 | return trimmed; | ||
498 | } | ||
499 | |||
500 | CardViewItem *CardViewItem::nextItem() | ||
501 | { | ||
502 | CardViewItem *item = 0; | ||
503 | |||
504 | if (mView) | ||
505 | item = mView->itemAfter(this); | ||
506 | |||
507 | return item; | ||
508 | } | ||
509 | |||
510 | void CardViewItem::repaintCard() | ||
511 | { | ||
512 | if (mView) | ||
513 | mView->repaintItem(this); | ||
514 | } | ||
515 | |||
516 | void CardViewItem::setCaption(const QString &caption) | ||
517 | { | ||
518 | d->mCaption = caption; | ||
519 | repaintCard(); | ||
520 | } | ||
521 | |||
522 | QString CardViewItem::fieldValue(const QString &label) | ||
523 | { | ||
524 | QPtrListIterator< CardViewItem::Field > iter(d->mFieldList); | ||
525 | for (iter.toFirst(); iter.current(); ++iter) | ||
526 | if ((*iter)->first == label) | ||
527 | return (*iter)->second; | ||
528 | |||
529 | return QString(); | ||
530 | } | ||
531 | |||
532 | |||
533 | void CardViewItem::showFullString( const QPoint &itempos, CardViewTip *tip ) | ||
534 | { | ||
535 | bool trimmed( false ); | ||
536 | QString s; | ||
537 | int mrg = mView->itemMargin(); | ||
538 | int y = mView->d->mBFm->height() + 6 + mrg; | ||
539 | int w = mView->itemWidth() - (2*mrg); | ||
540 | int lw; | ||
541 | bool drawLabels = mView->drawFieldLabels(); | ||
542 | bool isLabel = drawLabels && itempos.x() < w/2 ? true : false; | ||
543 | |||
544 | if ( itempos.y() < y ) | ||
545 | { | ||
546 | if ( itempos.y() < 8 + mrg || itempos.y() > y - 4 ) | ||
547 | return; | ||
548 | // this is the caption | ||
549 | s = caption(); | ||
550 | trimmed = mView->d->mBFm->width( s ) > w - 4; | ||
551 | y = 2 + mrg; | ||
552 | lw = 0; | ||
553 | isLabel=true; | ||
554 | } else { | ||
555 | // find the field | ||
556 | Field *f = fieldAt( itempos ); | ||
557 | if ( !f || ( !mView->showEmptyFields() && f->second.isEmpty() ) ) | ||
558 | return; | ||
559 | |||
560 | // y position: | ||
561 | // header font height + 4px hader margin + 2px leading + item margin | ||
562 | // + actual field index * (fontheight + 2px leading) | ||
563 | int maxLines = mView->maxFieldLines(); | ||
564 | bool se = mView->showEmptyFields(); | ||
565 | int fh = mView->d->mFm->height(); | ||
566 | // { | ||
567 | Field *_f; | ||
568 | for (_f = d->mFieldList.first(); _f != f; _f = d->mFieldList.next()) | ||
569 | if ( se || ! _f->second.isEmpty() ) | ||
570 | y += ( QMIN(_f->second.contains('\n')+1, maxLines) * fh ) + 2; | ||
571 | // } | ||
572 | if ( isLabel && itempos.y() > y + fh ) | ||
573 | return; | ||
574 | // label or data? | ||
575 | s = isLabel ? f->first : f->second; | ||
576 | // trimmed? | ||
577 | int colonWidth = mView->d->mFm->width(":"); | ||
578 | lw = drawLabels ? // label width | ||
579 | QMIN( w/2 - 4 - mrg, d->maxLabelWidth + colonWidth + 4 ) : | ||
580 | 0; | ||
581 | int mw = isLabel ? lw - colonWidth : w - lw - (mrg*2); // max width for string | ||
582 | if ( isLabel ) | ||
583 | { | ||
584 | trimmed = mView->d->mFm->width( s ) > mw - colonWidth; | ||
585 | } else { | ||
586 | QRect r( mView->d->mFm->boundingRect( 0, 0, INT_MAX, INT_MAX, Qt::AlignTop|Qt::AlignLeft, s ) ); | ||
587 | trimmed = r.width() > mw || r.height()/fh > QMIN(s.contains('\n') + 1, maxLines); | ||
588 | } | ||
589 | } | ||
590 | if ( trimmed ) | ||
591 | { | ||
592 | tip->setFont( (isLabel && !lw) ? mView->headerFont() : mView->font() ); // if condition is true, a header | ||
593 | tip->setText( s ); | ||
594 | tip->adjustSize(); | ||
595 | // find a proper position | ||
596 | int lx; | ||
597 | lx = isLabel || !drawLabels ? mrg : lw + mrg + 2 /*-1*/; | ||
598 | QPoint pnt(mView->contentsToViewport( QPoint(d->x, d->y) )); | ||
599 | pnt += QPoint(lx, y); | ||
600 | if ( pnt.x() < 0 ) | ||
601 | pnt.setX( 0 ); | ||
602 | if ( pnt.x() + tip->width() > mView->visibleWidth() ) | ||
603 | pnt.setX( mView->visibleWidth() - tip->width() ); | ||
604 | if ( pnt.y() + tip->height() > mView->visibleHeight() ) | ||
605 | pnt.setY( QMAX( 0, mView->visibleHeight() - tip->height() ) ); | ||
606 | // show | ||
607 | tip->move( pnt ); | ||
608 | tip->show(); | ||
609 | } | ||
610 | } | ||
611 | |||
612 | CardViewItem::Field *CardViewItem::fieldAt( const QPoint & itempos ) const | ||
613 | { | ||
614 | int ypos = mView->d->mBFm->height() + 7 + mView->d->mItemMargin; | ||
615 | int iy = itempos.y(); | ||
616 | // skip below caption | ||
617 | if ( iy <= ypos ) | ||
618 | return 0; | ||
619 | // try find a field | ||
620 | bool showEmpty = mView->showEmptyFields(); | ||
621 | int fh = mView->d->mFm->height(); | ||
622 | int maxLines = mView->maxFieldLines(); | ||
623 | Field *f; | ||
624 | for ( f = d->mFieldList.first(); f; f = d->mFieldList.next() ) | ||
625 | { | ||
626 | if ( showEmpty || !f->second.isEmpty() ) | ||
627 | ypos += ( QMIN( f->second.contains('\n')+1, maxLines ) *fh)+2; | ||
628 | if ( iy <= ypos ) | ||
629 | break; | ||
630 | } | ||
631 | return f ? f : 0; | ||
632 | } | ||
633 | //END CardViewItem | ||
634 | |||
635 | //BEGIN CardView | ||
636 | |||
637 | CardView::CardView(QWidget *parent, const char *name) | ||
638 | : QScrollView(parent, name), | ||
639 | d(new CardViewPrivate()) | ||
640 | { | ||
641 | d->mItemList.setAutoDelete(true); | ||
642 | d->mSeparatorList.setAutoDelete(true); | ||
643 | |||
644 | QFont f = font(); | ||
645 | d->mFm = new QFontMetrics(f); | ||
646 | f.setBold(true); | ||
647 | d->mHeaderFont = f; | ||
648 | d->mBFm = new QFontMetrics(f); | ||
649 | d->mTip = ( new CardViewTip( viewport() ) ), | ||
650 | d->mTip->hide(); | ||
651 | d->mTimer = ( new QTimer(this, "mouseTimer") ), | ||
652 | |||
653 | viewport()->setMouseTracking( true ); | ||
654 | viewport()->setFocusProxy(this); | ||
655 | viewport()->setFocusPolicy(WheelFocus); | ||
656 | viewport()->setBackgroundMode(PaletteBase); | ||
657 | |||
658 | connect( d->mTimer, SIGNAL(timeout()), this, SLOT(tryShowFullText()) ); | ||
659 | |||
660 | //US setBackgroundMode(PaletteBackground, PaletteBase); | ||
661 | setBackgroundMode(PaletteBackground); | ||
662 | |||
663 | // no reason for a vertical scrollbar | ||
664 | setVScrollBarMode(AlwaysOff); | ||
665 | } | ||
666 | |||
667 | CardView::~CardView() | ||
668 | { | ||
669 | delete d->mFm; | ||
670 | delete d->mBFm; | ||
671 | delete d; | ||
672 | d = 0; | ||
673 | } | ||
674 | |||
675 | void CardView::insertItem(CardViewItem *item) | ||
676 | { | ||
677 | d->mItemList.inSort(item); | ||
678 | setLayoutDirty(true); | ||
679 | } | ||
680 | |||
681 | void CardView::takeItem(CardViewItem *item) | ||
682 | { | ||
683 | if ( d->mCurrentItem == item ) | ||
684 | d->mCurrentItem = item->nextItem(); | ||
685 | d->mItemList.take(d->mItemList.findRef(item)); | ||
686 | |||
687 | setLayoutDirty(true); | ||
688 | } | ||
689 | |||
690 | void CardView::clear() | ||
691 | { | ||
692 | d->mItemList.clear(); | ||
693 | |||
694 | setLayoutDirty(true); | ||
695 | } | ||
696 | |||
697 | CardViewItem *CardView::currentItem() | ||
698 | { | ||
699 | if ( ! d->mCurrentItem && d->mItemList.count() ) | ||
700 | d->mCurrentItem = d->mItemList.first(); | ||
701 | return d->mCurrentItem; | ||
702 | } | ||
703 | |||
704 | void CardView::setCurrentItem( CardViewItem *item ) | ||
705 | { | ||
706 | if ( !item ) | ||
707 | return; | ||
708 | else if ( item->cardView() != this ) | ||
709 | { | ||
710 | kdDebug(5720)<<"CardView::setCurrentItem: Item ("<<item<<") not owned! Backing out.."<<endl; | ||
711 | return; | ||
712 | } | ||
713 | else if ( item == currentItem() ) | ||
714 | { | ||
715 | return; | ||
716 | } | ||
717 | |||
718 | if ( d->mSelectionMode == Single ) | ||
719 | { | ||
720 | setSelected( item, true ); | ||
721 | } | ||
722 | else | ||
723 | { | ||
724 | CardViewItem *it = d->mCurrentItem; | ||
725 | d->mCurrentItem = item; | ||
726 | if ( it ) | ||
727 | it->repaintCard(); | ||
728 | item->repaintCard(); | ||
729 | } | ||
730 | if ( ! d->mOnSeparator ) | ||
731 | ensureItemVisible( item ); | ||
732 | emit currentChanged( item ); | ||
733 | } | ||
734 | |||
735 | CardViewItem *CardView::itemAt(const QPoint &viewPos) | ||
736 | { | ||
737 | CardViewItem *item = 0; | ||
738 | QPtrListIterator<CardViewItem> iter(d->mItemList); | ||
739 | bool found = false; | ||
740 | for (iter.toFirst(); iter.current() && !found; ++iter) | ||
741 | { | ||
742 | item = *iter; | ||
743 | //if (item->d->mRect.contains(viewPos)) | ||
744 | if (QRect(item->d->x, item->d->y, d->mItemWidth, item->height()).contains(viewPos)) | ||
745 | found = true; | ||
746 | } | ||
747 | |||
748 | if (found) | ||
749 | return item; | ||
750 | |||
751 | return 0; | ||
752 | } | ||
753 | |||
754 | QRect CardView::itemRect(const CardViewItem *item) | ||
755 | { | ||
756 | //return item->d->mRect; | ||
757 | return QRect(item->d->x, item->d->y, d->mItemWidth, item->height()); | ||
758 | } | ||
759 | |||
760 | void CardView::ensureItemVisible(const CardViewItem *item) | ||
761 | { | ||
762 | ensureVisible(item->d->x , item->d->y, d->mItemSpacing, 0); | ||
763 | ensureVisible(item->d->x + d->mItemWidth, item->d->y, d->mItemSpacing, 0); | ||
764 | } | ||
765 | |||
766 | void CardView::repaintItem(const CardViewItem *item) | ||
767 | { | ||
768 | //repaintContents(item->d->mRect); | ||
769 | repaintContents( QRect(item->d->x, item->d->y, d->mItemWidth, item->height()) ); | ||
770 | } | ||
771 | |||
772 | void CardView::setSelectionMode(CardView::SelectionMode mode) | ||
773 | { | ||
774 | selectAll(false); | ||
775 | |||
776 | d->mSelectionMode = mode; | ||
777 | } | ||
778 | |||
779 | CardView::SelectionMode CardView::selectionMode() const | ||
780 | { | ||
781 | return d->mSelectionMode; | ||
782 | } | ||
783 | |||
784 | void CardView::selectAll(bool state) | ||
785 | { | ||
786 | QPtrListIterator<CardViewItem> iter(d->mItemList); | ||
787 | if (!state) | ||
788 | { | ||
789 | for (iter.toFirst(); iter.current(); ++iter) | ||
790 | { | ||
791 | if ((*iter)->isSelected()) | ||
792 | { | ||
793 | (*iter)->setSelected(false); | ||
794 | (*iter)->repaintCard(); | ||
795 | } | ||
796 | } | ||
797 | //emit selectionChanged(); // WARNING FIXME | ||
798 | emit selectionChanged(0); | ||
799 | } | ||
800 | else if (d->mSelectionMode != CardView::Single) | ||
801 | { | ||
802 | for (iter.toFirst(); iter.current(); ++iter) | ||
803 | { | ||
804 | (*iter)->setSelected(true); | ||
805 | } | ||
806 | |||
807 | if (d->mItemList.count() > 0) | ||
808 | { | ||
809 | // emit, since there must have been at least one selected | ||
810 | emit selectionChanged(); | ||
811 | //repaint();//??? | ||
812 | viewport()->update(); | ||
813 | } | ||
814 | } | ||
815 | } | ||
816 | |||
817 | void CardView::setSelected(CardViewItem *item, bool selected) | ||
818 | { | ||
819 | if ((item == 0) || (item->isSelected() == selected)) | ||
820 | return; | ||
821 | |||
822 | if ( selected && d->mCurrentItem != item ) | ||
823 | { | ||
824 | CardViewItem *it = d->mCurrentItem; | ||
825 | d->mCurrentItem = item; | ||
826 | if ( it ) | ||
827 | it->repaintCard(); | ||
828 | } | ||
829 | |||
830 | if (d->mSelectionMode == CardView::Single) | ||
831 | { | ||
832 | bool b = signalsBlocked(); | ||
833 | blockSignals(true); | ||
834 | selectAll(false); | ||
835 | blockSignals(b); | ||
836 | |||
837 | if (selected) | ||
838 | { | ||
839 | item->setSelected(selected); | ||
840 | item->repaintCard(); | ||
841 | emit selectionChanged(); | ||
842 | emit selectionChanged(item); | ||
843 | } | ||
844 | else | ||
845 | { | ||
846 | emit selectionChanged(); | ||
847 | emit selectionChanged(0); | ||
848 | } | ||
849 | } | ||
850 | else if (d->mSelectionMode == CardView::Multi) | ||
851 | { | ||
852 | item->setSelected(selected); | ||
853 | item->repaintCard(); | ||
854 | emit selectionChanged(); | ||
855 | } | ||
856 | else if (d->mSelectionMode == CardView::Extended) | ||
857 | { | ||
858 | bool b = signalsBlocked(); | ||
859 | blockSignals(true); | ||
860 | selectAll(false); | ||
861 | blockSignals(b); | ||
862 | |||
863 | item->setSelected(selected); | ||
864 | item->repaintCard(); | ||
865 | emit selectionChanged(); | ||
866 | } | ||
867 | } | ||
868 | |||
869 | bool CardView::isSelected(CardViewItem *item) const | ||
870 | { | ||
871 | return (item && item->isSelected()); | ||
872 | } | ||
873 | |||
874 | CardViewItem *CardView::selectedItem() const | ||
875 | { | ||
876 | // find the first selected item | ||
877 | QPtrListIterator<CardViewItem> iter(d->mItemList); | ||
878 | for (iter.toFirst(); iter.current(); ++iter) | ||
879 | { | ||
880 | if ((*iter)->isSelected()) | ||
881 | return *iter; | ||
882 | } | ||
883 | |||
884 | return 0; | ||
885 | } | ||
886 | |||
887 | CardViewItem *CardView::firstItem() const | ||
888 | { | ||
889 | return d->mItemList.first(); | ||
890 | } | ||
891 | |||
892 | int CardView::childCount() const | ||
893 | { | ||
894 | return d->mItemList.count(); | ||
895 | } | ||
896 | /*US | ||
897 | CardViewItem *CardView::findItem(const QString &text, const QString &label, | ||
898 | Qt::StringComparisonMode compare) | ||
899 | { | ||
900 | // IF the text is empty, we will return null, since empty text will | ||
901 | // match anything! | ||
902 | if (text.isEmpty()) | ||
903 | return 0; | ||
904 | |||
905 | QPtrListIterator<CardViewItem> iter(d->mItemList); | ||
906 | if (compare & Qt::BeginsWith) | ||
907 | { | ||
908 | QString value; | ||
909 | for (iter.toFirst(); iter.current(); ++iter) | ||
910 | { | ||
911 | value = (*iter)->fieldValue(label).upper(); | ||
912 | if (value.startsWith(text.upper())) | ||
913 | return *iter; | ||
914 | } | ||
915 | } | ||
916 | else | ||
917 | { | ||
918 | kdDebug(5720) << "CardView::findItem: search method not implemented" << endl; | ||
919 | } | ||
920 | |||
921 | return 0; | ||
922 | } | ||
923 | */ | ||
924 | |||
925 | uint CardView::columnWidth() | ||
926 | { | ||
927 | return d->mDrawSeparators ? | ||
928 | d->mItemWidth + ( 2 * d->mItemSpacing ) + d->mSepWidth : | ||
929 | d->mItemWidth + d->mItemSpacing; | ||
930 | } | ||
931 | |||
932 | void CardView::drawContents(QPainter *p, int clipx, int clipy, | ||
933 | int clipw, int cliph) | ||
934 | { | ||
935 | QScrollView::drawContents(p, clipx, clipy, clipw, cliph); | ||
936 | |||
937 | if (d->mLayoutDirty) | ||
938 | calcLayout(); | ||
939 | |||
940 | //kdDebug() << "CardView::drawContents: " << clipx << ", " << clipy | ||
941 | // << ", " << clipw << ", " << cliph << endl; | ||
942 | |||
943 | QColorGroup cg = viewport()->palette().active(); // allow setting costum colors in the viewport pale | ||
944 | |||
945 | QRect clipRect(clipx, clipy, clipw, cliph); | ||
946 | QRect cardRect; | ||
947 | QRect sepRect; | ||
948 | CardViewItem *item; | ||
949 | CardViewSeparator *sep; | ||
950 | |||
951 | // make sure the viewport is a pure background | ||
952 | viewport()->erase(clipRect); | ||
953 | |||
954 | // Now tell the cards to draw, if they are in the clip region | ||
955 | QPtrListIterator<CardViewItem> iter(d->mItemList); | ||
956 | for (iter.toFirst(); iter.current(); ++iter) | ||
957 | { | ||
958 | item = *iter; | ||
959 | cardRect.setRect( item->d->x, item->d->y, d->mItemWidth, item->height() ); | ||
960 | |||
961 | if (clipRect.intersects(cardRect) || clipRect.contains(cardRect)) | ||
962 | { | ||
963 | //kdDebug() << "\trepainting card at: " << cardRect.x() << ", " | ||
964 | // << cardRect.y() << endl; | ||
965 | |||
966 | // Tell the card to paint | ||
967 | p->save(); | ||
968 | p->translate(cardRect.x(), cardRect.y()); | ||
969 | item->paintCard(p, cg); | ||
970 | p->restore(); | ||
971 | } | ||
972 | } | ||
973 | |||
974 | // Followed by the separators if they are in the clip region | ||
975 | QPtrListIterator<CardViewSeparator> sepIter(d->mSeparatorList); | ||
976 | for (sepIter.toFirst(); sepIter.current(); ++sepIter) | ||
977 | { | ||
978 | sep = *sepIter; | ||
979 | sepRect = sep->mRect; | ||
980 | |||
981 | if (clipRect.intersects(sepRect) || clipRect.contains(sepRect)) | ||
982 | { | ||
983 | p->save(); | ||
984 | p->translate(sepRect.x(), sepRect.y()); | ||
985 | sep->paintSeparator(p, cg); | ||
986 | p->restore(); | ||
987 | } | ||
988 | } | ||
989 | } | ||
990 | |||
991 | void CardView::resizeEvent(QResizeEvent *e) | ||
992 | { | ||
993 | QScrollView::resizeEvent(e); | ||
994 | |||
995 | setLayoutDirty(true); | ||
996 | } | ||
997 | |||
998 | void CardView::calcLayout() | ||
999 | { | ||
1000 | //kdDebug() << "CardView::calcLayout:" << endl; | ||
1001 | |||
1002 | // Start in the upper left corner and layout all the | ||
1003 | // cars using their height and width | ||
1004 | int maxWidth = 0; | ||
1005 | int maxHeight = 0; | ||
1006 | int xPos = 0; | ||
1007 | int yPos = 0; | ||
1008 | int cardSpacing = d->mItemSpacing; | ||
1009 | |||
1010 | // delete the old separators | ||
1011 | d->mSeparatorList.clear(); | ||
1012 | |||
1013 | QPtrListIterator<CardViewItem> iter(d->mItemList); | ||
1014 | CardViewItem *item = 0; | ||
1015 | CardViewSeparator *sep = 0; | ||
1016 | xPos += cardSpacing; | ||
1017 | |||
1018 | for (iter.toFirst(); iter.current(); ++iter) | ||
1019 | { | ||
1020 | item = *iter; | ||
1021 | |||
1022 | yPos += cardSpacing; | ||
1023 | |||
1024 | if (yPos + item->height() + cardSpacing >= height() - horizontalScrollBar()->height()) | ||
1025 | { | ||
1026 | maxHeight = QMAX(maxHeight, yPos); | ||
1027 | |||
1028 | // Drawing in this column would be greater than the height | ||
1029 | // of the scroll view, so move to next column | ||
1030 | yPos = cardSpacing; | ||
1031 | xPos += cardSpacing + maxWidth; | ||
1032 | if (d->mDrawSeparators) | ||
1033 | { | ||
1034 | // Create a separator since the user asked | ||
1035 | sep = new CardViewSeparator(this); | ||
1036 | sep->mRect.moveTopLeft(QPoint(xPos, yPos+d->mItemMargin)); | ||
1037 | xPos += d->mSepWidth + cardSpacing; | ||
1038 | d->mSeparatorList.append(sep); | ||
1039 | } | ||
1040 | |||
1041 | maxWidth = 0; | ||
1042 | } | ||
1043 | |||
1044 | item->d->x = xPos; | ||
1045 | item->d->y = yPos; | ||
1046 | |||
1047 | yPos += item->height(); | ||
1048 | maxWidth = QMAX(maxWidth, d->mItemWidth); | ||
1049 | } | ||
1050 | |||
1051 | xPos += maxWidth; | ||
1052 | resizeContents( xPos + cardSpacing, maxHeight ); | ||
1053 | |||
1054 | // Update the height of all the separators now that we know the | ||
1055 | // max height of a column | ||
1056 | QPtrListIterator<CardViewSeparator> sepIter(d->mSeparatorList); | ||
1057 | for (sepIter.toFirst(); sepIter.current(); ++sepIter) | ||
1058 | { | ||
1059 | (*sepIter)->mRect.setHeight(maxHeight - 2*cardSpacing - 2*d->mItemMargin); | ||
1060 | } | ||
1061 | |||
1062 | d->mLayoutDirty = false; | ||
1063 | } | ||
1064 | |||
1065 | CardViewItem *CardView::itemAfter(CardViewItem *item) | ||
1066 | { | ||
1067 | /*int pos = */d->mItemList.findRef(item); | ||
1068 | return d->mItemList.next();//at(pos+1); | ||
1069 | } | ||
1070 | |||
1071 | uint CardView::itemMargin() | ||
1072 | { | ||
1073 | return d->mItemMargin; | ||
1074 | } | ||
1075 | |||
1076 | void CardView::setItemMargin( uint margin ) | ||
1077 | { | ||
1078 | if ( margin == d->mItemMargin ) | ||
1079 | return; | ||
1080 | |||
1081 | d->mItemMargin = margin; | ||
1082 | setLayoutDirty( true ); | ||
1083 | } | ||
1084 | |||
1085 | uint CardView::itemSpacing() | ||
1086 | { | ||
1087 | return d->mItemSpacing; | ||
1088 | } | ||
1089 | |||
1090 | void CardView::setItemSpacing( uint spacing ) | ||
1091 | { | ||
1092 | if ( spacing == d->mItemSpacing ) | ||
1093 | return; | ||
1094 | |||
1095 | d->mItemSpacing = spacing; | ||
1096 | setLayoutDirty( true ); | ||
1097 | } | ||
1098 | |||
1099 | void CardView::contentsMousePressEvent(QMouseEvent *e) | ||
1100 | { | ||
1101 | QScrollView::contentsMousePressEvent(e); | ||
1102 | |||
1103 | QPoint pos = e->pos(); | ||
1104 | d->mLastClickPos = pos; | ||
1105 | |||
1106 | CardViewItem *item = itemAt(pos); | ||
1107 | |||
1108 | if (item == 0) | ||
1109 | { | ||
1110 | d->mLastClickOnItem = false; | ||
1111 | if ( d->mOnSeparator) | ||
1112 | { | ||
1113 | d->mResizeAnchor = e->x()+contentsX(); | ||
1114 | d->colspace = (2*d->mItemSpacing) /*+ (2*d->mItemMargin)*/; | ||
1115 | int ccw = d->mItemWidth + d->colspace + d->mSepWidth; | ||
1116 | d->first = (contentsX()+d->mSepWidth)/ccw; | ||
1117 | d->pressed = (d->mResizeAnchor+d->mSepWidth)/ccw; | ||
1118 | d->span = d->pressed - d->first; | ||
1119 | d->firstX = d->first * ccw; | ||
1120 | if ( d->firstX ) d->firstX -= d->mSepWidth; // (no sep in col 0) | ||
1121 | } | ||
1122 | else | ||
1123 | { | ||
1124 | selectAll(false); | ||
1125 | } | ||
1126 | return; | ||
1127 | } | ||
1128 | |||
1129 | d->mLastClickOnItem = true; | ||
1130 | |||
1131 | CardViewItem *other = d->mCurrentItem; | ||
1132 | setCurrentItem( item ); | ||
1133 | |||
1134 | // Always emit the selection | ||
1135 | emit clicked(item); | ||
1136 | |||
1137 | // Check the selection type and update accordingly | ||
1138 | if (d->mSelectionMode == CardView::Single) | ||
1139 | { | ||
1140 | // make sure it isn't already selected | ||
1141 | if (item->isSelected()) | ||
1142 | return; | ||
1143 | |||
1144 | bool b = signalsBlocked(); | ||
1145 | blockSignals(true); | ||
1146 | selectAll(false); | ||
1147 | blockSignals(b); | ||
1148 | |||
1149 | item->setSelected(true); | ||
1150 | item->repaintCard(); | ||
1151 | emit selectionChanged(item); | ||
1152 | } | ||
1153 | |||
1154 | else if (d->mSelectionMode == CardView::Multi) | ||
1155 | { | ||
1156 | // toggle the selection | ||
1157 | item->setSelected(!item->isSelected()); | ||
1158 | item->repaintCard(); | ||
1159 | emit selectionChanged(); | ||
1160 | } | ||
1161 | |||
1162 | else if (d->mSelectionMode == CardView::Extended) | ||
1163 | { | ||
1164 | if ((e->button() & Qt::LeftButton) && | ||
1165 | (e->state() & Qt::ShiftButton)) | ||
1166 | { | ||
1167 | if ( item == other ) return; | ||
1168 | |||
1169 | bool s = ! item->isSelected(); | ||
1170 | |||
1171 | if ( s && ! (e->state() & ControlButton) ) | ||
1172 | { | ||
1173 | bool b = signalsBlocked(); | ||
1174 | blockSignals(true); | ||
1175 | selectAll(false); | ||
1176 | blockSignals(b); | ||
1177 | } | ||
1178 | |||
1179 | int from, to, a, b; | ||
1180 | a = d->mItemList.findRef( item ); | ||
1181 | b = d->mItemList.findRef( other ); | ||
1182 | from = a < b ? a : b; | ||
1183 | to = a > b ? a : b; | ||
1184 | //kdDebug()<<"selecting items "<<from<<" - "<<to<<" ( "<<s<<" )"<<endl; | ||
1185 | CardViewItem *aItem; | ||
1186 | for ( ; from <= to; from++ ) | ||
1187 | { | ||
1188 | aItem = d->mItemList.at( from ); | ||
1189 | aItem->setSelected( s ); | ||
1190 | repaintItem( aItem ); | ||
1191 | } | ||
1192 | emit selectionChanged(); | ||
1193 | } | ||
1194 | else if ((e->button() & Qt::LeftButton) && | ||
1195 | (e->state() & Qt::ControlButton)) | ||
1196 | { | ||
1197 | item->setSelected(!item->isSelected()); | ||
1198 | item->repaintCard(); | ||
1199 | emit selectionChanged(); | ||
1200 | } | ||
1201 | |||
1202 | else if (e->button() & Qt::LeftButton) | ||
1203 | { | ||
1204 | bool b = signalsBlocked(); | ||
1205 | blockSignals(true); | ||
1206 | selectAll(false); | ||
1207 | blockSignals(b); | ||
1208 | |||
1209 | item->setSelected(true); | ||
1210 | item->repaintCard(); | ||
1211 | emit selectionChanged(); | ||
1212 | } | ||
1213 | } | ||
1214 | |||
1215 | } | ||
1216 | |||
1217 | void CardView::contentsMouseReleaseEvent(QMouseEvent *e) | ||
1218 | { | ||
1219 | QScrollView::contentsMouseReleaseEvent(e); | ||
1220 | |||
1221 | if ( d->mResizeAnchor ) | ||
1222 | { | ||
1223 | // finish the resizing: | ||
1224 | unsetCursor(); | ||
1225 | // hide rubber bands | ||
1226 | int newiw = d->mItemWidth - ((d->mResizeAnchor - d->mRubberBandAnchor)/d->span); | ||
1227 | drawRubberBands( 0 ); | ||
1228 | // we should move to reflect the new position if we are scrolled. | ||
1229 | if ( contentsX() ) | ||
1230 | { | ||
1231 | int newX = QMAX( 0, ( d->pressed * ( newiw + d->colspace + d->mSepWidth ) ) - e->x() ); | ||
1232 | setContentsPos( newX, contentsY() ); | ||
1233 | } | ||
1234 | // set new item width | ||
1235 | setItemWidth( newiw ); | ||
1236 | // reset anchors | ||
1237 | d->mResizeAnchor = 0; | ||
1238 | d->mRubberBandAnchor = 0; | ||
1239 | return; | ||
1240 | } | ||
1241 | |||
1242 | // If there are accel keys, we will not emit signals | ||
1243 | if ((e->state() & Qt::ShiftButton) || (e->state() & Qt::ControlButton)) | ||
1244 | return; | ||
1245 | |||
1246 | // Get the item at this position | ||
1247 | CardViewItem *item = itemAt(e->pos()); | ||
1248 | |||
1249 | if (item && KGlobalSettings::singleClick()) | ||
1250 | { | ||
1251 | emit executed(item); | ||
1252 | } | ||
1253 | } | ||
1254 | |||
1255 | void CardView::contentsMouseDoubleClickEvent(QMouseEvent *e) | ||
1256 | { | ||
1257 | QScrollView::contentsMouseDoubleClickEvent(e); | ||
1258 | |||
1259 | CardViewItem *item = itemAt(e->pos()); | ||
1260 | |||
1261 | if (item) | ||
1262 | { | ||
1263 | d->mCurrentItem = item; | ||
1264 | } | ||
1265 | |||
1266 | if (item && !KGlobalSettings::singleClick()) | ||
1267 | { | ||
1268 | emit executed(item); | ||
1269 | } | ||
1270 | emit doubleClicked(item); | ||
1271 | } | ||
1272 | |||
1273 | void CardView::contentsMouseMoveEvent( QMouseEvent *e ) | ||
1274 | { | ||
1275 | // resizing | ||
1276 | if ( d->mResizeAnchor ) | ||
1277 | { | ||
1278 | int x = e->x(); | ||
1279 | if ( x != d->mRubberBandAnchor ) | ||
1280 | drawRubberBands( x ); | ||
1281 | return; | ||
1282 | } | ||
1283 | |||
1284 | if (d->mLastClickOnItem && (e->state() & Qt::LeftButton) && | ||
1285 | ((e->pos() - d->mLastClickPos).manhattanLength() > 4)) { | ||
1286 | |||
1287 | startDrag(); | ||
1288 | return; | ||
1289 | } | ||
1290 | |||
1291 | d->mTimer->start( 500 ); | ||
1292 | |||
1293 | // see if we are over a separator | ||
1294 | // only if we actually have them painted? | ||
1295 | if ( d->mDrawSeparators ) | ||
1296 | { | ||
1297 | int colcontentw = d->mItemWidth + (2*d->mItemSpacing); | ||
1298 | int colw = colcontentw + d->mSepWidth; | ||
1299 | int m = e->x()%colw; | ||
1300 | if ( m >= colcontentw && m > 0 ) | ||
1301 | { | ||
1302 | setCursor( SplitVCursor ); // Why does this fail sometimes? | ||
1303 | d->mOnSeparator = true; | ||
1304 | } | ||
1305 | else | ||
1306 | { | ||
1307 | setCursor( ArrowCursor ); | ||
1308 | d->mOnSeparator = false; | ||
1309 | } | ||
1310 | } | ||
1311 | } | ||
1312 | |||
1313 | void CardView::enterEvent( QEvent * ) | ||
1314 | { | ||
1315 | d->mTimer->start( 500 ); | ||
1316 | } | ||
1317 | |||
1318 | void CardView::leaveEvent( QEvent * ) | ||
1319 | { | ||
1320 | d->mTimer->stop(); | ||
1321 | if (d->mOnSeparator) | ||
1322 | { | ||
1323 | d->mOnSeparator = false; | ||
1324 | setCursor( ArrowCursor ); | ||
1325 | } | ||
1326 | } | ||
1327 | |||
1328 | void CardView::focusInEvent( QFocusEvent * ) | ||
1329 | { | ||
1330 | if (!d->mCurrentItem && d->mItemList.count() ) | ||
1331 | { | ||
1332 | setCurrentItem( d->mItemList.first() ); | ||
1333 | } | ||
1334 | else if ( d->mCurrentItem ) | ||
1335 | { | ||
1336 | d->mCurrentItem->repaintCard(); | ||
1337 | } | ||
1338 | } | ||
1339 | |||
1340 | void CardView::focusOutEvent( QFocusEvent * ) | ||
1341 | { | ||
1342 | if (d->mCurrentItem) | ||
1343 | d->mCurrentItem->repaintCard(); | ||
1344 | } | ||
1345 | |||
1346 | void CardView::keyPressEvent( QKeyEvent *e ) | ||
1347 | { | ||
1348 | if ( ! ( childCount() && d->mCurrentItem ) ) | ||
1349 | { | ||
1350 | e->ignore(); | ||
1351 | return; | ||
1352 | } | ||
1353 | |||
1354 | uint pos = d->mItemList.findRef( d->mCurrentItem ); | ||
1355 | CardViewItem *aItem = 0L; // item that gets the focus | ||
1356 | CardViewItem *old = d->mCurrentItem; | ||
1357 | |||
1358 | switch ( e->key() ) | ||
1359 | { | ||
1360 | case Key_Up: | ||
1361 | if ( pos > 0 ) | ||
1362 | { | ||
1363 | aItem = d->mItemList.at( pos - 1 ); | ||
1364 | setCurrentItem( aItem ); | ||
1365 | } | ||
1366 | break; | ||
1367 | case Key_Down: | ||
1368 | if ( pos < d->mItemList.count() - 1 ) | ||
1369 | { | ||
1370 | aItem = d->mItemList.at( pos + 1 ); | ||
1371 | setCurrentItem( aItem ); | ||
1372 | } | ||
1373 | break; | ||
1374 | case Key_Left: | ||
1375 | { | ||
1376 | // look for an item in the previous/next column, starting from | ||
1377 | // the vertical middle of the current item. | ||
1378 | // FIXME use nice calculatd measures!!! | ||
1379 | QPoint aPoint( d->mCurrentItem->d->x, d->mCurrentItem->d->y ); | ||
1380 | aPoint -= QPoint( 30,-(d->mCurrentItem->height()/2) ); | ||
1381 | aItem = itemAt( aPoint ); | ||
1382 | // maybe we hit some space below an item | ||
1383 | while ( !aItem && aPoint.y() > 27 ) | ||
1384 | { | ||
1385 | aPoint -= QPoint( 0, 16 ); | ||
1386 | aItem = itemAt( aPoint ); | ||
1387 | } | ||
1388 | if ( aItem ) | ||
1389 | setCurrentItem( aItem ); | ||
1390 | } | ||
1391 | break; | ||
1392 | case Key_Right: | ||
1393 | { | ||
1394 | // FIXME use nice calculated measures!!! | ||
1395 | QPoint aPoint( d->mCurrentItem->d->x + d->mItemWidth, d->mCurrentItem->d->y ); | ||
1396 | aPoint += QPoint( 30,(d->mCurrentItem->height()/2) ); | ||
1397 | aItem = itemAt( aPoint ); | ||
1398 | while ( !aItem && aPoint.y() > 27 ) | ||
1399 | { | ||
1400 | aPoint -= QPoint( 0, 16 ); | ||
1401 | aItem = itemAt( aPoint ); | ||
1402 | } | ||
1403 | if ( aItem ) | ||
1404 | setCurrentItem( aItem ); | ||
1405 | } | ||
1406 | break; | ||
1407 | case Key_Home: | ||
1408 | aItem = d->mItemList.first(); | ||
1409 | setCurrentItem( aItem ); | ||
1410 | break; | ||
1411 | case Key_End: | ||
1412 | aItem = d->mItemList.last(); | ||
1413 | setCurrentItem( aItem ); | ||
1414 | break; | ||
1415 | case Key_Prior: // PageUp | ||
1416 | { | ||
1417 | // QListView: "Make the item above the top visible and current" | ||
1418 | // TODO if contentsY(), pick the top item of the leftmost visible column | ||
1419 | if ( contentsX() <= 0 ) | ||
1420 | return; | ||
1421 | int cw = columnWidth(); | ||
1422 | int theCol = ( QMAX( 0, ( contentsX()/cw) * cw ) ) + d->mItemSpacing; | ||
1423 | aItem = itemAt( QPoint( theCol + 1, d->mItemSpacing + 1 ) ); | ||
1424 | if ( aItem ) | ||
1425 | setCurrentItem( aItem ); | ||
1426 | } | ||
1427 | break; | ||
1428 | case Key_Next: // PageDown | ||
1429 | { | ||
1430 | // QListView: "Make the item below the bottom visible and current" | ||
1431 | // find the first not fully visible column. | ||
1432 | // TODO: consider if a partly visible (or even hidden) item at the | ||
1433 | // bottom of the rightmost column exists | ||
1434 | int cw = columnWidth(); | ||
1435 | int theCol = ( (( contentsX() + visibleWidth() )/cw) * cw ) + d->mItemSpacing + 1; | ||
1436 | // if separators are on, we may need to we may be one column further right if only the spacing/sep is hidden | ||
1437 | if ( d->mDrawSeparators && cw - (( contentsX() + visibleWidth() )%cw) <= int( d->mItemSpacing + d->mSepWidth ) ) | ||
1438 | theCol += cw; | ||
1439 | |||
1440 | // make sure this is not too far right | ||
1441 | while ( theCol > contentsWidth() ) | ||
1442 | theCol -= columnWidth(); | ||
1443 | |||
1444 | aItem = itemAt( QPoint( theCol, d->mItemSpacing + 1 ) ); | ||
1445 | |||
1446 | if ( aItem ) | ||
1447 | setCurrentItem( aItem ); | ||
1448 | } | ||
1449 | break; | ||
1450 | case Key_Space: | ||
1451 | setSelected( d->mCurrentItem, !d->mCurrentItem->isSelected() ); | ||
1452 | emit selectionChanged(); | ||
1453 | break; | ||
1454 | case Key_Return: | ||
1455 | case Key_Enter: | ||
1456 | emit returnPressed( d->mCurrentItem ); | ||
1457 | emit executed( d->mCurrentItem ); | ||
1458 | break; | ||
1459 | default: | ||
1460 | if ( (e->state() & ControlButton) && e->key() == Key_A ) | ||
1461 | { | ||
1462 | // select all | ||
1463 | selectAll( true ); | ||
1464 | break; | ||
1465 | } | ||
1466 | // if we have a string, do autosearch | ||
1467 | else if ( ! e->text().isEmpty() && e->text()[0].isPrint() ) | ||
1468 | { | ||
1469 | |||
1470 | } | ||
1471 | break; | ||
1472 | } | ||
1473 | // handle selection | ||
1474 | if ( aItem ) | ||
1475 | { | ||
1476 | if ( d->mSelectionMode == CardView::Extended ) | ||
1477 | { | ||
1478 | if ( (e->state() & ShiftButton) ) | ||
1479 | { | ||
1480 | // shift button: toggle range | ||
1481 | // if control button is pressed, leave all items | ||
1482 | // and toggle selection current->old current | ||
1483 | // otherwise, ?????? | ||
1484 | bool s = ! aItem->isSelected(); | ||
1485 | int from, to, a, b; | ||
1486 | a = d->mItemList.findRef( aItem ); | ||
1487 | b = d->mItemList.findRef( old ); | ||
1488 | from = a < b ? a : b; | ||
1489 | to = a > b ? a : b; | ||
1490 | |||
1491 | if ( to - from > 1 ) | ||
1492 | { | ||
1493 | bool b = signalsBlocked(); | ||
1494 | blockSignals(true); | ||
1495 | selectAll(false); | ||
1496 | blockSignals(b); | ||
1497 | } | ||
1498 | |||
1499 | //kdDebug()<<"selecting items "<<from<<" - "<<to<<" ( "<<s<<" )"<<endl; | ||
1500 | CardViewItem *item; | ||
1501 | for ( ; from <= to; from++ ) | ||
1502 | { | ||
1503 | item = d->mItemList.at( from ); | ||
1504 | item->setSelected( s ); | ||
1505 | repaintItem( item ); | ||
1506 | } | ||
1507 | emit selectionChanged(); | ||
1508 | } | ||
1509 | else if ( (e->state() & ControlButton) ) | ||
1510 | { | ||
1511 | // control button: do nothing | ||
1512 | } | ||
1513 | else | ||
1514 | { | ||
1515 | // no button: move selection to this item | ||
1516 | bool b = signalsBlocked(); | ||
1517 | blockSignals(true); | ||
1518 | selectAll(false); | ||
1519 | blockSignals(b); | ||
1520 | |||
1521 | setSelected( aItem, true ); | ||
1522 | emit selectionChanged(); | ||
1523 | } | ||
1524 | } | ||
1525 | } | ||
1526 | } | ||
1527 | |||
1528 | void CardView::contentsWheelEvent( QWheelEvent * e ) | ||
1529 | { | ||
1530 | scrollBy(2*e->delta()/-3, 0); | ||
1531 | } | ||
1532 | |||
1533 | void CardView::setLayoutDirty(bool dirty) | ||
1534 | { | ||
1535 | if (d->mLayoutDirty != dirty) | ||
1536 | { | ||
1537 | d->mLayoutDirty = dirty; | ||
1538 | repaint(); | ||
1539 | } | ||
1540 | } | ||
1541 | |||
1542 | void CardView::setDrawCardBorder(bool enabled) | ||
1543 | { | ||
1544 | if (enabled != d->mDrawCardBorder) | ||
1545 | { | ||
1546 | d->mDrawCardBorder = enabled; | ||
1547 | repaint(); | ||
1548 | } | ||
1549 | } | ||
1550 | |||
1551 | bool CardView::drawCardBorder() const | ||
1552 | { | ||
1553 | return d->mDrawCardBorder; | ||
1554 | } | ||
1555 | |||
1556 | void CardView::setDrawColSeparators(bool enabled) | ||
1557 | { | ||
1558 | if (enabled != d->mDrawSeparators) | ||
1559 | { | ||
1560 | d->mDrawSeparators = enabled; | ||
1561 | setLayoutDirty(true); | ||
1562 | } | ||
1563 | } | ||
1564 | |||
1565 | bool CardView::drawColSeparators() const | ||
1566 | { | ||
1567 | return d->mDrawSeparators; | ||
1568 | } | ||
1569 | |||
1570 | void CardView::setDrawFieldLabels(bool enabled) | ||
1571 | { | ||
1572 | if (enabled != d->mDrawFieldLabels) | ||
1573 | { | ||
1574 | d->mDrawFieldLabels = enabled; | ||
1575 | repaint(); | ||
1576 | } | ||
1577 | } | ||
1578 | |||
1579 | bool CardView::drawFieldLabels() const | ||
1580 | { | ||
1581 | return d->mDrawFieldLabels; | ||
1582 | } | ||
1583 | |||
1584 | void CardView::setShowEmptyFields(bool show) | ||
1585 | { | ||
1586 | if (show != d->mShowEmptyFields) | ||
1587 | { | ||
1588 | d->mShowEmptyFields = show; | ||
1589 | setLayoutDirty(true); | ||
1590 | } | ||
1591 | } | ||
1592 | |||
1593 | bool CardView::showEmptyFields() const | ||
1594 | { | ||
1595 | return d->mShowEmptyFields; | ||
1596 | } | ||
1597 | |||
1598 | void CardView::startDrag() | ||
1599 | { | ||
1600 | // The default implementation is a no-op. It must be | ||
1601 | // reimplemented in a subclass to be useful | ||
1602 | } | ||
1603 | void CardView::tryShowFullText() | ||
1604 | { | ||
1605 | d->mTimer->stop(); | ||
1606 | // if we have an item | ||
1607 | QPoint cpos = viewportToContents( viewport()->mapFromGlobal( QCursor::pos() ) ); | ||
1608 | CardViewItem *item = itemAt( cpos ); | ||
1609 | if ( item ) | ||
1610 | { | ||
1611 | // query it for a value to display | ||
1612 | //QString s = item ? item->caption() : "(no item)"; | ||
1613 | //kdDebug()<<"MOUSE REST: "<<s<<endl; | ||
1614 | QPoint ipos = cpos - itemRect( item ).topLeft(); | ||
1615 | item->showFullString( ipos, d->mTip ); | ||
1616 | } | ||
1617 | } | ||
1618 | |||
1619 | void CardView::drawRubberBands( int pos ) | ||
1620 | { | ||
1621 | if ( pos && ((pos-d->firstX)/d->span) - d->colspace - d->mSepWidth < MIN_ITEM_WIDTH ) return; | ||
1622 | |||
1623 | int tmpcw = (d->mRubberBandAnchor-d->firstX)/d->span; | ||
1624 | int x = d->firstX + tmpcw - d->mSepWidth - contentsX(); | ||
1625 | int h = visibleHeight(); | ||
1626 | |||
1627 | QPainter p( viewport() ); | ||
1628 | p.setRasterOp( XorROP ); | ||
1629 | p.setPen( gray ); | ||
1630 | p.setBrush( gray ); | ||
1631 | uint n = d->first; | ||
1632 | // erase | ||
1633 | if ( d->mRubberBandAnchor ) | ||
1634 | do { | ||
1635 | p.drawRect( x, 0, 2, h ); | ||
1636 | x += tmpcw; | ||
1637 | n++; | ||
1638 | } while ( x < visibleWidth() && n < d->mSeparatorList.count() ); | ||
1639 | // paint new | ||
1640 | if ( ! pos ) return; | ||
1641 | tmpcw = (pos - d->firstX)/d->span; | ||
1642 | n = d->first; | ||
1643 | x = d->firstX + tmpcw - d->mSepWidth - contentsX(); | ||
1644 | do { | ||
1645 | p.drawRect( x, 0, 2, h ); | ||
1646 | x += tmpcw; | ||
1647 | n++; | ||
1648 | } while ( x < visibleWidth() && n < d->mSeparatorList.count() ); | ||
1649 | d->mRubberBandAnchor = pos; | ||
1650 | } | ||
1651 | |||
1652 | |||
1653 | int CardView::itemWidth() const | ||
1654 | { | ||
1655 | return d->mItemWidth; | ||
1656 | } | ||
1657 | |||
1658 | void CardView::setItemWidth( int w ) | ||
1659 | { | ||
1660 | if ( w == d->mItemWidth ) | ||
1661 | return; | ||
1662 | if ( w < MIN_ITEM_WIDTH ) | ||
1663 | w = MIN_ITEM_WIDTH; | ||
1664 | d->mItemWidth = w; | ||
1665 | setLayoutDirty( true ); | ||
1666 | #ifndef KAB_EMBEDDED | ||
1667 | updateContents(); | ||
1668 | #else //KAB_EMBEDDED | ||
1669 | //US updateContents( d->contentsX(), d->contentsY(), visibleWidth(), visibleHeight() ); | ||
1670 | qDebug("CardView::setItemWidth has to be verified"); | ||
1671 | updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
1672 | #endif //KAB_EMBEDDED | ||
1673 | } | ||
1674 | |||
1675 | void CardView::setHeaderFont( const QFont &fnt ) | ||
1676 | { | ||
1677 | d->mHeaderFont = fnt; | ||
1678 | delete d->mBFm; | ||
1679 | d->mBFm = new QFontMetrics( fnt ); | ||
1680 | } | ||
1681 | |||
1682 | QFont CardView::headerFont() const | ||
1683 | { | ||
1684 | return d->mHeaderFont; | ||
1685 | } | ||
1686 | |||
1687 | void CardView::setFont( const QFont &fnt ) | ||
1688 | { | ||
1689 | QScrollView::setFont( fnt ); | ||
1690 | delete d->mFm; | ||
1691 | d->mFm = new QFontMetrics( fnt ); | ||
1692 | } | ||
1693 | |||
1694 | int CardView::separatorWidth() | ||
1695 | { | ||
1696 | return d->mSepWidth; | ||
1697 | } | ||
1698 | |||
1699 | void CardView::setSeparatorWidth( int width ) | ||
1700 | { | ||
1701 | d->mSepWidth = width; | ||
1702 | setLayoutDirty( true ); // hmm, actually I could just adjust the x'es... | ||
1703 | } | ||
1704 | |||
1705 | int CardView::maxFieldLines() const | ||
1706 | { | ||
1707 | return d->mMaxFieldLines; | ||
1708 | } | ||
1709 | |||
1710 | void CardView::setMaxFieldLines( int howmany ) | ||
1711 | { | ||
1712 | d->mMaxFieldLines = howmany ? howmany : INT_MAX; | ||
1713 | // FIXME update, forcing the items to recalc height!! | ||
1714 | } | ||
1715 | //END Cardview | ||
1716 | |||
1717 | #ifndef KAB_EMBEDDED | ||
1718 | #include "cardview.moc" | ||
1719 | #endif //KAB_EMBEDDED | ||