00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "cardview.h"
00025
00026 #include <qpainter.h>
00027 #include <qpixmap.h>
00028
00029 #include <kdebug.h>
00030 #include <kglobalsettings.h>
00031
00033
00034
00035
00036
00037
00038
00039
00040 class CardViewItemList : public QPtrList<CardViewItem>
00041 {
00042 protected:
00043 virtual int compareItems(QPtrCollection::Item item1,
00044 QPtrCollection::Item item2)
00045 {
00046 CardViewItem *cItem1 = (CardViewItem*)item1;
00047 CardViewItem *cItem2 = (CardViewItem*)item2;
00048
00049 if ( cItem1 == cItem2 )
00050 return 0;
00051
00052 if ((cItem1 == 0) || (cItem2 == 0))
00053 return cItem1 ? -1 : 1;
00054
00055 if (cItem1->caption() < cItem2->caption())
00056 return -1;
00057
00058 else if (cItem1->caption() > cItem2->caption())
00059 return 1;
00060
00061 return 0;
00062 }
00063
00064 private:
00065 };
00066
00068
00069 class CardViewSeparator
00070 {
00071 friend class CardView;
00072
00073 public:
00074 CardViewSeparator(CardView *view)
00075 : mView(view)
00076 {
00077 mRect = QRect(0, 0, 2, 0);
00078 }
00079
00080 ~CardViewSeparator() {}
00081
00082 void paintSeparator(QPainter *p, QColorGroup &cg)
00083 {
00084 p->fillRect(0, 0, mRect.width(), mRect.height(),
00085 cg.brush(QColorGroup::Button));
00086 }
00087
00088 void repaintSeparator()
00089 {
00090 mView->repaintContents(mRect);
00091 }
00092
00093 private:
00094 CardView *mView;
00095 QRect mRect;
00096 };
00097
00099
00100
00101 class CardViewPrivate
00102 {
00103 public:
00104 CardViewPrivate() {}
00105
00106 CardViewItemList mItemList;
00107 QPtrList<CardViewSeparator> mSeparatorList;
00108 QFontMetrics *mFm;
00109 QFontMetrics *mBFm;
00110 CardView::SelectionMode mSelectionMode;
00111 bool mDrawCardBorder;
00112 bool mDrawSeparators;
00113 bool mDrawFieldLabels;
00114 bool mLayoutDirty;
00115 bool mLastClickOnItem;
00116 QPoint mLastClickPos;
00117 };
00118
00119 class CardViewItemPrivate
00120 {
00121 public:
00122 CardViewItemPrivate() {}
00123
00124 QString mCaption;
00125 QPtrList< CardViewItem::Field > mFieldList;
00126 bool mSelected;
00127 QRect mRect;
00128 int mWidth;
00129 };
00130
00132
00133
00134 CardViewItem::CardViewItem(CardView *parent, QString caption)
00135 : d(new CardViewItemPrivate()), mView(parent)
00136 {
00137 d->mCaption = caption;
00138
00139 initialize();
00140 }
00141
00142 CardViewItem::~CardViewItem()
00143 {
00144
00145 if (mView != 0)
00146 mView->takeItem(this);
00147
00148 delete d;
00149 }
00150
00151 void CardViewItem::initialize()
00152 {
00153 d->mSelected = false;
00154 d->mRect = QRect(0, 0, 0, 0);
00155 d->mWidth = 200;
00156 d->mFieldList.setAutoDelete(true);
00157
00158 calcRect();
00159
00160
00161 if (mView != 0)
00162 mView->insertItem(this);
00163 }
00164
00165 void CardViewItem::paintCard(QPainter *p, QColorGroup &cg)
00166 {
00167
00168 if (!mView)
00169 return;
00170
00171 QPen pen;
00172 QBrush brush;
00173 QFontMetrics fm = *(mView->d->mFm);
00174 QFontMetrics bFm = *(mView->d->mBFm);
00175 bool drawLabels = mView->d->mDrawFieldLabels;
00176 bool drawBorder = mView->d->mDrawCardBorder;
00177 int labelXPos = 2;
00178 int labelWidth = d->mWidth/2 - 4;
00179 int valueXPos = d->mWidth/2;
00180 int valueWidth = d->mWidth/2 - 4;
00181
00182 if (!drawLabels)
00183 {
00184 valueXPos = labelXPos;
00185 valueWidth = d->mWidth - 4;
00186 }
00187
00188
00189 if (isSelected())
00190 pen = QPen(cg.highlight(), 1);
00191 else
00192 pen = QPen(cg.button(), 1);
00193 p->setPen(pen);
00194
00195
00196 if (drawBorder)
00197 p->drawRect(0, 0, d->mRect.width(), d->mRect.height());
00198
00199
00200
00201
00202
00203
00204 if (isSelected())
00205 brush = cg.brush(QColorGroup::Highlight);
00206 else
00207 brush = cg.brush(QColorGroup::Button);
00208 p->fillRect(0, 0, d->mRect.width(), 4 + bFm.height(), brush);
00209
00210
00211 p->save();
00212 QFont bFont = p->font();
00213 bFont.setBold(true);
00214 p->setFont(bFont);
00215 if (isSelected())
00216 p->setPen(cg.highlightedText());
00217 else
00218 p->setPen(cg.text());
00219 p->drawText(2, 1 + bFm.height(), trimString(d->mCaption, d->mWidth-4, bFm));
00220 p->restore();
00221
00222
00223 QPtrListIterator< CardViewItem::Field > iter(d->mFieldList);
00224 QString label, value;
00225 int yPos = 4 + bFm.height() + 1 + fm.height();
00226 p->setPen(cg.text());
00227 for (iter.toFirst(); iter.current(); ++iter)
00228 {
00229 label = trimString((*iter)->first, labelWidth, fm);
00230 value = trimString((*iter)->second, valueWidth, fm);
00231
00232 if (drawLabels)
00233 {
00234 p->drawText(labelXPos, yPos, label);
00235 p->drawText(labelXPos + fm.width(label), yPos, ":");
00236 }
00237 p->drawText(valueXPos, yPos, value);
00238
00239 yPos += fm.height() + 2;
00240 }
00241 }
00242
00243 const QString &CardViewItem::caption() const
00244 {
00245 return d->mCaption;
00246 }
00247
00248 void CardViewItem::calcRect()
00249 {
00250
00251
00252
00253
00254
00255
00256
00257
00258 int baseHeight = 8;
00259
00260
00261
00262 int fieldHeight = d->mFieldList.count() * (mView->d->mFm->height() + 2);
00263
00264
00265 fieldHeight += mView->d->mBFm->height();
00266
00267
00268
00269
00270
00271 d->mRect.setHeight(fieldHeight + baseHeight);
00272 d->mRect.setWidth(d->mWidth);
00273 }
00274
00275 bool CardViewItem::isSelected() const
00276 {
00277 return d->mSelected;
00278 }
00279
00280 void CardViewItem::setSelected(bool selected)
00281 {
00282 d->mSelected = selected;
00283 }
00284
00285 void CardViewItem::insertField(const QString &label, const QString &value)
00286 {
00287 CardViewItem::Field *f = new CardViewItem::Field(label, value);
00288 d->mFieldList.append(f);
00289 calcRect();
00290
00291 if (mView)
00292 mView->setLayoutDirty(true);
00293 }
00294
00295 void CardViewItem::removeField(const QString &label)
00296 {
00297 CardViewItem::Field *f;
00298
00299 QPtrListIterator< CardViewItem::Field > iter(d->mFieldList);
00300 for (iter.toFirst(); iter.current(); ++iter)
00301 {
00302 f = *iter;
00303 if (f->first == label)
00304 break;
00305 }
00306
00307 if (*iter)
00308 d->mFieldList.remove(*iter);
00309
00310 calcRect();
00311
00312 if (mView)
00313 mView->setLayoutDirty(true);
00314 }
00315
00316 void CardViewItem::clearFields()
00317 {
00318 d->mFieldList.clear();
00319 calcRect();
00320
00321 if (mView)
00322 mView->setLayoutDirty(true);
00323 }
00324
00325 QString CardViewItem::trimString(const QString &text, int width,
00326 QFontMetrics &fm)
00327 {
00328 if (fm.width(text) <= width)
00329 return text;
00330
00331 QString dots = "...";
00332 int dotWidth = fm.width(dots);
00333 QString trimmed;
00334 int charNum = 0;
00335
00336 while (fm.width(trimmed) + dotWidth < width)
00337 {
00338 trimmed += text[charNum];
00339 charNum++;
00340 }
00341
00342
00343 trimmed = trimmed.left(trimmed.length()-1);
00344 trimmed += dots;
00345
00346 return trimmed;
00347 }
00348
00349 CardViewItem *CardViewItem::nextItem()
00350 {
00351 CardViewItem *item = 0;
00352
00353 if (mView)
00354 item = mView->itemAfter(this);
00355
00356 return item;
00357 }
00358
00359 void CardViewItem::repaintCard()
00360 {
00361 if (mView)
00362 mView->viewport()->repaint();
00363 }
00364
00365 void CardViewItem::setCaption(const QString &caption)
00366 {
00367 d->mCaption = caption;
00368
00369 if (mView)
00370 mView->viewport()->repaint();
00371 }
00372
00373 QString CardViewItem::fieldValue(const QString &label)
00374 {
00375 QPtrListIterator< CardViewItem::Field > iter(d->mFieldList);
00376 for (iter.toFirst(); iter.current(); ++iter)
00377 if ((*iter)->first == label)
00378 return (*iter)->second;
00379
00380 return QString();
00381 }
00382
00384
00385
00386 CardView::CardView(QWidget *parent, const char *name)
00387 : QScrollView(parent, name), d(new CardViewPrivate())
00388 {
00389 d->mItemList.setAutoDelete(true);
00390 d->mSeparatorList.setAutoDelete(true);
00391
00392 QFont f = font();
00393 d->mFm = new QFontMetrics(f);
00394 f.setBold(true);
00395 d->mBFm = new QFontMetrics(f);
00396 d->mSelectionMode = CardView::Multi;
00397 d->mDrawCardBorder = true;
00398 d->mDrawFieldLabels = true;
00399 d->mDrawSeparators = true;
00400 d->mLayoutDirty = true;
00401 d->mLastClickOnItem = false;
00402 d->mLastClickPos = QPoint(0, 0);
00403
00404 viewport()->setFocusProxy(this);
00405 viewport()->setFocusPolicy(WheelFocus);
00406 viewport()->setBackgroundMode(NoBackground);
00407 setBackgroundMode(PaletteBackground, PaletteBase);
00408 }
00409
00410 CardView::~CardView()
00411 {
00412 delete d->mFm;
00413 delete d->mBFm;
00414 delete d;
00415 }
00416
00417 void CardView::insertItem(CardViewItem *item)
00418 {
00419 d->mItemList.inSort(item);
00420
00421 setLayoutDirty(true);
00422 }
00423
00424 void CardView::takeItem(CardViewItem *item)
00425 {
00426 d->mItemList.take(d->mItemList.findRef(item));
00427
00428 setLayoutDirty(true);
00429 }
00430
00431 void CardView::clear()
00432 {
00433 d->mItemList.clear();
00434
00435 setLayoutDirty(true);
00436 }
00437
00438 CardViewItem *CardView::itemAt(const QPoint &viewPos)
00439 {
00440 CardViewItem *item = 0;
00441 QPtrListIterator<CardViewItem> iter(d->mItemList);
00442 bool found = false;
00443 for (iter.toFirst(); iter.current() && !found; ++iter)
00444 {
00445 item = *iter;
00446 if (item->d->mRect.contains(viewPos))
00447 found = true;
00448 }
00449
00450 if (found)
00451 return item;
00452
00453 return 0;
00454 }
00455
00456 QRect CardView::itemRect(const CardViewItem *item)
00457 {
00458 return item->d->mRect;
00459 }
00460
00461 void CardView::ensureItemVisible(const CardViewItem *item)
00462 {
00463 QRect cardRect = item->d->mRect;
00464
00465 ensureVisible(cardRect.x(), cardRect.y(),
00466 cardRect.width(), cardRect.height());
00467 }
00468
00469 void CardView::setSelectionMode(CardView::SelectionMode mode)
00470 {
00471 selectAll(false);
00472
00473 d->mSelectionMode = mode;
00474 }
00475
00476 CardView::SelectionMode CardView::selectionMode() const
00477 {
00478 return d->mSelectionMode;
00479 }
00480
00481 void CardView::selectAll(bool state)
00482 {
00483 QPtrListIterator<CardViewItem> iter(d->mItemList);
00484 if (!state)
00485 {
00486 for (iter.toFirst(); iter.current(); ++iter)
00487 {
00488 if ((*iter)->isSelected())
00489 {
00490 (*iter)->setSelected(false);
00491 (*iter)->repaintCard();
00492 }
00493 }
00494
00495 emit selectionChanged();
00496 emit selectionChanged(0);
00497 }
00498 else if (d->mSelectionMode != CardView::Single)
00499 {
00500 for (iter.toFirst(); iter.current(); ++iter)
00501 {
00502 (*iter)->setSelected(true);
00503 }
00504
00505 if (d->mItemList.count() > 0)
00506 {
00507
00508 emit selectionChanged();
00509 repaint();
00510 }
00511 }
00512 }
00513
00514 void CardView::setSelected(CardViewItem *item, bool selected)
00515 {
00516 if ((item == 0) || (item->isSelected() == selected))
00517 return;
00518
00519 if (d->mSelectionMode == CardView::Single)
00520 {
00521 bool b = signalsBlocked();
00522 blockSignals(true);
00523 selectAll(false);
00524 blockSignals(b);
00525
00526 if (selected)
00527 {
00528 item->setSelected(selected);
00529 item->repaintCard();
00530 emit selectionChanged();
00531 emit selectionChanged(item);
00532 }
00533 else
00534 {
00535 emit selectionChanged();
00536 emit selectionChanged(0);
00537 }
00538 }
00539 else if (d->mSelectionMode == CardView::Multi)
00540 {
00541 item->setSelected(selected);
00542 item->repaintCard();
00543 emit selectionChanged();
00544 }
00545 else if (d->mSelectionMode == CardView::Extended)
00546 {
00547 bool b = signalsBlocked();
00548 blockSignals(true);
00549 selectAll(false);
00550 blockSignals(b);
00551
00552 item->setSelected(selected);
00553 item->repaintCard();
00554 emit selectionChanged();
00555 }
00556 }
00557
00558 bool CardView::isSelected(CardViewItem *item) const
00559 {
00560 return (item && item->isSelected());
00561 }
00562
00563 CardViewItem *CardView::selectedItem() const
00564 {
00565
00566 QPtrListIterator<CardViewItem> iter(d->mItemList);
00567 for (iter.toFirst(); iter.current(); ++iter)
00568 {
00569 if ((*iter)->isSelected())
00570 return *iter;
00571 }
00572
00573 return 0;
00574 }
00575
00576 CardViewItem *CardView::firstItem() const
00577 {
00578 return d->mItemList.first();
00579 }
00580
00581 int CardView::childCount() const
00582 {
00583 return d->mItemList.count();
00584 }
00585
00586 CardViewItem *CardView::findItem(const QString &text, const QString &label,
00587 Qt::StringComparisonMode compare)
00588 {
00589
00590
00591 if (text.isEmpty())
00592 return 0;
00593
00594 QPtrListIterator<CardViewItem> iter(d->mItemList);
00595 if (compare & Qt::BeginsWith)
00596 {
00597 QString value;
00598 for (iter.toFirst(); iter.current(); ++iter)
00599 {
00600 value = (*iter)->fieldValue(label).upper();
00601 if (value.startsWith(text.upper()))
00602 return *iter;
00603 }
00604 }
00605 else
00606 {
00607 kdDebug() << "CardView::findItem: search method not implemented" << endl;
00608 }
00609
00610 return 0;
00611 }
00612
00613 void CardView::viewportPaintEvent( QPaintEvent * )
00614 {
00615 QPixmap pm( viewport()->width(), viewport()->height() );
00616 QPainter p;
00617
00618 p.begin( &pm, viewport() );
00619
00620 if (d->mLayoutDirty)
00621 calcLayout();
00622
00623 QColorGroup cg = palette().active();
00624 pm.fill( cg.color( QColorGroup::Base ) );
00625
00626 CardViewItem *item;
00627 CardViewSeparator *sep;
00628
00629
00630 QPtrListIterator<CardViewItem> iter(d->mItemList);
00631 for (iter.toFirst(); iter.current(); ++iter)
00632 {
00633 item = *iter;
00634 QRect cardRect = item->d->mRect;
00635
00636
00637 p.save();
00638 p.translate( cardRect.x() - contentsX(), cardRect.y() - contentsY() );
00639 item->paintCard( &p, cg );
00640 p.restore();
00641 }
00642
00643
00644 QPtrListIterator<CardViewSeparator> sepIter(d->mSeparatorList);
00645 for (sepIter.toFirst(); sepIter.current(); ++sepIter)
00646 {
00647 sep = *sepIter;
00648 QRect sepRect = sep->mRect;
00649
00650 p.save();
00651 p.translate(sepRect.x() - contentsX(), sepRect.y() - contentsY() );
00652 sep->paintSeparator(&p, cg);
00653 p.restore();
00654 }
00655
00656 p.end();
00657 bitBlt( viewport(), 0, 0, &pm );
00658 }
00659
00660 void CardView::resizeEvent(QResizeEvent *e)
00661 {
00662 QScrollView::resizeEvent(e);
00663
00664 setLayoutDirty(true);
00665 }
00666
00667 void CardView::calcLayout()
00668 {
00669
00670
00671
00672
00673 int maxWidth = 0;
00674 int maxHeight = 0;
00675 int xPos = 0;
00676 int yPos = 0;
00677 int cardSpacing = 10;
00678
00679
00680 d->mSeparatorList.clear();
00681
00682 QPtrListIterator<CardViewItem> iter(d->mItemList);
00683 CardViewItem *item = 0;
00684 CardViewSeparator *sep = 0;
00685 xPos += cardSpacing;
00686
00687 for (iter.toFirst(); iter.current(); ++iter)
00688 {
00689 item = *iter;
00690
00691 yPos += cardSpacing;
00692
00693 if (yPos + item->d->mRect.height() + cardSpacing > height())
00694 {
00695 maxHeight = QMAX(maxHeight, yPos);
00696
00697
00698
00699 yPos = cardSpacing;
00700 xPos += cardSpacing + maxWidth;
00701 if (d->mDrawSeparators)
00702 {
00703
00704 sep = new CardViewSeparator(this);
00705 sep->mRect.moveTopLeft(QPoint(xPos, 2*yPos));
00706 xPos += sep->mRect.width() + cardSpacing;
00707 d->mSeparatorList.append(sep);
00708 }
00709
00710 maxWidth = 0;
00711 }
00712
00713 item->d->mRect.moveTopLeft(QPoint(xPos, yPos));
00714
00715 yPos += item->d->mRect.height();
00716 maxWidth = QMAX(maxWidth, item->d->mRect.width());
00717 }
00718
00719 xPos += maxWidth;
00720 resizeContents(xPos + 10, viewport()->height());
00721
00722
00723
00724 QPtrListIterator<CardViewSeparator> sepIter(d->mSeparatorList);
00725 for (sepIter.toFirst(); sepIter.current(); ++sepIter)
00726 {
00727 (*sepIter)->mRect.setHeight(maxHeight - 4*cardSpacing);
00728 }
00729
00730 d->mLayoutDirty = false;
00731 }
00732
00733 CardViewItem *CardView::itemAfter(CardViewItem *item)
00734 {
00735 int pos = d->mItemList.findRef(item);
00736 if ( pos == -1 )
00737 return 0L;
00738
00739 return d->mItemList.at(pos+1);
00740 }
00741
00742 void CardView::mousePressEvent(QMouseEvent *e)
00743 {
00744 QScrollView::mousePressEvent(e);
00745
00746 QPoint pos = e->pos();
00747 d->mLastClickPos = pos;
00748
00749 CardViewItem *item = itemAt(viewportToContents(pos));
00750
00751 if (item == 0)
00752 {
00753 d->mLastClickOnItem = false;
00754 selectAll(false);
00755 return;
00756 }
00757
00758 d->mLastClickOnItem = true;
00759
00760
00761 emit clicked(item);
00762
00763
00764 if (d->mSelectionMode == CardView::Single)
00765 {
00766
00767 if (item->isSelected())
00768 return;
00769
00770 bool b = signalsBlocked();
00771 blockSignals(true);
00772 selectAll(false);
00773 blockSignals(b);
00774
00775 item->setSelected(true);
00776 item->repaintCard();
00777 emit selectionChanged(item);
00778 }
00779
00780 else if (d->mSelectionMode == CardView::Multi)
00781 {
00782
00783 item->setSelected(!item->isSelected());
00784 item->repaintCard();
00785 emit selectionChanged();
00786 }
00787
00788 else if (d->mSelectionMode == CardView::Extended)
00789 {
00790 if ((e->button() & Qt::LeftButton) &&
00791 (e->state() & Qt::ControlButton))
00792 {
00793 item->setSelected(!item->isSelected());
00794 item->repaintCard();
00795 emit selectionChanged();
00796 }
00797
00798 else if ((e->button() & Qt::LeftButton) &&
00799 (e->state() & Qt::ShiftButton))
00800 {
00801 kdDebug() << "CardView::mousePressEvent: shift-click not implemented"
00802 << endl;
00803 }
00804
00805 else if (e->button() & Qt::LeftButton)
00806 {
00807 bool b = signalsBlocked();
00808 blockSignals(true);
00809 selectAll(false);
00810 blockSignals(b);
00811
00812 item->setSelected(true);
00813 item->repaintCard();
00814 emit selectionChanged();
00815 }
00816 }
00817
00818 }
00819
00820 void CardView::mouseReleaseEvent(QMouseEvent *e)
00821 {
00822 QScrollView::mouseReleaseEvent(e);
00823
00824
00825 if ((e->state() & Qt::ShiftButton) || (e->state() & Qt::ControlButton))
00826 return;
00827
00828
00829 CardViewItem *item = itemAt(viewportToContents(e->pos()));
00830
00831 if (item && KGlobalSettings::singleClick())
00832 {
00833 emit executed(item);
00834 }
00835 }
00836
00837 void CardView::mouseDoubleClickEvent(QMouseEvent *e)
00838 {
00839 QScrollView::mouseDoubleClickEvent(e);
00840
00841 CardViewItem *item = itemAt(viewportToContents(e->pos()));
00842
00843 if (item && !KGlobalSettings::singleClick())
00844 {
00845 emit executed(item);
00846 }
00847
00848 emit doubleClicked(item);
00849 }
00850
00851 void CardView::setLayoutDirty(bool dirty)
00852 {
00853 if (d->mLayoutDirty != dirty)
00854 {
00855 d->mLayoutDirty = dirty;
00856 repaint();
00857 }
00858 }
00859
00860 void CardView::setDrawCardBorder(bool enabled)
00861 {
00862 if (enabled != d->mDrawCardBorder)
00863 {
00864 d->mDrawCardBorder = enabled;
00865 repaint();
00866 }
00867 }
00868
00869 bool CardView::drawCardBorder() const
00870 {
00871 return d->mDrawCardBorder;
00872 }
00873
00874 void CardView::setDrawColSeparators(bool enabled)
00875 {
00876 if (enabled != d->mDrawSeparators)
00877 {
00878 d->mDrawSeparators = enabled;
00879 setLayoutDirty(true);
00880 }
00881 }
00882
00883 bool CardView::drawColSeparators() const
00884 {
00885 return d->mDrawSeparators;
00886 }
00887
00888 void CardView::setDrawFieldLabels(bool enabled)
00889 {
00890 if (enabled != d->mDrawFieldLabels)
00891 {
00892 d->mDrawFieldLabels = enabled;
00893 repaint();
00894 }
00895 }
00896
00897 bool CardView::drawFieldLabels() const
00898 {
00899 return d->mDrawFieldLabels;
00900 }
00901
00902 void CardView::startDrag()
00903 {
00904
00905
00906 }
00907
00908 void CardView::mouseMoveEvent(QMouseEvent *e)
00909 {
00910 if (d->mLastClickOnItem && (e->state() & Qt::LeftButton) &&
00911 ((e->pos() - d->mLastClickPos).manhattanLength() > 4))
00912 startDrag();
00913 }
00914
00915 #include "cardview.moc"