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