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 library is free software; you can redistribute it and/or * |
6 | * modify it under the terms of the GNU Library General Public * |
7 | * License as published by the Free Software Foundation; either * |
8 | * version 2 of the License, or (at your option) any later version. * |
9 | * * |
10 | * This library 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 GNU * |
13 | * Library General Public License for more details. * |
14 | * * |
15 | * You should have received a copy of the GNU Library General Public License * |
16 | * along with this library; see the file COPYING.LIB. If not, write to * |
17 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * |
18 | * Boston, MA 02110-1301, USA. * |
19 | *****************************************************************************/ |
20 | |
21 | #include "kurlnavigatorbutton_p.h" |
22 | |
23 | #include "kurlnavigator.h" |
24 | #include "kurlnavigatormenu_p.h" |
25 | #include "kdirsortfilterproxymodel.h" |
26 | |
27 | #include <kio/job.h> |
28 | #include <kio/jobclasses.h> |
29 | #include <kglobalsettings.h> |
30 | #include <klocale.h> |
31 | #include <kstringhandler.h> |
32 | |
33 | #include <QtCore/QTimer> |
34 | #include <QtGui/QPainter> |
35 | #include <QtGui/QKeyEvent> |
36 | #include <QtGui/QStyleOption> |
37 | |
38 | namespace KDEPrivate |
39 | { |
40 | |
41 | QPointer<KUrlNavigatorMenu> KUrlNavigatorButton::; |
42 | |
43 | KUrlNavigatorButton::KUrlNavigatorButton(const KUrl& url, QWidget* parent) : |
44 | KUrlNavigatorButtonBase(parent), |
45 | m_hoverArrow(false), |
46 | m_pendingTextChange(false), |
47 | m_replaceButton(false), |
48 | m_showMnemonic(false), |
49 | m_wheelSteps(0), |
50 | m_url(url), |
51 | m_subDir(), |
52 | m_openSubDirsTimer(0), |
53 | m_subDirsJob(0) |
54 | { |
55 | setAcceptDrops(true); |
56 | setUrl(url); |
57 | setMouseTracking(true); |
58 | |
59 | m_openSubDirsTimer = new QTimer(this); |
60 | m_openSubDirsTimer->setSingleShot(true); |
61 | m_openSubDirsTimer->setInterval(300); |
62 | connect(m_openSubDirsTimer, SIGNAL(timeout()), this, SLOT(startSubDirsJob())); |
63 | |
64 | connect(this, SIGNAL(pressed()), this, SLOT(requestSubDirs())); |
65 | } |
66 | |
67 | KUrlNavigatorButton::~KUrlNavigatorButton() |
68 | { |
69 | } |
70 | |
71 | void KUrlNavigatorButton::setUrl(const KUrl& url) |
72 | { |
73 | m_url = url; |
74 | |
75 | bool startTextResolving = !m_url.isLocalFile(); |
76 | if (startTextResolving) { |
77 | // Doing a text-resolving with KIO::stat() for all non-local |
78 | // URLs leads to problems for protocols where a limit is given for |
79 | // the number of parallel connections. A black-list |
80 | // is given where KIO::stat() should not be used: |
81 | static QSet<QString> protocols; |
82 | if (protocols.isEmpty()) { |
83 | protocols << "fish" << "ftp" << "nfs" << "sftp" << "smb" << "webdav" ; |
84 | } |
85 | startTextResolving = !protocols.contains(m_url.protocol()); |
86 | } |
87 | |
88 | if (startTextResolving) { |
89 | m_pendingTextChange = true; |
90 | KIO::StatJob* job = KIO::stat(m_url, KIO::HideProgressInfo); |
91 | connect(job, SIGNAL(result(KJob*)), |
92 | this, SLOT(statFinished(KJob*))); |
93 | emit startedTextResolving(); |
94 | } else { |
95 | setText(m_url.fileName().replace('&', "&&" )); |
96 | } |
97 | } |
98 | |
99 | KUrl KUrlNavigatorButton::url() const |
100 | { |
101 | return m_url; |
102 | } |
103 | |
104 | void KUrlNavigatorButton::setText(const QString& text) |
105 | { |
106 | QString adjustedText = text; |
107 | if (adjustedText.isEmpty()) { |
108 | adjustedText = m_url.protocol(); |
109 | } |
110 | // Assure that the button always consists of one line |
111 | adjustedText.remove(QLatin1Char('\n')); |
112 | |
113 | KUrlNavigatorButtonBase::setText(adjustedText); |
114 | updateMinimumWidth(); |
115 | |
116 | // Assure that statFinished() does not overwrite a text that has been |
117 | // set by a client of the URL navigator button |
118 | m_pendingTextChange = false; |
119 | } |
120 | |
121 | void KUrlNavigatorButton::setActiveSubDirectory(const QString& subDir) |
122 | { |
123 | m_subDir = subDir; |
124 | |
125 | // We use a different (bold) font on active, so the size hint changes |
126 | updateGeometry(); |
127 | update(); |
128 | } |
129 | |
130 | QString KUrlNavigatorButton::activeSubDirectory() const |
131 | { |
132 | return m_subDir; |
133 | } |
134 | |
135 | QSize KUrlNavigatorButton::sizeHint() const |
136 | { |
137 | QFont adjustedFont(font()); |
138 | adjustedFont.setBold(m_subDir.isEmpty()); |
139 | // the minimum size is textWidth + arrowWidth() + 2 * BorderWidth; for the |
140 | // preferred size we add the BorderWidth 2 times again for having an uncluttered look |
141 | const int width = QFontMetrics(adjustedFont).width(plainText()) + arrowWidth() + 4 * BorderWidth; |
142 | return QSize(width, KUrlNavigatorButtonBase::sizeHint().height()); |
143 | } |
144 | |
145 | void KUrlNavigatorButton::setShowMnemonic(bool show) |
146 | { |
147 | if (m_showMnemonic != show) { |
148 | m_showMnemonic = show; |
149 | update(); |
150 | } |
151 | } |
152 | |
153 | bool KUrlNavigatorButton::showMnemonic() const |
154 | { |
155 | return m_showMnemonic; |
156 | } |
157 | |
158 | void KUrlNavigatorButton::paintEvent(QPaintEvent* event) |
159 | { |
160 | Q_UNUSED(event); |
161 | |
162 | QPainter painter(this); |
163 | |
164 | QFont adjustedFont(font()); |
165 | adjustedFont.setBold(m_subDir.isEmpty()); |
166 | painter.setFont(adjustedFont); |
167 | |
168 | int buttonWidth = width(); |
169 | int preferredWidth = sizeHint().width(); |
170 | if (preferredWidth < minimumWidth()) { |
171 | preferredWidth = minimumWidth(); |
172 | } |
173 | if (buttonWidth > preferredWidth) { |
174 | buttonWidth = preferredWidth; |
175 | } |
176 | const int buttonHeight = height(); |
177 | |
178 | const QColor fgColor = foregroundColor(); |
179 | drawHoverBackground(&painter); |
180 | |
181 | int textLeft = 0; |
182 | int textWidth = buttonWidth; |
183 | |
184 | const bool leftToRight = (layoutDirection() == Qt::LeftToRight); |
185 | |
186 | if (!m_subDir.isEmpty()) { |
187 | // draw arrow |
188 | const int arrowSize = arrowWidth(); |
189 | const int arrowX = leftToRight ? (buttonWidth - arrowSize) - BorderWidth : BorderWidth; |
190 | const int arrowY = (buttonHeight - arrowSize) / 2; |
191 | |
192 | QStyleOption option; |
193 | option.initFrom(this); |
194 | option.rect = QRect(arrowX, arrowY, arrowSize, arrowSize); |
195 | option.palette = palette(); |
196 | option.palette.setColor(QPalette::Text, fgColor); |
197 | option.palette.setColor(QPalette::WindowText, fgColor); |
198 | option.palette.setColor(QPalette::ButtonText, fgColor); |
199 | |
200 | if (m_hoverArrow) { |
201 | // highlight the background of the arrow to indicate that the directories |
202 | // popup can be opened by a mouse click |
203 | QColor hoverColor = palette().color(QPalette::HighlightedText); |
204 | hoverColor.setAlpha(96); |
205 | painter.setPen(Qt::NoPen); |
206 | painter.setBrush(hoverColor); |
207 | |
208 | int hoverX = arrowX; |
209 | if (!leftToRight) { |
210 | hoverX -= BorderWidth; |
211 | } |
212 | painter.drawRect(QRect(hoverX, 0, arrowSize + BorderWidth, buttonHeight)); |
213 | } |
214 | |
215 | if (leftToRight) { |
216 | style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &option, &painter, this); |
217 | } else { |
218 | style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &option, &painter, this); |
219 | textLeft += arrowSize + 2 * BorderWidth; |
220 | } |
221 | |
222 | textWidth -= arrowSize + 2 * BorderWidth; |
223 | } |
224 | |
225 | painter.setPen(fgColor); |
226 | const bool clipped = isTextClipped(); |
227 | const QRect textRect(textLeft, 0, textWidth, buttonHeight); |
228 | if (clipped) { |
229 | QColor bgColor = fgColor; |
230 | bgColor.setAlpha(0); |
231 | QLinearGradient gradient(textRect.topLeft(), textRect.topRight()); |
232 | if (leftToRight) { |
233 | gradient.setColorAt(0.8, fgColor); |
234 | gradient.setColorAt(1.0, bgColor); |
235 | } else { |
236 | gradient.setColorAt(0.0, bgColor); |
237 | gradient.setColorAt(0.2, fgColor); |
238 | } |
239 | |
240 | QPen pen; |
241 | pen.setBrush(QBrush(gradient)); |
242 | painter.setPen(pen); |
243 | } |
244 | |
245 | int textFlags = clipped ? Qt::AlignVCenter : Qt::AlignCenter; |
246 | if (m_showMnemonic) { |
247 | textFlags |= Qt::TextShowMnemonic; |
248 | painter.drawText(textRect, textFlags, text()); |
249 | } else { |
250 | painter.drawText(textRect, textFlags, plainText()); |
251 | } |
252 | } |
253 | |
254 | void KUrlNavigatorButton::enterEvent(QEvent* event) |
255 | { |
256 | KUrlNavigatorButtonBase::enterEvent(event); |
257 | |
258 | // if the text is clipped due to a small window width, the text should |
259 | // be shown as tooltip |
260 | if (isTextClipped()) { |
261 | setToolTip(plainText()); |
262 | } |
263 | } |
264 | |
265 | void KUrlNavigatorButton::leaveEvent(QEvent* event) |
266 | { |
267 | KUrlNavigatorButtonBase::leaveEvent(event); |
268 | setToolTip(QString()); |
269 | |
270 | if (m_hoverArrow) { |
271 | m_hoverArrow = false; |
272 | update(); |
273 | } |
274 | } |
275 | |
276 | void KUrlNavigatorButton::keyPressEvent(QKeyEvent* event) |
277 | { |
278 | switch (event->key()) { |
279 | case Qt::Key_Enter: |
280 | case Qt::Key_Return: |
281 | emit clicked(m_url, Qt::LeftButton); |
282 | break; |
283 | case Qt::Key_Down: |
284 | case Qt::Key_Space: |
285 | startSubDirsJob(); |
286 | break; |
287 | default: |
288 | KUrlNavigatorButtonBase::keyPressEvent(event); |
289 | } |
290 | } |
291 | |
292 | void KUrlNavigatorButton::dropEvent(QDropEvent* event) |
293 | { |
294 | const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); |
295 | if (!urls.isEmpty()) { |
296 | setDisplayHintEnabled(DraggedHint, true); |
297 | |
298 | emit urlsDropped(m_url, event); |
299 | |
300 | setDisplayHintEnabled(DraggedHint, false); |
301 | update(); |
302 | } |
303 | } |
304 | |
305 | void KUrlNavigatorButton::dragEnterEvent(QDragEnterEvent* event) |
306 | { |
307 | if (event->mimeData()->hasUrls()) { |
308 | setDisplayHintEnabled(DraggedHint, true); |
309 | event->acceptProposedAction(); |
310 | |
311 | update(); |
312 | } |
313 | } |
314 | |
315 | void KUrlNavigatorButton::dragMoveEvent(QDragMoveEvent* event) |
316 | { |
317 | QRect rect = event->answerRect(); |
318 | if (isAboveArrow(rect.center().x())) { |
319 | m_hoverArrow = true; |
320 | update(); |
321 | |
322 | if (m_subDirsMenu == 0) { |
323 | requestSubDirs(); |
324 | } else if (m_subDirsMenu->parent() != this) { |
325 | m_subDirsMenu->close(); |
326 | m_subDirsMenu->deleteLater(); |
327 | m_subDirsMenu = 0; |
328 | |
329 | requestSubDirs(); |
330 | } |
331 | } else { |
332 | if (m_openSubDirsTimer->isActive()) { |
333 | cancelSubDirsRequest(); |
334 | } |
335 | delete m_subDirsMenu; |
336 | m_subDirsMenu = 0; |
337 | m_hoverArrow = false; |
338 | update(); |
339 | } |
340 | } |
341 | |
342 | void KUrlNavigatorButton::dragLeaveEvent(QDragLeaveEvent* event) |
343 | { |
344 | KUrlNavigatorButtonBase::dragLeaveEvent(event); |
345 | |
346 | m_hoverArrow = false; |
347 | setDisplayHintEnabled(DraggedHint, false); |
348 | update(); |
349 | } |
350 | |
351 | void KUrlNavigatorButton::mousePressEvent(QMouseEvent* event) |
352 | { |
353 | if (isAboveArrow(event->x()) && (event->button() == Qt::LeftButton)) { |
354 | // the mouse is pressed above the [>] button |
355 | startSubDirsJob(); |
356 | } |
357 | KUrlNavigatorButtonBase::mousePressEvent(event); |
358 | } |
359 | |
360 | void KUrlNavigatorButton::mouseReleaseEvent(QMouseEvent* event) |
361 | { |
362 | if (!isAboveArrow(event->x()) || (event->button() != Qt::LeftButton)) { |
363 | // the mouse has been released above the text area and not |
364 | // above the [>] button |
365 | emit clicked(m_url, event->button()); |
366 | cancelSubDirsRequest(); |
367 | } |
368 | KUrlNavigatorButtonBase::mouseReleaseEvent(event); |
369 | } |
370 | |
371 | void KUrlNavigatorButton::mouseMoveEvent(QMouseEvent* event) |
372 | { |
373 | KUrlNavigatorButtonBase::mouseMoveEvent(event); |
374 | |
375 | const bool hoverArrow = isAboveArrow(event->x()); |
376 | if (hoverArrow != m_hoverArrow) { |
377 | m_hoverArrow = hoverArrow; |
378 | update(); |
379 | } |
380 | } |
381 | |
382 | void KUrlNavigatorButton::wheelEvent(QWheelEvent* event) |
383 | { |
384 | if (event->orientation() == Qt::Vertical) { |
385 | m_wheelSteps = event->delta() / 120; |
386 | m_replaceButton = true; |
387 | startSubDirsJob(); |
388 | } |
389 | |
390 | KUrlNavigatorButtonBase::wheelEvent(event); |
391 | } |
392 | |
393 | void KUrlNavigatorButton::requestSubDirs() |
394 | { |
395 | if (!m_openSubDirsTimer->isActive() && (m_subDirsJob == 0)) { |
396 | m_openSubDirsTimer->start(); |
397 | } |
398 | } |
399 | |
400 | void KUrlNavigatorButton::startSubDirsJob() |
401 | { |
402 | if (m_subDirsJob != 0) { |
403 | return; |
404 | } |
405 | |
406 | const KUrl url = m_replaceButton ? m_url.upUrl() : m_url; |
407 | m_subDirsJob = KIO::listDir(url, KIO::HideProgressInfo, false /*no hidden files*/); |
408 | m_subDirs.clear(); // just to be ++safe |
409 | |
410 | connect(m_subDirsJob, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)), |
411 | this, SLOT(addEntriesToSubDirs(KIO::Job*,KIO::UDSEntryList))); |
412 | |
413 | if (m_replaceButton) { |
414 | connect(m_subDirsJob, SIGNAL(result(KJob*)), this, SLOT(replaceButton(KJob*))); |
415 | } else { |
416 | connect(m_subDirsJob, SIGNAL(result(KJob*)), this, SLOT(openSubDirsMenu(KJob*))); |
417 | } |
418 | } |
419 | |
420 | void KUrlNavigatorButton::addEntriesToSubDirs(KIO::Job* job, const KIO::UDSEntryList& entries) |
421 | { |
422 | Q_ASSERT(job == m_subDirsJob); |
423 | Q_UNUSED(job); |
424 | |
425 | foreach (const KIO::UDSEntry& entry, entries) { |
426 | if (entry.isDir()) { |
427 | const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME); |
428 | QString displayName = entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME); |
429 | if (displayName.isEmpty()) { |
430 | displayName = name; |
431 | } |
432 | if ((name != QLatin1String("." )) && (name != QLatin1String(".." ))) { |
433 | m_subDirs.append(qMakePair(name, displayName)); |
434 | } |
435 | } |
436 | } |
437 | } |
438 | |
439 | void KUrlNavigatorButton::urlsDropped(QAction* action, QDropEvent* event) |
440 | { |
441 | const int result = action->data().toInt(); |
442 | KUrl url = m_url; |
443 | url.addPath(m_subDirs.at(result).first); |
444 | urlsDropped(url, event); |
445 | } |
446 | |
447 | void KUrlNavigatorButton::(QAction* action) |
448 | { |
449 | const int result = action->data().toInt(); |
450 | KUrl url = m_url; |
451 | url.addPath(m_subDirs.at(result).first); |
452 | emit clicked(url, Qt::MidButton); |
453 | } |
454 | |
455 | void KUrlNavigatorButton::statFinished(KJob* job) |
456 | { |
457 | if (m_pendingTextChange) { |
458 | m_pendingTextChange = false; |
459 | |
460 | const KIO::UDSEntry entry = static_cast<KIO::StatJob*>(job)->statResult(); |
461 | QString name = entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME); |
462 | if (name.isEmpty()) { |
463 | name = m_url.fileName(); |
464 | } |
465 | setText(name); |
466 | |
467 | emit finishedTextResolving(); |
468 | } |
469 | } |
470 | |
471 | /** |
472 | * Helper function for openSubDirsMenu |
473 | */ |
474 | static bool naturalLessThan(const QPair<QString, QString>& s1, const QPair<QString, QString>& s2) |
475 | { |
476 | return KStringHandler::naturalCompare(s1.first, s2.first, Qt::CaseInsensitive) < 0; |
477 | } |
478 | |
479 | void KUrlNavigatorButton::(KJob* job) |
480 | { |
481 | Q_ASSERT(job == m_subDirsJob); |
482 | m_subDirsJob = 0; |
483 | |
484 | if (job->error() || m_subDirs.isEmpty()) { |
485 | // clear listing |
486 | return; |
487 | } |
488 | |
489 | qSort(m_subDirs.begin(), m_subDirs.end(), naturalLessThan); |
490 | setDisplayHintEnabled(PopupActiveHint, true); |
491 | update(); // ensure the button is drawn highlighted |
492 | |
493 | if (m_subDirsMenu != 0) { |
494 | m_subDirsMenu->close(); |
495 | m_subDirsMenu->deleteLater(); |
496 | m_subDirsMenu = 0; |
497 | } |
498 | |
499 | m_subDirsMenu = new KUrlNavigatorMenu(this); |
500 | initMenu(m_subDirsMenu, 0); |
501 | |
502 | const bool leftToRight = (layoutDirection() == Qt::LeftToRight); |
503 | const int = leftToRight ? width() - arrowWidth() - BorderWidth : 0; |
504 | const QPoint = parentWidget()->mapToGlobal(geometry().bottomLeft() + QPoint(popupX, 0)); |
505 | |
506 | QPointer<QObject> guard(this); |
507 | |
508 | const QAction* action = m_subDirsMenu->exec(popupPos); |
509 | |
510 | // If 'this' has been deleted in the menu's nested event loop, we have to return |
511 | // immediatedely because any access to a member variable might cause a crash. |
512 | if (!guard) { |
513 | return; |
514 | } |
515 | |
516 | if (action != 0) { |
517 | const int result = action->data().toInt(); |
518 | KUrl url = m_url; |
519 | url.addPath(m_subDirs[result].first); |
520 | emit clicked(url, Qt::LeftButton); |
521 | } |
522 | |
523 | m_subDirs.clear(); |
524 | delete m_subDirsMenu; |
525 | m_subDirsMenu = 0; |
526 | |
527 | setDisplayHintEnabled(PopupActiveHint, false); |
528 | } |
529 | |
530 | void KUrlNavigatorButton::replaceButton(KJob* job) |
531 | { |
532 | Q_ASSERT(job == m_subDirsJob); |
533 | m_subDirsJob = 0; |
534 | m_replaceButton = false; |
535 | |
536 | if (job->error() || m_subDirs.isEmpty()) { |
537 | return; |
538 | } |
539 | |
540 | qSort(m_subDirs.begin(), m_subDirs.end(), naturalLessThan); |
541 | |
542 | // Get index of the directory that is shown currently in the button |
543 | const QString currentDir = m_url.fileName(); |
544 | int currentIndex = 0; |
545 | const int subDirsCount = m_subDirs.count(); |
546 | while (currentIndex < subDirsCount) { |
547 | if (m_subDirs[currentIndex].first == currentDir) { |
548 | break; |
549 | } |
550 | ++currentIndex; |
551 | } |
552 | |
553 | // Adjust the index by respecting the wheel steps and |
554 | // trigger a replacing of the button content |
555 | int targetIndex = currentIndex - m_wheelSteps; |
556 | if (targetIndex < 0) { |
557 | targetIndex = 0; |
558 | } else if (targetIndex >= subDirsCount) { |
559 | targetIndex = subDirsCount - 1; |
560 | } |
561 | |
562 | KUrl url = m_url.upUrl(); |
563 | url.addPath(m_subDirs[targetIndex].first); |
564 | |
565 | emit clicked(url, Qt::LeftButton); |
566 | |
567 | m_subDirs.clear(); |
568 | } |
569 | |
570 | void KUrlNavigatorButton::cancelSubDirsRequest() |
571 | { |
572 | m_openSubDirsTimer->stop(); |
573 | if (m_subDirsJob != 0) { |
574 | m_subDirsJob->kill(); |
575 | m_subDirsJob = 0; |
576 | } |
577 | } |
578 | |
579 | QString KUrlNavigatorButton::plainText() const |
580 | { |
581 | // Replace all "&&" by '&' and remove all single |
582 | // '&' characters |
583 | const QString source = text(); |
584 | const int sourceLength = source.length(); |
585 | |
586 | QString dest; |
587 | dest.reserve(sourceLength); |
588 | |
589 | int sourceIndex = 0; |
590 | int destIndex = 0; |
591 | while (sourceIndex < sourceLength) { |
592 | if (source.at(sourceIndex) == QLatin1Char('&')) { |
593 | ++sourceIndex; |
594 | if (sourceIndex >= sourceLength) { |
595 | break; |
596 | } |
597 | } |
598 | dest[destIndex] = source.at(sourceIndex); |
599 | ++sourceIndex; |
600 | ++destIndex; |
601 | } |
602 | |
603 | return dest; |
604 | } |
605 | |
606 | int KUrlNavigatorButton::arrowWidth() const |
607 | { |
608 | // if there isn't arrow then return 0 |
609 | int width = 0; |
610 | if (!m_subDir.isEmpty()) { |
611 | width = height() / 2; |
612 | if (width < 4) { |
613 | width = 4; |
614 | } |
615 | } |
616 | |
617 | return width; |
618 | } |
619 | |
620 | bool KUrlNavigatorButton::isAboveArrow(int x) const |
621 | { |
622 | const bool leftToRight = (layoutDirection() == Qt::LeftToRight); |
623 | return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth()); |
624 | } |
625 | |
626 | bool KUrlNavigatorButton::isTextClipped() const |
627 | { |
628 | int availableWidth = width() - 2 * BorderWidth; |
629 | if (!m_subDir.isEmpty()) { |
630 | availableWidth -= arrowWidth() - BorderWidth; |
631 | } |
632 | |
633 | QFont adjustedFont(font()); |
634 | adjustedFont.setBold(m_subDir.isEmpty()); |
635 | return QFontMetrics(adjustedFont).width(plainText()) >= availableWidth; |
636 | } |
637 | |
638 | void KUrlNavigatorButton::updateMinimumWidth() |
639 | { |
640 | const int oldMinWidth = minimumWidth(); |
641 | |
642 | int minWidth = sizeHint().width(); |
643 | if (minWidth < 40) { |
644 | minWidth = 40; |
645 | } |
646 | else if (minWidth > 150) { |
647 | // don't let an overlong path name waste all the URL navigator space |
648 | minWidth = 150; |
649 | } |
650 | if (oldMinWidth != minWidth) { |
651 | setMinimumWidth(minWidth); |
652 | } |
653 | } |
654 | |
655 | void KUrlNavigatorButton::(KUrlNavigatorMenu* , int startIndex) |
656 | { |
657 | connect(menu, SIGNAL(middleMouseButtonClicked(QAction*)), |
658 | this, SLOT(slotMenuActionClicked(QAction*))); |
659 | connect(menu, SIGNAL(urlsDropped(QAction*,QDropEvent*)), |
660 | this, SLOT(urlsDropped(QAction*,QDropEvent*))); |
661 | |
662 | menu->setLayoutDirection(Qt::LeftToRight); |
663 | |
664 | const int maxIndex = startIndex + 30; // Don't show more than 30 items in a menu |
665 | const int lastIndex = qMin(m_subDirs.count() - 1, maxIndex); |
666 | for (int i = startIndex; i <= lastIndex; ++i) { |
667 | const QString subDirName = m_subDirs[i].first; |
668 | const QString subDirDisplayName = m_subDirs[i].second; |
669 | QString text = KStringHandler::csqueeze(subDirDisplayName, 60); |
670 | text.replace(QLatin1Char('&'), QLatin1String("&&" )); |
671 | QAction* action = new QAction(text, this); |
672 | if (m_subDir == subDirName) { |
673 | QFont font(action->font()); |
674 | font.setBold(true); |
675 | action->setFont(font); |
676 | } |
677 | action->setData(i); |
678 | menu->addAction(action); |
679 | } |
680 | if (m_subDirs.count() > maxIndex) { |
681 | // If too much items are shown, move them into a sub menu |
682 | menu->addSeparator(); |
683 | KUrlNavigatorMenu* = new KUrlNavigatorMenu(menu); |
684 | subDirsMenu->setTitle(i18nc("@action:inmenu" , "More" )); |
685 | initMenu(subDirsMenu, maxIndex); |
686 | menu->addMenu(subDirsMenu); |
687 | } |
688 | } |
689 | |
690 | } // namespace KDEPrivate |
691 | |
692 | #include "kurlnavigatorbutton_p.moc" |
693 | |