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