00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "ui/urlitemview.h"
00022
00023
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
00033 #include <KDebug>
00034 #include <KGlobalSettings>
00035 #include <KIconLoader>
00036 #include <KColorScheme>
00037
00038
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
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
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
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
00196 if (q->model()->hasChildren(prevHeader)) {
00197
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
00249
00250
00251
00252
00253
00254
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
00275
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
00329 scrollBar->setValue(scrollBar->value() - bottomDifference);
00330 } else {
00331
00332 scrollBar->setValue(scrollBar->value() - topDifference);
00333 }
00334 }
00335 }
00336 break;
00337 case PositionAtTop:
00338 {
00339
00340 }
00341 default:
00342 Q_ASSERT(false);
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
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"