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 bool blocked = signalsBlocked();
01028 blockSignals( true );
01029 selectAll( false );
01030 blockSignals( blocked );
01031
01032
01033 item->setSelected( true );
01034
01035 emit contextMenuRequested( item, mapToGlobal( pos ) );
01036 return;
01037 }
01038
01039
01040 if ( d->mSelectionMode == CardView::Single ) {
01041
01042 if ( item->isSelected() )
01043 return;
01044
01045 bool b = signalsBlocked();
01046 blockSignals( true );
01047 selectAll( false );
01048 blockSignals( b );
01049
01050 item->setSelected( true );
01051 item->repaintCard();
01052 emit selectionChanged( item );
01053 } else if ( d->mSelectionMode == CardView::Multi ) {
01054
01055 item->setSelected( !item->isSelected() );
01056 item->repaintCard();
01057 emit selectionChanged();
01058 } else if ( d->mSelectionMode == CardView::Extended ) {
01059 if ( (e->button() & Qt::LeftButton) && (e->state() & Qt::ShiftButton) ) {
01060 if ( item == other )
01061 return;
01062
01063 bool s = !item->isSelected();
01064
01065 if ( s && !(e->state() & ControlButton) ) {
01066 bool b = signalsBlocked();
01067 blockSignals( true );
01068 selectAll( false );
01069 blockSignals( b );
01070 }
01071
01072 int from, to, a, b;
01073 a = d->mItemList.findRef( item );
01074 b = d->mItemList.findRef( other );
01075 from = a < b ? a : b;
01076 to = a > b ? a : b;
01077
01078 CardViewItem *aItem;
01079 for ( ; from <= to; from++ ) {
01080 aItem = d->mItemList.at( from );
01081 aItem->setSelected( s );
01082 repaintItem( aItem );
01083 }
01084
01085 emit selectionChanged();
01086 } else if ( (e->button() & Qt::LeftButton) && (e->state() & Qt::ControlButton) ) {
01087 item->setSelected( !item->isSelected() );
01088 item->repaintCard();
01089 emit selectionChanged();
01090 } else if ( e->button() & Qt::LeftButton ) {
01091 bool b = signalsBlocked();
01092 blockSignals( true );
01093 selectAll( false );
01094 blockSignals( b );
01095
01096 item->setSelected( true );
01097 item->repaintCard();
01098 emit selectionChanged();
01099 }
01100 }
01101 }
01102
01103 void CardView::contentsMouseReleaseEvent( QMouseEvent *e )
01104 {
01105 QScrollView::contentsMouseReleaseEvent( e );
01106
01107 if ( d->mResizeAnchor && d->mSpan ) {
01108 unsetCursor();
01109
01110 int newiw = d->mItemWidth - ((d->mResizeAnchor - d->mRubberBandAnchor) / d->mSpan);
01111 drawRubberBands( 0 );
01112
01113 if ( contentsX() ) {
01114 int newX = QMAX( 0, ( d->mPressed * ( newiw + d->mColspace + d->mSepWidth ) ) - e->x() );
01115 setContentsPos( newX, contentsY() );
01116 }
01117
01118 setItemWidth( newiw );
01119
01120 d->mResizeAnchor = 0;
01121 d->mRubberBandAnchor = 0;
01122 return;
01123 }
01124
01125
01126 if ( (e->state() & Qt::ShiftButton) || (e->state() & Qt::ControlButton) )
01127 return;
01128
01129
01130 CardViewItem *item = itemAt( e->pos() );
01131
01132 if ( item && KGlobalSettings::singleClick() )
01133 emit executed( item );
01134 }
01135
01136 void CardView::contentsMouseDoubleClickEvent( QMouseEvent *e )
01137 {
01138 QScrollView::contentsMouseDoubleClickEvent( e );
01139
01140 CardViewItem *item = itemAt( e->pos() );
01141
01142 if ( item )
01143 d->mCurrentItem = item;
01144
01145 if ( item && !KGlobalSettings::singleClick() )
01146 emit executed(item);
01147
01148 emit doubleClicked( item );
01149 }
01150
01151 void CardView::contentsMouseMoveEvent( QMouseEvent *e )
01152 {
01153
01154 if ( d->mResizeAnchor ) {
01155 int x = e->x();
01156 if ( x != d->mRubberBandAnchor )
01157 drawRubberBands( x );
01158 return;
01159 }
01160
01161 if ( d->mLastClickOnItem && (e->state() & Qt::LeftButton) &&
01162 ((e->pos() - d->mLastClickPos).manhattanLength() > 4)) {
01163
01164 startDrag();
01165 return;
01166 }
01167
01168 d->mTimer->start( 500 );
01169
01170
01171
01172 if ( d->mDrawSeparators ) {
01173 int colcontentw = d->mItemWidth + (2 * d->mItemSpacing);
01174 int colw = colcontentw + d->mSepWidth;
01175 int m = e->x() % colw;
01176 if ( m >= colcontentw && m > 0 ) {
01177 setCursor( SplitHCursor );
01178 d->mOnSeparator = true;
01179 } else {
01180 setCursor( ArrowCursor );
01181 d->mOnSeparator = false;
01182 }
01183 }
01184 }
01185
01186 void CardView::enterEvent( QEvent* )
01187 {
01188 d->mTimer->start( 500 );
01189 }
01190
01191 void CardView::leaveEvent( QEvent* )
01192 {
01193 d->mTimer->stop();
01194 if ( d->mOnSeparator ) {
01195 d->mOnSeparator = false;
01196 setCursor( ArrowCursor );
01197 }
01198 }
01199
01200 void CardView::focusInEvent( QFocusEvent* )
01201 {
01202 if ( !d->mCurrentItem && d->mItemList.count() )
01203 setCurrentItem( d->mItemList.first() );
01204 else if ( d->mCurrentItem )
01205 d->mCurrentItem->repaintCard();
01206 }
01207
01208 void CardView::focusOutEvent( QFocusEvent* )
01209 {
01210 if ( d->mCurrentItem )
01211 d->mCurrentItem->repaintCard();
01212 }
01213
01214 void CardView::keyPressEvent( QKeyEvent *e )
01215 {
01216 if ( !(childCount() && d->mCurrentItem) ) {
01217 e->ignore();
01218 return;
01219 }
01220
01221 uint pos = d->mItemList.findRef( d->mCurrentItem );
01222 CardViewItem *aItem = 0;
01223 CardViewItem *old = d->mCurrentItem;
01224
01225 switch ( e->key() ) {
01226 case Key_Up:
01227 if ( pos > 0 ) {
01228 aItem = d->mItemList.at( pos - 1 );
01229 setCurrentItem( aItem );
01230 }
01231 break;
01232 case Key_Down:
01233 if ( pos < d->mItemList.count() - 1 ) {
01234 aItem = d->mItemList.at( pos + 1 );
01235 setCurrentItem( aItem );
01236 }
01237 break;
01238 case Key_Left:
01239 {
01240
01241
01242
01243 QPoint aPoint( d->mCurrentItem->d->x, d->mCurrentItem->d->y );
01244 aPoint -= QPoint( 30, -(d->mCurrentItem->height() / 2) );
01245 aItem = itemAt( aPoint );
01246
01247 while ( !aItem && aPoint.y() > 27 ) {
01248 aPoint -= QPoint( 0, 16 );
01249 aItem = itemAt( aPoint );
01250 }
01251 if ( aItem )
01252 setCurrentItem( aItem );
01253
01254 break;
01255 }
01256 case Key_Right:
01257 {
01258
01259 QPoint aPoint( d->mCurrentItem->d->x + d->mItemWidth, d->mCurrentItem->d->y );
01260 aPoint += QPoint( 30, (d->mCurrentItem->height() / 2) );
01261 aItem = itemAt( aPoint );
01262 while ( !aItem && aPoint.y() > 27 ) {
01263 aPoint -= QPoint( 0, 16 );
01264 aItem = itemAt( aPoint );
01265 }
01266 if ( aItem )
01267 setCurrentItem( aItem );
01268
01269 break;
01270 }
01271 case Key_Home:
01272 aItem = d->mItemList.first();
01273 setCurrentItem( aItem );
01274 break;
01275 case Key_End:
01276 aItem = d->mItemList.last();
01277 setCurrentItem( aItem );
01278 break;
01279 case Key_Prior:
01280 {
01281
01282
01283 if ( contentsX() <= 0 )
01284 return;
01285 int cw = columnWidth();
01286 int theCol = ( QMAX( 0, ( contentsX() / cw) * cw ) ) + d->mItemSpacing;
01287 aItem = itemAt( QPoint( theCol + 1, d->mItemSpacing + 1 ) );
01288 if ( aItem )
01289 setCurrentItem( aItem );
01290
01291 break;
01292 }
01293 case Key_Next:
01294 {
01295
01296
01297
01298
01299 int cw = columnWidth();
01300 int theCol = ( (( contentsX() + visibleWidth() ) / cw) * cw ) + d->mItemSpacing + 1;
01301
01302 if ( d->mDrawSeparators && cw - (( contentsX() + visibleWidth() ) % cw) <= int( d->mItemSpacing + d->mSepWidth ) )
01303 theCol += cw;
01304
01305
01306 while ( theCol > contentsWidth() )
01307 theCol -= columnWidth();
01308
01309 aItem = itemAt( QPoint( theCol, d->mItemSpacing + 1 ) );
01310
01311 if ( aItem )
01312 setCurrentItem( aItem );
01313
01314 break;
01315 }
01316 case Key_Space:
01317 setSelected( d->mCurrentItem, !d->mCurrentItem->isSelected() );
01318 emit selectionChanged();
01319 break;
01320 case Key_Return:
01321 case Key_Enter:
01322 emit returnPressed( d->mCurrentItem );
01323 emit executed( d->mCurrentItem );
01324 break;
01325 case Key_Menu:
01326 emit contextMenuRequested( d->mCurrentItem, viewport()->mapToGlobal(
01327 itemRect(d->mCurrentItem).center() ) );
01328 break;
01329 default:
01330 if ( (e->state() & ControlButton) && e->key() == Key_A ) {
01331
01332 selectAll( true );
01333 break;
01334 } else if ( !e->text().isEmpty() && e->text()[ 0 ].isPrint() ) {
01335
01336 }
01337 break;
01338 }
01339
01340
01341 if ( aItem ) {
01342 if ( d->mSelectionMode == CardView::Extended ) {
01343 if ( e->state() & ShiftButton ) {
01344
01345
01346
01347
01348 bool s = ! aItem->isSelected();
01349 int from, to, a, b;
01350 a = d->mItemList.findRef( aItem );
01351 b = d->mItemList.findRef( old );
01352 from = a < b ? a : b;
01353 to = a > b ? a : b;
01354
01355 if ( to - from > 1 ) {
01356 bool b = signalsBlocked();
01357 blockSignals( true );
01358 selectAll( false );
01359 blockSignals( b );
01360 }
01361
01362 CardViewItem *item;
01363 for ( ; from <= to; from++ ) {
01364 item = d->mItemList.at( from );
01365 item->setSelected( s );
01366 repaintItem( item );
01367 }
01368
01369 emit selectionChanged();
01370 } else if ( e->state() & ControlButton ) {
01371
01372 } else {
01373
01374 bool b = signalsBlocked();
01375 blockSignals( true );
01376 selectAll( false );
01377 blockSignals( b );
01378
01379 setSelected( aItem, true );
01380 emit selectionChanged();
01381 }
01382 }
01383 }
01384 }
01385
01386 void CardView::contentsWheelEvent( QWheelEvent *e )
01387 {
01388 scrollBy( 2 * e->delta() / -3, 0 );
01389 }
01390
01391 void CardView::setLayoutDirty( bool dirty )
01392 {
01393 if ( d->mLayoutDirty != dirty ) {
01394 d->mLayoutDirty = dirty;
01395 repaint();
01396 }
01397 }
01398
01399 void CardView::setDrawCardBorder( bool enabled )
01400 {
01401 if ( enabled != d->mDrawCardBorder ) {
01402 d->mDrawCardBorder = enabled;
01403 repaint();
01404 }
01405 }
01406
01407 bool CardView::drawCardBorder() const
01408 {
01409 return d->mDrawCardBorder;
01410 }
01411
01412 void CardView::setDrawColSeparators( bool enabled )
01413 {
01414 if ( enabled != d->mDrawSeparators ) {
01415 d->mDrawSeparators = enabled;
01416 setLayoutDirty( true );
01417 }
01418 }
01419
01420 bool CardView::drawColSeparators() const
01421 {
01422 return d->mDrawSeparators;
01423 }
01424
01425 void CardView::setDrawFieldLabels( bool enabled )
01426 {
01427 if ( enabled != d->mDrawFieldLabels ) {
01428 d->mDrawFieldLabels = enabled;
01429 repaint();
01430 }
01431 }
01432
01433 bool CardView::drawFieldLabels() const
01434 {
01435 return d->mDrawFieldLabels;
01436 }
01437
01438 void CardView::setShowEmptyFields( bool show )
01439 {
01440 if ( show != d->mShowEmptyFields ) {
01441 d->mShowEmptyFields = show;
01442 setLayoutDirty( true );
01443 }
01444 }
01445
01446 bool CardView::showEmptyFields() const
01447 {
01448 return d->mShowEmptyFields;
01449 }
01450
01451 void CardView::startDrag()
01452 {
01453
01454
01455 }
01456
01457 void CardView::tryShowFullText()
01458 {
01459 d->mTimer->stop();
01460
01461 QPoint cpos = viewportToContents( viewport()->mapFromGlobal( QCursor::pos() ) );
01462 CardViewItem *item = itemAt( cpos );
01463 if ( item ) {
01464
01465 QPoint ipos = cpos - itemRect( item ).topLeft();
01466 item->showFullString( ipos, d->mTip );
01467 }
01468 }
01469
01470 void CardView::drawRubberBands( int pos )
01471 {
01472 if ( pos && d &&
01473 (!d->mSpan || ((pos - d->mFirstX) / d->mSpan) - d->mColspace - d->mSepWidth < MIN_ITEM_WIDTH) )
01474 return;
01475
01476 int tmpcw = (d->mRubberBandAnchor - d->mFirstX) / d->mSpan;
01477 int x = d->mFirstX + tmpcw - d->mSepWidth - contentsX();
01478 int h = visibleHeight();
01479
01480 QPainter p( viewport() );
01481 p.setRasterOp( XorROP );
01482 p.setPen( gray );
01483 p.setBrush( gray );
01484 uint n = d->mFirst;
01485
01486 if ( d->mRubberBandAnchor )
01487 do {
01488 p.drawRect( x, 0, 2, h );
01489 x += tmpcw;
01490 n++;
01491 } while ( x < visibleWidth() && n < d->mSeparatorList.count() );
01492
01493 if ( ! pos )
01494 return;
01495 tmpcw = (pos - d->mFirstX) / d->mSpan;
01496 n = d->mFirst;
01497 x = d->mFirstX + tmpcw - d->mSepWidth - contentsX();
01498 do {
01499 p.drawRect( x, 0, 2, h );
01500 x += tmpcw;
01501 n++;
01502 } while ( x < visibleWidth() && n < d->mSeparatorList.count() );
01503 d->mRubberBandAnchor = pos;
01504 }
01505
01506 int CardView::itemWidth() const
01507 {
01508 return d->mItemWidth;
01509 }
01510
01511 void CardView::setItemWidth( int w )
01512 {
01513 if ( w == d->mItemWidth )
01514 return;
01515 if ( w < MIN_ITEM_WIDTH )
01516 w = MIN_ITEM_WIDTH;
01517 d->mItemWidth = w;
01518 setLayoutDirty( true );
01519 updateContents();
01520 }
01521
01522 void CardView::setHeaderFont( const QFont &fnt )
01523 {
01524 d->mHeaderFont = fnt;
01525 delete d->mBFm;
01526 d->mBFm = new QFontMetrics( fnt );
01527 }
01528
01529 QFont CardView::headerFont() const
01530 {
01531 return d->mHeaderFont;
01532 }
01533
01534 void CardView::setFont( const QFont &fnt )
01535 {
01536 QScrollView::setFont( fnt );
01537 delete d->mFm;
01538 d->mFm = new QFontMetrics( fnt );
01539 }
01540
01541 int CardView::separatorWidth() const
01542 {
01543 return d->mSepWidth;
01544 }
01545
01546 void CardView::setSeparatorWidth( int width )
01547 {
01548 d->mSepWidth = width;
01549 setLayoutDirty( true );
01550 }
01551
01552 int CardView::maxFieldLines() const
01553 {
01554 return d->mMaxFieldLines;
01555 }
01556
01557 void CardView::setMaxFieldLines( int howmany )
01558 {
01559 d->mMaxFieldLines = howmany ? howmany : INT_MAX;
01560
01561 }
01562
01563 #include "cardview.moc"