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