2 * This file is part of the KDE project
3 * Copyright (C) 2007 Rafael Fernández López <ereslibre@gmail.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
21 // NOTE: rectForIndex() not virtual on QListView !! relevant ?
25 #include <QPaintEvent>
26 #include <QSortFilterProxyModel>
30 #include "klistview.h"
31 #include "klistview_p.h"
32 #include "kitemcategorizer.h"
34 KListView::Private::Private(KListView
*listView
)
36 , modelSortCapable(false)
43 KListView::Private::~Private()
47 QModelIndexList
KListView::Private::intersectionSet(const QRect
&rect
) const
49 // FIXME: boost me, I suck (ereslibre)
51 QModelIndexList modelIndexList
;
54 for (int i
= 0; i
< listView
->model()->rowCount(); i
++)
56 index
= listView
->model()->index(i
, 0);
58 if (rect
.intersects(listView
->visualRect(index
)))
59 modelIndexList
.append(index
);
62 return modelIndexList
;
65 KListView::KListView(QWidget
*parent
)
67 , d(new Private(this))
71 KListView::~KListView()
75 QObject::disconnect(this->model(), SIGNAL(layoutChanged()),
76 this , SLOT(itemsLayoutChanged()));
82 void KListView::setModel(QAbstractItemModel
*model
)
84 QSortFilterProxyModel
*proxyModel
=
85 qobject_cast
<QSortFilterProxyModel
*>(model
);
87 if (this->model() && this->model()->rowCount())
89 QObject::disconnect(this->model(), SIGNAL(layoutChanged()),
90 this , SLOT(itemsLayoutChanged()));
92 rowsAboutToBeRemovedArtifficial(QModelIndex(), 0,
93 this->model()->rowCount() - 1);
96 d
->modelSortCapable
= (proxyModel
!= 0);
97 d
->proxyModel
= proxyModel
;
99 // If the model was initialized before applying to the view, we update
100 // internal data structure of the view with the model information
101 if (model
->rowCount())
103 rowsInsertedArtifficial(QModelIndex(), 0, model
->rowCount() - 1);
106 QListView::setModel(model
);
108 QObject::connect(model
, SIGNAL(layoutChanged()),
109 this , SLOT(itemsLayoutChanged()));
112 QRect
KListView::visualRect(const QModelIndex
&index
) const
114 // FIXME: right to left languages (ereslibre)
115 // FIXME: drag & drop support (ereslibre)
116 // FIXME: do not forget to remove itemWidth's hard-coded values that were
117 // only for testing purposes. We might like to calculate the best
118 // width, but what we would really like for sure is that all items
119 // have the same width, as well as the same height (ereslibre)
121 if ((viewMode() == KListView::ListMode
) || !d
->modelSortCapable
||
124 return QListView::visualRect(index
);
127 QRect
retRect(spacing(), spacing(), 0, 0);
128 int viewportWidth
= viewport()->width() - spacing();
129 int dx
= -horizontalOffset();
130 int dy
= -verticalOffset();
132 if (verticalScrollBar() && !verticalScrollBar()->isHidden())
133 viewportWidth
-= verticalScrollBar()->width();
135 int itemHeight
= sizeHintForIndex(index
).height();
136 int itemWidth
= 130; // NOTE: ghosts in here !
137 int itemWidthPlusSeparation
= spacing() + itemWidth
;
138 int elementsPerRow
= viewportWidth
/ itemWidthPlusSeparation
;
141 QModelIndex currentIndex
= d
->proxyModel
->index(index
.row(), 0);
142 QString itemCategory
= d
->itemCategorizer
->categoryForItem(currentIndex
,
143 d
->proxyModel
->sortRole());
144 int naturalRow
= index
.row() / elementsPerRow
;
145 int naturalTop
= naturalRow
* itemHeight
+ naturalRow
* spacing();
148 int lastIndexShown
= -1;
149 foreach (QString category
, d
->categories
)
151 retRect
.setTop(retRect
.top() + spacing());
153 if (category
== itemCategory
)
158 rowsForCategory
= (d
->elementsPerCategory
[category
] / elementsPerRow
);
160 if ((d
->elementsPerCategory
[category
] % elementsPerRow
) ||
166 lastIndexShown
+= d
->elementsPerCategory
[category
];
168 retRect
.setTop(retRect
.top() + categoryHeight(viewOptions()) +
169 (rowsForCategory
* spacing() * 2) +
170 (rowsForCategory
* itemHeight
));
173 int rowToPosition
= (index
.row() - (lastIndexShown
+ 1)) / elementsPerRow
;
174 int columnToPosition
= (index
.row() - (lastIndexShown
+ 1)) %
177 retRect
.setTop(retRect
.top() + (rowToPosition
* spacing() * 2) +
178 (rowToPosition
* itemHeight
));
180 retRect
.setLeft(retRect
.left() + (columnToPosition
* spacing()) +
181 (columnToPosition
* itemWidth
));
183 retRect
.setWidth(130); // NOTE: ghosts in here !
184 retRect
.setHeight(itemHeight
);
186 retRect
.adjust(dx
, dy
, dx
, dy
);
191 KItemCategorizer
*KListView::itemCategorizer() const
193 return d
->itemCategorizer
;
196 void KListView::setItemCategorizer(KItemCategorizer
*itemCategorizer
)
198 d
->itemCategorizer
= itemCategorizer
;
201 itemsLayoutChanged();
204 QModelIndex
KListView::indexAt(const QPoint
&point
) const
208 if ((viewMode() == KListView::ListMode
) || !d
->modelSortCapable
||
211 return QListView::indexAt(point
);
214 QModelIndexList item
= d
->intersectionSet(QRect(point
, point
));
216 if (item
.count() == 1)
224 int KListView::sizeHintForRow(int row
) const
226 if ((viewMode() == KListView::ListMode
) || !d
->modelSortCapable
||
229 return QListView::sizeHintForRow(row
);
232 QModelIndex index
= d
->proxyModel
->index(0, 0);
234 if (!index
.isValid())
237 return sizeHintForIndex(index
).height() + categoryHeight(viewOptions()) +
241 void KListView::drawNewCategory(const QString
&category
,
242 const QStyleOptionViewItem
&option
,
245 painter
->drawText(option
.rect
.topLeft(), category
);
248 int KListView::categoryHeight(const QStyleOptionViewItem
&option
) const
250 return option
.fontMetrics
.height();
253 void KListView::paintEvent(QPaintEvent
*event
)
255 if ((viewMode() == KListView::ListMode
) || !d
->modelSortCapable
||
258 QListView::paintEvent(event
);
265 QStyleOptionViewItemV3 option
= viewOptions();
266 QPainter
painter(viewport());
267 QRect area
= event
->rect();
268 const bool focus
= (hasFocus() || viewport()->hasFocus()) &&
269 currentIndex().isValid();
270 const QStyle::State state
= option
.state
;
271 const bool enabled
= (state
& QStyle::State_Enabled
) != 0;
275 QString prevCategory
;
276 QModelIndexList dirtyIndexes
= d
->intersectionSet(area
);
277 foreach (const QModelIndex
&index
, dirtyIndexes
)
279 option
.state
= state
;
280 option
.rect
= visualRect(index
);
281 if (selectionModel() && selectionModel()->isSelected(index
))
282 option
.state
|= QStyle::State_Selected
;
285 QPalette::ColorGroup cg
;
286 if ((d
->proxyModel
->flags(index
) & Qt::ItemIsEnabled
) == 0)
288 option
.state
&= ~QStyle::State_Enabled
;
289 cg
= QPalette::Disabled
;
293 cg
= QPalette::Normal
;
295 option
.palette
.setCurrentColorGroup(cg
);
297 if (focus
&& currentIndex() == index
)
299 option
.state
|= QStyle::State_HasFocus
;
300 if (this->state() == EditingState
)
301 option
.state
|= QStyle::State_Editing
;
304 if (index
== d
->hovered
)
305 option
.state
|= QStyle::State_MouseOver
;
307 option
.state
&= ~QStyle::State_MouseOver
;
309 if (prevCategory
!= d
->itemCategorizer
->categoryForItem(index
,
310 d
->proxyModel
->sortRole()))
312 prevCategory
= d
->itemCategorizer
->categoryForItem(index
,
313 d
->proxyModel
->sortRole());
314 drawNewCategory(prevCategory
, option
, &painter
);
316 itemDelegate(index
)->paint(&painter
, option
, index
);
320 void KListView::setSelection(const QRect
&rect
,
321 QItemSelectionModel::SelectionFlags flags
)
323 // TODO: implement me
325 QListView::setSelection(rect
, flags
);
327 /*if ((viewMode() == KListView::ListMode) || !d->modelSortCapable ||
330 QListView::setSelection(rect, flags);
335 for (int i = 0; i < d->proxyModel->rowCount(); i++)
337 index = d->proxyModel->index(i, 0);
338 if (rect.intersects(visualRect(index)))
340 selectionModel()->select(index, QItemSelectionModel::Select);
344 selectionModel()->select(index, QItemSelectionModel::Deselect);
348 //selectionModel()->select(selection, flags);
351 void KListView::timerEvent(QTimerEvent
*event
)
353 QListView::timerEvent(event
);
355 if ((viewMode() == KListView::ListMode
) || !d
->modelSortCapable
||
362 void KListView::rowsInserted(const QModelIndex
&parent
,
366 QListView::rowsInserted(parent
, start
, end
);
367 rowsInsertedArtifficial(parent
, start
, end
);
370 void KListView::rowsAboutToBeRemoved(const QModelIndex
&parent
,
374 QListView::rowsAboutToBeRemoved(parent
, start
, end
);
375 rowsAboutToBeRemovedArtifficial(parent
, start
, end
);
378 void KListView::rowsInsertedArtifficial(const QModelIndex
&parent
,
382 if ((viewMode() == KListView::ListMode
) || !d
->modelSortCapable
||
390 for (int i
= start
; i
<= end
; i
++)
392 index
= d
->proxyModel
->index(i
, 0, parent
);
393 category
= d
->itemCategorizer
->categoryForItem(index
,
394 d
->proxyModel
->sortRole());
396 if (d
->elementsPerCategory
.contains(category
))
397 d
->elementsPerCategory
[category
]++;
400 d
->elementsPerCategory
.insert(category
, 1);
401 d
->categories
.append(category
);
406 void KListView::rowsAboutToBeRemovedArtifficial(const QModelIndex
&parent
,
410 if ((viewMode() == KListView::ListMode
) || !d
->modelSortCapable
||
416 d
->hovered
= QModelIndex();
420 for (int i
= start
; i
<= end
; i
++)
422 index
= d
->proxyModel
->index(i
, 0, parent
);
423 category
= d
->itemCategorizer
->categoryForItem(index
,
424 d
->proxyModel
->sortRole());
426 if (d
->elementsPerCategory
.contains(category
))
428 d
->elementsPerCategory
[category
]--;
430 if (!d
->elementsPerCategory
[category
])
432 d
->elementsPerCategory
.remove(category
);
433 d
->categories
.removeAll(category
);
439 void KListView::itemsLayoutChanged()
441 d
->elementsPerCategory
.clear();
442 d
->categories
.clear();
444 if (d
->proxyModel
&& d
->proxyModel
->rowCount())
445 rowsInsertedArtifficial(QModelIndex(), 0,
446 d
->proxyModel
->rowCount() - 1);
449 #include "klistview.moc"