kaddressbook

cardview.cpp

00001 /*
00002     This file is part of KAddressBook.
00003     Copyright (c) 2002 Mike Pilone <mpilone@slac.com>
00004 
00005     This program is free software; you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License as published by
00007     the Free Software Foundation; either version 2 of the License, or
00008     (at your option) any later version.
00009 
00010     This program is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License
00016     along with this program; if not, write to the Free Software
00017     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018 
00019     As a special exception, permission is given to link this program
00020     with any edition of Qt, and distribute the resulting executable,
00021     without including the source code for Qt in the source distribution.
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 // Warning: make sure you use findRef() instead of find() to find an
00065 //          item! Only the pointer value is unique in the list.
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;           // internal margin in items
00156     uint mItemSpacing;          // spacing between items, column seperators and border
00157     int mItemWidth;             // width of all items
00158     uint mMaxFieldLines;        // Max lines to dispaly pr field
00159     CardViewItem *mCurrentItem;
00160     QPoint mLastClickPos;
00161     QTimer *mTimer;             // times out if mouse rests for more than 500 msecs
00162     CardViewTip *mTip;          // passed to the item under a resting cursor to display full text
00163     bool mOnSeparator;          // set/reset on mouse movement
00164     // for resizing by dragging the separators
00165     int mResizeAnchor;          // uint, ulong? the mouse down separator left
00166     int mRubberBandAnchor;      // for erasing rubber bands
00167     // data used for resizing.
00168     // as they are beeded by each mouse move while resizing, we store them here,
00169     // saving 8 calculations in each mouse move.
00170     int mColspace;               // amount of space between items pr column
00171     uint mFirst;                 // the first col to anchor at for painting rubber bands
00172     int mFirstX;                 // X position of first in pixel
00173     int mPressed;                // the colummn that was pressed on at resizing start
00174     int mSpan;                   // pressed - first
00175     // key completion
00176     QString mCompText;          // current completion string
00177     QDateTime mCompUpdated;     // ...was updated at this time
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;                      // horizontal position, set by the view
00189     int y;                      // vertical position, set by the view
00190     int maxLabelWidth;          // the width of the widest label, according to the view font.
00191     int hcache;                 // height cache
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   // Remove ourself from the view
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   // Add ourself to the view
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; // extra space for the colon
00247 
00248   if ( !drawLabels ) {
00249     valueXPos = labelXPos;
00250     valueWidth = w - 4;
00251   }
00252 
00253   // Draw a simple box
00254   if ( isSelected() )
00255     pen = QPen( cg.highlight(), 1 );
00256   else
00257     pen = QPen( cg.button(), 1 );
00258   p->setPen( pen );
00259 
00260   // Draw the border - this is only draw if the user asks for it.
00261   if ( drawBorder )
00262     p->drawRect( mg, mg, w, h );
00263 
00264   // set the proper pen color for the caption box
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   // Now paint the caption
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   // Go through the fields and draw them
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   // if we are the current item and the view has focus, draw focus rect
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   // use cache
00335   if ( allowCache && d->hcache )
00336     return d->hcache;
00337 
00338   // Base height:
00339   //  2 for line width
00340   //  2 for top caption pad
00341   //  2 for bottom caption pad
00342   //   2 pad for the end
00343   // + 2 times the advised margin
00344   int baseHeight = 8 + ( 2 * mView->itemMargin() );
00345 
00346   //  size of font for each field
00347   //  2 pad for each field
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   // height of caption font (bold)
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   // Now trim the last char, since it put the width over the top
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     // this is the caption
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     // find the field
00496     Field *f = fieldAt( itempos );
00497     if ( !f || ( !mView->showEmptyFields() && f->second.isEmpty() ) )
00498       return;
00499 
00500     // y position:
00501     // header font height + 4px hader margin + 2px leading + item margin
00502     // + actual field index * (fontheight + 2px leading)
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     // find a proper position
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     // show
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   // skip below caption
00554   if ( iy <= ypos )
00555     return 0;
00556   // try find a field
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   // no reason for a vertical scrollbar
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       // emit, since there must have been at least one selected
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   // find the first selected item
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   // If the text is empty, we will return null, since empty text will
00811   // match anything!
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   // allow setting costum colors in the viewport pale
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   // make sure the viewport is a pure background
00855   viewport()->erase( clipRect );
00856 
00857   // Now tell the cards to draw, if they are in the clip region
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       // Tell the card to paint
00865       p->save();
00866       p->translate( cardRect.x(), cardRect.y() );
00867       item->paintCard( p, cg );
00868       p->restore();
00869     }
00870   }
00871 
00872   // Followed by the separators if they are in the clip region
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   // Start in the upper left corner and layout all the
00897   // cars using their height and width
00898   int maxWidth = 0;
00899   int maxHeight = 0;
00900   int xPos = 0;
00901   int yPos = 0;
00902   int cardSpacing = d->mItemSpacing;
00903 
00904   // delete the old separators
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       // Drawing in this column would be greater than the height
00921       // of the scroll view, so move to next column
00922       yPos = cardSpacing;
00923       xPos += cardSpacing + maxWidth;
00924       if ( d->mDrawSeparators ) {
00925         // Create a separator since the user asked
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   // Update the height of all the separators now that we know the
00946   // max height of a column
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   // Always emit the selection
01022   emit clicked( item );
01023 
01024   // The RMB click
01025   if ( e->button() & Qt::RightButton ) {
01026     // clear previous selection
01027     bool blocked = signalsBlocked();
01028     blockSignals( true );
01029     selectAll( false );
01030     blockSignals( blocked );
01031 
01032     // select current item
01033     item->setSelected( true );
01034 
01035     emit contextMenuRequested( item, mapToGlobal( pos ) );
01036     return;
01037   }
01038 
01039   // Check the selection type and update accordingly
01040   if ( d->mSelectionMode == CardView::Single ) {
01041     // make sure it isn't already selected
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     // toggle the selection
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     // hide rubber bands
01110     int newiw = d->mItemWidth - ((d->mResizeAnchor - d->mRubberBandAnchor) / d->mSpan);
01111     drawRubberBands( 0 );
01112     // we should move to reflect the new position if we are scrolled.
01113     if ( contentsX() ) {
01114       int newX = QMAX( 0, ( d->mPressed * ( newiw + d->mColspace + d->mSepWidth ) ) - e->x() );
01115       setContentsPos( newX, contentsY() );
01116     }
01117     // set new item width
01118     setItemWidth( newiw );
01119     // reset anchors
01120     d->mResizeAnchor = 0;
01121     d->mRubberBandAnchor = 0;
01122     return;
01123   }
01124 
01125   // If there are accel keys, we will not emit signals
01126   if ( (e->state() & Qt::ShiftButton) || (e->state() & Qt::ControlButton) )
01127     return;
01128 
01129   // Get the item at this position
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   // resizing
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   // see if we are over a separator
01171   // only if we actually have them painted?
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       // look for an item in the previous/next column, starting from
01241       // the vertical middle of the current item.
01242       // FIXME use nice calculatd measures!!!
01243       QPoint aPoint( d->mCurrentItem->d->x, d->mCurrentItem->d->y );
01244       aPoint -= QPoint( 30, -(d->mCurrentItem->height() / 2) );
01245       aItem = itemAt( aPoint );
01246       // maybe we hit some space below an item
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       // FIXME use nice calculated measures!!!
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: // PageUp
01280     {
01281       // QListView: "Make the item above the top visible and current"
01282       // TODO if contentsY(), pick the top item of the leftmost visible column
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:  // PageDown
01294     {
01295       // QListView: "Make the item below the bottom visible and current"
01296       // find the first not fully visible column.
01297       // TODO: consider if a partly visible (or even hidden) item at the
01298       //       bottom of the rightmost column exists
01299       int cw = columnWidth();
01300       int theCol = ( (( contentsX() + visibleWidth() ) / cw) * cw ) + d->mItemSpacing + 1;
01301       // if separators are on, we may need to we may be one column further right if only the spacing/sep is hidden
01302       if ( d->mDrawSeparators && cw - (( contentsX() + visibleWidth() ) % cw) <= int( d->mItemSpacing + d->mSepWidth ) )
01303         theCol += cw;
01304 
01305       // make sure this is not too far right
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         // select all
01332         selectAll( true );
01333         break;
01334       } else if ( !e->text().isEmpty() && e->text()[ 0 ].isPrint() ) {
01335         // if we have a string, do autosearch
01336       }
01337       break;
01338   }
01339 
01340   // handle selection
01341   if ( aItem ) {
01342     if ( d->mSelectionMode == CardView::Extended ) {
01343       if ( e->state() & ShiftButton ) {
01344         // shift button: toggle range
01345         // if control button is pressed, leave all items
01346         // and toggle selection current->old current
01347         // otherwise, ??????
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         // control button: do nothing
01372       } else {
01373         // no button: move selection to this item
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   // The default implementation is a no-op. It must be
01454   // reimplemented in a subclass to be useful
01455 }
01456 
01457 void CardView::tryShowFullText()
01458 {
01459   d->mTimer->stop();
01460   // if we have an item
01461   QPoint cpos = viewportToContents( viewport()->mapFromGlobal( QCursor::pos() ) );
01462   CardViewItem *item = itemAt( cpos );
01463   if ( item ) {
01464     // query it for a value to display
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   // erase
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   // paint new
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   // FIXME update, forcing the items to recalc height!!
01561 }
01562 
01563 #include "cardview.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys