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