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     // select current item
01027     item->setSelected( true );
01028 
01029     emit contextMenuRequested( item, mapToGlobal( pos ) );
01030     return;
01031   }
01032 
01033   // Check the selection type and update accordingly
01034   if ( d->mSelectionMode == CardView::Single ) {
01035     // make sure it isn't already selected
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     // toggle the selection
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     // hide rubber bands
01104     int newiw = d->mItemWidth - ((d->mResizeAnchor - d->mRubberBandAnchor) / d->mSpan);
01105     drawRubberBands( 0 );
01106     // we should move to reflect the new position if we are scrolled.
01107     if ( contentsX() ) {
01108       int newX = QMAX( 0, ( d->mPressed * ( newiw + d->mColspace + d->mSepWidth ) ) - e->x() );
01109       setContentsPos( newX, contentsY() );
01110     }
01111     // set new item width
01112     setItemWidth( newiw );
01113     // reset anchors
01114     d->mResizeAnchor = 0;
01115     d->mRubberBandAnchor = 0;
01116     return;
01117   }
01118 
01119   // If there are accel keys, we will not emit signals
01120   if ( (e->state() & Qt::ShiftButton) || (e->state() & Qt::ControlButton) )
01121     return;
01122 
01123   // Get the item at this position
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   // resizing
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   // see if we are over a separator
01165   // only if we actually have them painted?
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       // look for an item in the previous/next column, starting from
01235       // the vertical middle of the current item.
01236       // FIXME use nice calculatd measures!!!
01237       QPoint aPoint( d->mCurrentItem->d->x, d->mCurrentItem->d->y );
01238       aPoint -= QPoint( 30, -(d->mCurrentItem->height() / 2) );
01239       aItem = itemAt( aPoint );
01240       // maybe we hit some space below an item
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       // FIXME use nice calculated measures!!!
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: // PageUp
01274     {
01275       // QListView: "Make the item above the top visible and current"
01276       // TODO if contentsY(), pick the top item of the leftmost visible column
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:  // PageDown
01288     {
01289       // QListView: "Make the item below the bottom visible and current"
01290       // find the first not fully visible column.
01291       // TODO: consider if a partly visible (or even hidden) item at the
01292       //       bottom of the rightmost column exists
01293       int cw = columnWidth();
01294       int theCol = ( (( contentsX() + visibleWidth() ) / cw) * cw ) + d->mItemSpacing + 1;
01295       // if separators are on, we may need to we may be one column further right if only the spacing/sep is hidden
01296       if ( d->mDrawSeparators && cw - (( contentsX() + visibleWidth() ) % cw) <= int( d->mItemSpacing + d->mSepWidth ) )
01297         theCol += cw;
01298 
01299       // make sure this is not too far right
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         // select all
01326         selectAll( true );
01327         break;
01328       } else if ( !e->text().isEmpty() && e->text()[ 0 ].isPrint() ) {
01329         // if we have a string, do autosearch
01330       }
01331       break;
01332   }
01333 
01334   // handle selection
01335   if ( aItem ) {
01336     if ( d->mSelectionMode == CardView::Extended ) {
01337       if ( e->state() & ShiftButton ) {
01338         // shift button: toggle range
01339         // if control button is pressed, leave all items
01340         // and toggle selection current->old current
01341         // otherwise, ??????
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         // control button: do nothing
01366       } else {
01367         // no button: move selection to this item
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   // The default implementation is a no-op. It must be
01448   // reimplemented in a subclass to be useful
01449 }
01450 
01451 void CardView::tryShowFullText()
01452 {
01453   d->mTimer->stop();
01454   // if we have an item
01455   QPoint cpos = viewportToContents( viewport()->mapFromGlobal( QCursor::pos() ) );
01456   CardViewItem *item = itemAt( cpos );
01457   if ( item ) {
01458     // query it for a value to display
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   // erase
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   // paint new
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   // FIXME update, forcing the items to recalc height!!
01555 }
01556 
01557 #include "cardview.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys