• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdelibs API Reference
  • KDE Home
  • Contact Us
 

KDEUI

  • sources
  • kde-4.14
  • kdelibs
  • kdeui
  • itemviews
kcategorizedview.cpp
Go to the documentation of this file.
1 
32 #include "kcategorizedview.h"
33 #include "kcategorizedview_p.h"
34 
35 #include <math.h> // trunc on C99 compliant systems
36 #include <kdefakes.h> // trunc for not C99 compliant systems
37 
38 #include <QPainter>
39 #include <QScrollBar>
40 #include <QPaintEvent>
41 
42 #include "kcategorydrawer.h"
43 #include "kcategorizedsortfilterproxymodel.h"
44 
45 //BEGIN: Private part
46 
47 struct KCategorizedView::Private::Item
48 {
49  Item()
50  : topLeft(QPoint())
51  , size(QSize())
52  {
53  }
54 
55  QPoint topLeft;
56  QSize size;
57 };
58 
59 struct KCategorizedView::Private::Block
60 {
61  Block()
62  : topLeft(QPoint())
63  , height(-1)
64  , firstIndex(QModelIndex())
65  , quarantineStart(QModelIndex())
66  , items(QList<Item>())
67  , outOfQuarantine(false)
68  , alternate(false)
69  , collapsed(false)
70  {
71  }
72 
73  bool operator!=(const Block &rhs) const
74  {
75  return firstIndex != rhs.firstIndex;
76  }
77 
78  static bool lessThan(const Block &left, const Block &right)
79  {
80  Q_ASSERT(left.firstIndex.isValid());
81  Q_ASSERT(right.firstIndex.isValid());
82  return left.firstIndex.row() < right.firstIndex.row();
83  }
84 
85  QPoint topLeft;
86  int height;
87  QPersistentModelIndex firstIndex;
88  // if we have n elements on this block, and we inserted an element at position i. The quarantine
89  // will start at index (i, column, parent). This means that for all elements j where i <= j <= n, the
90  // visual rect position of item j will have to be recomputed (cannot use the cached point). The quarantine
91  // will only affect the current block, since the rest of blocks can be affected only in the way
92  // that the whole block will have different offset, but items will keep the same relative position
93  // in terms of their parent blocks.
94  QPersistentModelIndex quarantineStart;
95  QList<Item> items;
96 
97  // this affects the whole block, not items separately. items contain the topLeft point relative
98  // to the block. Because of insertions or removals a whole block can be moved, so the whole block
99  // will enter in quarantine, what is faster than moving all items in absolute terms.
100  bool outOfQuarantine;
101 
102  // should we alternate its color ? is just a hint, could not be used
103  bool alternate;
104  bool collapsed;
105 };
106 
107 KCategorizedView::Private::Private(KCategorizedView *q)
108  : q(q)
109  , proxyModel(0)
110  , categoryDrawer(0)
111  , categoryDrawerV2(0)
112  , categoryDrawerV3(0)
113  , categorySpacing(5)
114  , alternatingBlockColors(false)
115  , collapsibleBlocks(false)
116  , hoveredBlock(new Block())
117  , hoveredIndex(QModelIndex())
118  , pressedPosition(QPoint())
119  , rubberBandRect(QRect())
120 {
121 }
122 
123 KCategorizedView::Private::~Private()
124 {
125  delete hoveredBlock;
126 }
127 
128 bool KCategorizedView::Private::isCategorized() const
129 {
130  return proxyModel && categoryDrawer && proxyModel->isCategorizedModel();
131 }
132 
133 QStyleOptionViewItemV4 KCategorizedView::Private::blockRect(const QModelIndex &representative)
134 {
135  QStyleOptionViewItemV4 option(q->viewOptions());
136  const int height = categoryDrawer->categoryHeight(representative, option);
137  const QString categoryDisplay = representative.data(KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
138  QPoint pos = blockPosition(categoryDisplay);
139  pos.ry() -= height;
140  option.rect.setTopLeft(pos);
141  option.rect.setWidth(viewportWidth() + categoryDrawer->leftMargin() + categoryDrawer->rightMargin());
142  option.rect.setHeight(height + blockHeight(categoryDisplay));
143  option.rect = mapToViewport(option.rect);
144 
145  return option;
146 }
147 
148 QPair<QModelIndex, QModelIndex> KCategorizedView::Private::intersectingIndexesWithRect(const QRect &_rect) const
149 {
150  const int rowCount = proxyModel->rowCount();
151 
152  const QRect rect = _rect.normalized();
153 
154  // binary search to find out the top border
155  int bottom = 0;
156  int top = rowCount - 1;
157  while (bottom <= top) {
158  const int middle = (bottom + top) / 2;
159  const QModelIndex index = proxyModel->index(middle, q->modelColumn(), q->rootIndex());
160  const QRect itemRect = q->visualRect(index);
161  if (itemRect.bottomRight().y() <= rect.topLeft().y()) {
162  bottom = middle + 1;
163  } else {
164  top = middle - 1;
165  }
166  }
167 
168  const QModelIndex bottomIndex = proxyModel->index(bottom, q->modelColumn(), q->rootIndex());
169 
170  // binary search to find out the bottom border
171  bottom = 0;
172  top = rowCount - 1;
173  while (bottom <= top) {
174  const int middle = (bottom + top) / 2;
175  const QModelIndex index = proxyModel->index(middle, q->modelColumn(), q->rootIndex());
176  const QRect itemRect = q->visualRect(index);
177  if (itemRect.topLeft().y() <= rect.bottomRight().y()) {
178  bottom = middle + 1;
179  } else {
180  top = middle - 1;
181  }
182  }
183 
184  const QModelIndex topIndex = proxyModel->index(top, q->modelColumn(), q->rootIndex());
185 
186  return qMakePair(bottomIndex, topIndex);
187 }
188 
189 QPoint KCategorizedView::Private::blockPosition(const QString &category)
190 {
191  Block &block = blocks[category];
192 
193  if (block.outOfQuarantine && !block.topLeft.isNull()) {
194  return block.topLeft;
195  }
196 
197  QPoint res(categorySpacing, 0);
198 
199  const QModelIndex index = block.firstIndex;
200 
201  for (QHash<QString, Private::Block>::Iterator it = blocks.begin(); it != blocks.end(); ++it) {
202  Block &block = *it;
203  const QModelIndex categoryIndex = block.firstIndex;
204  if (index.row() < categoryIndex.row()) {
205  continue;
206  }
207  res.ry() += categoryDrawer->categoryHeight(categoryIndex, q->viewOptions()) + categorySpacing;
208  if (index.row() == categoryIndex.row()) {
209  continue;
210  }
211  res.ry() += blockHeight(it.key());
212  }
213 
214  block.outOfQuarantine = true;
215  block.topLeft = res;
216 
217  return res;
218 }
219 
220 int KCategorizedView::Private::blockHeight(const QString &category)
221 {
222  Block &block = blocks[category];
223 
224  if (block.collapsed) {
225  return 0;
226  }
227 
228  if (block.height > -1) {
229  return block.height;
230  }
231 
232  const QModelIndex firstIndex = block.firstIndex;
233  const QModelIndex lastIndex = proxyModel->index(firstIndex.row() + block.items.count() - 1, q->modelColumn(), q->rootIndex());
234  const QRect topLeft = q->visualRect(firstIndex);
235  QRect bottomRight = q->visualRect(lastIndex);
236 
237  if (hasGrid()) {
238  bottomRight.setHeight(qMax(bottomRight.height(), q->gridSize().height()));
239  } else {
240  if (!q->uniformItemSizes()) {
241  bottomRight.setHeight(highestElementInLastRow(block) + q->spacing() * 2);
242  }
243  }
244 
245  const int height = bottomRight.bottomRight().y() - topLeft.topLeft().y() + 1;
246  block.height = height;
247 
248  return height;
249 }
250 
251 int KCategorizedView::Private::viewportWidth() const
252 {
253  return q->viewport()->width() - categorySpacing * 2 - categoryDrawer->leftMargin() - categoryDrawer->rightMargin();
254 }
255 
256 void KCategorizedView::Private::regenerateAllElements()
257 {
258  for (QHash<QString, Block>::Iterator it = blocks.begin(); it != blocks.end(); ++it) {
259  Block &block = *it;
260  block.outOfQuarantine = false;
261  block.quarantineStart = block.firstIndex;
262  block.height = -1;
263  }
264 }
265 
266 void KCategorizedView::Private::rowsInserted(const QModelIndex &parent, int start, int end)
267 {
268  if (!isCategorized()) {
269  return;
270  }
271 
272  for (int i = start; i <= end; ++i) {
273  const QModelIndex index = proxyModel->index(i, q->modelColumn(), parent);
274 
275  Q_ASSERT(index.isValid());
276 
277  const QString category = categoryForIndex(index);
278 
279  Block &block = blocks[category];
280 
281  //BEGIN: update firstIndex
282  // save as firstIndex in block if
283  // - it forced the category creation (first element on this category)
284  // - it is before the first row on that category
285  const QModelIndex firstIndex = block.firstIndex;
286  if (!firstIndex.isValid() || index.row() < firstIndex.row()) {
287  block.firstIndex = index;
288  }
289  //END: update firstIndex
290 
291  Q_ASSERT(block.firstIndex.isValid());
292 
293  const int firstIndexRow = block.firstIndex.row();
294 
295  block.items.insert(index.row() - firstIndexRow, Private::Item());
296  block.height = -1;
297 
298  q->visualRect(index);
299  q->viewport()->update();
300  }
301 
302  //BEGIN: update the items that are in quarantine in affected categories
303  {
304  const QModelIndex lastIndex = proxyModel->index(end, q->modelColumn(), parent);
305  const QString category = categoryForIndex(lastIndex);
306  Private::Block &block = blocks[category];
307  block.quarantineStart = block.firstIndex;
308  }
309  //END: update the items that are in quarantine in affected categories
310 
311  //BEGIN: mark as in quarantine those categories that are under the affected ones
312  {
313  const QModelIndex firstIndex = proxyModel->index(start, q->modelColumn(), parent);
314  const QString category = categoryForIndex(firstIndex);
315  const QModelIndex firstAffectedCategory = blocks[category].firstIndex;
316  //BEGIN: order for marking as alternate those blocks that are alternate
317  QList<Block> blockList = blocks.values();
318  qSort(blockList.begin(), blockList.end(), Block::lessThan);
319  QList<int> firstIndexesRows;
320  foreach (const Block &block, blockList) {
321  firstIndexesRows << block.firstIndex.row();
322  }
323  //END: order for marking as alternate those blocks that are alternate
324  for (QHash<QString, Private::Block>::Iterator it = blocks.begin(); it != blocks.end(); ++it) {
325  Private::Block &block = *it;
326  if (block.firstIndex.row() > firstAffectedCategory.row()) {
327  block.outOfQuarantine = false;
328  block.alternate = firstIndexesRows.indexOf(block.firstIndex.row()) % 2;
329  } else if (block.firstIndex.row() == firstAffectedCategory.row()) {
330  block.alternate = firstIndexesRows.indexOf(block.firstIndex.row()) % 2;
331  }
332  }
333  }
334  //END: mark as in quarantine those categories that are under the affected ones
335 }
336 
337 QRect KCategorizedView::Private::mapToViewport(const QRect &rect) const
338 {
339  const int dx = -q->horizontalOffset();
340  const int dy = -q->verticalOffset();
341  return rect.adjusted(dx, dy, dx, dy);
342 }
343 
344 QRect KCategorizedView::Private::mapFromViewport(const QRect &rect) const
345 {
346  const int dx = q->horizontalOffset();
347  const int dy = q->verticalOffset();
348  return rect.adjusted(dx, dy, dx, dy);
349 }
350 
351 int KCategorizedView::Private::highestElementInLastRow(const Block &block) const
352 {
353  //Find the highest element in the last row
354  const QModelIndex lastIndex = proxyModel->index(block.firstIndex.row() + block.items.count() - 1, q->modelColumn(), q->rootIndex());
355  const QRect prevRect = q->visualRect(lastIndex);
356  int res = prevRect.height();
357  QModelIndex prevIndex = proxyModel->index(lastIndex.row() - 1, q->modelColumn(), q->rootIndex());
358  if (!prevIndex.isValid()) {
359  return res;
360  }
361  Q_FOREVER {
362  const QRect tempRect = q->visualRect(prevIndex);
363  if (tempRect.topLeft().y() < prevRect.topLeft().y()) {
364  break;
365  }
366  res = qMax(res, tempRect.height());
367  if (prevIndex == block.firstIndex) {
368  break;
369  }
370  prevIndex = proxyModel->index(prevIndex.row() - 1, q->modelColumn(), q->rootIndex());
371  }
372 
373  return res;
374 }
375 
376 bool KCategorizedView::Private::hasGrid() const
377 {
378  const QSize gridSize = q->gridSize();
379  return gridSize.isValid() && !gridSize.isNull();
380 }
381 
382 QString KCategorizedView::Private::categoryForIndex(const QModelIndex &index) const
383 {
384  const QModelIndex categoryIndex = index.model()->index(index.row(), proxyModel->sortColumn(), index.parent());
385  return categoryIndex.data(KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
386 }
387 
388 void KCategorizedView::Private::leftToRightVisualRect(const QModelIndex &index, Item &item,
389  const Block &block, const QPoint &blockPos) const
390 {
391  const int firstIndexRow = block.firstIndex.row();
392 
393  if (hasGrid()) {
394  const int relativeRow = index.row() - firstIndexRow;
395  const int maxItemsPerRow = qMax(viewportWidth() / q->gridSize().width(), 1);
396  if (q->layoutDirection() == Qt::LeftToRight) {
397  item.topLeft.rx() = (relativeRow % maxItemsPerRow) * q->gridSize().width() + blockPos.x() + categoryDrawer->leftMargin();
398  } else {
399  item.topLeft.rx() = viewportWidth() - ((relativeRow % maxItemsPerRow) + 1) * q->gridSize().width() + categoryDrawer->leftMargin() + categorySpacing;
400  }
401  item.topLeft.ry() = (relativeRow / maxItemsPerRow) * q->gridSize().height();
402  } else {
403  if (q->uniformItemSizes()) {
404  const int relativeRow = index.row() - firstIndexRow;
405  const QSize itemSize = q->sizeHintForIndex(index);
406  const int maxItemsPerRow = qMax((viewportWidth() - q->spacing()) / (itemSize.width() + q->spacing()), 1);
407  if (q->layoutDirection() == Qt::LeftToRight) {
408  item.topLeft.rx() = (relativeRow % maxItemsPerRow) * itemSize.width() + blockPos.x() + categoryDrawer->leftMargin();
409  } else {
410  item.topLeft.rx() = viewportWidth() - (relativeRow % maxItemsPerRow) * itemSize.width() + categoryDrawer->leftMargin() + categorySpacing;
411  }
412  item.topLeft.ry() = (relativeRow / maxItemsPerRow) * itemSize.height();
413  } else {
414  const QSize currSize = q->sizeHintForIndex(index);
415  if (index != block.firstIndex) {
416  const int viewportW = viewportWidth() - q->spacing();
417  QModelIndex prevIndex = proxyModel->index(index.row() - 1, q->modelColumn(), q->rootIndex());
418  QRect prevRect = q->visualRect(prevIndex);
419  prevRect = mapFromViewport(prevRect);
420  if ((prevRect.bottomRight().x() + 1) + currSize.width() - blockPos.x() + q->spacing() > viewportW) {
421  // we have to check the whole previous row, and see which one was the
422  // highest.
423  Q_FOREVER {
424  prevIndex = proxyModel->index(prevIndex.row() - 1, q->modelColumn(), q->rootIndex());
425  const QRect tempRect = q->visualRect(prevIndex);
426  if (tempRect.topLeft().y() < prevRect.topLeft().y()) {
427  break;
428  }
429  if (tempRect.bottomRight().y() > prevRect.bottomRight().y()) {
430  prevRect = tempRect;
431  }
432  if (prevIndex == block.firstIndex) {
433  break;
434  }
435  }
436  if (q->layoutDirection() == Qt::LeftToRight) {
437  item.topLeft.rx() = categoryDrawer->leftMargin() + blockPos.x() + q->spacing();
438  } else {
439  item.topLeft.rx() = viewportWidth() - currSize.width() + categoryDrawer->leftMargin() + categorySpacing;
440  }
441  item.topLeft.ry() = (prevRect.bottomRight().y() + 1) + q->spacing() - blockPos.y();
442  } else {
443  if (q->layoutDirection() == Qt::LeftToRight) {
444  item.topLeft.rx() = (prevRect.bottomRight().x() + 1) + q->spacing();
445  } else {
446  item.topLeft.rx() = (prevRect.bottomLeft().x() - 1) - q->spacing() - item.size.width() + categoryDrawer->leftMargin() + categorySpacing;
447  }
448  item.topLeft.ry() = prevRect.topLeft().y() - blockPos.y();
449  }
450  } else {
451  if (q->layoutDirection() == Qt::LeftToRight) {
452  item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing();
453  } else {
454  item.topLeft.rx() = viewportWidth() - currSize.width() + categoryDrawer->leftMargin() + categorySpacing;
455  }
456  item.topLeft.ry() = q->spacing();
457  }
458  }
459  }
460  item.size = q->sizeHintForIndex(index);
461 }
462 
463 void KCategorizedView::Private::topToBottomVisualRect(const QModelIndex &index, Item &item,
464  const Block &block, const QPoint &blockPos) const
465 {
466  const int firstIndexRow = block.firstIndex.row();
467 
468  if (hasGrid()) {
469  const int relativeRow = index.row() - firstIndexRow;
470  item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin();
471  item.topLeft.ry() = relativeRow * q->gridSize().height();
472  } else {
473  if (q->uniformItemSizes()) {
474  const int relativeRow = index.row() - firstIndexRow;
475  const QSize itemSize = q->sizeHintForIndex(index);
476  item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin();
477  item.topLeft.ry() = relativeRow * itemSize.height();
478  } else {
479  if (index != block.firstIndex) {
480  QModelIndex prevIndex = proxyModel->index(index.row() - 1, q->modelColumn(), q->rootIndex());
481  QRect prevRect = q->visualRect(prevIndex);
482  prevRect = mapFromViewport(prevRect);
483  item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing();
484  item.topLeft.ry() = (prevRect.bottomRight().y() + 1) + q->spacing() - blockPos.y();
485  } else {
486  item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing();
487  item.topLeft.ry() = q->spacing();
488  }
489  }
490  }
491  item.size = q->sizeHintForIndex(index);
492  item.size.setWidth(viewportWidth());
493 }
494 
495 void KCategorizedView::Private::_k_slotCollapseOrExpandClicked(QModelIndex)
496 {
497 }
498 
499 //END: Private part
500 
501 //BEGIN: Public part
502 
503 KCategorizedView::KCategorizedView(QWidget *parent)
504  : QListView(parent)
505  , d(new Private(this))
506 {
507 }
508 
509 KCategorizedView::~KCategorizedView()
510 {
511  delete d;
512 }
513 
514 void KCategorizedView::setModel(QAbstractItemModel *model)
515 {
516  if (d->proxyModel == model) {
517  return;
518  }
519 
520  d->blocks.clear();
521 
522  if (d->proxyModel) {
523  disconnect(d->proxyModel, SIGNAL(layoutChanged()), this, SLOT(slotLayoutChanged()));
524  }
525 
526  d->proxyModel = dynamic_cast<KCategorizedSortFilterProxyModel*>(model);
527 
528  if (d->proxyModel) {
529  connect(d->proxyModel, SIGNAL(layoutChanged()), this, SLOT(slotLayoutChanged()));
530  }
531 
532  QListView::setModel(model);
533 
534  // if the model already had information inserted, update our data structures to it
535  if (model->rowCount()) {
536  slotLayoutChanged();
537  }
538 }
539 
540 void KCategorizedView::setGridSize(const QSize &size)
541 {
542  setGridSizeOwn(size);
543 }
544 
545 void KCategorizedView::setGridSizeOwn(const QSize &size)
546 {
547  d->regenerateAllElements();
548  QListView::setGridSize(size);
549 }
550 
551 QRect KCategorizedView::visualRect(const QModelIndex &index) const
552 {
553  if (!d->isCategorized()) {
554  return QListView::visualRect(index);
555  }
556 
557  if (!index.isValid()) {
558  return QRect();
559  }
560 
561  const QString category = d->categoryForIndex(index);
562 
563  if (!d->blocks.contains(category)) {
564  return QRect();
565  }
566 
567  Private::Block &block = d->blocks[category];
568  const int firstIndexRow = block.firstIndex.row();
569 
570  Q_ASSERT(block.firstIndex.isValid());
571 
572  if (index.row() - firstIndexRow < 0 || index.row() - firstIndexRow >= block.items.count()) {
573  return QRect();
574  }
575 
576  const QPoint blockPos = d->blockPosition(category);
577 
578  Private::Item &ritem = block.items[index.row() - firstIndexRow];
579 
580  if (ritem.topLeft.isNull() || (block.quarantineStart.isValid() &&
581  index.row() >= block.quarantineStart.row())) {
582  if (flow() == LeftToRight) {
583  d->leftToRightVisualRect(index, ritem, block, blockPos);
584  } else {
585  d->topToBottomVisualRect(index, ritem, block, blockPos);
586  }
587 
588  //BEGIN: update the quarantine start
589  const bool wasLastIndex = (index.row() == (block.firstIndex.row() + block.items.count() - 1));
590  if (index.row() == block.quarantineStart.row()) {
591  if (wasLastIndex) {
592  block.quarantineStart = QModelIndex();
593  } else {
594  const QModelIndex nextIndex = d->proxyModel->index(index.row() + 1, modelColumn(), rootIndex());
595  block.quarantineStart = nextIndex;
596  }
597  }
598  //END: update the quarantine start
599  }
600 
601  // we get now the absolute position through the relative position of the parent block. do not
602  // save this on ritem, since this would override the item relative position in block terms.
603  Private::Item item(ritem);
604  item.topLeft.ry() += blockPos.y();
605 
606  const QSize sizeHint = item.size;
607 
608  if (d->hasGrid()) {
609  const QSize sizeGrid = gridSize();
610  const QSize resultingSize = sizeHint.boundedTo(sizeGrid);
611  QRect res(item.topLeft.x() + ((sizeGrid.width() - resultingSize.width()) / 2),
612  item.topLeft.y(), resultingSize.width(), resultingSize.height());
613  if (block.collapsed) {
614  // we can still do binary search, while we "hide" items. We move those items in collapsed
615  // blocks to the left and set a 0 height.
616  res.setLeft(-resultingSize.width());
617  res.setHeight(0);
618  }
619  return d->mapToViewport(res);
620  }
621 
622  QRect res(item.topLeft.x(), item.topLeft.y(), sizeHint.width(), sizeHint.height());
623  if (block.collapsed) {
624  // we can still do binary search, while we "hide" items. We move those items in collapsed
625  // blocks to the left and set a 0 height.
626  res.setLeft(-sizeHint.width());
627  res.setHeight(0);
628  }
629  return d->mapToViewport(res);
630 }
631 
632 KCategoryDrawer *KCategorizedView::categoryDrawer() const
633 {
634  return d->categoryDrawer;
635 }
636 
637 void KCategorizedView::setCategoryDrawer(KCategoryDrawer *categoryDrawer)
638 {
639  if (d->categoryDrawerV2) {
640  disconnect(d->categoryDrawerV2, SIGNAL(collapseOrExpandClicked(QModelIndex)),
641  this, SLOT(_k_slotCollapseOrExpandClicked(QModelIndex)));
642  }
643 
644  d->categoryDrawer = categoryDrawer;
645  d->categoryDrawerV2 = dynamic_cast<KCategoryDrawerV2*>(categoryDrawer);
646  d->categoryDrawerV3 = dynamic_cast<KCategoryDrawerV3*>(categoryDrawer);
647 
648  if (d->categoryDrawerV2) {
649  connect(d->categoryDrawerV2, SIGNAL(collapseOrExpandClicked(QModelIndex)),
650  this, SLOT(_k_slotCollapseOrExpandClicked(QModelIndex)));
651  }
652 }
653 
654 int KCategorizedView::categorySpacing() const
655 {
656  return d->categorySpacing;
657 }
658 
659 void KCategorizedView::setCategorySpacing(int categorySpacing)
660 {
661  if (d->categorySpacing == categorySpacing) {
662  return;
663  }
664 
665  d->categorySpacing = categorySpacing;
666 
667  for (QHash<QString, Private::Block>::Iterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
668  Private::Block &block = *it;
669  block.outOfQuarantine = false;
670  }
671 }
672 
673 bool KCategorizedView::alternatingBlockColors() const
674 {
675  return d->alternatingBlockColors;
676 }
677 
678 void KCategorizedView::setAlternatingBlockColors(bool enable)
679 {
680  d->alternatingBlockColors = enable;
681 }
682 
683 bool KCategorizedView::collapsibleBlocks() const
684 {
685  return d->collapsibleBlocks;
686 }
687 
688 void KCategorizedView::setCollapsibleBlocks(bool enable)
689 {
690  d->collapsibleBlocks = enable;
691 }
692 
693 QModelIndexList KCategorizedView::block(const QString &category)
694 {
695  QModelIndexList res;
696  const Private::Block &block = d->blocks[category];
697  if (block.height == -1) {
698  return res;
699  }
700  QModelIndex current = block.firstIndex;
701  const int first = current.row();
702  for (int i = 1; i <= block.items.count(); ++i) {
703  if (current.isValid()) {
704  res << current;
705  }
706  current = d->proxyModel->index(first + i, modelColumn(), rootIndex());
707  }
708  return res;
709 }
710 
711 QModelIndexList KCategorizedView::block(const QModelIndex &representative)
712 {
713  return block(representative.data(KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString());
714 }
715 
716 QModelIndex KCategorizedView::indexAt(const QPoint &point) const
717 {
718  if (!d->isCategorized()) {
719  return QListView::indexAt(point);
720  }
721 
722  const int rowCount = d->proxyModel->rowCount();
723  if (!rowCount) {
724  return QModelIndex();
725  }
726 
727  // Binary search that will try to spot if there is an index under point
728  int bottom = 0;
729  int top = rowCount - 1;
730  while (bottom <= top) {
731  const int middle = (bottom + top) / 2;
732  const QModelIndex index = d->proxyModel->index(middle, modelColumn(), rootIndex());
733  const QRect rect = visualRect(index);
734  if (rect.contains(point)) {
735  if (index.model()->flags(index) & Qt::ItemIsEnabled) {
736  return index;
737  }
738  return QModelIndex();
739  }
740  bool directionCondition;
741  if (layoutDirection() == Qt::LeftToRight) {
742  directionCondition = point.x() >= rect.bottomLeft().x();
743  } else {
744  directionCondition = point.x() <= rect.bottomRight().x();
745  }
746  if (point.y() < rect.topLeft().y()) {
747  top = middle - 1;
748  } else if (directionCondition) {
749  bottom = middle + 1;
750  } else if (point.y() <= rect.bottomRight().y()) {
751  top = middle - 1;
752  } else {
753  bool after = true;
754  for (int i = middle - 1;i >= bottom;i--) {
755  const QModelIndex newIndex =
756  d->proxyModel->index(i, modelColumn(), rootIndex());
757  const QRect newRect = visualRect(newIndex);
758  if (newRect.topLeft().y() < rect.topLeft().y()) {
759  break;
760  } else if (newRect.contains(point)) {
761  if (newIndex.model()->flags(newIndex) & Qt::ItemIsEnabled) {
762  return newIndex;
763  }
764  return QModelIndex();
765  } else if ((layoutDirection() == Qt::LeftToRight) ?
766  (newRect.topLeft().x() <= point.x()) :
767  (newRect.topRight().x() >= point.x())) {
768  break;
769  } else if (newRect.bottomRight().y() >= point.y()) {
770  after = false;
771  }
772  }
773  if (!after) {
774  return QModelIndex();
775  }
776  bottom = middle + 1;
777  }
778  }
779  return QModelIndex();
780 }
781 
782 void KCategorizedView::reset()
783 {
784  d->blocks.clear();
785  QListView::reset();
786 }
787 
788 void KCategorizedView::paintEvent(QPaintEvent *event)
789 {
790  if (!d->isCategorized()) {
791  QListView::paintEvent(event);
792  return;
793  }
794 
795  const QPair<QModelIndex, QModelIndex> intersecting = d->intersectingIndexesWithRect(viewport()->rect().intersected(event->rect()));
796 
797  QPainter p(viewport());
798  p.save();
799 
800  Q_ASSERT(selectionModel()->model() == d->proxyModel);
801 
802  //BEGIN: draw categories
803  QHash<QString, Private::Block>::ConstIterator it(d->blocks.constBegin());
804  while (it != d->blocks.constEnd()) {
805  const Private::Block &block = *it;
806  const QModelIndex categoryIndex = d->proxyModel->index(block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
807  QStyleOptionViewItemV4 option(viewOptions());
808  option.features |= d->alternatingBlockColors && block.alternate ? QStyleOptionViewItemV4::Alternate
809  : QStyleOptionViewItemV4::None;
810  option.state |= !d->collapsibleBlocks || !block.collapsed ? QStyle::State_Open
811  : QStyle::State_None;
812  const int height = d->categoryDrawer->categoryHeight(categoryIndex, option);
813  QPoint pos = d->blockPosition(it.key());
814  pos.ry() -= height;
815  option.rect.setTopLeft(pos);
816  option.rect.setWidth(d->viewportWidth() + d->categoryDrawer->leftMargin() + d->categoryDrawer->rightMargin());
817  option.rect.setHeight(height + d->blockHeight(it.key()));
818  option.rect = d->mapToViewport(option.rect);
819  if (!option.rect.intersects(viewport()->rect())) {
820  ++it;
821  continue;
822  }
823  d->categoryDrawer->drawCategory(categoryIndex, d->proxyModel->sortRole(), option, &p);
824  ++it;
825  }
826  //END: draw categories
827 
828  if (intersecting.first.isValid() && intersecting.second.isValid()) {
829  //BEGIN: draw items
830  int i = intersecting.first.row();
831  int indexToCheckIfBlockCollapsed = i;
832  QModelIndex categoryIndex;
833  QString category;
834  Private::Block *block = 0;
835  while (i <= intersecting.second.row()) {
836  //BEGIN: first check if the block is collapsed. if so, we have to skip the item painting
837  if (i == indexToCheckIfBlockCollapsed) {
838  categoryIndex = d->proxyModel->index(i, d->proxyModel->sortColumn(), rootIndex());
839  category = categoryIndex.data(KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
840  block = &d->blocks[category];
841  indexToCheckIfBlockCollapsed = block->firstIndex.row() + block->items.count();
842  if (block->collapsed) {
843  i = indexToCheckIfBlockCollapsed;
844  continue;
845  }
846  }
847  //END: first check if the block is collapsed. if so, we have to skip the item painting
848 
849  Q_ASSERT(block);
850 
851  const bool alternateItem = (i - block->firstIndex.row()) % 2;
852 
853  const QModelIndex index = d->proxyModel->index(i, modelColumn(), rootIndex());
854  const Qt::ItemFlags flags = d->proxyModel->flags(index);
855  QStyleOptionViewItemV4 option(viewOptions());
856  option.rect = visualRect(index);
857  option.widget = this;
858  option.features |= wordWrap() ? QStyleOptionViewItemV2::WrapText
859  : QStyleOptionViewItemV2::None;
860  option.features |= alternatingRowColors() && alternateItem ? QStyleOptionViewItemV4::Alternate
861  : QStyleOptionViewItemV4::None;
862  if (flags & Qt::ItemIsSelectable) {
863  option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected
864  : QStyle::State_None;
865  } else {
866  option.state &= ~QStyle::State_Selected;
867  }
868  option.state |= (index == currentIndex()) ? QStyle::State_HasFocus
869  : QStyle::State_None;
870  if (!(flags & Qt::ItemIsEnabled)) {
871  option.state &= ~QStyle::State_Enabled;
872  } else {
873  option.state |= (index == d->hoveredIndex) ? QStyle::State_MouseOver
874  : QStyle::State_None;
875  }
876 
877  itemDelegate(index)->paint(&p, option, index);
878  ++i;
879  }
880  //END: draw items
881  }
882 
883  //BEGIN: draw selection rect
884  if (isSelectionRectVisible() && d->rubberBandRect.isValid()) {
885  QStyleOptionRubberBand opt;
886  opt.initFrom(this);
887  opt.shape = QRubberBand::Rectangle;
888  opt.opaque = false;
889  opt.rect = d->mapToViewport(d->rubberBandRect).intersected(viewport()->rect().adjusted(-16, -16, 16, 16));
890  p.save();
891  style()->drawControl(QStyle::CE_RubberBand, &opt, &p);
892  p.restore();
893  }
894  //END: draw selection rect
895 
896  p.restore();
897 }
898 
899 void KCategorizedView::resizeEvent(QResizeEvent *event)
900 {
901  d->regenerateAllElements();
902  QListView::resizeEvent(event);
903 }
904 
905 void KCategorizedView::setSelection(const QRect &rect,
906  QItemSelectionModel::SelectionFlags flags)
907 {
908  if (!d->isCategorized()) {
909  QListView::setSelection(rect, flags);
910  return;
911  }
912 
913  if (rect.topLeft() == rect.bottomRight()) {
914  const QModelIndex index = indexAt(rect.topLeft());
915  selectionModel()->select(index, flags);
916  return;
917  }
918 
919  const QPair<QModelIndex, QModelIndex> intersecting = d->intersectingIndexesWithRect(rect);
920 
921  QItemSelection selection;
922 
923  //TODO: think of a faster implementation
924  QModelIndex firstIndex;
925  QModelIndex lastIndex;
926  for (int i = intersecting.first.row(); i <= intersecting.second.row(); ++i) {
927  const QModelIndex index = d->proxyModel->index(i, modelColumn(), rootIndex());
928  const bool visualRectIntersects = visualRect(index).intersects(rect);
929  if (firstIndex.isValid()) {
930  if (visualRectIntersects) {
931  lastIndex = index;
932  } else {
933  selection << QItemSelectionRange(firstIndex, lastIndex);
934  firstIndex = QModelIndex();
935  }
936  } else if (visualRectIntersects) {
937  firstIndex = index;
938  lastIndex = index;
939  }
940  }
941 
942  if (firstIndex.isValid()) {
943  selection << QItemSelectionRange(firstIndex, lastIndex);
944  }
945 
946  selectionModel()->select(selection, flags);
947 }
948 
949 void KCategorizedView::mouseMoveEvent(QMouseEvent *event)
950 {
951  QListView::mouseMoveEvent(event);
952  d->hoveredIndex = indexAt(event->pos());
953  const SelectionMode itemViewSelectionMode = selectionMode();
954  if (state() == DragSelectingState && isSelectionRectVisible() && itemViewSelectionMode != SingleSelection
955  && itemViewSelectionMode != NoSelection) {
956  QRect rect(d->pressedPosition, event->pos() + QPoint(horizontalOffset(), verticalOffset()));
957  rect = rect.normalized();
958  update(rect.united(d->rubberBandRect));
959  d->rubberBandRect = rect;
960  }
961  if (!d->categoryDrawerV2) {
962  return;
963  }
964  QHash<QString, Private::Block>::ConstIterator it(d->blocks.constBegin());
965  while (it != d->blocks.constEnd()) {
966  const Private::Block &block = *it;
967  const QModelIndex categoryIndex = d->proxyModel->index(block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
968  QStyleOptionViewItemV4 option(viewOptions());
969  const int height = d->categoryDrawer->categoryHeight(categoryIndex, option);
970  QPoint pos = d->blockPosition(it.key());
971  pos.ry() -= height;
972  option.rect.setTopLeft(pos);
973  option.rect.setWidth(d->viewportWidth() + d->categoryDrawer->leftMargin() + d->categoryDrawer->rightMargin());
974  option.rect.setHeight(height + d->blockHeight(it.key()));
975  option.rect = d->mapToViewport(option.rect);
976  const QPoint mousePos = viewport()->mapFromGlobal(QCursor::pos());
977  if (option.rect.contains(mousePos)) {
978  if (d->categoryDrawerV3 && d->hoveredBlock->height != -1 && *d->hoveredBlock != block) {
979  const QModelIndex categoryIndex = d->proxyModel->index(d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
980  const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex);
981  d->categoryDrawerV3->mouseLeft(categoryIndex, option.rect);
982  *d->hoveredBlock = block;
983  d->hoveredCategory = it.key();
984  viewport()->update(option.rect);
985  } else if (d->hoveredBlock->height == -1) {
986  *d->hoveredBlock = block;
987  d->hoveredCategory = it.key();
988  } else if (d->categoryDrawerV3) {
989  d->categoryDrawerV3->mouseMoved(categoryIndex, option.rect, event);
990  } else {
991  d->categoryDrawerV2->mouseButtonMoved(categoryIndex, event);
992  }
993  viewport()->update(option.rect);
994  return;
995  }
996  ++it;
997  }
998  if (d->categoryDrawerV3 && d->hoveredBlock->height != -1) {
999  const QModelIndex categoryIndex = d->proxyModel->index(d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
1000  const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex);
1001  d->categoryDrawerV3->mouseLeft(categoryIndex, option.rect);
1002  *d->hoveredBlock = Private::Block();
1003  d->hoveredCategory = QString();
1004  viewport()->update(option.rect);
1005  }
1006 }
1007 
1008 void KCategorizedView::mousePressEvent(QMouseEvent *event)
1009 {
1010  if (event->button() == Qt::LeftButton) {
1011  d->pressedPosition = event->pos();
1012  d->pressedPosition.rx() += horizontalOffset();
1013  d->pressedPosition.ry() += verticalOffset();
1014  }
1015  if (!d->categoryDrawerV2) {
1016  QListView::mousePressEvent(event);
1017  return;
1018  }
1019  QHash<QString, Private::Block>::ConstIterator it(d->blocks.constBegin());
1020  while (it != d->blocks.constEnd()) {
1021  const Private::Block &block = *it;
1022  const QModelIndex categoryIndex = d->proxyModel->index(block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
1023  const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex);
1024  const QPoint mousePos = viewport()->mapFromGlobal(QCursor::pos());
1025  if (option.rect.contains(mousePos)) {
1026  if (d->categoryDrawerV3) {
1027  d->categoryDrawerV3->mouseButtonPressed(categoryIndex, option.rect, event);
1028  } else {
1029  d->categoryDrawerV2->mouseButtonPressed(categoryIndex, event);
1030  }
1031  viewport()->update(option.rect);
1032  if (!event->isAccepted()) {
1033  QListView::mousePressEvent(event);
1034  }
1035  return;
1036  }
1037  ++it;
1038  }
1039  QListView::mousePressEvent(event);
1040 }
1041 
1042 void KCategorizedView::mouseReleaseEvent(QMouseEvent *event)
1043 {
1044  d->pressedPosition = QPoint();
1045  d->rubberBandRect = QRect();
1046  if (!d->categoryDrawerV2) {
1047  QListView::mouseReleaseEvent(event);
1048  return;
1049  }
1050  QHash<QString, Private::Block>::ConstIterator it(d->blocks.constBegin());
1051  while (it != d->blocks.constEnd()) {
1052  const Private::Block &block = *it;
1053  const QModelIndex categoryIndex = d->proxyModel->index(block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
1054  const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex);
1055  const QPoint mousePos = viewport()->mapFromGlobal(QCursor::pos());
1056  if (option.rect.contains(mousePos)) {
1057  if (d->categoryDrawerV3) {
1058  d->categoryDrawerV3->mouseButtonReleased(categoryIndex, option.rect, event);
1059  } else {
1060  d->categoryDrawerV2->mouseButtonReleased(categoryIndex, event);
1061  }
1062  viewport()->update(option.rect);
1063  if (!event->isAccepted()) {
1064  QListView::mouseReleaseEvent(event);
1065  }
1066  return;
1067  }
1068  ++it;
1069  }
1070  QListView::mouseReleaseEvent(event);
1071 }
1072 
1073 void KCategorizedView::leaveEvent(QEvent *event)
1074 {
1075  QListView::leaveEvent(event);
1076  if (d->hoveredIndex.isValid()) {
1077  viewport()->update(visualRect(d->hoveredIndex));
1078  d->hoveredIndex = QModelIndex();
1079  }
1080  if (d->categoryDrawerV3 && d->hoveredBlock->height != -1) {
1081  const QModelIndex categoryIndex = d->proxyModel->index(d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
1082  const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex);
1083  d->categoryDrawerV3->mouseLeft(categoryIndex, option.rect);
1084  *d->hoveredBlock = Private::Block();
1085  d->hoveredCategory = QString();
1086  viewport()->update(option.rect);
1087  }
1088 }
1089 
1090 void KCategorizedView::startDrag(Qt::DropActions supportedActions)
1091 {
1092  QListView::startDrag(supportedActions);
1093 }
1094 
1095 void KCategorizedView::dragMoveEvent(QDragMoveEvent *event)
1096 {
1097  QListView::dragMoveEvent(event);
1098  d->hoveredIndex = indexAt(event->pos());
1099 }
1100 
1101 void KCategorizedView::dragEnterEvent(QDragEnterEvent *event)
1102 {
1103  QListView::dragEnterEvent(event);
1104 }
1105 
1106 void KCategorizedView::dragLeaveEvent(QDragLeaveEvent *event)
1107 {
1108  QListView::dragLeaveEvent(event);
1109 }
1110 
1111 void KCategorizedView::dropEvent(QDropEvent *event)
1112 {
1113  QListView::dropEvent(event);
1114 }
1115 
1116 //TODO: improve se we take into account collapsed blocks
1117 //TODO: take into account when there is no grid and no uniformItemSizes
1118 QModelIndex KCategorizedView::moveCursor(CursorAction cursorAction,
1119  Qt::KeyboardModifiers modifiers)
1120 {
1121  if (!d->isCategorized()) {
1122  return QListView::moveCursor(cursorAction, modifiers);
1123  }
1124 
1125  const QModelIndex current = currentIndex();
1126  const QRect currentRect = visualRect(current);
1127  if (!current.isValid()) {
1128  const int rowCount = d->proxyModel->rowCount(rootIndex());
1129  if (!rowCount) {
1130  return QModelIndex();
1131  }
1132  return d->proxyModel->index(0, modelColumn(), rootIndex());
1133  }
1134 
1135  switch (cursorAction) {
1136  case MoveLeft: {
1137  if (!current.row()) {
1138  return QModelIndex();
1139  }
1140  const QModelIndex previous = d->proxyModel->index(current.row() - 1, modelColumn(), rootIndex());
1141  const QRect previousRect = visualRect(previous);
1142  if (previousRect.top() == currentRect.top()) {
1143  return previous;
1144  }
1145 
1146  return QModelIndex();
1147  }
1148  case MoveRight: {
1149  if (current.row() == d->proxyModel->rowCount() - 1) {
1150  return QModelIndex();
1151  }
1152  const QModelIndex next = d->proxyModel->index(current.row() + 1, modelColumn(), rootIndex());
1153  const QRect nextRect = visualRect(next);
1154  if (nextRect.top() == currentRect.top()) {
1155  return next;
1156  }
1157 
1158  return QModelIndex();
1159  }
1160  case MoveDown: {
1161  if (d->hasGrid() || uniformItemSizes()) {
1162  const QModelIndex current = currentIndex();
1163  const QSize itemSize = d->hasGrid() ? gridSize()
1164  : sizeHintForIndex(current);
1165  const Private::Block &block = d->blocks[d->categoryForIndex(current)];
1166  const int maxItemsPerRow = qMax(d->viewportWidth() / itemSize.width(), 1);
1167  const bool canMove = current.row() + maxItemsPerRow < block.firstIndex.row() +
1168  block.items.count();
1169 
1170  if (canMove) {
1171  return d->proxyModel->index(current.row() + maxItemsPerRow, modelColumn(), rootIndex());
1172  }
1173 
1174  const int currentRelativePos = (current.row() - block.firstIndex.row()) % maxItemsPerRow;
1175  const QModelIndex nextIndex = d->proxyModel->index(block.firstIndex.row() + block.items.count(), modelColumn(), rootIndex());
1176 
1177  if (!nextIndex.isValid()) {
1178  return QModelIndex();
1179  }
1180 
1181  const Private::Block &nextBlock = d->blocks[d->categoryForIndex(nextIndex)];
1182 
1183  if (nextBlock.items.count() <= currentRelativePos) {
1184  return QModelIndex();
1185  }
1186 
1187  if (currentRelativePos < (block.items.count() % maxItemsPerRow)) {
1188  return d->proxyModel->index(nextBlock.firstIndex.row() + currentRelativePos, modelColumn(), rootIndex());
1189  }
1190 
1191  return QModelIndex();
1192  }
1193  }
1194  case MoveUp: {
1195  if (d->hasGrid() || uniformItemSizes()) {
1196  const QModelIndex current = currentIndex();
1197  const QSize itemSize = d->hasGrid() ? gridSize()
1198  : sizeHintForIndex(current);
1199  const Private::Block &block = d->blocks[d->categoryForIndex(current)];
1200  const int maxItemsPerRow = qMax(d->viewportWidth() / itemSize.width(), 1);
1201  const bool canMove = current.row() - maxItemsPerRow >= block.firstIndex.row();
1202 
1203  if (canMove) {
1204  return d->proxyModel->index(current.row() - maxItemsPerRow, modelColumn(), rootIndex());
1205  }
1206 
1207  const int currentRelativePos = (current.row() - block.firstIndex.row()) % maxItemsPerRow;
1208  const QModelIndex prevIndex = d->proxyModel->index(block.firstIndex.row() - 1, modelColumn(), rootIndex());
1209 
1210  if (!prevIndex.isValid()) {
1211  return QModelIndex();
1212  }
1213 
1214  const Private::Block &prevBlock = d->blocks[d->categoryForIndex(prevIndex)];
1215 
1216  if (prevBlock.items.count() <= currentRelativePos) {
1217  return QModelIndex();
1218  }
1219 
1220  const int remainder = prevBlock.items.count() % maxItemsPerRow;
1221  if (currentRelativePos < remainder) {
1222  return d->proxyModel->index(prevBlock.firstIndex.row() + prevBlock.items.count() - remainder + currentRelativePos, modelColumn(), rootIndex());
1223  }
1224 
1225  return QModelIndex();
1226  }
1227  }
1228  default:
1229  break;
1230  }
1231 
1232  return QModelIndex();
1233 }
1234 
1235 void KCategorizedView::rowsAboutToBeRemoved(const QModelIndex &parent,
1236  int start,
1237  int end)
1238 {
1239  if (!d->isCategorized()) {
1240  QListView::rowsAboutToBeRemoved(parent, start, end);
1241  return;
1242  }
1243 
1244  *d->hoveredBlock = Private::Block();
1245  d->hoveredCategory = QString();
1246 
1247  if (end - start + 1 == d->proxyModel->rowCount()) {
1248  d->blocks.clear();
1249  QListView::rowsAboutToBeRemoved(parent, start, end);
1250  return;
1251  }
1252 
1253  // Removal feels a bit more complicated than insertion. Basically we can consider there are
1254  // 3 different cases when going to remove items. (*) represents an item, Items between ([) and
1255  // (]) are the ones which are marked for removal.
1256  //
1257  // - 1st case:
1258  // ... * * * * * * [ * * * ...
1259  //
1260  // The items marked for removal are the last part of this category. No need to mark any item
1261  // of this category as in quarantine, because no special offset will be pushed to items at
1262  // the right because of any changes (since the removed items are those on the right most part
1263  // of the category).
1264  //
1265  // - 2nd case:
1266  // ... * * * * * * ] * * * ...
1267  //
1268  // The items marked for removal are the first part of this category. We have to mark as in
1269  // quarantine all items in this category. Absolutely all. All items will have to be moved to
1270  // the left (or moving up, because rows got a different offset).
1271  //
1272  // - 3rd case:
1273  // ... * * [ * * * * ] * * ...
1274  //
1275  // The items marked for removal are in between of this category. We have to mark as in
1276  // quarantine only those items that are at the right of the end of the removal interval,
1277  // (starting on "]").
1278  //
1279  // It hasn't been explicitly said, but when we remove, we have to mark all blocks that are
1280  // located under the top most affected category as in quarantine (the block itself, as a whole),
1281  // because such a change can force it to have a different offset (note that items themselves
1282  // contain relative positions to the block, so marking the block as in quarantine is enough).
1283  //
1284  // Also note that removal implicitly means that we have to update correctly firstIndex of each
1285  // block, and in general keep updated the internal information of elements.
1286 
1287  QStringList listOfCategoriesMarkedForRemoval;
1288 
1289  QString lastCategory;
1290  int alreadyRemoved = 0;
1291  for (int i = start; i <= end; ++i) {
1292  const QModelIndex index = d->proxyModel->index(i, modelColumn(), parent);
1293 
1294  Q_ASSERT(index.isValid());
1295 
1296  const QString category = d->categoryForIndex(index);
1297 
1298  if (lastCategory != category) {
1299  lastCategory = category;
1300  alreadyRemoved = 0;
1301  }
1302 
1303  Private::Block &block = d->blocks[category];
1304  block.items.removeAt(i - block.firstIndex.row() - alreadyRemoved);
1305  ++alreadyRemoved;
1306 
1307  if (!block.items.count()) {
1308  listOfCategoriesMarkedForRemoval << category;
1309  }
1310 
1311  block.height = -1;
1312 
1313  viewport()->update();
1314  }
1315 
1316  //BEGIN: update the items that are in quarantine in affected categories
1317  {
1318  const QModelIndex lastIndex = d->proxyModel->index(end, modelColumn(), parent);
1319  const QString category = d->categoryForIndex(lastIndex);
1320  Private::Block &block = d->blocks[category];
1321  if (block.items.count() && start <= block.firstIndex.row() && end >= block.firstIndex.row()) {
1322  block.firstIndex = d->proxyModel->index(end + 1, modelColumn(), parent);
1323  }
1324  block.quarantineStart = block.firstIndex;
1325  }
1326  //END: update the items that are in quarantine in affected categories
1327 
1328  Q_FOREACH (const QString &category, listOfCategoriesMarkedForRemoval) {
1329  d->blocks.remove(category);
1330  }
1331 
1332  //BEGIN: mark as in quarantine those categories that are under the affected ones
1333  {
1334  //BEGIN: order for marking as alternate those blocks that are alternate
1335  QList<Private::Block> blockList = d->blocks.values();
1336  qSort(blockList.begin(), blockList.end(), Private::Block::lessThan);
1337  QList<int> firstIndexesRows;
1338  foreach (const Private::Block &block, blockList) {
1339  firstIndexesRows << block.firstIndex.row();
1340  }
1341  //END: order for marking as alternate those blocks that are alternate
1342  for (QHash<QString, Private::Block>::Iterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
1343  Private::Block &block = *it;
1344  if (block.firstIndex.row() > start) {
1345  block.outOfQuarantine = false;
1346  block.alternate = firstIndexesRows.indexOf(block.firstIndex.row()) % 2;
1347  } else if (block.firstIndex.row() == start) {
1348  block.alternate = firstIndexesRows.indexOf(block.firstIndex.row()) % 2;
1349  }
1350  }
1351  }
1352  //END: mark as in quarantine those categories that are under the affected ones
1353 
1354  QListView::rowsAboutToBeRemoved(parent, start, end);
1355 }
1356 
1357 void KCategorizedView::updateGeometries()
1358 {
1359  const int oldVerticalOffset = verticalOffset();
1360  const Qt::ScrollBarPolicy verticalP = verticalScrollBarPolicy(), horizontalP = horizontalScrollBarPolicy();
1361 
1362  //BEGIN bugs 213068, 287847 ------------------------------------------------------------
1363  /*
1364  * QListView::updateGeometries() has it's own opinion on whether the scrollbars should be visible (valid range) or not
1365  * and triggers a (sometimes additionally timered) resize through ::layoutChildren()
1366  * http://qt.gitorious.org/qt/qt/blobs/4.7/src/gui/itemviews/qlistview.cpp#line1499
1367  * (the comment above the main block isn't all accurate, layoutChldren is called regardless of the policy)
1368  *
1369  * As a result QListView and KCategorizedView occasionally started a race on the scrollbar visibility, effectively blocking the UI
1370  * So we prevent QListView from having an own opinion on the scrollbar visibility by
1371  * fixing it before calling the baseclass QListView::updateGeometries()
1372  *
1373  * Since the implicit show/hide by the followin range setting will cause further resizes if the policy is Qt::ScrollBarAsNeeded
1374  * we keep it static until we're done, then restore the original value and ultimately change the scrollbar visibility ourself.
1375  */
1376  if (d->isCategorized()) { // important! - otherwise we'd pollute the setting if the view is initially not categorized
1377  setVerticalScrollBarPolicy((verticalP == Qt::ScrollBarAlwaysOn || verticalScrollBar()->isVisibleTo(this)) ?
1378  Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
1379  setHorizontalScrollBarPolicy((horizontalP == Qt::ScrollBarAlwaysOn || horizontalScrollBar()->isVisibleTo(this)) ?
1380  Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
1381  }
1382  //END bugs 213068, 287847 --------------------------------------------------------------
1383 
1384  QListView::updateGeometries();
1385 
1386  if (!d->isCategorized()) {
1387  return;
1388  }
1389 
1390  const int rowCount = d->proxyModel->rowCount();
1391  if (!rowCount) {
1392  verticalScrollBar()->setRange(0, 0);
1393  // unconditional, see function end todo
1394  horizontalScrollBar()->setRange(0, 0);
1395  return;
1396  }
1397 
1398  const QModelIndex lastIndex = d->proxyModel->index(rowCount - 1, modelColumn(), rootIndex());
1399  Q_ASSERT(lastIndex.isValid());
1400  QRect lastItemRect = visualRect(lastIndex);
1401 
1402  if (d->hasGrid()) {
1403  lastItemRect.setSize(lastItemRect.size().expandedTo(gridSize()));
1404  } else {
1405  if (uniformItemSizes()) {
1406  QSize itemSize = sizeHintForIndex(lastIndex);
1407  itemSize.setHeight(itemSize.height() + spacing());
1408  lastItemRect.setSize(itemSize);
1409  } else {
1410  QSize itemSize = sizeHintForIndex(lastIndex);
1411  const QString category = d->categoryForIndex(lastIndex);
1412  itemSize.setHeight(d->highestElementInLastRow(d->blocks[category]) + spacing());
1413  lastItemRect.setSize(itemSize);
1414  }
1415  }
1416 
1417  const int bottomRange = lastItemRect.bottomRight().y() + verticalOffset() - viewport()->height();
1418 
1419  if (verticalScrollMode() == ScrollPerItem) {
1420  verticalScrollBar()->setSingleStep(lastItemRect.height());
1421  const int rowsPerPage = qMax(viewport()->height() / lastItemRect.height(), 1);
1422  verticalScrollBar()->setPageStep(rowsPerPage * lastItemRect.height());
1423  }
1424 
1425  verticalScrollBar()->setRange(0, bottomRange);
1426  verticalScrollBar()->setValue(oldVerticalOffset);
1427 
1428  //TODO: also consider working with the horizontal scroll bar. since at this level I am not still
1429  // supporting "top to bottom" flow, there is no real problem. If I support that someday
1430  // (think how to draw categories), we would have to take care of the horizontal scroll bar too.
1431  // In theory, as KCategorizedView has been designed, there is no need of horizontal scroll bar.
1432  horizontalScrollBar()->setRange(0, 0);
1433 
1434  //BEGIN bugs 213068, 287847 ------------------------------------------------------------
1435  // restoring values from above ...
1436  setVerticalScrollBarPolicy(verticalP);
1437  setHorizontalScrollBarPolicy(horizontalP);
1438  // ... and correct the visibility
1439  bool validRange = verticalScrollBar()->maximum() != verticalScrollBar()->minimum();
1440  if (verticalP == Qt::ScrollBarAsNeeded && (verticalScrollBar()->isVisibleTo(this) != validRange))
1441  verticalScrollBar()->setVisible(validRange);
1442  validRange = horizontalScrollBar()->maximum() > horizontalScrollBar()->minimum();
1443  if (horizontalP == Qt::ScrollBarAsNeeded && (horizontalScrollBar()->isVisibleTo(this) != validRange))
1444  horizontalScrollBar()->setVisible(validRange);
1445  //END bugs 213068, 287847 --------------------------------------------------------------
1446 }
1447 
1448 void KCategorizedView::currentChanged(const QModelIndex &current,
1449  const QModelIndex &previous)
1450 {
1451  QListView::currentChanged(current, previous);
1452 }
1453 
1454 void KCategorizedView::dataChanged(const QModelIndex &topLeft,
1455  const QModelIndex &bottomRight)
1456 {
1457  QListView::dataChanged(topLeft, bottomRight);
1458  if (!d->isCategorized()) {
1459  return;
1460  }
1461 
1462  *d->hoveredBlock = Private::Block();
1463  d->hoveredCategory = QString();
1464 
1465  //BEGIN: since the model changed data, we need to reconsider item sizes
1466  int i = topLeft.row();
1467  int indexToCheck = i;
1468  QModelIndex categoryIndex;
1469  QString category;
1470  Private::Block *block;
1471  while (i <= bottomRight.row()) {
1472  const QModelIndex currIndex = d->proxyModel->index(i, modelColumn(), rootIndex());
1473  if (i == indexToCheck) {
1474  categoryIndex = d->proxyModel->index(i, d->proxyModel->sortColumn(), rootIndex());
1475  category = categoryIndex.data(KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
1476  block = &d->blocks[category];
1477  block->quarantineStart = currIndex;
1478  indexToCheck = block->firstIndex.row() + block->items.count();
1479  }
1480  visualRect(currIndex);
1481  ++i;
1482  }
1483  //END: since the model changed data, we need to reconsider item sizes
1484 }
1485 
1486 void KCategorizedView::rowsInserted(const QModelIndex &parent,
1487  int start,
1488  int end)
1489 {
1490  QListView::rowsInserted(parent, start, end);
1491  if (!d->isCategorized()) {
1492  return;
1493  }
1494 
1495  *d->hoveredBlock = Private::Block();
1496  d->hoveredCategory = QString();
1497  d->rowsInserted(parent, start, end);
1498 }
1499 
1500 #ifndef KDE_NO_DEPRECATED
1501 void KCategorizedView::rowsInsertedArtifficial(const QModelIndex &parent,
1502  int start,
1503  int end)
1504 {
1505  Q_UNUSED(parent);
1506  Q_UNUSED(start);
1507  Q_UNUSED(end);
1508 }
1509 #endif
1510 
1511 #ifndef KDE_NO_DEPRECATED
1512 void KCategorizedView::rowsRemoved(const QModelIndex &parent,
1513  int start,
1514  int end)
1515 {
1516  Q_UNUSED(parent);
1517  Q_UNUSED(start);
1518  Q_UNUSED(end);
1519 }
1520 #endif
1521 
1522 void KCategorizedView::slotLayoutChanged()
1523 {
1524  if (!d->isCategorized()) {
1525  return;
1526  }
1527 
1528  d->blocks.clear();
1529  *d->hoveredBlock = Private::Block();
1530  d->hoveredCategory = QString();
1531  if (d->proxyModel->rowCount()) {
1532  d->rowsInserted(rootIndex(), 0, d->proxyModel->rowCount() - 1);
1533  }
1534 }
1535 
1536 //END: Public part
1537 
1538 #include "kcategorizedview.moc"
kcategorizedview.h
KCategorizedView::leaveEvent
virtual void leaveEvent(QEvent *event)
Reimplemented from QWidget.
Definition: kcategorizedview.cpp:1073
QModelIndex
kcategorizedsortfilterproxymodel.h
QSize::boundedTo
QSize boundedTo(const QSize &otherSize) const
QEvent
QResizeEvent
QWidget
QAbstractItemModel::rowCount
virtual int rowCount(const QModelIndex &parent) const =0
QSize::isValid
bool isValid() const
QSize::setHeight
void setHeight(int height)
QAbstractItemView::reset
virtual void reset()
QPoint::rx
int & rx()
QPoint::ry
int & ry()
KCategorizedView::mouseMoveEvent
virtual void mouseMoveEvent(QMouseEvent *event)
Reimplemented from QWidget.
Definition: kcategorizedview.cpp:949
QListView::dataChanged
virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
KCategorizedView::currentChanged
virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous)
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:1448
QRect::normalized
QRect normalized() const
QRect::topRight
QPoint topRight() const
QAbstractItemModel::index
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const =0
QAbstractItemView::alternatingRowColors
bool alternatingRowColors() const
QSize::width
int width() const
KCategorizedView::indexAt
virtual QModelIndex indexAt(const QPoint &point) const
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:716
QItemSelectionRange
QListView::startDrag
virtual void startDrag(QFlags< Qt::DropAction > supportedActions)
KCategorizedView::categorySpacing
int categorySpacing() const
QAbstractItemView::selectionMode
QAbstractItemView::SelectionMode selectionMode() const
KCategorizedView::rowsRemoved
virtual void rowsRemoved(const QModelIndex &parent, int start, int end)
Definition: kcategorizedview.cpp:1512
KCategorizedView::resizeEvent
virtual void resizeEvent(QResizeEvent *event)
Reimplemented from QWidget.
Definition: kcategorizedview.cpp:899
KCategorizedView::alternatingBlockColors
bool alternatingBlockColors() const
QAbstractSlider::setRange
void setRange(int min, int max)
QAbstractItemView::selectionModel
QItemSelectionModel * selectionModel() const
KCategorizedView::rowsInsertedArtifficial
virtual void rowsInsertedArtifficial(const QModelIndex &parent, int start, int end)
Definition: kcategorizedview.cpp:1501
KCategorizedView::updateGeometries
virtual void updateGeometries()
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:1357
QDragMoveEvent
KCategorizedView::dragEnterEvent
virtual void dragEnterEvent(QDragEnterEvent *event)
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:1101
QWidget::style
QStyle * style() const
QAbstractItemView::setModel
virtual void setModel(QAbstractItemModel *model)
QListView::verticalOffset
virtual int verticalOffset() const
KCategorizedView::collapsibleBlocks
bool collapsibleBlocks() const
KCategorizedView::setCollapsibleBlocks
void setCollapsibleBlocks(bool enable)
Sets whether blocks can be collapsed or not.
Definition: kcategorizedview.cpp:688
QListView::mouseReleaseEvent
virtual void mouseReleaseEvent(QMouseEvent *e)
QRect::bottomRight
QPoint bottomRight() const
QDropEvent::pos
const QPoint & pos() const
QListView::moveCursor
virtual QModelIndex moveCursor(CursorAction cursorAction, QFlags< Qt::KeyboardModifier > modifiers)
KCategorizedSortFilterProxyModel
This class lets you categorize a view.
Definition: kcategorizedsortfilterproxymodel.h:45
QSize::isNull
bool isNull() const
KCategorizedView::dragLeaveEvent
virtual void dragLeaveEvent(QDragLeaveEvent *event)
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:1106
QWidget::setVisible
virtual void setVisible(bool visible)
QRect::intersects
bool intersects(const QRect &rectangle) const
QPainter::save
void save()
QRect::bottomLeft
QPoint bottomLeft() const
QAbstractScrollArea::viewport
QWidget * viewport() const
QRect::height
int height() const
QAbstractSlider::setPageStep
void setPageStep(int)
QPoint
QAbstractItemView::verticalScrollMode
ScrollMode verticalScrollMode() const
operator!=
bool operator!=(const KEntry &k1, const KEntry &k2)
QMouseEvent
QListView::updateGeometries
virtual void updateGeometries()
QListView::wordWrap
bool wordWrap() const
KCategorizedView::slotLayoutChanged
virtual void slotLayoutChanged()
Definition: kcategorizedview.cpp:1522
QObject::disconnect
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
KCategorizedView::setGridSizeOwn
void setGridSizeOwn(const QSize &size)
Definition: kcategorizedview.cpp:545
QWidget::update
void update()
QAbstractScrollArea::sizeHint
virtual QSize sizeHint() const
QPoint::x
int x() const
QPoint::y
int y() const
KCategorizedView::setCategoryDrawer
void setCategoryDrawer(KCategoryDrawer *categoryDrawer)
The category drawer that will be used for drawing categories.
Definition: kcategorizedview.cpp:637
QWidget::isVisibleTo
bool isVisibleTo(QWidget *ancestor) const
QList::indexOf
int indexOf(const T &value, int from) const
QListView
QStyleOption::initFrom
void initFrom(const QWidget *widget)
QWidget::size
QSize size() const
QPaintEvent::rect
const QRect & rect() const
KCategoryDrawer
Definition: kcategorydrawer.h:43
QRect
QModelIndex::isValid
bool isValid() const
QEvent::isAccepted
bool isAccepted() const
QListView::rowsInserted
virtual void rowsInserted(const QModelIndex &parent, int start, int end)
KCategorizedView
Item view for listing items in a categorized fashion optionally.
Definition: kcategorizedview.h:79
QWidget::layoutDirection
Qt::LayoutDirection layoutDirection() const
QHash
QListView::modelColumn
int modelColumn() const
QRect::top
int top() const
KCategorizedView::categoryDrawer
KCategoryDrawer * categoryDrawer() const
Returns the current category drawer.
Definition: kcategorizedview.cpp:632
KCategorizedView::setGridSize
void setGridSize(const QSize &size)
Calls to setGridSizeOwn().
Definition: kcategorizedview.cpp:540
QListView::rowsAboutToBeRemoved
virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
QStyleOptionRubberBand
QStyle
QMouseEvent::button
Qt::MouseButton button() const
KCategorizedView::dataChanged
virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:1454
QDropEvent
QAbstractScrollArea::horizontalScrollBarPolicy
Qt::ScrollBarPolicy horizontalScrollBarPolicy() const
QPainter
QItemSelectionModel::select
virtual void select(const QModelIndex &index, QFlags< QItemSelectionModel::SelectionFlag > command)
KCategorizedView::rowsAboutToBeRemoved
virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:1235
KCategorizedView::~KCategorizedView
~KCategorizedView()
Definition: kcategorizedview.cpp:509
QModelIndex::row
int row() const
QHash::begin
iterator begin()
QAbstractItemView::itemDelegate
QAbstractItemDelegate * itemDelegate() const
QListView::visualRect
virtual QRect visualRect(const QModelIndex &index) const
KCategorizedView::visualRect
virtual QRect visualRect(const QModelIndex &index) const
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:551
KCategoryDrawerV3
Definition: kcategorydrawer.h:150
QListView::horizontalOffset
virtual int horizontalOffset() const
KCategorizedView::reset
virtual void reset()
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:782
QAbstractItemView::dragEnterEvent
virtual void dragEnterEvent(QDragEnterEvent *event)
QRect::contains
bool contains(const QPoint &point, bool proper) const
QString
QList
QAbstractSlider::minimum
minimum
QModelIndex::parent
QModelIndex parent() const
QAbstractItemView::sizeHintForIndex
QSize sizeHintForIndex(const QModelIndex &index) const
QAbstractScrollArea::verticalScrollBar
QScrollBar * verticalScrollBar() const
KCategorizedView::setSelection
virtual void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags)
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:905
QStringList
KCategorizedView::setModel
virtual void setModel(QAbstractItemModel *model)
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:514
QWidget::rect
QRect rect() const
KStandardAction::next
KAction * next(const QObject *recvr, const char *slot, QObject *parent)
Scroll down one page.
Definition: kstandardaction.cpp:414
KCategorizedView::KCategorizedView
KCategorizedView(QWidget *parent=0)
Definition: kcategorizedview.cpp:503
QPair
QRect::adjusted
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const
QAbstractItemDelegate::paint
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const =0
QList::end
iterator end()
QListView::uniformItemSizes
bool uniformItemSizes() const
QSize
QListView::paintEvent
virtual void paintEvent(QPaintEvent *e)
QAbstractSlider::setValue
void setValue(int)
lessThan
bool lessThan(const QString &left, const QString &right)
Definition: kcompletion.cpp:812
KCategorizedView::rowsInserted
virtual void rowsInserted(const QModelIndex &parent, int start, int end)
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:1486
QAbstractItemView::rootIndex
QModelIndex rootIndex() const
QDragLeaveEvent
QListView::viewOptions
virtual QStyleOptionViewItem viewOptions() const
kcategorydrawer.h
QPainter::restore
void restore()
QAbstractItemView::state
State state() const
QListView::spacing
int spacing() const
QListView::isSelectionRectVisible
bool isSelectionRectVisible() const
QListView::dragMoveEvent
virtual void dragMoveEvent(QDragMoveEvent *e)
QListView::resizeEvent
virtual void resizeEvent(QResizeEvent *e)
QItemSelection
QListView::dropEvent
virtual void dropEvent(QDropEvent *e)
QPersistentModelIndex
QListView::indexAt
virtual QModelIndex indexAt(const QPoint &p) const
QModelIndex::model
const QAbstractItemModel * model() const
QListView::dragLeaveEvent
virtual void dragLeaveEvent(QDragLeaveEvent *e)
QListView::mouseMoveEvent
virtual void mouseMoveEvent(QMouseEvent *e)
QCursor::pos
QPoint pos()
KCategorizedView::block
QModelIndexList block(const QString &category)
Definition: kcategorizedview.cpp:693
QDragEnterEvent
KCategoryDrawerV2
Definition: kcategorydrawer.h:113
QModelIndex::data
QVariant data(int role) const
KCategorizedView::dropEvent
virtual void dropEvent(QDropEvent *event)
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:1111
QAbstractSlider::setSingleStep
void setSingleStep(int)
Qt::DropActions
typedef DropActions
QAbstractItemView::mousePressEvent
virtual void mousePressEvent(QMouseEvent *event)
QListView::setSelection
virtual void setSelection(const QRect &rect, QFlags< QItemSelectionModel::SelectionFlag > command)
KCategorizedView::setAlternatingBlockColors
void setAlternatingBlockColors(bool enable)
Sets whether blocks should be drawn with alternating colors.
Definition: kcategorizedview.cpp:678
QRect::setHeight
void setHeight(int height)
QItemSelectionModel::isSelected
bool isSelected(const QModelIndex &index) const
QStyle::drawControl
virtual void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const =0
QWidget::mapFromGlobal
QPoint mapFromGlobal(const QPoint &pos) const
QSize::height
int height() const
QRect::topLeft
QPoint topLeft() const
QAbstractItemModel
QAbstractScrollArea::horizontalScrollBar
QScrollBar * horizontalScrollBar() const
Item
QAbstractItemModel::flags
virtual Qt::ItemFlags flags(const QModelIndex &index) const
QListView::setGridSize
void setGridSize(const QSize &size)
KCategorizedView::mouseReleaseEvent
virtual void mouseReleaseEvent(QMouseEvent *event)
Reimplemented from QWidget.
Definition: kcategorizedview.cpp:1042
QMouseEvent::pos
const QPoint & pos() const
QStyleOptionViewItemV4
QPaintEvent
KCategorizedView::setCategorySpacing
void setCategorySpacing(int categorySpacing)
Stablishes the category spacing.
Definition: kcategorizedview.cpp:659
QAbstractItemView::model
QAbstractItemModel * model() const
KCategorizedSortFilterProxyModel::CategoryDisplayRole
This role is used for asking the category to a given index.
Definition: kcategorizedsortfilterproxymodel.h:52
QAbstractItemView::currentIndex
QModelIndex currentIndex() const
KCategorizedView::moveCursor
virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:1118
KCategorizedView::mousePressEvent
virtual void mousePressEvent(QMouseEvent *event)
Reimplemented from QWidget.
Definition: kcategorizedview.cpp:1008
QAbstractSlider::maximum
maximum
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QListView::flow
Flow flow() const
QWidget::leaveEvent
virtual void leaveEvent(QEvent *event)
KCategorizedView::paintEvent
virtual void paintEvent(QPaintEvent *event)
Reimplemented from QWidget.
Definition: kcategorizedview.cpp:788
QItemSelectionModel::SelectionFlags
typedef SelectionFlags
KStandardShortcut::end
const KShortcut & end()
Goto end of the document.
Definition: kstandardshortcut.cpp:348
QVariant::toString
QString toString() const
QAbstractScrollArea::verticalScrollBarPolicy
Qt::ScrollBarPolicy verticalScrollBarPolicy() const
QRect::setLeft
void setLeft(int x)
QListView::currentChanged
virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous)
QList::begin
iterator begin()
KCategorizedView::startDrag
virtual void startDrag(Qt::DropActions supportedActions)
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:1090
KCategorizedView::dragMoveEvent
virtual void dragMoveEvent(QDragMoveEvent *event)
Reimplemented from QAbstractItemView.
Definition: kcategorizedview.cpp:1095
QWidget::height
int height() const
Qt::KeyboardModifiers
typedef KeyboardModifiers
Qt::ItemFlags
typedef ItemFlags
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:23:58 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal