]> cloud.milkyroute.net Git - dolphin.git/blob - src/views/selectiontoggle.cpp
Apply the cursor asynchronously. This fixes the issue that a pointing-hand cursor...
[dolphin.git] / src / views / selectiontoggle.cpp
1 /***************************************************************************
2 * Copyright (C) 2008 by Peter Penz <peter.penz@gmx.at> *
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 "selectiontoggle.h"
21
22 #include <kglobalsettings.h>
23 #include <kicon.h>
24 #include <kiconloader.h>
25 #include <kiconeffect.h>
26 #include <klocale.h>
27
28 #include <QApplication>
29 #include <QPainter>
30 #include <QPaintEvent>
31 #include <QRect>
32 #include <QTimer>
33 #include <QTimeLine>
34
35 SelectionToggle::SelectionToggle(QWidget* parent) :
36 QAbstractButton(parent),
37 m_isHovered(false),
38 m_leftMouseButtonPressed(false),
39 m_appliedArrowCursor(false),
40 m_fadingValue(0),
41 m_margin(0),
42 m_icon(),
43 m_fadingTimeLine(0)
44 {
45 setFocusPolicy(Qt::NoFocus);
46 parent->installEventFilter(this);
47 resize(sizeHint());
48 setIconOverlay(isChecked());
49 connect(this, SIGNAL(toggled(bool)),
50 this, SLOT(setIconOverlay(bool)));
51 connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)),
52 this, SLOT(refreshIcon()));
53 }
54
55 SelectionToggle::~SelectionToggle()
56 {
57 }
58
59 QSize SelectionToggle::sizeHint() const
60 {
61 return QSize(16, 16);
62 }
63
64 void SelectionToggle::reset()
65 {
66 m_url = KUrl();
67 hide();
68 }
69
70 void SelectionToggle::setUrl(const KUrl& url)
71 {
72 m_url = url;
73 if (!url.isEmpty()) {
74 startFading();
75 }
76 }
77
78 void SelectionToggle::setMargin(int margin)
79 {
80 if (margin != m_margin) {
81 m_margin = margin;
82 update();
83 }
84 }
85
86 int SelectionToggle::margin() const
87 {
88 return m_margin;
89 }
90
91 KUrl SelectionToggle::url() const
92 {
93 return m_url;
94 }
95
96 void SelectionToggle::setVisible(bool visible)
97 {
98 QAbstractButton::setVisible(visible);
99
100 stopFading();
101 if (visible) {
102 startFading();
103 }
104
105 }
106
107 bool SelectionToggle::eventFilter(QObject* obj, QEvent* event)
108 {
109 if (obj == parent()) {
110 switch (event->type()) {
111 case QEvent::Leave:
112 hide();
113 break;
114
115 case QEvent::MouseMove:
116 if (m_leftMouseButtonPressed) {
117 // Don't forward mouse move events to the viewport,
118 // otherwise a rubberband selection will be shown when
119 // clicking on the selection toggle and moving the mouse
120 // above the viewport.
121 return true;
122 }
123 break;
124
125 default:
126 break;
127 }
128 }
129
130 return QAbstractButton::eventFilter(obj, event);
131 }
132
133 void SelectionToggle::enterEvent(QEvent* event)
134 {
135 QAbstractButton::enterEvent(event);
136
137 if (!m_appliedArrowCursor) {
138 m_appliedArrowCursor = true;
139 // Apply the arrow asynchronously. This is required for
140 // the following usecase:
141 // 1. Cursor is above the viewport left beside an item
142 // 2. Cursor is moved above the item, so that the selection-toggle
143 // and the item are entered equally.
144 // In this situation it is not defined who gets the enter-event first.
145 // As the selection-toggle is above the item, it should overwrite possible
146 // cursor changes done by the item.
147 QTimer::singleShot(0, this, SLOT(applyArrowCursor()));
148 }
149
150 // if the mouse cursor is above the selection toggle, display
151 // it immediately without fading timer
152 m_isHovered = true;
153 if (m_fadingTimeLine != 0) {
154 m_fadingTimeLine->stop();
155 }
156 m_fadingValue = 255;
157 setToolTip(isChecked() ? i18nc("@info:tooltip", "Deselect Item") :
158 i18nc("@info:tooltip", "Select Item"));
159 update();
160 }
161
162 void SelectionToggle::leaveEvent(QEvent* event)
163 {
164 QAbstractButton::leaveEvent(event);
165
166 if (m_appliedArrowCursor) {
167 // Reset the cursor asynchronously. See SelectionToggle::enterEvent()
168 // for a more information.
169 m_appliedArrowCursor = false;
170 QTimer::singleShot(0, this, SLOT(restoreCursor()));
171 }
172
173 m_isHovered = false;
174 update();
175 }
176
177 void SelectionToggle::mousePressEvent(QMouseEvent* event)
178 {
179 QAbstractButton::mousePressEvent(event);
180 m_leftMouseButtonPressed = (event->buttons() & Qt::LeftButton);
181 }
182
183 void SelectionToggle::mouseReleaseEvent(QMouseEvent* event)
184 {
185 QAbstractButton::mouseReleaseEvent(event);
186 m_leftMouseButtonPressed = (event->buttons() & Qt::LeftButton);
187 }
188
189 void SelectionToggle::resizeEvent(QResizeEvent* event)
190 {
191 QAbstractButton::resizeEvent(event);
192 setIconOverlay(isChecked());
193 }
194
195 void SelectionToggle::paintEvent(QPaintEvent* event)
196 {
197 QPainter painter(this);
198 painter.setClipRect(event->rect());
199
200 // draw the icon overlay
201 const QPoint pos(m_margin, m_margin);
202 if (m_isHovered) {
203 KIconEffect *iconEffect = KIconLoader::global()->iconEffect();
204 QPixmap activeIcon = iconEffect->apply(m_icon, KIconLoader::Desktop, KIconLoader::ActiveState);
205 painter.drawPixmap(pos, activeIcon);
206 } else {
207 if (m_fadingValue < 255) {
208 // apply an alpha mask respecting the fading value to the icon
209 QPixmap icon = m_icon;
210 QPixmap alphaMask(icon.width(), icon.height());
211 const QColor color(m_fadingValue, m_fadingValue, m_fadingValue);
212 alphaMask.fill(color);
213 icon.setAlphaChannel(alphaMask);
214 painter.drawPixmap(pos, icon);
215 } else {
216 // no fading is required
217 painter.drawPixmap(pos, m_icon);
218 }
219 }
220
221 }
222
223 void SelectionToggle::setFadingValue(int value)
224 {
225 m_fadingValue = value;
226 if (m_fadingValue >= 255) {
227 Q_ASSERT(m_fadingTimeLine != 0);
228 m_fadingTimeLine->stop();
229 }
230 update();
231 }
232
233 void SelectionToggle::setIconOverlay(bool checked)
234 {
235 const char* icon = checked ? "list-remove" : "list-add";
236 const int size = qMin(width() - 2 * m_margin, height() - 2 * m_margin);
237 m_icon = KIconLoader::global()->loadIcon(icon,
238 KIconLoader::NoGroup,
239 size);
240 update();
241 }
242
243 void SelectionToggle::refreshIcon()
244 {
245 setIconOverlay(isChecked());
246 }
247
248 void SelectionToggle::applyArrowCursor()
249 {
250 QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
251 }
252
253 void SelectionToggle::restoreCursor()
254 {
255 QApplication::restoreOverrideCursor();
256 }
257
258 void SelectionToggle::startFading()
259 {
260 Q_ASSERT(m_fadingTimeLine == 0);
261
262 const bool animate = KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects;
263 const int duration = animate ? 600 : 1;
264
265 m_fadingTimeLine = new QTimeLine(duration, this);
266 connect(m_fadingTimeLine, SIGNAL(frameChanged(int)),
267 this, SLOT(setFadingValue(int)));
268 m_fadingTimeLine->setFrameRange(0, 255);
269 m_fadingTimeLine->start();
270 m_fadingValue = 0;
271 }
272
273 void SelectionToggle::stopFading()
274 {
275 if (m_fadingTimeLine != 0) {
276 m_fadingTimeLine->stop();
277 delete m_fadingTimeLine;
278 m_fadingTimeLine = 0;
279 }
280 m_fadingValue = 0;
281 }
282
283 #include "selectiontoggle.moc"