• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Applets

urlitemview.cpp

Go to the documentation of this file.
00001 /*  
00002     Copyright 2007 Robert Knight <robertknight@gmail.com>
00003 
00004     This library is free software; you can redistribute it and/or
00005     modify it under the terms of the GNU Library General Public
00006     License as published by the Free Software Foundation; either
00007     version 2 of the License, or (at your option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     Library General Public License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to
00016     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017     Boston, MA 02110-1301, USA.
00018 */
00019 
00020 // Own
00021 #include "ui/urlitemview.h"
00022 
00023 // Qt
00024 #include <QtCore/QHash>
00025 #include <QtCore/QPersistentModelIndex>
00026 
00027 #include <QtGui/QMouseEvent>
00028 #include <QtGui/QPainter>
00029 #include <QtGui/QPaintEvent>
00030 #include <QtGui/QScrollBar>
00031 
00032 // KDE
00033 #include <KDebug>
00034 #include <KGlobalSettings>
00035 #include <KIconLoader>
00036 #include <KColorScheme>
00037 
00038 // Local
00039 #include "core/models.h"
00040 #include "ui/itemdelegate.h"
00041 
00042 using namespace Kickoff;
00043 
00044 class UrlItemView::Private
00045 {
00046 public:
00047     Private(UrlItemView *parent)
00048         : q(parent)
00049         , contentsHeight(0)
00050         , itemStateProvider(0)
00051     {
00052     }
00053 
00054     void doLayout()
00055     {
00056         // clear existing layout information
00057         itemRects.clear();
00058         visualOrder.clear();
00059 
00060         if (!q->model()) {
00061             return;
00062         }
00063 
00064         int verticalOffset = ItemDelegate::TOP_OFFSET;
00065         int horizontalOffset = 0;
00066         int row = 0;
00067         int visualColumn = 0;
00068 
00069         QModelIndex branch = currentRootIndex;
00070 
00071         while (true) {
00072             if (itemChildOffsets[branch]+row >= q->model()->rowCount(branch) ||
00073                 branch != currentRootIndex && row > MAX_CHILD_ROWS) {
00074 
00075                 if (branch.isValid()) {
00076                     row = branch.row()+1;
00077                     branch = branch.parent();
00078                     continue;
00079                 } else {
00080                     break;
00081                 }
00082             }
00083 
00084             QModelIndex child = q->model()->index(row+itemChildOffsets[branch],0,branch);
00085 
00086             if (q->model()->hasChildren(child)) {
00087                 QSize childSize = calculateHeaderSize(child);
00088                 QRect rect(QPoint(ItemDelegate::HEADER_LEFT_MARGIN, verticalOffset), childSize);
00089                 //kDebug() << "header is" << rect;
00090                 itemRects.insert(child, rect);
00091 
00092                 verticalOffset += childSize.height();
00093                 horizontalOffset = 0; 
00094                 branch = child;
00095                 row = 0;
00096                 visualColumn = 0;
00097             } else {
00098                 QSize childSize = calculateItemSize(child);
00099                 //kDebug() <<  QRect(QPoint(horizontalOffset,verticalOffset), childSize);
00100 
00101                 itemRects.insert(child,QRect(QPoint(horizontalOffset,verticalOffset),
00102                                              childSize));
00103 
00104                 if (childSize.isValid()) {
00105                     visualOrder << child;
00106                 }
00107 
00108                 horizontalOffset += contentWidth() / MAX_COLUMNS;
00109 
00110                 visualColumn++;
00111                 row++;
00112 
00113                 bool wasLastRow = row+itemChildOffsets[branch] >= q->model()->rowCount(branch);
00114                 bool nextItemIsBranch = false;
00115                 if (!wasLastRow) { 
00116                      QModelIndex nextIndex =q->model()->index(row+itemChildOffsets[branch],0,branch);
00117                      nextItemIsBranch = q->model()->hasChildren(nextIndex);
00118                 }
00119 
00120                 if (visualColumn >= MAX_COLUMNS || wasLastRow || nextItemIsBranch) {
00121                     horizontalOffset = 0; 
00122                     visualColumn = 0;
00123                 }
00124 
00125                 verticalOffset += childSize.height();
00126             }
00127         }
00128         contentsHeight = verticalOffset;
00129 
00130         updateScrollBarRange();
00131     }
00132 
00133     void drawHeader(QPainter *painter,
00134                     const QModelIndex& index,
00135                     const QStyleOptionViewItem& option)
00136     {
00137         const bool first = isFirstHeader(index);
00138         const int rightMargin = q->style()->pixelMetric(QStyle::PM_ScrollBarExtent) + 6;
00139         const int dy = (first ? 4 : ItemDelegate::HEADER_TOP_MARGIN);
00140 
00141         painter->save();
00142         painter->setRenderHint(QPainter::Antialiasing, false);
00143 
00144         if (!first) {
00145             QLinearGradient gradient(option.rect.topLeft(), option.rect.topRight());
00146             gradient.setColorAt(0.0, Qt::transparent);
00147             gradient.setColorAt(0.1, option.palette.midlight().color());
00148             gradient.setColorAt(0.5, option.palette.mid().color());
00149             gradient.setColorAt(0.9, option.palette.midlight().color());
00150             gradient.setColorAt(1.0, Qt::transparent);
00151             painter->setPen(QPen(gradient, 1));
00152 
00153             painter->drawLine(option.rect.x() + 6, option.rect.y() + dy + 2,
00154                               option.rect.right() - rightMargin , option.rect.y() + dy + 2);
00155         }
00156 
00157         painter->setFont(KGlobalSettings::smallestReadableFont());
00158         painter->setPen(QPen(KColorScheme(QPalette::Active).foreground(KColorScheme::InactiveText), 0));
00159         QString text = index.data(Qt::DisplayRole).value<QString>();
00160         painter->drawText(option.rect.adjusted(0, dy, -rightMargin, 0),
00161                           Qt::AlignVCenter|Qt::AlignRight, text);
00162         painter->restore();
00163     }
00164 
00165     void updateScrollBarRange()
00166     {
00167         int pageSize = q->height();
00168         q->verticalScrollBar()->setRange(0,contentsHeight-pageSize);
00169         q->verticalScrollBar()->setPageStep(pageSize);
00170         q->verticalScrollBar()->setSingleStep(q->sizeHintForRow(0));
00171     }
00172 
00173     int contentWidth() const
00174     {
00175         return q->width() - q->style()->pixelMetric(QStyle::PM_ScrollBarExtent) + 2;
00176     }
00177 
00178     QSize calculateItemSize(const QModelIndex& index) const 
00179     {
00180         if (itemStateProvider && !itemStateProvider->isVisible(index)) {
00181             return QSize();
00182         } else {
00183             return  QSize(contentWidth() / MAX_COLUMNS, q->sizeHintForIndex(index).height());
00184         }
00185     }
00186 
00187     bool isFirstHeader(const QModelIndex &index) const
00188     {
00189         if (index.row() == 0) {
00190             return q->model()->hasChildren(index);
00191         }
00192 
00193         QModelIndex prevHeader = index.sibling(index.row() - 1, index.column());
00194         while (prevHeader.isValid()) {
00195             //kDebug() << "checking" << prevHeader.data(Qt::DisplayRole).value<QString>();
00196             if (q->model()->hasChildren(prevHeader)) {
00197                 //kDebug() << "it has children";
00198                 return false;
00199             }
00200 
00201             prevHeader = prevHeader.sibling(prevHeader.row() - 1, prevHeader.column());
00202         }
00203 
00204         return true;
00205     }
00206 
00207     QSize calculateHeaderSize(const QModelIndex& index) const
00208     {
00209         const QFontMetrics fm(KGlobalSettings::smallestReadableFont());
00210         int minHeight = ItemDelegate::HEADER_HEIGHT;
00211         const bool isFirst = isFirstHeader(index);
00212 
00213         if (itemStateProvider && !itemStateProvider->isVisible(index)) {
00214             return QSize();
00215         } else if (isFirst) {
00216             minHeight = ItemDelegate::FIRST_HEADER_HEIGHT;
00217         }
00218 
00219         return QSize(q->width() - ItemDelegate::HEADER_LEFT_MARGIN,
00220                      qMax(fm.height() + (isFirst ? 4 : ItemDelegate::HEADER_TOP_MARGIN), minHeight)
00221                      + ItemDelegate::HEADER_BOTTOM_MARGIN) ;
00222     }
00223 
00224     QPoint mapFromViewport(const QPoint& point) const
00225     {
00226         return point + QPoint(0,q->verticalOffset());
00227     }
00228 
00229     QPoint mapToViewport(const QPoint& point) const
00230     {
00231         return point - QPoint(0,q->verticalOffset());
00232     }
00233 
00234     UrlItemView * const q;
00235     QPersistentModelIndex currentRootIndex;
00236     QPersistentModelIndex hoveredIndex;
00237     QPersistentModelIndex watchedIndexForDrag;
00238 
00239     QHash<QModelIndex,int> itemChildOffsets;
00240     QHash<QModelIndex,QRect> itemRects;
00241     QList<QModelIndex> visualOrder;
00242 
00243     int contentsHeight;
00244     ItemStateProvider *itemStateProvider;
00245 
00246     static const int MAX_COLUMNS = 1;
00247 
00248     // TODO Eventually it will be possible to restrict each branch to only showing
00249     // a given number of children, with Next/Previous arrows to view more children
00250     //
00251     // eg.  
00252     //
00253     // Recent Documents  [1-10 of 100]            Next
00254     // Recent Applications [10-20 of 30] Previous|Next
00255     //
00256     static const int MAX_CHILD_ROWS = 1000;
00257 };
00258 
00259 UrlItemView::UrlItemView(QWidget *parent)
00260     : QAbstractItemView(parent)
00261     , d(new Private(this))
00262 {
00263     setIconSize(QSize(KIconLoader::SizeMedium, KIconLoader::SizeMedium));
00264     setMouseTracking(true);
00265 }
00266 
00267 UrlItemView::~UrlItemView()
00268 {
00269     delete d;
00270 }
00271 
00272 QModelIndex UrlItemView::indexAt(const QPoint& point) const
00273 {
00274     // simple linear search through the item rects, this will
00275     // be inefficient when the viewport is large
00276     QHashIterator<QModelIndex,QRect> iter(d->itemRects);
00277     while (iter.hasNext()) {
00278         iter.next();
00279         if (iter.value().contains(d->mapFromViewport(point))) {
00280             return iter.key();
00281         }
00282     }
00283     return QModelIndex();
00284 }
00285 
00286 void UrlItemView::setModel(QAbstractItemModel *model)
00287 {
00288     QAbstractItemView::setModel(model);
00289 
00290     if (model) {
00291         connect(model,SIGNAL(rowsRemoved(QModelIndex,int,int)),this,SLOT(updateLayout()));
00292         connect(model,SIGNAL(rowsInserted(QModelIndex,int,int)),this,SLOT(updateLayout()));
00293         connect(model,SIGNAL(modelReset()),this,SLOT(updateLayout()));
00294     }
00295 
00296     d->currentRootIndex = QModelIndex();
00297     d->itemChildOffsets.clear();
00298     updateLayout();
00299 }
00300 
00301 void UrlItemView::updateLayout()
00302 {
00303     d->doLayout();
00304 
00305     if (viewport()->isVisible()) {
00306         viewport()->update();
00307     }
00308 }
00309 
00310 void UrlItemView::scrollTo(const QModelIndex& index, ScrollHint hint)
00311 {
00312     QRect itemRect = d->itemRects[index];
00313     QRect viewedRect = QRect(d->mapFromViewport(QPoint(0,0)),
00314                              size());
00315     int topDifference = viewedRect.top()-itemRect.top();
00316     int bottomDifference = viewedRect.bottom()-itemRect.bottom();
00317     QScrollBar *scrollBar = verticalScrollBar();
00318 
00319     if (!itemRect.isValid())
00320         return;
00321 
00322     switch (hint) {
00323         case EnsureVisible:
00324             {
00325                if (!viewedRect.contains(itemRect)) {
00326 
00327                    if (topDifference < 0) {
00328                         // scroll view down
00329                         scrollBar->setValue(scrollBar->value() - bottomDifference); 
00330                    } else {
00331                         // scroll view up
00332                         scrollBar->setValue(scrollBar->value() - topDifference);
00333                    }
00334                } 
00335             }
00336             break;
00337         case PositionAtTop:
00338             {
00339                 //d->viewportOffset = itemRect.top();
00340             }
00341         default:
00342             Q_ASSERT(false); // Not implemented
00343     }
00344 }
00345 
00346 QRect UrlItemView::visualRect(const QModelIndex& index) const
00347 {
00348     QRect itemRect = d->itemRects[index];
00349     if (!itemRect.isValid()) {
00350         return itemRect;
00351     }
00352 
00353     itemRect.moveTopLeft(d->mapToViewport(itemRect.topLeft()));
00354     return itemRect;
00355 }
00356 
00357 int UrlItemView::horizontalOffset() const
00358 {
00359     return 0;
00360 }
00361 
00362 bool UrlItemView::isIndexHidden(const QModelIndex&) const
00363 {
00364     return false;
00365 }
00366 
00367 QModelIndex UrlItemView::moveCursor(CursorAction cursorAction,Qt::KeyboardModifiers )
00368 {
00369     QModelIndex index = currentIndex();
00370 
00371     int visualIndex = d->visualOrder.indexOf(index);
00372 
00373     switch (cursorAction) {
00374         case MoveUp:
00375                 if (!currentIndex().isValid()) {
00376                     const QModelIndex root = model()->index(0, 0);
00377                     index = model()->index(model()->rowCount(root) - 1, 0, root);
00378                 } else {
00379                     visualIndex = qMax(0,visualIndex-1);
00380                 }
00381             break;
00382         case MoveDown:
00383                 if (!currentIndex().isValid()) {
00384                     const QModelIndex root = model()->index(0, 0);
00385                     index = model()->index(0, 0, root);
00386                 } else {
00387                     visualIndex = qMin(d->visualOrder.count()-1,visualIndex+1);
00388                 }
00389             break;
00390         default:
00391                 // Do nothing
00392             break;
00393     }
00394 
00395     d->hoveredIndex = QModelIndex();
00396 
00397     return currentIndex().isValid() ? d->visualOrder.value(visualIndex,QModelIndex())
00398                                     : index;
00399 }
00400 
00401 void UrlItemView::setSelection(const QRect& rect,QItemSelectionModel::SelectionFlags flags)
00402 {
00403     QItemSelection selection;
00404     selection.select(indexAt(rect.topLeft()),indexAt(rect.bottomRight()));
00405     selectionModel()->select(selection,flags);
00406 }
00407 
00408 int UrlItemView::verticalOffset() const
00409 {
00410     return verticalScrollBar()->value(); 
00411 }
00412 
00413 QRegion UrlItemView::visualRegionForSelection(const QItemSelection& selection) const{
00414     QRegion region;
00415     foreach(const QModelIndex& index,selection.indexes()) {
00416         region |= visualRect(index);
00417     }
00418     return region;
00419 }
00420 
00421 void UrlItemView::paintEvent(QPaintEvent *event)
00422 {
00423     if (!model()) {
00424         return;
00425     }
00426 
00427     QPainter painter(viewport());
00428     painter.setRenderHint(QPainter::Antialiasing);
00429 
00430     QHashIterator<QModelIndex,QRect> indexIter(d->itemRects);
00431     while (indexIter.hasNext()) {
00432         indexIter.next();
00433         const QRect itemRect = visualRect(indexIter.key());
00434         const QModelIndex index = indexIter.key();
00435 
00436         if (event->region().contains(itemRect)) {
00437             QStyleOptionViewItem option = viewOptions();
00438             option.rect = itemRect;
00439 
00440             if (selectionModel()->isSelected(index)) {
00441                 option.state |= QStyle::State_Selected;
00442             }
00443             if (index == d->hoveredIndex) {
00444                 option.state |= QStyle::State_MouseOver;
00445             }
00446             if (index == currentIndex()) {
00447                 option.state |= QStyle::State_HasFocus;
00448             }
00449 
00450             if (model()->hasChildren(index)) {
00451                 d->drawHeader(&painter, index, option);
00452             } else {
00453                 if (option.rect.left() == 0) {
00454                     option.rect.setLeft(option.rect.left() + ItemDelegate::ITEM_LEFT_MARGIN);
00455                     option.rect.setRight(option.rect.right() - ItemDelegate::ITEM_RIGHT_MARGIN);
00456                 }
00457                 itemDelegate(index)->paint(&painter,option,index);
00458             }
00459         }
00460     }
00461 }
00462 
00463 void UrlItemView::resizeEvent(QResizeEvent *)
00464 {
00465     updateLayout();
00466 }
00467 
00468 void UrlItemView::mouseMoveEvent(QMouseEvent *event)
00469 {
00470     const QModelIndex itemUnderMouse = indexAt(event->pos());
00471     if (itemUnderMouse != d->hoveredIndex && state() == NoState) {
00472         update(itemUnderMouse);
00473         update(d->hoveredIndex);
00474 
00475         d->hoveredIndex = itemUnderMouse;
00476         setCurrentIndex(d->hoveredIndex);
00477     }
00478     QAbstractItemView::mouseMoveEvent(event);
00479 }
00480 
00481 void UrlItemView::mousePressEvent(QMouseEvent *event)
00482 {
00483     d->watchedIndexForDrag = indexAt(event->pos());
00484     QAbstractItemView::mousePressEvent(event);
00485 }
00486 
00487 void UrlItemView::mouseReleaseEvent(QMouseEvent *event)
00488 {
00489     d->watchedIndexForDrag = QModelIndex();
00490 }
00491 
00492 void UrlItemView::setItemStateProvider(ItemStateProvider *provider)
00493 {
00494     d->itemStateProvider = provider;
00495 }
00496 
00497 void UrlItemView::startDrag(Qt::DropActions supportedActions) 
00498 {
00499     kDebug() << "Starting UrlItemView drag with actions" << supportedActions;
00500 
00501     if (!d->watchedIndexForDrag.isValid()) {
00502         return;
00503     }
00504 
00505     QMimeData *mimeData = model()->mimeData(selectionModel()->selectedIndexes());
00506 
00507     if (!mimeData || mimeData->text().isNull()) {
00508         return;
00509     }
00510 
00511     QDrag *drag = new QDrag(this);
00512     drag->setMimeData(mimeData);
00513 
00514     QModelIndex idx = selectionModel()->selectedIndexes().first();
00515     QIcon icon = idx.data(Qt::DecorationRole).value<QIcon>();
00516     drag->setPixmap(icon.pixmap(IconSize(KIconLoader::Desktop)));
00517 
00518     Qt::DropAction dropAction = drag->exec();
00519     QAbstractItemView::startDrag(supportedActions);
00520 }
00521 
00522 void UrlItemView::dropEvent(QDropEvent *)
00523 {
00524     kDebug() << "UrlItemView drop event";
00525 }
00526 
00527 void UrlItemView::leaveEvent(QEvent *event)
00528 {
00529     d->hoveredIndex = QModelIndex();
00530     setCurrentIndex(QModelIndex());
00531 }
00532 
00533 ItemStateProvider *UrlItemView::itemStateProvider() const
00534 {
00535     return d->itemStateProvider;
00536 }
00537 #include "urlitemview.moc"

Applets

Skip menu "Applets"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal