]> cloud.milkyroute.net Git - dolphin.git/blob - src/urlnavigatorbutton.cpp
Prevent a flickering of the URL navigator buttons by reusing existing buttons (before...
[dolphin.git] / src / urlnavigatorbutton.cpp
1 /***************************************************************************
2 * Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at) *
3 * Copyright (C) 2006 by Aaron J. Seigo (<aseigo@kde.org>) *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program 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 *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
19 ***************************************************************************/
20
21 #include "urlnavigatorbutton.h"
22
23 #include <assert.h>
24
25 #include "urlnavigator.h"
26
27 #include <kio/job.h>
28 #include <kio/jobclasses.h>
29 #include <kglobalsettings.h>
30 #include <kmenu.h>
31
32 #include <QPainter>
33 #include <QPaintEvent>
34 #include <QTimer>
35
36 UrlNavigatorButton::UrlNavigatorButton(int index, UrlNavigator* parent) :
37 UrlButton(parent),
38 m_index(-1),
39 m_popupDelay(0),
40 m_listJob(0)
41 {
42 setAcceptDrops(true);
43 setMinimumWidth(arrowWidth());
44 setIndex(index);
45 connect(this, SIGNAL(clicked()), this, SLOT(updateNavigatorUrl()));
46
47 m_popupDelay = new QTimer(this);
48 connect(m_popupDelay, SIGNAL(timeout()), this, SLOT(startListJob()));
49 connect(this, SIGNAL(pressed()), this, SLOT(startPopupDelay()));
50 }
51
52 UrlNavigatorButton::~UrlNavigatorButton()
53 {
54 }
55
56 void UrlNavigatorButton::setIndex(int index)
57 {
58 m_index = index;
59
60 if (m_index < 0) {
61 return;
62 }
63
64 QString path(urlNavigator()->url().pathOrUrl());
65 setText(path.section('/', index, index));
66
67 // Check whether the button indicates the full path of the Url. If
68 // this is the case, the button is marked as 'active'.
69 ++index;
70 QFont adjustedFont(font());
71 if (path.section('/', index, index).isEmpty()) {
72 setDisplayHintEnabled(ActivatedHint, true);
73 adjustedFont.setBold(true);
74 }
75 else {
76 setDisplayHintEnabled(ActivatedHint, false);
77 adjustedFont.setBold(false);
78 }
79
80 setFont(adjustedFont);
81 update();
82 }
83
84 QSize UrlNavigatorButton::sizeHint() const
85 {
86 const int width = fontMetrics().width(text()) + (arrowWidth() * 4);
87 return QSize(width, UrlButton::sizeHint().height());
88 }
89
90 void UrlNavigatorButton::paintEvent(QPaintEvent* event)
91 {
92 QPainter painter(this);
93 painter.setClipRect(event->rect());
94 const int buttonWidth = width();
95 const int buttonHeight = height();
96
97 QColor backgroundColor;
98 QColor foregroundColor;
99 const bool isHighlighted = isDisplayHintEnabled(EnteredHint) ||
100 isDisplayHintEnabled(DraggedHint) ||
101 isDisplayHintEnabled(PopupActiveHint);
102 if (isHighlighted) {
103 backgroundColor = KGlobalSettings::highlightColor();
104 foregroundColor = KGlobalSettings::highlightedTextColor();
105 }
106 else {
107 backgroundColor = palette().brush(QPalette::Background).color();
108 foregroundColor = KGlobalSettings::buttonTextColor();
109 }
110
111 // dimm the colors if the parent view does not have the focus
112 const bool isActive = urlNavigator()->isActive();
113 if (!isActive) {
114 QColor dimmColor(palette().brush(QPalette::Background).color());
115 foregroundColor = mixColors(foregroundColor, dimmColor);
116 if (isHighlighted) {
117 backgroundColor = mixColors(backgroundColor, dimmColor);
118 }
119 }
120
121 // draw button background
122 painter.setPen(Qt::NoPen);
123 painter.setBrush(backgroundColor);
124 painter.drawRect(0, 0, buttonWidth, buttonHeight);
125
126 int textWidth = buttonWidth;
127 if (isDisplayHintEnabled(ActivatedHint) && isActive || isHighlighted) {
128 painter.setPen(foregroundColor);
129 }
130 else {
131 // dimm the foreground color by mixing it with the background
132 foregroundColor = mixColors(foregroundColor, backgroundColor);
133 painter.setPen(foregroundColor);
134 }
135
136 if (!isDisplayHintEnabled(ActivatedHint)) {
137 // draw arrow
138 const int border = 2; // horizontal border
139 const int middleY = height() / 2;
140 const int width = arrowWidth();
141 const int startX = (buttonWidth - width) - (2 * border);
142 const int startTopY = middleY - (width - 1);
143 const int startBottomY = middleY + (width - 1);
144 for (int i = 0; i < width; ++i) {
145 painter.drawLine(startX, startTopY + i, startX + i, startTopY + i);
146 painter.drawLine(startX, startBottomY - i, startX + i, startBottomY - i);
147 }
148
149 textWidth = startX - border;
150 }
151
152 const bool clipped = isTextClipped();
153 const int align = clipped ? Qt::AlignVCenter : Qt::AlignCenter;
154 painter.drawText(QRect(0, 0, textWidth, buttonHeight), align, text());
155
156 if (clipped) {
157 // Blend the right area of the text with the background, as the
158 // text is clipped.
159 // TODO: use alpha blending in Qt4 instead of drawing the text that often
160 const int blendSteps = 16;
161
162 QColor blendColor(backgroundColor);
163 const int redInc = (foregroundColor.red() - backgroundColor.red()) / blendSteps;
164 const int greenInc = (foregroundColor.green() - backgroundColor.green()) / blendSteps;
165 const int blueInc = (foregroundColor.blue() - backgroundColor.blue()) / blendSteps;
166 for (int i = 0; i < blendSteps; ++i) {
167 painter.setClipRect(QRect(textWidth - i, 0, 1, buttonHeight));
168 painter.setPen(blendColor);
169 painter.drawText(QRect(0, 0, textWidth, buttonHeight), align, text());
170
171 blendColor.setRgb(blendColor.red() + redInc,
172 blendColor.green() + greenInc,
173 blendColor.blue() + blueInc);
174 }
175 }
176 }
177
178 void UrlNavigatorButton::enterEvent(QEvent* event)
179 {
180 UrlButton::enterEvent(event);
181
182 // if the text is clipped due to a small window width, the text should
183 // be shown as tooltip
184 if (isTextClipped()) {
185 setToolTip(text());
186 }
187 }
188
189 void UrlNavigatorButton::leaveEvent(QEvent* event)
190 {
191 UrlButton::leaveEvent(event);
192 setToolTip(QString());
193 }
194
195 void UrlNavigatorButton::dropEvent(QDropEvent* event)
196 {
197 if (m_index < 0) {
198 return;
199 }
200
201 const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
202 if (!urls.isEmpty()) {
203 event->acceptProposedAction();
204
205 setDisplayHintEnabled(DraggedHint, true);
206
207 QString path(urlNavigator()->url().prettyUrl());
208 path = path.section('/', 0, m_index + 2);
209
210 urlNavigator()->dropUrls(urls, KUrl(path));
211
212 setDisplayHintEnabled(DraggedHint, false);
213 update();
214 }
215 }
216
217 void UrlNavigatorButton::dragEnterEvent(QDragEnterEvent* event)
218 {
219 if (event->mimeData()->hasUrls()) {
220 setDisplayHintEnabled(DraggedHint, true);
221 event->acceptProposedAction();
222
223 update();
224 }
225 }
226
227 void UrlNavigatorButton::dragLeaveEvent(QDragLeaveEvent* event)
228 {
229 UrlButton::dragLeaveEvent(event);
230
231 setDisplayHintEnabled(DraggedHint, false);
232 update();
233 }
234
235
236 void UrlNavigatorButton::updateNavigatorUrl()
237 {
238 if (m_index < 0) {
239 return;
240 }
241
242 urlNavigator()->setUrl(urlNavigator()->url(m_index));
243 }
244
245 void UrlNavigatorButton::startPopupDelay()
246 {
247 if (m_popupDelay->isActive() || m_listJob || m_index < 0) {
248 return;
249 }
250
251 m_popupDelay->setSingleShot(true);
252 m_popupDelay->start(300);
253 }
254
255 void UrlNavigatorButton::stopPopupDelay()
256 {
257 m_popupDelay->stop();
258 if (m_listJob) {
259 m_listJob->kill();
260 m_listJob = 0;
261 }
262 }
263
264 void UrlNavigatorButton::startListJob()
265 {
266 if (m_listJob) {
267 return;
268 }
269
270 const KUrl& url = urlNavigator()->url(m_index);
271 m_listJob = KIO::listDir(url, false, false);
272 m_subdirs.clear(); // just to be ++safe
273
274 connect(m_listJob, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList &)),
275 this, SLOT(entriesList(KIO::Job*, const KIO::UDSEntryList&)));
276 connect(m_listJob, SIGNAL(result(KJob*)), this, SLOT(listJobFinished(KJob*)));
277 }
278
279 void UrlNavigatorButton::entriesList(KIO::Job* job, const KIO::UDSEntryList& entries)
280 {
281 if (job != m_listJob) {
282 return;
283 }
284
285 KIO::UDSEntryList::const_iterator it = entries.constBegin();
286 KIO::UDSEntryList::const_iterator itEnd = entries.constEnd();
287 while (it != itEnd) {
288 QString name;
289 //bool isDir = false;
290 KIO::UDSEntry entry = *it;
291
292 /* KDE3 reference:
293 KIO::UDSEntry::const_iterator atomIt = entry.constBegin();
294 KIO::UDSEntry::const_iterator atomEndIt = entry.constEnd();
295
296 while (atomIt != atomEndIt) {
297 switch ((*atomIt).m_uds) {
298 case KIO::UDS_NAME:
299 name = (*atomIt).m_str;
300 break;
301 case KIO::UDS_FILE_TYPE:
302 isDir = S_ISDIR((*atomIt).m_long);
303 break;
304 default:
305 break;
306 }
307 ++atomIt;
308 }
309 if (isDir) {
310 m_subdirs.append(name);
311 }
312 */
313
314 if (entry.isDir()) {
315 m_subdirs.append(entry.stringValue(KIO::UDS_NAME));
316 }
317
318 ++it;
319 }
320
321 m_subdirs.sort();
322 }
323
324 void UrlNavigatorButton::listJobFinished(KJob* job)
325 {
326 if (job != m_listJob) {
327 return;
328 }
329
330 if (job->error() || m_subdirs.isEmpty()) {
331 // clear listing
332 return;
333 }
334
335 setDisplayHintEnabled(PopupActiveHint, true);
336 update(); // ensure the button is drawn highlighted
337
338 KMenu* dirsMenu = new KMenu(this);
339 QStringList::const_iterator it = m_subdirs.constBegin();
340 QStringList::const_iterator itEnd = m_subdirs.constEnd();
341 int i = 0;
342 while (it != itEnd) {
343 QAction* action = new QAction(*it, this);
344 action->setData(i);
345 dirsMenu->addAction(action);
346 ++it;
347 ++i;
348 }
349
350 const QAction* action = dirsMenu->exec(urlNavigator()->mapToGlobal(geometry().bottomLeft()));
351 if (action != 0) {
352 const int result = action->data().toInt();
353 KUrl url = urlNavigator()->url(m_index);
354 url.addPath(m_subdirs[result]);
355 urlNavigator()->setUrl(url);
356 }
357
358 m_listJob = 0;
359 m_subdirs.clear();
360 delete dirsMenu;
361 dirsMenu = 0;
362
363 setDisplayHintEnabled(PopupActiveHint, false);
364 }
365
366 int UrlNavigatorButton::arrowWidth() const
367 {
368 int width = (height() / 2) - 7;
369 if (width < 4) {
370 width = 4;
371 }
372 return width;
373 }
374
375 bool UrlNavigatorButton::isTextClipped() const
376 {
377 int availableWidth = width();
378 if (!isDisplayHintEnabled(ActivatedHint)) {
379 availableWidth -= arrowWidth() + 1;
380 }
381
382 QFontMetrics fontMetrics(font());
383 return fontMetrics.width(text()) >= availableWidth;
384 }
385
386 #include "urlnavigatorbutton.moc"