]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kitemlistheader.cpp
Icon-rectangle and selection-toggle optimizations
[dolphin.git] / src / kitemviews / kitemlistheader.cpp
1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
19
20 #include "kitemlistheader_p.h"
21
22 #include <KAction>
23 #include <KMenu>
24 #include "kitemmodelbase.h"
25
26 #include <QApplication>
27 #include <QGraphicsSceneHoverEvent>
28 #include <QPainter>
29 #include <QStyleOptionHeader>
30
31 #include <KDebug>
32
33 KItemListHeader::KItemListHeader(QGraphicsWidget* parent) :
34 QGraphicsWidget(parent),
35 m_model(0),
36 m_visibleRoles(),
37 m_visibleRolesWidths(),
38 m_hoveredRoleIndex(-1),
39 m_pressedRoleIndex(-1),
40 m_roleOperation(NoRoleOperation),
41 m_pressedMousePos()
42 {
43 setAcceptHoverEvents(true);
44
45 QStyleOptionHeader option;
46 const QSize headerSize = style()->sizeFromContents(QStyle::CT_HeaderSection, &option, QSize());
47 resize(0, headerSize.height());
48 }
49
50 KItemListHeader::~KItemListHeader()
51 {
52 }
53
54 void KItemListHeader::setModel(KItemModelBase* model)
55 {
56 if (m_model == model) {
57 return;
58 }
59
60 if (m_model) {
61 disconnect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
62 this, SLOT(slotSortRoleChanged(QByteArray,QByteArray)));
63 disconnect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
64 this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder)));
65 }
66
67 m_model = model;
68
69 if (m_model) {
70 connect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
71 this, SLOT(slotSortRoleChanged(QByteArray,QByteArray)));
72 connect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
73 this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder)));
74 }
75 }
76
77 KItemModelBase* KItemListHeader::model() const
78 {
79 return m_model;
80 }
81
82 void KItemListHeader::setVisibleRoles(const QList<QByteArray>& roles)
83 {
84 m_visibleRoles = roles;
85 update();
86 }
87
88 QList<QByteArray> KItemListHeader::visibleRoles() const
89 {
90 return m_visibleRoles;
91 }
92
93 void KItemListHeader::setVisibleRolesWidths(const QHash<QByteArray, qreal> rolesWidths)
94 {
95 m_visibleRolesWidths = rolesWidths;
96
97 // Assure that no width is smaller than the minimum allowed width
98 const qreal minWidth = minimumRoleWidth();
99 QMutableHashIterator<QByteArray, qreal> it(m_visibleRolesWidths);
100 while (it.hasNext()) {
101 it.next();
102 if (it.value() < minWidth) {
103 m_visibleRolesWidths.insert(it.key(), minWidth);
104 }
105 }
106
107 update();
108 }
109
110 QHash<QByteArray, qreal> KItemListHeader::visibleRolesWidths() const
111 {
112 return m_visibleRolesWidths;
113 }
114
115 qreal KItemListHeader::minimumRoleWidth() const
116 {
117 QFontMetricsF fontMetrics(font());
118 return fontMetrics.height() * 4;
119 }
120
121 void KItemListHeader::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
122 {
123 Q_UNUSED(option);
124 Q_UNUSED(widget);
125
126 if (!m_model) {
127 return;
128 }
129
130 // Draw roles
131 painter->setFont(font());
132 painter->setPen(palette().text().color());
133
134 qreal x = 0;
135 int orderIndex = 0;
136 foreach (const QByteArray& role, m_visibleRoles) {
137 const qreal roleWidth = m_visibleRolesWidths.value(role);
138 const QRectF rect(x, 0, roleWidth, size().height());
139 paintRole(painter, role, rect, orderIndex);
140 x += roleWidth;
141 ++orderIndex;
142 }
143
144 // Draw background without roles
145 QStyleOption opt;
146 opt.init(widget);
147 opt.rect = QRect(x, 0, size().width() - x, size().height());
148 opt.state |= QStyle::State_Horizontal;
149 style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, painter);
150 }
151
152 void KItemListHeader::mousePressEvent(QGraphicsSceneMouseEvent* event)
153 {
154 if (event->button() & Qt::LeftButton) {
155 updatePressedRoleIndex(event->pos());
156 m_pressedMousePos = event->pos();
157 m_roleOperation = isAboveRoleGrip(m_pressedMousePos, m_pressedRoleIndex) ?
158 ResizeRoleOperation : NoRoleOperation;
159 event->accept();
160 } else {
161 event->ignore();
162 }
163 }
164
165 void KItemListHeader::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
166 {
167 QGraphicsWidget::mouseReleaseEvent(event);
168
169 if (m_pressedRoleIndex == -1) {
170 return;
171 }
172
173 if (m_roleOperation == NoRoleOperation) {
174 // Only a click has been done and no moving or resizing has been started
175 const QByteArray sortRole = m_model->sortRole();
176 const int sortRoleIndex = m_visibleRoles.indexOf(sortRole);
177 if (m_pressedRoleIndex == sortRoleIndex) {
178 // Toggle the sort order
179 const Qt::SortOrder previous = m_model->sortOrder();
180 const Qt::SortOrder current = (m_model->sortOrder() == Qt::AscendingOrder) ?
181 Qt::DescendingOrder : Qt::AscendingOrder;
182 m_model->setSortOrder(current);
183 emit sortOrderChanged(current, previous);
184 } else {
185 // Change the sort role
186 const QByteArray previous = m_model->sortRole();
187 const QByteArray current = m_visibleRoles.at(m_pressedRoleIndex);
188 m_model->setSortRole(current);
189 emit sortRoleChanged(current, previous);
190 }
191 }
192
193 m_pressedRoleIndex = -1;
194 m_roleOperation = NoRoleOperation;
195 update();
196 }
197
198 void KItemListHeader::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
199 {
200 QGraphicsWidget::mouseMoveEvent(event);
201
202 if (m_roleOperation == ResizeRoleOperation) {
203 const QByteArray pressedRole = m_visibleRoles.at(m_pressedRoleIndex);
204
205 qreal previousWidth = m_visibleRolesWidths.value(pressedRole);
206 qreal currentWidth = previousWidth;
207 currentWidth += event->pos().x() - event->lastPos().x();
208 currentWidth = qMax(minimumRoleWidth(), currentWidth);
209
210 m_visibleRolesWidths.insert(pressedRole, currentWidth);
211 update();
212
213 emit visibleRoleWidthChanged(pressedRole, currentWidth, previousWidth);
214 } else if ((event->pos() - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
215 kDebug() << "Moving of role not supported yet";
216 m_roleOperation = MoveRoleOperation;
217 }
218 }
219
220 void KItemListHeader::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
221 {
222 QGraphicsWidget::hoverEnterEvent(event);
223 updateHoveredRoleIndex(event->pos());
224 }
225
226 void KItemListHeader::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
227 {
228 QGraphicsWidget::hoverLeaveEvent(event);
229 if (m_hoveredRoleIndex != -1) {
230 m_hoveredRoleIndex = -1;
231 update();
232 }
233 }
234
235 void KItemListHeader::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
236 {
237 QGraphicsWidget::hoverMoveEvent(event);
238
239 const QPointF& pos = event->pos();
240 updateHoveredRoleIndex(pos);
241 if (m_hoveredRoleIndex >= 0 && isAboveRoleGrip(pos, m_hoveredRoleIndex)) {
242 setCursor(Qt::SplitHCursor);
243 } else {
244 unsetCursor();
245 }
246 }
247
248 void KItemListHeader::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous)
249 {
250 Q_UNUSED(current);
251 Q_UNUSED(previous);
252 update();
253 }
254
255 void KItemListHeader::slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous)
256 {
257 Q_UNUSED(current);
258 Q_UNUSED(previous);
259 update();
260 }
261
262 void KItemListHeader::paintRole(QPainter* painter,
263 const QByteArray& role,
264 const QRectF& rect,
265 int orderIndex)
266 {
267 // The following code is based on the code from QHeaderView::paintSection().
268 // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
269 QStyleOptionHeader option;
270 option.section = orderIndex;
271 option.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal;
272 if (isEnabled()) {
273 option.state |= QStyle::State_Enabled;
274 }
275 if (window() && window()->isActiveWindow()) {
276 option.state |= QStyle::State_Active;
277 }
278 if (m_hoveredRoleIndex == orderIndex) {
279 option.state |= QStyle::State_MouseOver;
280 }
281 if (m_pressedRoleIndex == orderIndex) {
282 option.state |= QStyle::State_Sunken;
283 }
284 if (m_model->sortRole() == role) {
285 option.sortIndicator = (m_model->sortOrder() == Qt::AscendingOrder) ?
286 QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
287 }
288 option.rect = rect.toRect();
289
290 if (m_visibleRoles.count() == 1) {
291 option.position = QStyleOptionHeader::OnlyOneSection;
292 } else if (orderIndex == 0) {
293 option.position = QStyleOptionHeader::Beginning;
294 } else if (orderIndex == m_visibleRoles.count() - 1) {
295 option.position = QStyleOptionHeader::End;
296 } else {
297 option.position = QStyleOptionHeader::Middle;
298 }
299
300 option.orientation = Qt::Horizontal;
301 option.selectedPosition = QStyleOptionHeader::NotAdjacent;
302 option.text = m_model->roleDescription(role);
303
304 style()->drawControl(QStyle::CE_Header, &option, painter);
305 }
306
307 void KItemListHeader::updatePressedRoleIndex(const QPointF& pos)
308 {
309 const int pressedIndex = roleIndexAt(pos);
310 if (m_pressedRoleIndex != pressedIndex) {
311 m_pressedRoleIndex = pressedIndex;
312 update();
313 }
314 }
315
316 void KItemListHeader::updateHoveredRoleIndex(const QPointF& pos)
317 {
318 const int hoverIndex = roleIndexAt(pos);
319 if (m_hoveredRoleIndex != hoverIndex) {
320 m_hoveredRoleIndex = hoverIndex;
321 update();
322 }
323 }
324
325 int KItemListHeader::roleIndexAt(const QPointF& pos) const
326 {
327 int index = -1;
328
329 qreal x = 0;
330 foreach (const QByteArray& role, m_visibleRoles) {
331 ++index;
332 x += m_visibleRolesWidths.value(role);
333 if (pos.x() <= x) {
334 break;
335 }
336 }
337
338 return index;
339 }
340
341 bool KItemListHeader::isAboveRoleGrip(const QPointF& pos, int roleIndex) const
342 {
343 qreal x = 0;
344 for (int i = 0; i <= roleIndex; ++i) {
345 const QByteArray role = m_visibleRoles.at(i);
346 x += m_visibleRolesWidths.value(role);
347 }
348
349 const int grip = style()->pixelMetric(QStyle::PM_HeaderGripMargin);
350 return pos.x() >= (x - grip) && pos.x() <= x;
351 }
352
353 #include "kitemlistheader_p.moc"