kaddressbook Library API Documentation

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