00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "cardview.h"
00026
00027 #include <limits.h>
00028
00029 #include <qpainter.h>
00030 #include <qtimer.h>
00031 #include <qdatetime.h>
00032 #include <qlabel.h>
00033 #include <qstyle.h>
00034 #include <qcursor.h>
00035 #include <qtooltip.h>
00036
00037 #include <kdebug.h>
00038 #include <kglobalsettings.h>
00039
00040
00041 #define MIN_ITEM_WIDTH 80
00042
00043
00045
00046 class CardViewTip : public QLabel {
00047 public:
00048 CardViewTip(QWidget *parent=0, const char *name=0) : QLabel( parent, name )
00049 {
00050 setPalette( QToolTip::palette() );
00051 setFrameStyle( Panel|Plain );
00052 setMidLineWidth(0);
00053 setIndent(1);
00054 }
00055
00056 ~CardViewTip() {};
00057 protected:
00058 void leaveEvent( QEvent * )
00059 {
00060 hide();
00061 }
00062 };
00063
00065
00066
00067
00068
00069
00070
00071
00072 class CardViewItemList : public QPtrList<CardViewItem>
00073 {
00074 protected:
00075 virtual int compareItems(QPtrCollection::Item item1,
00076 QPtrCollection::Item item2)
00077 {
00078 CardViewItem *cItem1 = (CardViewItem*)item1;
00079 CardViewItem *cItem2 = (CardViewItem*)item2;
00080
00081 if ( cItem1 == cItem2 )
00082 return 0;
00083
00084 if ((cItem1 == 0) || (cItem2 == 0))
00085 return cItem1 ? -1 : 1;
00086
00087 if (cItem1->caption() < cItem2->caption())
00088 return -1;
00089
00090 else if (cItem1->caption() > cItem2->caption())
00091 return 1;
00092
00093 return 0;
00094 }
00095
00096 private:
00097
00098
00099
00100
00101 };
00102
00104
00105 class CardViewSeparator
00106 {
00107 friend class CardView;
00108
00109 public:
00110 CardViewSeparator(CardView *view)
00111 : mView(view)
00112 {
00113 mRect = QRect(0, 0, view->separatorWidth(), 0);
00114 }
00115
00116 ~CardViewSeparator() {}
00117
00118 void paintSeparator(QPainter *p, QColorGroup &cg)
00119 {
00120 p->fillRect(0, 0, mRect.width(), mRect.height(),
00121 cg.brush(QColorGroup::Button));
00122 }
00123
00124 void repaintSeparator()
00125 {
00126 mView->repaintContents(mRect);
00127 }
00128
00129 private:
00130 CardView *mView;
00131 QRect mRect;
00132 };
00133
00134
00135
00136
00137
00138 class CardViewPrivate
00139 {
00140 public:
00141 CardViewPrivate()
00142 : mSelectionMode( CardView::Multi ),
00143 mDrawCardBorder( true ),
00144 mDrawFieldLabels( true ),
00145 mDrawSeparators( true),
00146 mSepWidth( 2 ),
00147 mShowEmptyFields( false ),
00148 mLayoutDirty( true ),
00149 mLastClickOnItem( false ),
00150 mItemMargin( 0 ),
00151 mItemSpacing( 10 ),
00152 mItemWidth( 200 ),
00153 mMaxFieldLines( INT_MAX ),
00154 mCurrentItem( 0L ),
00155 mLastClickPos( QPoint(0, 0) ),
00156 mRubberBandAnchor( 0 ),
00157 mCompText( QString::null )
00158 {};
00159
00160 CardViewItemList mItemList;
00161 QPtrList<CardViewSeparator> mSeparatorList;
00162 QFontMetrics *mFm;
00163 QFontMetrics *mBFm;
00164 QFont mHeaderFont;
00165 CardView::SelectionMode mSelectionMode;
00166 bool mDrawCardBorder;
00167 bool mDrawFieldLabels;
00168 bool mDrawSeparators;
00169 int mSepWidth;
00170 bool mShowEmptyFields;
00171 bool mLayoutDirty;
00172 bool mLastClickOnItem;
00173 uint mItemMargin;
00174 uint mItemSpacing;
00175 int mItemWidth;
00176 uint mMaxFieldLines;
00177 CardViewItem *mCurrentItem;
00178 QPoint mLastClickPos;
00179 QTimer *mTimer;
00180 CardViewTip *mTip;
00181 bool mOnSeparator;
00182
00183 int mResizeAnchor;
00184 int mRubberBandAnchor;
00185
00186
00187
00188 int colspace;
00189 uint first;
00190 int firstX;
00191 int pressed;
00192 int span;
00193
00194 QString mCompText;
00195 QDateTime mCompUpdated;
00196 };
00197
00198 class CardViewItemPrivate
00199 {
00200 public:
00201 CardViewItemPrivate() {}
00202
00203 QString mCaption;
00204 QPtrList< CardViewItem::Field > mFieldList;
00205 bool mSelected;
00206 int x;
00207 int y;
00208 int maxLabelWidth;
00209 int hcache;
00210 };
00211
00212
00213
00214
00215 CardViewItem::CardViewItem(CardView *parent, QString caption)
00216 : d(new CardViewItemPrivate()), mView(parent)
00217 {
00218 d->mCaption = caption;
00219
00220 initialize();
00221 }
00222
00223 CardViewItem::~CardViewItem()
00224 {
00225
00226 if (mView != 0)
00227 mView->takeItem(this);
00228
00229 delete d;
00230 d = 0;
00231 }
00232
00233 void CardViewItem::initialize()
00234 {
00235 d->mSelected = false;
00236 d->mFieldList.setAutoDelete(true);
00237 d->maxLabelWidth = 0;
00238 d->hcache=0;
00239
00240
00241
00242
00243 if (mView != 0)
00244 mView->insertItem(this);
00245 }
00246
00247 void CardViewItem::paintCard(QPainter *p, QColorGroup &cg)
00248 {
00249
00250 if (!mView)
00251 return;
00252
00253 QPen pen;
00254 QBrush brush;
00255 QFontMetrics fm = *(mView->d->mFm);
00256 QFontMetrics bFm = *(mView->d->mBFm);
00257 bool drawLabels = mView->d->mDrawFieldLabels;
00258 bool drawBorder = mView->d->mDrawCardBorder;
00259 int mg = mView->itemMargin();
00260 int w = mView->itemWidth() - (mg*2);
00261 int h = height() - (mg*2);
00262 const int colonWidth( fm.width(":") );
00263 int labelXPos = 2 + mg;
00264 int labelWidth = QMIN( w/2 - 4 - mg, d->maxLabelWidth + colonWidth + 4 );
00265 int valueXPos = labelWidth + 4 + mg;
00266 int valueWidth = w - labelWidth - 4 - mg;
00267
00268 p->setFont( mView->font() );
00269 labelWidth -= colonWidth;
00270
00271 if (!drawLabels)
00272 {
00273 valueXPos = labelXPos;
00274 valueWidth = w - 4;
00275 }
00276
00277
00278 if (isSelected())
00279 pen = QPen(cg.highlight(), 1);
00280 else
00281 pen = QPen(cg.button(), 1);
00282 p->setPen(pen);
00283
00284
00285 if (drawBorder)
00286 p->drawRect( mg, mg, w, h );
00287
00288
00289 if (isSelected())
00290 brush = cg.brush(QColorGroup::Highlight);
00291 else
00292 brush = cg.brush(QColorGroup::Button);
00293
00294 p->fillRect(mg, mg, w, 4 + bFm.height(), brush);
00295
00296
00297 p->save();
00298 QFont bFont = mView->headerFont();
00299
00300 p->setFont(bFont);
00301 if (isSelected())
00302 p->setPen(cg.highlightedText());
00303 else
00304 p->setPen(cg.buttonText());
00305 p->drawText(2+mg, 2+mg + bFm.ascent(), trimString(d->mCaption, w-4, bFm));
00306 p->restore();
00307
00308
00309 QPtrListIterator< CardViewItem::Field > iter(d->mFieldList);
00310 QString label, value;
00311 int yPos = mg + 4 + bFm.height() + fm.height();
00312 p->setPen(cg.text());
00313
00314 int fh = fm.height();
00315 int cln( 0 );
00316 QString tmp;
00317 int maxLines = mView->maxFieldLines();
00318 for (iter.toFirst(); iter.current(); ++iter)
00319 {
00320 value = (*iter)->second;
00321 if ( value.isEmpty() && ! mView->d->mShowEmptyFields )
00322 continue;
00323
00324 if (drawLabels)
00325 {
00326 label = trimString((*iter)->first, labelWidth, fm);
00327 p->drawText(labelXPos, yPos, label + ":");
00328 }
00329
00330 for (cln=0; cln <= maxLines; cln++)
00331 {
00332 tmp = value.section('\n',cln,cln);
00333 if ( !tmp.isEmpty() ) p->drawText( valueXPos, yPos + cln*fh, trimString( tmp, valueWidth, fm ) );
00334 else break;
00335 }
00336
00337 if ( cln == 0 ) cln = 1;
00338 yPos += cln * fh + 2;
00339 }
00340
00341
00342 if ( mView->currentItem() == this && mView->hasFocus() )
00343 {
00344 mView->style().drawPrimitive( QStyle::PE_FocusRect, p,
00345 QRect(0, 0, mView->itemWidth(), h+(2*mg)), cg,
00346 QStyle::Style_FocusAtBorder,
00347 QStyleOption( isSelected() ? cg.highlight() : cg.base() ) );
00348 }
00349 }
00350
00351 const QString &CardViewItem::caption() const
00352 {
00353 return d->mCaption;
00354 }
00355
00356
00357 int CardViewItem::height( bool allowCache ) const
00358 {
00359
00360 if ( allowCache && d->hcache )
00361 return d->hcache;
00362
00363
00364
00365
00366
00367
00368
00369 int baseHeight = 8 + ( 2 * mView->itemMargin() );
00370
00371
00372
00373
00374
00375 bool sef = mView->showEmptyFields();
00376 int fh = mView->d->mFm->height();
00377
00378 int fieldHeight = 0;
00379 int lines;
00380 int maxLines( mView->maxFieldLines() );
00381 QPtrListIterator< CardViewItem::Field > iter(d->mFieldList);
00382 for (iter.toFirst(); iter.current(); ++iter)
00383 {
00384 if ( !sef && (*iter)->second.isEmpty() )
00385 continue;
00386 lines = QMIN( (*iter)->second.contains('\n') + 1, maxLines );
00387 fieldHeight += ( lines * fh ) + 2;
00388 }
00389
00390
00391 fieldHeight += mView->d->mBFm->height();
00392 d->hcache = baseHeight + fieldHeight;
00393 return d->hcache;
00394 }
00395
00396 bool CardViewItem::isSelected() const
00397 {
00398 return d->mSelected;
00399 }
00400
00401 void CardViewItem::setSelected(bool selected)
00402 {
00403 d->mSelected = selected;
00404 }
00405
00406 void CardViewItem::insertField(const QString &label, const QString &value)
00407 {
00408 CardViewItem::Field *f = new CardViewItem::Field(label, value);
00409 d->mFieldList.append(f);
00410 d->hcache=0;
00411
00412 if (mView)
00413 {
00414 mView->setLayoutDirty(true);
00415 d->maxLabelWidth = QMAX( mView->d->mFm->width( label ), d->maxLabelWidth );
00416 }
00417 }
00418
00419 void CardViewItem::removeField(const QString &label)
00420 {
00421 CardViewItem::Field *f;
00422
00423 QPtrListIterator< CardViewItem::Field > iter(d->mFieldList);
00424 for (iter.toFirst(); iter.current(); ++iter)
00425 {
00426 f = *iter;
00427 if (f->first == label)
00428 break;
00429 }
00430
00431 if (*iter)
00432 d->mFieldList.remove(*iter);
00433 d->hcache = 0;
00434
00435 if (mView)
00436 mView->setLayoutDirty(true);
00437 }
00438
00439 void CardViewItem::clearFields()
00440 {
00441 d->mFieldList.clear();
00442 d->hcache = 0;
00443
00444 if (mView)
00445 mView->setLayoutDirty(true);
00446 }
00447
00448 QString CardViewItem::trimString(const QString &text, int width,
00449 QFontMetrics &fm)
00450 {
00451 if (fm.width(text) <= width)
00452 return text;
00453
00454 QString dots = "...";
00455 int dotWidth = fm.width(dots);
00456 QString trimmed;
00457 int charNum = 0;
00458
00459 while (fm.width(trimmed) + dotWidth < width)
00460 {
00461 trimmed += text[charNum];
00462 charNum++;
00463 }
00464
00465
00466 trimmed = trimmed.left(trimmed.length()-1);
00467 trimmed += dots;
00468
00469 return trimmed;
00470 }
00471
00472 CardViewItem *CardViewItem::nextItem()
00473 {
00474 CardViewItem *item = 0;
00475
00476 if (mView)
00477 item = mView->itemAfter(this);
00478
00479 return item;
00480 }
00481
00482 void CardViewItem::repaintCard()
00483 {
00484 if (mView)
00485 mView->repaintItem(this);
00486 }
00487
00488 void CardViewItem::setCaption(const QString &caption)
00489 {
00490 d->mCaption = caption;
00491 repaintCard();
00492 }
00493
00494 QString CardViewItem::fieldValue(const QString &label)
00495 {
00496 QPtrListIterator< CardViewItem::Field > iter(d->mFieldList);
00497 for (iter.toFirst(); iter.current(); ++iter)
00498 if ((*iter)->first == label)
00499 return (*iter)->second;
00500
00501 return QString();
00502 }
00503
00504
00505 void CardViewItem::showFullString( const QPoint &itempos, CardViewTip *tip )
00506 {
00507 bool trimmed( false );
00508 QString s;
00509 int mrg = mView->itemMargin();
00510 int y = mView->d->mBFm->height() + 6 + mrg;
00511 int w = mView->itemWidth() - (2*mrg);
00512 int lw;
00513 bool drawLabels = mView->drawFieldLabels();
00514 bool isLabel = drawLabels && itempos.x() < w/2 ? true : false;
00515
00516 if ( itempos.y() < y )
00517 {
00518 if ( itempos.y() < 8 + mrg || itempos.y() > y - 4 )
00519 return;
00520
00521 s = caption();
00522 trimmed = mView->d->mBFm->width( s ) > w - 4;
00523 y = 2 + mrg;
00524 lw = 0;
00525 isLabel=true;
00526 } else {
00527
00528 Field *f = fieldAt( itempos );
00529 if ( !f || ( !mView->showEmptyFields() && f->second.isEmpty() ) )
00530 return;
00531
00532
00533
00534
00535 int maxLines = mView->maxFieldLines();
00536 bool se = mView->showEmptyFields();
00537 int fh = mView->d->mFm->height();
00538
00539 Field *_f;
00540 for (_f = d->mFieldList.first(); _f != f; _f = d->mFieldList.next())
00541 if ( se || ! _f->second.isEmpty() )
00542 y += ( QMIN(_f->second.contains('\n')+1, maxLines) * fh ) + 2;
00543
00544 if ( isLabel && itempos.y() > y + fh )
00545 return;
00546
00547 s = isLabel ? f->first : f->second;
00548
00549 int colonWidth = mView->d->mFm->width(":");
00550 lw = drawLabels ?
00551 QMIN( w/2 - 4 - mrg, d->maxLabelWidth + colonWidth + 4 ) :
00552 0;
00553 int mw = isLabel ? lw - colonWidth : w - lw - (mrg*2);
00554 if ( isLabel )
00555 {
00556 trimmed = mView->d->mFm->width( s ) > mw - colonWidth;
00557 } else {
00558 QRect r( mView->d->mFm->boundingRect( 0, 0, INT_MAX, INT_MAX, Qt::AlignTop|Qt::AlignLeft, s ) );
00559 trimmed = r.width() > mw || r.height()/fh > QMIN(s.contains('\n') + 1, maxLines);
00560 }
00561 }
00562 if ( trimmed )
00563 {
00564 tip->setFont( (isLabel && !lw) ? mView->headerFont() : mView->font() );
00565 tip->setText( s );
00566 tip->adjustSize();
00567
00568 int lx;
00569 lx = isLabel || !drawLabels ? mrg : lw + mrg + 2 ;
00570 QPoint pnt(mView->contentsToViewport( QPoint(d->x, d->y) ));
00571 pnt += QPoint(lx, y);
00572 if ( pnt.x() < 0 )
00573 pnt.setX( 0 );
00574 if ( pnt.x() + tip->width() > mView->visibleWidth() )
00575 pnt.setX( mView->visibleWidth() - tip->width() );
00576 if ( pnt.y() + tip->height() > mView->visibleHeight() )
00577 pnt.setY( QMAX( 0, mView->visibleHeight() - tip->height() ) );
00578
00579 tip->move( pnt );
00580 tip->show();
00581 }
00582 }
00583
00584 CardViewItem::Field *CardViewItem::fieldAt( const QPoint & itempos ) const
00585 {
00586 int ypos = mView->d->mBFm->height() + 7 + mView->d->mItemMargin;
00587 int iy = itempos.y();
00588
00589 if ( iy <= ypos )
00590 return 0;
00591
00592 bool showEmpty = mView->showEmptyFields();
00593 int fh = mView->d->mFm->height();
00594 int maxLines = mView->maxFieldLines();
00595 Field *f;
00596 for ( f = d->mFieldList.first(); f; f = d->mFieldList.next() )
00597 {
00598 if ( showEmpty || !f->second.isEmpty() )
00599 ypos += ( QMIN( f->second.contains('\n')+1, maxLines ) *fh)+2;
00600 if ( iy <= ypos )
00601 break;
00602 }
00603 return f ? f : 0;
00604 }
00605
00606
00607
00608
00609 CardView::CardView(QWidget *parent, const char *name)
00610 : QScrollView(parent, name),
00611 d(new CardViewPrivate())
00612 {
00613 d->mItemList.setAutoDelete(true);
00614 d->mSeparatorList.setAutoDelete(true);
00615
00616 QFont f = font();
00617 d->mFm = new QFontMetrics(f);
00618 f.setBold(true);
00619 d->mHeaderFont = f;
00620 d->mBFm = new QFontMetrics(f);
00621 d->mTip = ( new CardViewTip( viewport() ) ),
00622 d->mTip->hide();
00623 d->mTimer = ( new QTimer(this, "mouseTimer") ),
00624
00625 viewport()->setMouseTracking( true );
00626 viewport()->setFocusProxy(this);
00627 viewport()->setFocusPolicy(WheelFocus);
00628 viewport()->setBackgroundMode(PaletteBase);
00629
00630 connect( d->mTimer, SIGNAL(timeout()), this, SLOT(tryShowFullText()) );
00631
00632 setBackgroundMode(PaletteBackground, PaletteBase);
00633
00634
00635 setVScrollBarMode(AlwaysOff);
00636 }
00637
00638 CardView::~CardView()
00639 {
00640 delete d->mFm;
00641 delete d->mBFm;
00642 delete d;
00643 d = 0;
00644 }
00645
00646 void CardView::insertItem(CardViewItem *item)
00647 {
00648 d->mItemList.inSort(item);
00649 setLayoutDirty(true);
00650 }
00651
00652 void CardView::takeItem(CardViewItem *item)
00653 {
00654 if ( d->mCurrentItem == item )
00655 d->mCurrentItem = item->nextItem();
00656 d->mItemList.take(d->mItemList.findRef(item));
00657
00658 setLayoutDirty(true);
00659 }
00660
00661 void CardView::clear()
00662 {
00663 d->mItemList.clear();
00664
00665 setLayoutDirty(true);
00666 }
00667
00668 CardViewItem *CardView::currentItem()
00669 {
00670 if ( ! d->mCurrentItem && d->mItemList.count() )
00671 d->mCurrentItem = d->mItemList.first();
00672 return d->mCurrentItem;
00673 }
00674
00675 void CardView::setCurrentItem( CardViewItem *item )
00676 {
00677 if ( !item )
00678 return;
00679 else if ( item->cardView() != this )
00680 {
00681 kdDebug(5720)<<"CardView::setCurrentItem: Item ("<<item<<") not owned! Backing out.."<<endl;
00682 return;
00683 }
00684 else if ( item == currentItem() )
00685 {
00686 return;
00687 }
00688
00689 if ( d->mSelectionMode == Single )
00690 {
00691 setSelected( item, true );
00692 }
00693 else
00694 {
00695 CardViewItem *it = d->mCurrentItem;
00696 d->mCurrentItem = item;
00697 if ( it )
00698 it->repaintCard();
00699 item->repaintCard();
00700 }
00701 if ( ! d->mOnSeparator )
00702 ensureItemVisible( item );
00703 emit currentChanged( item );
00704 }
00705
00706 CardViewItem *CardView::itemAt(const QPoint &viewPos)
00707 {
00708 CardViewItem *item = 0;
00709 QPtrListIterator<CardViewItem> iter(d->mItemList);
00710 bool found = false;
00711 for (iter.toFirst(); iter.current() && !found; ++iter)
00712 {
00713 item = *iter;
00714
00715 if (QRect(item->d->x, item->d->y, d->mItemWidth, item->height()).contains(viewPos))
00716 found = true;
00717 }
00718
00719 if (found)
00720 return item;
00721
00722 return 0;
00723 }
00724
00725 QRect CardView::itemRect(const CardViewItem *item)
00726 {
00727
00728 return QRect(item->d->x, item->d->y, d->mItemWidth, item->height());
00729 }
00730
00731 void CardView::ensureItemVisible(const CardViewItem *item)
00732 {
00733 ensureVisible(item->d->x , item->d->y, d->mItemSpacing, 0);
00734 ensureVisible(item->d->x + d->mItemWidth, item->d->y, d->mItemSpacing, 0);
00735 }
00736
00737 void CardView::repaintItem(const CardViewItem *item)
00738 {
00739
00740 repaintContents( QRect(item->d->x, item->d->y, d->mItemWidth, item->height()) );
00741 }
00742
00743 void CardView::setSelectionMode(CardView::SelectionMode mode)
00744 {
00745 selectAll(false);
00746
00747 d->mSelectionMode = mode;
00748 }
00749
00750 CardView::SelectionMode CardView::selectionMode() const
00751 {
00752 return d->mSelectionMode;
00753 }
00754
00755 void CardView::selectAll(bool state)
00756 {
00757 QPtrListIterator<CardViewItem> iter(d->mItemList);
00758 if (!state)
00759 {
00760 for (iter.toFirst(); iter.current(); ++iter)
00761 {
00762 if ((*iter)->isSelected())
00763 {
00764 (*iter)->setSelected(false);
00765 (*iter)->repaintCard();
00766 }
00767 }
00768
00769 emit selectionChanged(0);
00770 }
00771 else if (d->mSelectionMode != CardView::Single)
00772 {
00773 for (iter.toFirst(); iter.current(); ++iter)
00774 {
00775 (*iter)->setSelected(true);
00776 }
00777
00778 if (d->mItemList.count() > 0)
00779 {
00780
00781 emit selectionChanged();
00782
00783 viewport()->update();
00784 }
00785 }
00786 }
00787
00788 void CardView::setSelected(CardViewItem *item, bool selected)
00789 {
00790 if ((item == 0) || (item->isSelected() == selected))
00791 return;
00792
00793 if ( selected && d->mCurrentItem != item )
00794 {
00795 CardViewItem *it = d->mCurrentItem;
00796 d->mCurrentItem = item;
00797 if ( it )
00798 it->repaintCard();
00799 }
00800
00801 if (d->mSelectionMode == CardView::Single)
00802 {
00803 bool b = signalsBlocked();
00804 blockSignals(true);
00805 selectAll(false);
00806 blockSignals(b);
00807
00808 if (selected)
00809 {
00810 item->setSelected(selected);
00811 item->repaintCard();
00812 emit selectionChanged();
00813 emit selectionChanged(item);
00814 }
00815 else
00816 {
00817 emit selectionChanged();
00818 emit selectionChanged(0);
00819 }
00820 }
00821 else if (d->mSelectionMode == CardView::Multi)
00822 {
00823 item->setSelected(selected);
00824 item->repaintCard();
00825 emit selectionChanged();
00826 }
00827 else if (d->mSelectionMode == CardView::Extended)
00828 {
00829 bool b = signalsBlocked();
00830 blockSignals(true);
00831 selectAll(false);
00832 blockSignals(b);
00833
00834 item->setSelected(selected);
00835 item->repaintCard();
00836 emit selectionChanged();
00837 }
00838 }
00839
00840 bool CardView::isSelected(CardViewItem *item) const
00841 {
00842 return (item && item->isSelected());
00843 }
00844
00845 CardViewItem *CardView::selectedItem() const
00846 {
00847
00848 QPtrListIterator<CardViewItem> iter(d->mItemList);
00849 for (iter.toFirst(); iter.current(); ++iter)
00850 {
00851 if ((*iter)->isSelected())
00852 return *iter;
00853 }
00854
00855 return 0;
00856 }
00857
00858 CardViewItem *CardView::firstItem() const
00859 {
00860 return d->mItemList.first();
00861 }
00862
00863 int CardView::childCount() const
00864 {
00865 return d->mItemList.count();
00866 }
00867
00868 CardViewItem *CardView::findItem(const QString &text, const QString &label,
00869 Qt::StringComparisonMode compare)
00870 {
00871
00872
00873 if (text.isEmpty())
00874 return 0;
00875
00876 QPtrListIterator<CardViewItem> iter(d->mItemList);
00877 if (compare & Qt::BeginsWith)
00878 {
00879 QString value;
00880 for (iter.toFirst(); iter.current(); ++iter)
00881 {
00882 value = (*iter)->fieldValue(label).upper();
00883 if (value.startsWith(text.upper()))
00884 return *iter;
00885 }
00886 }
00887 else
00888 {
00889 kdDebug(5720) << "CardView::findItem: search method not implemented" << endl;
00890 }
00891
00892 return 0;
00893 }
00894
00895 uint CardView::columnWidth()
00896 {
00897 return d->mDrawSeparators ?
00898 d->mItemWidth + ( 2 * d->mItemSpacing ) + d->mSepWidth :
00899 d->mItemWidth + d->mItemSpacing;
00900 }
00901
00902 void CardView::drawContents(QPainter *p, int clipx, int clipy,
00903 int clipw, int cliph)
00904 {
00905 QScrollView::drawContents(p, clipx, clipy, clipw, cliph);
00906
00907 if (d->mLayoutDirty)
00908 calcLayout();
00909
00910
00911
00912
00913 QColorGroup cg = viewport()->palette().active();
00914
00915 QRect clipRect(clipx, clipy, clipw, cliph);
00916 QRect cardRect;
00917 QRect sepRect;
00918 CardViewItem *item;
00919 CardViewSeparator *sep;
00920
00921
00922 viewport()->erase(clipRect);
00923
00924
00925 QPtrListIterator<CardViewItem> iter(d->mItemList);
00926 for (iter.toFirst(); iter.current(); ++iter)
00927 {
00928 item = *iter;
00929 cardRect.setRect( item->d->x, item->d->y, d->mItemWidth, item->height() );
00930
00931 if (clipRect.intersects(cardRect) || clipRect.contains(cardRect))
00932 {
00933
00934
00935
00936
00937 p->save();
00938 p->translate(cardRect.x(), cardRect.y());
00939 item->paintCard(p, cg);
00940 p->restore();
00941 }
00942 }
00943
00944
00945 QPtrListIterator<CardViewSeparator> sepIter(d->mSeparatorList);
00946 for (sepIter.toFirst(); sepIter.current(); ++sepIter)
00947 {
00948 sep = *sepIter;
00949 sepRect = sep->mRect;
00950
00951 if (clipRect.intersects(sepRect) || clipRect.contains(sepRect))
00952 {
00953 p->save();
00954 p->translate(sepRect.x(), sepRect.y());
00955 sep->paintSeparator(p, cg);
00956 p->restore();
00957 }
00958 }
00959 }
00960
00961 void CardView::resizeEvent(QResizeEvent *e)
00962 {
00963 QScrollView::resizeEvent(e);
00964
00965 setLayoutDirty(true);
00966 }
00967
00968 void CardView::calcLayout()
00969 {
00970
00971
00972
00973
00974 int maxWidth = 0;
00975 int maxHeight = 0;
00976 int xPos = 0;
00977 int yPos = 0;
00978 int cardSpacing = d->mItemSpacing;
00979
00980
00981 d->mSeparatorList.clear();
00982
00983 QPtrListIterator<CardViewItem> iter(d->mItemList);
00984 CardViewItem *item = 0;
00985 CardViewSeparator *sep = 0;
00986 xPos += cardSpacing;
00987
00988 for (iter.toFirst(); iter.current(); ++iter)
00989 {
00990 item = *iter;
00991
00992 yPos += cardSpacing;
00993
00994 if (yPos + item->height() + cardSpacing >= height() - horizontalScrollBar()->height())
00995 {
00996 maxHeight = QMAX(maxHeight, yPos);
00997
00998
00999
01000 yPos = cardSpacing;
01001 xPos += cardSpacing + maxWidth;
01002 if (d->mDrawSeparators)
01003 {
01004
01005 sep = new CardViewSeparator(this);
01006 sep->mRect.moveTopLeft(QPoint(xPos, yPos+d->mItemMargin));
01007 xPos += d->mSepWidth + cardSpacing;
01008 d->mSeparatorList.append(sep);
01009 }
01010
01011 maxWidth = 0;
01012 }
01013
01014 item->d->x = xPos;
01015 item->d->y = yPos;
01016
01017 yPos += item->height();
01018 maxWidth = QMAX(maxWidth, d->mItemWidth);
01019 }
01020
01021 xPos += maxWidth;
01022 resizeContents( xPos + cardSpacing, maxHeight );
01023
01024
01025
01026 QPtrListIterator<CardViewSeparator> sepIter(d->mSeparatorList);
01027 for (sepIter.toFirst(); sepIter.current(); ++sepIter)
01028 {
01029 (*sepIter)->mRect.setHeight(maxHeight - 2*cardSpacing - 2*d->mItemMargin);
01030 }
01031
01032 d->mLayoutDirty = false;
01033 }
01034
01035 CardViewItem *CardView::itemAfter(CardViewItem *item)
01036 {
01037 d->mItemList.findRef(item);
01038 return d->mItemList.next();
01039 }
01040
01041 uint CardView::itemMargin()
01042 {
01043 return d->mItemMargin;
01044 }
01045
01046 void CardView::setItemMargin( uint margin )
01047 {
01048 if ( margin == d->mItemMargin )
01049 return;
01050
01051 d->mItemMargin = margin;
01052 setLayoutDirty( true );
01053 }
01054
01055 uint CardView::itemSpacing()
01056 {
01057 return d->mItemSpacing;
01058 }
01059
01060 void CardView::setItemSpacing( uint spacing )
01061 {
01062 if ( spacing == d->mItemSpacing )
01063 return;
01064
01065 d->mItemSpacing = spacing;
01066 setLayoutDirty( true );
01067 }
01068
01069 void CardView::contentsMousePressEvent(QMouseEvent *e)
01070 {
01071 QScrollView::contentsMousePressEvent(e);
01072
01073 QPoint pos = e->pos();
01074 d->mLastClickPos = pos;
01075
01076 CardViewItem *item = itemAt(pos);
01077
01078 if (item == 0)
01079 {
01080 d->mLastClickOnItem = false;
01081 if ( d->mOnSeparator)
01082 {
01083 d->mResizeAnchor = e->x()+contentsX();
01084 d->colspace = (2*d->mItemSpacing) ;
01085 int ccw = d->mItemWidth + d->colspace + d->mSepWidth;
01086 d->first = (contentsX()+d->mSepWidth)/ccw;
01087 d->pressed = (d->mResizeAnchor+d->mSepWidth)/ccw;
01088 d->span = d->pressed - d->first;
01089 d->firstX = d->first * ccw;
01090 if ( d->firstX ) d->firstX -= d->mSepWidth;
01091 }
01092 else
01093 {
01094 selectAll(false);
01095 }
01096 return;
01097 }
01098
01099 d->mLastClickOnItem = true;
01100
01101 CardViewItem *other = d->mCurrentItem;
01102 setCurrentItem( item );
01103
01104
01105 emit clicked(item);
01106
01107
01108 if ( e->button() & Qt::RightButton ) {
01109
01110 bool blocked = signalsBlocked();
01111 blockSignals( true );
01112 selectAll( false );
01113 blockSignals( blocked );
01114
01115
01116 item->setSelected( true );
01117
01118 emit contextMenuRequested( item, mapToGlobal( e->pos() ) );
01119 return;
01120 }
01121
01122
01123 if (d->mSelectionMode == CardView::Single)
01124 {
01125
01126 if (item->isSelected())
01127 return;
01128
01129 bool b = signalsBlocked();
01130 blockSignals(true);
01131 selectAll(false);
01132 blockSignals(b);
01133
01134 item->setSelected(true);
01135 item->repaintCard();
01136 emit selectionChanged(item);
01137 }
01138
01139 else if (d->mSelectionMode == CardView::Multi)
01140 {
01141
01142 item->setSelected(!item->isSelected());
01143 item->repaintCard();
01144 emit selectionChanged();
01145 }
01146
01147 else if (d->mSelectionMode == CardView::Extended)
01148 {
01149 if ((e->button() & Qt::LeftButton) &&
01150 (e->state() & Qt::ShiftButton))
01151 {
01152 if ( item == other ) return;
01153
01154 bool s = ! item->isSelected();
01155
01156 if ( s && ! (e->state() & ControlButton) )
01157 {
01158 bool b = signalsBlocked();
01159 blockSignals(true);
01160 selectAll(false);
01161 blockSignals(b);
01162 }
01163
01164 int from, to, a, b;
01165 a = d->mItemList.findRef( item );
01166 b = d->mItemList.findRef( other );
01167 from = a < b ? a : b;
01168 to = a > b ? a : b;
01169
01170 CardViewItem *aItem;
01171 for ( ; from <= to; from++ )
01172 {
01173 aItem = d->mItemList.at( from );
01174 aItem->setSelected( s );
01175 repaintItem( aItem );
01176 }
01177 emit selectionChanged();
01178 }
01179 else if ((e->button() & Qt::LeftButton) &&
01180 (e->state() & Qt::ControlButton))
01181 {
01182 item->setSelected(!item->isSelected());
01183 item->repaintCard();
01184 emit selectionChanged();
01185 }
01186
01187 else if (e->button() & Qt::LeftButton)
01188 {
01189 bool b = signalsBlocked();
01190 blockSignals(true);
01191 selectAll(false);
01192 blockSignals(b);
01193
01194 item->setSelected(true);
01195 item->repaintCard();
01196 emit selectionChanged();
01197 }
01198 }
01199 }
01200
01201 void CardView::contentsMouseReleaseEvent(QMouseEvent *e)
01202 {
01203 QScrollView::contentsMouseReleaseEvent(e);
01204
01205 if ( d->mResizeAnchor )
01206 {
01207
01208 unsetCursor();
01209
01210 int newiw = d->mItemWidth - ((d->mResizeAnchor - d->mRubberBandAnchor)/d->span);
01211 drawRubberBands( 0 );
01212
01213 if ( contentsX() )
01214 {
01215 int newX = QMAX( 0, ( d->pressed * ( newiw + d->colspace + d->mSepWidth ) ) - e->x() );
01216 setContentsPos( newX, contentsY() );
01217 }
01218
01219 setItemWidth( newiw );
01220
01221 d->mResizeAnchor = 0;
01222 d->mRubberBandAnchor = 0;
01223 return;
01224 }
01225
01226
01227 if ((e->state() & Qt::ShiftButton) || (e->state() & Qt::ControlButton))
01228 return;
01229
01230
01231 CardViewItem *item = itemAt(e->pos());
01232
01233 if (item && KGlobalSettings::singleClick())
01234 {
01235 emit executed(item);
01236 }
01237 }
01238
01239 void CardView::contentsMouseDoubleClickEvent(QMouseEvent *e)
01240 {
01241 QScrollView::contentsMouseDoubleClickEvent(e);
01242
01243 CardViewItem *item = itemAt(e->pos());
01244
01245 if (item)
01246 {
01247 d->mCurrentItem = item;
01248 }
01249
01250 if (item && !KGlobalSettings::singleClick())
01251 {
01252 emit executed(item);
01253 }
01254 emit doubleClicked(item);
01255 }
01256
01257 void CardView::contentsMouseMoveEvent( QMouseEvent *e )
01258 {
01259
01260 if ( d->mResizeAnchor )
01261 {
01262 int x = e->x();
01263 if ( x != d->mRubberBandAnchor )
01264 drawRubberBands( x );
01265 return;
01266 }
01267
01268 if (d->mLastClickOnItem && (e->state() & Qt::LeftButton) &&
01269 ((e->pos() - d->mLastClickPos).manhattanLength() > 4)) {
01270
01271 startDrag();
01272 return;
01273 }
01274
01275 d->mTimer->start( 500 );
01276
01277
01278
01279 if ( d->mDrawSeparators )
01280 {
01281 int colcontentw = d->mItemWidth + (2*d->mItemSpacing);
01282 int colw = colcontentw + d->mSepWidth;
01283 int m = e->x()%colw;
01284 if ( m >= colcontentw && m > 0 )
01285 {
01286 setCursor( SplitHCursor );
01287 d->mOnSeparator = true;
01288 }
01289 else
01290 {
01291 setCursor( ArrowCursor );
01292 d->mOnSeparator = false;
01293 }
01294 }
01295 }
01296
01297 void CardView::enterEvent( QEvent * )
01298 {
01299 d->mTimer->start( 500 );
01300 }
01301
01302 void CardView::leaveEvent( QEvent * )
01303 {
01304 d->mTimer->stop();
01305 if (d->mOnSeparator)
01306 {
01307 d->mOnSeparator = false;
01308 setCursor( ArrowCursor );
01309 }
01310 }
01311
01312 void CardView::focusInEvent( QFocusEvent * )
01313 {
01314 if (!d->mCurrentItem && d->mItemList.count() )
01315 {
01316 setCurrentItem( d->mItemList.first() );
01317 }
01318 else if ( d->mCurrentItem )
01319 {
01320 d->mCurrentItem->repaintCard();
01321 }
01322 }
01323
01324 void CardView::focusOutEvent( QFocusEvent * )
01325 {
01326 if (d->mCurrentItem)
01327 d->mCurrentItem->repaintCard();
01328 }
01329
01330 void CardView::keyPressEvent( QKeyEvent *e )
01331 {
01332 if ( ! ( childCount() && d->mCurrentItem ) )
01333 {
01334 e->ignore();
01335 return;
01336 }
01337
01338 uint pos = d->mItemList.findRef( d->mCurrentItem );
01339 CardViewItem *aItem = 0L;
01340 CardViewItem *old = d->mCurrentItem;
01341
01342 switch ( e->key() )
01343 {
01344 case Key_Up:
01345 if ( pos > 0 )
01346 {
01347 aItem = d->mItemList.at( pos - 1 );
01348 setCurrentItem( aItem );
01349 }
01350 break;
01351 case Key_Down:
01352 if ( pos < d->mItemList.count() - 1 )
01353 {
01354 aItem = d->mItemList.at( pos + 1 );
01355 setCurrentItem( aItem );
01356 }
01357 break;
01358 case Key_Left:
01359 {
01360
01361
01362
01363 QPoint aPoint( d->mCurrentItem->d->x, d->mCurrentItem->d->y );
01364 aPoint -= QPoint( 30,-(d->mCurrentItem->height()/2) );
01365 aItem = itemAt( aPoint );
01366
01367 while ( !aItem && aPoint.y() > 27 )
01368 {
01369 aPoint -= QPoint( 0, 16 );
01370 aItem = itemAt( aPoint );
01371 }
01372 if ( aItem )
01373 setCurrentItem( aItem );
01374 }
01375 break;
01376 case Key_Right:
01377 {
01378
01379 QPoint aPoint( d->mCurrentItem->d->x + d->mItemWidth, d->mCurrentItem->d->y );
01380 aPoint += QPoint( 30,(d->mCurrentItem->height()/2) );
01381 aItem = itemAt( aPoint );
01382 while ( !aItem && aPoint.y() > 27 )
01383 {
01384 aPoint -= QPoint( 0, 16 );
01385 aItem = itemAt( aPoint );
01386 }
01387 if ( aItem )
01388 setCurrentItem( aItem );
01389 }
01390 break;
01391 case Key_Home:
01392 aItem = d->mItemList.first();
01393 setCurrentItem( aItem );
01394 break;
01395 case Key_End:
01396 aItem = d->mItemList.last();
01397 setCurrentItem( aItem );
01398 break;
01399 case Key_Prior:
01400 {
01401
01402
01403 if ( contentsX() <= 0 )
01404 return;
01405 int cw = columnWidth();
01406 int theCol = ( QMAX( 0, ( contentsX()/cw) * cw ) ) + d->mItemSpacing;
01407 aItem = itemAt( QPoint( theCol + 1, d->mItemSpacing + 1 ) );
01408 if ( aItem )
01409 setCurrentItem( aItem );
01410 }
01411 break;
01412 case Key_Next:
01413 {
01414
01415
01416
01417
01418 int cw = columnWidth();
01419 int theCol = ( (( contentsX() + visibleWidth() )/cw) * cw ) + d->mItemSpacing + 1;
01420
01421 if ( d->mDrawSeparators && cw - (( contentsX() + visibleWidth() )%cw) <= int( d->mItemSpacing + d->mSepWidth ) )
01422 theCol += cw;
01423
01424
01425 while ( theCol > contentsWidth() )
01426 theCol -= columnWidth();
01427
01428 aItem = itemAt( QPoint( theCol, d->mItemSpacing + 1 ) );
01429
01430 if ( aItem )
01431 setCurrentItem( aItem );
01432 }
01433 break;
01434 case Key_Space:
01435 setSelected( d->mCurrentItem, !d->mCurrentItem->isSelected() );
01436 emit selectionChanged();
01437 break;
01438 case Key_Return:
01439 case Key_Enter:
01440 emit returnPressed( d->mCurrentItem );
01441 emit executed( d->mCurrentItem );
01442 break;
01443 case Key_Menu:
01444 emit contextMenuRequested( d->mCurrentItem, viewport()->mapToGlobal(
01445 itemRect(d->mCurrentItem).center() ) );
01446 break;
01447 default:
01448 if ( (e->state() & ControlButton) && e->key() == Key_A )
01449 {
01450
01451 selectAll( true );
01452 break;
01453 }
01454
01455 else if ( ! e->text().isEmpty() && e->text()[0].isPrint() )
01456 {
01457
01458 }
01459 break;
01460 }
01461
01462 if ( aItem )
01463 {
01464 if ( d->mSelectionMode == CardView::Extended )
01465 {
01466 if ( (e->state() & ShiftButton) )
01467 {
01468
01469
01470
01471
01472 bool s = ! aItem->isSelected();
01473 int from, to, a, b;
01474 a = d->mItemList.findRef( aItem );
01475 b = d->mItemList.findRef( old );
01476 from = a < b ? a : b;
01477 to = a > b ? a : b;
01478
01479 if ( to - from > 1 )
01480 {
01481 bool b = signalsBlocked();
01482 blockSignals(true);
01483 selectAll(false);
01484 blockSignals(b);
01485 }
01486
01487
01488 CardViewItem *item;
01489 for ( ; from <= to; from++ )
01490 {
01491 item = d->mItemList.at( from );
01492 item->setSelected( s );
01493 repaintItem( item );
01494 }
01495 emit selectionChanged();
01496 }
01497 else if ( (e->state() & ControlButton) )
01498 {
01499
01500 }
01501 else
01502 {
01503
01504 bool b = signalsBlocked();
01505 blockSignals(true);
01506 selectAll(false);
01507 blockSignals(b);
01508
01509 setSelected( aItem, true );
01510 emit selectionChanged();
01511 }
01512 }
01513 }
01514 }
01515
01516 void CardView::contentsWheelEvent( QWheelEvent * e )
01517 {
01518 scrollBy(2*e->delta()/-3, 0);
01519 }
01520
01521 void CardView::setLayoutDirty(bool dirty)
01522 {
01523 if (d->mLayoutDirty != dirty)
01524 {
01525 d->mLayoutDirty = dirty;
01526 repaint();
01527 }
01528 }
01529
01530 void CardView::setDrawCardBorder(bool enabled)
01531 {
01532 if (enabled != d->mDrawCardBorder)
01533 {
01534 d->mDrawCardBorder = enabled;
01535 repaint();
01536 }
01537 }
01538
01539 bool CardView::drawCardBorder() const
01540 {
01541 return d->mDrawCardBorder;
01542 }
01543
01544 void CardView::setDrawColSeparators(bool enabled)
01545 {
01546 if (enabled != d->mDrawSeparators)
01547 {
01548 d->mDrawSeparators = enabled;
01549 setLayoutDirty(true);
01550 }
01551 }
01552
01553 bool CardView::drawColSeparators() const
01554 {
01555 return d->mDrawSeparators;
01556 }
01557
01558 void CardView::setDrawFieldLabels(bool enabled)
01559 {
01560 if (enabled != d->mDrawFieldLabels)
01561 {
01562 d->mDrawFieldLabels = enabled;
01563 repaint();
01564 }
01565 }
01566
01567 bool CardView::drawFieldLabels() const
01568 {
01569 return d->mDrawFieldLabels;
01570 }
01571
01572 void CardView::setShowEmptyFields(bool show)
01573 {
01574 if (show != d->mShowEmptyFields)
01575 {
01576 d->mShowEmptyFields = show;
01577 setLayoutDirty(true);
01578 }
01579 }
01580
01581 bool CardView::showEmptyFields() const
01582 {
01583 return d->mShowEmptyFields;
01584 }
01585
01586 void CardView::startDrag()
01587 {
01588
01589
01590 }
01591 void CardView::tryShowFullText()
01592 {
01593 d->mTimer->stop();
01594
01595 QPoint cpos = viewportToContents( viewport()->mapFromGlobal( QCursor::pos() ) );
01596 CardViewItem *item = itemAt( cpos );
01597 if ( item )
01598 {
01599
01600
01601
01602 QPoint ipos = cpos - itemRect( item ).topLeft();
01603 item->showFullString( ipos, d->mTip );
01604 }
01605 }
01606
01607 void CardView::drawRubberBands( int pos )
01608 {
01609 if ( pos && ((pos-d->firstX)/d->span) - d->colspace - d->mSepWidth < MIN_ITEM_WIDTH ) return;
01610
01611 int tmpcw = (d->mRubberBandAnchor-d->firstX)/d->span;
01612 int x = d->firstX + tmpcw - d->mSepWidth - contentsX();
01613 int h = visibleHeight();
01614
01615 QPainter p( viewport() );
01616 p.setRasterOp( XorROP );
01617 p.setPen( gray );
01618 p.setBrush( gray );
01619 uint n = d->first;
01620
01621 if ( d->mRubberBandAnchor )
01622 do {
01623 p.drawRect( x, 0, 2, h );
01624 x += tmpcw;
01625 n++;
01626 } while ( x < visibleWidth() && n < d->mSeparatorList.count() );
01627
01628 if ( ! pos ) return;
01629 tmpcw = (pos - d->firstX)/d->span;
01630 n = d->first;
01631 x = d->firstX + tmpcw - d->mSepWidth - contentsX();
01632 do {
01633 p.drawRect( x, 0, 2, h );
01634 x += tmpcw;
01635 n++;
01636 } while ( x < visibleWidth() && n < d->mSeparatorList.count() );
01637 d->mRubberBandAnchor = pos;
01638 }
01639
01640
01641 int CardView::itemWidth() const
01642 {
01643 return d->mItemWidth;
01644 }
01645
01646 void CardView::setItemWidth( int w )
01647 {
01648 if ( w == d->mItemWidth )
01649 return;
01650 if ( w < MIN_ITEM_WIDTH )
01651 w = MIN_ITEM_WIDTH;
01652 d->mItemWidth = w;
01653 setLayoutDirty( true );
01654 updateContents();
01655 }
01656
01657 void CardView::setHeaderFont( const QFont &fnt )
01658 {
01659 d->mHeaderFont = fnt;
01660 delete d->mBFm;
01661 d->mBFm = new QFontMetrics( fnt );
01662 }
01663
01664 QFont CardView::headerFont() const
01665 {
01666 return d->mHeaderFont;
01667 }
01668
01669 void CardView::setFont( const QFont &fnt )
01670 {
01671 QScrollView::setFont( fnt );
01672 delete d->mFm;
01673 d->mFm = new QFontMetrics( fnt );
01674 }
01675
01676 int CardView::separatorWidth()
01677 {
01678 return d->mSepWidth;
01679 }
01680
01681 void CardView::setSeparatorWidth( int width )
01682 {
01683 d->mSepWidth = width;
01684 setLayoutDirty( true );
01685 }
01686
01687 int CardView::maxFieldLines() const
01688 {
01689 return d->mMaxFieldLines;
01690 }
01691
01692 void CardView::setMaxFieldLines( int howmany )
01693 {
01694 d->mMaxFieldLines = howmany ? howmany : INT_MAX;
01695
01696 }
01697
01698
01699 #include "cardview.moc"