KIO

kurlnavigatorbutton.cpp
1/*
2 SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at>
3 SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kurlnavigatorbutton_p.h"
9
10#include "../utils_p.h"
11#include "kurlnavigator.h"
12#include "kurlnavigatormenu_p.h"
13#include <kio/listjob.h>
14#include <kio/statjob.h>
15
16#include <KLocalizedString>
17#include <KStringHandler>
18
19#include <QCollator>
20#include <QKeyEvent>
21#include <QMimeData>
22#include <QPainter>
23#include <QStyleOption>
24#include <QTimer>
25
26namespace KDEPrivate
27{
28QPointer<KUrlNavigatorMenu> KUrlNavigatorButton::m_subDirsMenu;
29
30KUrlNavigatorButton::KUrlNavigatorButton(const QUrl &url, KUrlNavigator *parent)
31 : KUrlNavigatorButtonBase(parent)
32 , m_hoverOverArrow(false)
33 , m_hoverOverButton(false)
34 , m_pendingTextChange(false)
35 , m_replaceButton(false)
36 , m_showMnemonic(false)
37 , m_drawSeparator(true)
38 , m_wheelSteps(0)
39 , m_url(url)
40 , m_subDir()
41 , m_openSubDirsTimer(nullptr)
42 , m_subDirsJob(nullptr)
43 , m_padding(5)
44{
45 setAcceptDrops(true);
46 setUrl(url);
47 setMouseTracking(true);
48
49 m_openSubDirsTimer = new QTimer(this);
50 m_openSubDirsTimer->setSingleShot(true);
51 m_openSubDirsTimer->setInterval(300);
52 connect(m_openSubDirsTimer, &QTimer::timeout, this, &KUrlNavigatorButton::startSubDirsJob);
53
54 connect(this, &QAbstractButton::pressed, this, &KUrlNavigatorButton::requestSubDirs);
55}
56
57KUrlNavigatorButton::~KUrlNavigatorButton()
58{
59}
60
61void KUrlNavigatorButton::setUrl(const QUrl &url)
62{
63 m_url = url;
64
65 // Doing a text-resolving with KIO::stat() for all non-local
66 // URLs leads to problems for protocols where a limit is given for
67 // the number of parallel connections. A black-list
68 // is given where KIO::stat() should not be used:
69 static const QSet<QString> protocolBlacklist = QSet<QString>{
70 QStringLiteral("nfs"),
71 QStringLiteral("fish"),
72 QStringLiteral("ftp"),
73 QStringLiteral("sftp"),
74 QStringLiteral("smb"),
75 QStringLiteral("webdav"),
76 QStringLiteral("mtp"),
77 };
78
79 const bool startTextResolving = m_url.isValid() && !m_url.isLocalFile() && !protocolBlacklist.contains(m_url.scheme());
80
81 if (startTextResolving) {
82 m_pendingTextChange = true;
84 connect(job, &KJob::result, this, &KUrlNavigatorButton::statFinished);
85 Q_EMIT startedTextResolving();
86 } else {
87 setText(m_url.fileName().replace(QLatin1Char('&'), QLatin1String("&&")));
88 }
90}
91
92QUrl KUrlNavigatorButton::url() const
93{
94 return m_url;
95}
96
97void KUrlNavigatorButton::setText(const QString &text)
98{
99 QString adjustedText = text;
100 if (adjustedText.isEmpty()) {
101 adjustedText = m_url.scheme();
102 }
103 // Assure that the button always consists of one line
104 adjustedText.remove(QLatin1Char('\n'));
105
106 KUrlNavigatorButtonBase::setText(adjustedText);
107 updateMinimumWidth();
108
109 // Assure that statFinished() does not overwrite a text that has been
110 // set by a client of the URL navigator button
111 m_pendingTextChange = false;
112}
113
114void KUrlNavigatorButton::setActiveSubDirectory(const QString &subDir)
115{
116 m_subDir = subDir;
117
118 // We use a different (bold) font on active, so the size hint changes
119 updateGeometry();
120 update();
121}
122
123QString KUrlNavigatorButton::activeSubDirectory() const
124{
125 return m_subDir;
126}
127
128QSize KUrlNavigatorButton::sizeHint() const
129{
130 QFont adjustedFont(font());
131 adjustedFont.setBold(m_subDir.isEmpty());
132 // preferred width is textWidth, iconWidth and padding combined
133 // add extra padding in end to make sure the space between divider and button is consistent
134 // the first padding is used between icon and text, second in the end of text
135 const int width = m_padding + textWidth() + arrowWidth() + m_padding;
136 return QSize(width, KUrlNavigatorButtonBase::sizeHint().height());
137}
138
139void KUrlNavigatorButton::setShowMnemonic(bool show)
140{
141 if (m_showMnemonic != show) {
142 m_showMnemonic = show;
143 update();
144 }
145}
146
147bool KUrlNavigatorButton::showMnemonic() const
148{
149 return m_showMnemonic;
150}
151
152void KUrlNavigatorButton::setDrawSeparator(bool draw)
153{
154 if (m_drawSeparator != draw) {
155 m_drawSeparator = draw;
156 update();
157 }
158}
159
160bool KUrlNavigatorButton::drawSeparator() const
161{
162 return m_drawSeparator;
163}
164
165void KUrlNavigatorButton::paintEvent(QPaintEvent *event)
166{
167 Q_UNUSED(event);
168
169 QPainter painter(this);
170
171 QFont adjustedFont(font());
172 adjustedFont.setBold(m_subDir.isEmpty());
173 painter.setFont(adjustedFont);
174
175 int buttonWidth = width();
176 int arrowWidth = KUrlNavigatorButton::arrowWidth();
177
178 int preferredWidth = sizeHint().width();
179 if (preferredWidth < minimumWidth()) {
180 preferredWidth = minimumWidth();
181 }
182 if (buttonWidth > preferredWidth) {
183 buttonWidth = preferredWidth;
184 }
185 const int buttonHeight = height();
186 const QColor fgColor = foregroundColor();
187 const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
188
189 // Prepare sizes for icon
190 QRect textRect;
191 const int textRectWidth = buttonWidth - arrowWidth - m_padding;
192 if (leftToRight) {
193 textRect = QRect(m_padding, 0, textRectWidth, buttonHeight);
194 } else {
195 // If no separator is drawn, we can start writing text from 0
196 textRect = QRect(m_drawSeparator ? arrowWidth : 0, 0, textRectWidth, buttonHeight);
197 }
198
199 drawHoverBackground(&painter);
200
201 // Draw gradient overlay if text is clipped
202 painter.setPen(fgColor);
203 const bool clipped = isTextClipped();
204 if (clipped) {
205 QColor bgColor = fgColor;
206 bgColor.setAlpha(0);
207 QLinearGradient gradient(textRect.topLeft(), textRect.topRight());
208 if (leftToRight) {
209 gradient.setFinalStop(QPoint(gradient.finalStop().x() - m_padding, gradient.finalStop().y()));
210 gradient.setColorAt(0.8, fgColor);
211 gradient.setColorAt(1.0, bgColor);
212 } else {
213 gradient.setStart(QPoint(gradient.start().x() + m_padding, gradient.start().y()));
214 gradient.setColorAt(0.0, bgColor);
215 gradient.setColorAt(0.2, fgColor);
216 }
217
218 QPen pen;
219 pen.setBrush(QBrush(gradient));
220 painter.setPen(pen);
221 }
222
223 // Draw folder name
224 int textFlags = Qt::AlignVCenter;
225 if (m_showMnemonic) {
226 textFlags |= Qt::TextShowMnemonic;
227 painter.drawText(textRect, textFlags, text());
228 } else {
229 painter.drawText(textRect, textFlags, plainText());
230 }
231
232 // Draw separator arrow
233 if (m_drawSeparator) {
234 QStyleOption option;
235 option.initFrom(this);
236 option.palette = palette();
237 option.palette.setColor(QPalette::Text, fgColor);
238 option.palette.setColor(QPalette::WindowText, fgColor);
239 option.palette.setColor(QPalette::ButtonText, fgColor);
240
241 if (leftToRight) {
242 option.rect = QRect(textRect.right(), 0, arrowWidth, buttonHeight);
243 } else {
244 // Separator is the first item in RtL mode
245 option.rect = QRect(0, 0, arrowWidth, buttonHeight);
246 }
247
248 if (!m_hoverOverArrow) {
249 option.state = QStyle::State_None;
250 }
251 style()->drawPrimitive(leftToRight ? QStyle::PE_IndicatorArrowRight : QStyle::PE_IndicatorArrowLeft, &option, &painter, this);
252 }
253}
254
255void KUrlNavigatorButton::enterEvent(QEnterEvent *event)
256{
257 KUrlNavigatorButtonBase::enterEvent(event);
258
259 // if the text is clipped due to a small window width, the text should
260 // be shown as tooltip
261 if (isTextClipped()) {
262 setToolTip(plainText());
263 }
264 if (!m_hoverOverButton) {
265 m_hoverOverButton = true;
266 update();
267 }
268}
269
270void KUrlNavigatorButton::leaveEvent(QEvent *event)
271{
272 KUrlNavigatorButtonBase::leaveEvent(event);
273 setToolTip(QString());
274
275 if (m_hoverOverArrow) {
276 m_hoverOverArrow = false;
277 update();
278 }
279 if (m_hoverOverButton) {
280 m_hoverOverButton = false;
281 update();
282 }
283}
284
285void KUrlNavigatorButton::keyPressEvent(QKeyEvent *event)
286{
287 switch (event->key()) {
288 case Qt::Key_Enter:
289 case Qt::Key_Return:
290 Q_EMIT navigatorButtonActivated(m_url, Qt::LeftButton, event->modifiers());
291 break;
292 case Qt::Key_Down:
293 case Qt::Key_Space:
294 startSubDirsJob();
295 break;
296 default:
297 KUrlNavigatorButtonBase::keyPressEvent(event);
298 }
299}
300
301void KUrlNavigatorButton::dropEvent(QDropEvent *event)
302{
303 if (event->mimeData()->hasUrls()) {
304 setDisplayHintEnabled(DraggedHint, true);
305
306 Q_EMIT urlsDroppedOnNavButton(m_url, event);
307
308 setDisplayHintEnabled(DraggedHint, false);
309 update();
310 }
311}
312
313void KUrlNavigatorButton::dragEnterEvent(QDragEnterEvent *event)
314{
315 if (event->mimeData()->hasUrls()) {
316 setDisplayHintEnabled(DraggedHint, true);
317 event->acceptProposedAction();
318
319 update();
320 }
321}
322
323void KUrlNavigatorButton::dragMoveEvent(QDragMoveEvent *event)
324{
325 QRect rect = event->answerRect();
326
327 if (isAboveSeparator(rect.center().x())) {
328 m_hoverOverArrow = true;
329 update();
330
331 if (m_subDirsMenu == nullptr) {
332 requestSubDirs();
333 } else if (m_subDirsMenu->parent() != this) {
334 m_subDirsMenu->close();
335 m_subDirsMenu->deleteLater();
336 m_subDirsMenu = nullptr;
337
338 requestSubDirs();
339 }
340 } else {
341 if (m_openSubDirsTimer->isActive()) {
342 cancelSubDirsRequest();
343 }
344 if (m_subDirsMenu) {
345 m_subDirsMenu->deleteLater();
346 m_subDirsMenu = nullptr;
347 }
348 m_hoverOverArrow = false;
349 update();
350 }
351}
352
353void KUrlNavigatorButton::dragLeaveEvent(QDragLeaveEvent *event)
354{
355 KUrlNavigatorButtonBase::dragLeaveEvent(event);
356
357 m_hoverOverArrow = false;
358 setDisplayHintEnabled(DraggedHint, false);
359 update();
360}
361
362void KUrlNavigatorButton::mousePressEvent(QMouseEvent *event)
363{
364 if (isAboveSeparator(qRound(event->position().x())) && (event->button() == Qt::LeftButton)) {
365 // the mouse is pressed above the folder button
366 startSubDirsJob();
367 }
368 KUrlNavigatorButtonBase::mousePressEvent(event);
369}
370
371void KUrlNavigatorButton::mouseReleaseEvent(QMouseEvent *event)
372{
373 if (!isAboveSeparator(qRound(event->position().x())) || (event->button() != Qt::LeftButton)) {
374 // the mouse has been released above the text area and not
375 // above the folder button
376 Q_EMIT navigatorButtonActivated(m_url, event->button(), event->modifiers());
377 cancelSubDirsRequest();
378 }
379 KUrlNavigatorButtonBase::mouseReleaseEvent(event);
380}
381
382void KUrlNavigatorButton::mouseMoveEvent(QMouseEvent *event)
383{
384 KUrlNavigatorButtonBase::mouseMoveEvent(event);
385
386 const bool hoverOverIcon = isAboveSeparator(qRound(event->position().x()));
387 if (hoverOverIcon != m_hoverOverArrow) {
388 m_hoverOverArrow = hoverOverIcon;
389 update();
390 }
391}
392
393void KUrlNavigatorButton::wheelEvent(QWheelEvent *event)
394{
395 if (event->angleDelta().y() != 0) {
396 m_wheelSteps = event->angleDelta().y() / 120;
397 m_replaceButton = true;
398 startSubDirsJob();
399 }
400
401 KUrlNavigatorButtonBase::wheelEvent(event);
402}
403
404void KUrlNavigatorButton::requestSubDirs()
405{
406 if (!m_openSubDirsTimer->isActive() && (m_subDirsJob == nullptr)) {
407 m_openSubDirsTimer->start();
408 }
409}
410
411void KUrlNavigatorButton::startSubDirsJob()
412{
413 if (m_subDirsJob != nullptr) {
414 return;
415 }
416
417 const QUrl url = m_replaceButton ? KIO::upUrl(m_url) : m_url;
418 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
419 Q_ASSERT(urlNavigator);
420 m_subDirsJob =
421 KIO::listDir(url, KIO::HideProgressInfo, urlNavigator->showHiddenFolders() ? KIO::ListJob::ListFlag::IncludeHidden : KIO::ListJob::ListFlags{});
422 m_subDirs.clear(); // just to be ++safe
423
424 connect(m_subDirsJob, &KIO::ListJob::entries, this, &KUrlNavigatorButton::addEntriesToSubDirs);
425
426 if (m_replaceButton) {
427 connect(m_subDirsJob, &KJob::result, this, &KUrlNavigatorButton::replaceButton);
428 } else {
429 connect(m_subDirsJob, &KJob::result, this, &KUrlNavigatorButton::openSubDirsMenu);
430 }
431}
432
433void KUrlNavigatorButton::addEntriesToSubDirs(KIO::Job *job, const KIO::UDSEntryList &entries)
434{
435 Q_ASSERT(job == m_subDirsJob);
436 Q_UNUSED(job);
437
438 for (const KIO::UDSEntry &entry : entries) {
439 if (entry.isDir()) {
440 const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME);
442 if (displayName.isEmpty()) {
444 }
445 if (name != QLatin1String(".") && name != QLatin1String("..")) {
446 m_subDirs.push_back({name, displayName});
447 }
448 }
449 }
450}
451
452void KUrlNavigatorButton::slotUrlsDropped(QAction *action, QDropEvent *event)
453{
454 const int result = action->data().toInt();
455 QUrl url(m_url);
456 url.setPath(Utils::concatPaths(url.path(), m_subDirs.at(result).name));
457 Q_EMIT urlsDroppedOnNavButton(url, event);
458}
459
460void KUrlNavigatorButton::slotMenuActionClicked(QAction *action, Qt::MouseButton button)
461{
462 const int result = action->data().toInt();
463 QUrl url(m_url);
464 url.setPath(Utils::concatPaths(url.path(), m_subDirs.at(result).name));
465 Q_EMIT navigatorButtonActivated(url, button, Qt::NoModifier);
466}
467
468void KUrlNavigatorButton::statFinished(KJob *job)
469{
470 const KIO::UDSEntry entry = static_cast<KIO::StatJob *>(job)->statResult();
471
472 if (m_pendingTextChange) {
473 m_pendingTextChange = false;
474
476 if (name.isEmpty()) {
477 name = m_url.fileName();
478 }
479 setText(name);
480
481 Q_EMIT finishedTextResolving();
482 }
483
484 const QString iconName = entry.stringValue(KIO::UDSEntry::UDS_ICON_NAME);
485 if (!iconName.isEmpty()) {
486 setIcon(QIcon::fromTheme(iconName));
487 }
488}
489
490/**
491 * Helper struct for sorting folder names
492 */
493struct FolderNameNaturalLessThan {
494 FolderNameNaturalLessThan(bool sortHiddenLast)
495 : m_sortHiddenLast(sortHiddenLast)
496 {
497 m_collator.setCaseSensitivity(Qt::CaseInsensitive);
498 m_collator.setNumericMode(true);
499 }
500
501 bool operator()(const KUrlNavigatorButton::SubDirInfo &a, const KUrlNavigatorButton::SubDirInfo &b)
502 {
503 if (m_sortHiddenLast) {
504 const bool isHiddenA = a.name.startsWith(QLatin1Char('.'));
505 const bool isHiddenB = b.name.startsWith(QLatin1Char('.'));
506 if (isHiddenA && !isHiddenB) {
507 return false;
508 }
509 if (!isHiddenA && isHiddenB) {
510 return true;
511 }
512 }
513 return m_collator.compare(a.name, b.name) < 0;
514 }
515
516private:
517 QCollator m_collator;
518 bool m_sortHiddenLast;
519};
520
521void KUrlNavigatorButton::openSubDirsMenu(KJob *job)
522{
523 Q_ASSERT(job == m_subDirsJob);
524 m_subDirsJob = nullptr;
525
526 if (job->error() || m_subDirs.empty()) {
527 // clear listing
528 return;
529 }
530
531 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
532 Q_ASSERT(urlNavigator);
533 FolderNameNaturalLessThan less(urlNavigator->showHiddenFolders() && urlNavigator->sortHiddenFoldersLast());
534 std::sort(m_subDirs.begin(), m_subDirs.end(), less);
535 setDisplayHintEnabled(PopupActiveHint, true);
536 update(); // ensure the button is drawn highlighted
537
538 if (m_subDirsMenu != nullptr) {
539 m_subDirsMenu->close();
540 m_subDirsMenu->deleteLater();
541 m_subDirsMenu = nullptr;
542 }
543
544 m_subDirsMenu = new KUrlNavigatorMenu(this);
545 initMenu(m_subDirsMenu, 0);
546
547 const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
548 const int popupX = leftToRight ? width() - arrowWidth() : 0;
549 const QPoint popupPos = parentWidget()->mapToGlobal(geometry().bottomLeft() + QPoint(popupX, 0));
550
551 QPointer<QObject> guard(this);
552
553 m_subDirsMenu->exec(popupPos);
554
555 // If 'this' has been deleted in the menu's nested event loop, we have to return
556 // immediately because any access to a member variable might cause a crash.
557 if (!guard) {
558 return;
559 }
560
561 m_subDirs.clear();
562 delete m_subDirsMenu;
563 m_subDirsMenu = nullptr;
564
565 setDisplayHintEnabled(PopupActiveHint, false);
566}
567
568void KUrlNavigatorButton::replaceButton(KJob *job)
569{
570 Q_ASSERT(job == m_subDirsJob);
571 m_subDirsJob = nullptr;
572 m_replaceButton = false;
573
574 if (job->error() || m_subDirs.empty()) {
575 return;
576 }
577
578 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
579 Q_ASSERT(urlNavigator);
580 FolderNameNaturalLessThan less(urlNavigator->showHiddenFolders() && urlNavigator->sortHiddenFoldersLast());
581 std::sort(m_subDirs.begin(), m_subDirs.end(), less);
582
583 // Get index of the directory that is shown currently in the button
584 const QString currentDir = m_url.fileName();
585 int currentIndex = 0;
586 const int subDirsCount = m_subDirs.size();
587 while (currentIndex < subDirsCount) {
588 if (m_subDirs[currentIndex].name == currentDir) {
589 break;
590 }
591 ++currentIndex;
592 }
593
594 // Adjust the index by respecting the wheel steps and
595 // trigger a replacing of the button content
596 int targetIndex = currentIndex - m_wheelSteps;
597 if (targetIndex < 0) {
598 targetIndex = 0;
599 } else if (targetIndex >= subDirsCount) {
600 targetIndex = subDirsCount - 1;
601 }
602
603 QUrl url(KIO::upUrl(m_url));
604 url.setPath(Utils::concatPaths(url.path(), m_subDirs[targetIndex].name));
605 Q_EMIT navigatorButtonActivated(url, Qt::LeftButton, Qt::NoModifier);
606
607 m_subDirs.clear();
608}
609
610void KUrlNavigatorButton::cancelSubDirsRequest()
611{
612 m_openSubDirsTimer->stop();
613 if (m_subDirsJob != nullptr) {
614 m_subDirsJob->kill();
615 m_subDirsJob = nullptr;
616 }
617}
618
619QString KUrlNavigatorButton::plainText() const
620{
621 // Replace all "&&" by '&' and remove all single
622 // '&' characters
623 const QString source = text();
624 const int sourceLength = source.length();
625
626 QString dest;
627 dest.resize(sourceLength);
628
629 int sourceIndex = 0;
630 int destIndex = 0;
631 while (sourceIndex < sourceLength) {
632 if (source.at(sourceIndex) == QLatin1Char('&')) {
633 ++sourceIndex;
634 if (sourceIndex >= sourceLength) {
635 break;
636 }
637 }
638 dest[destIndex] = source.at(sourceIndex);
639 ++sourceIndex;
640 ++destIndex;
641 }
642
643 dest.resize(destIndex);
644
645 return dest;
646}
647
648int KUrlNavigatorButton::arrowWidth() const
649{
650 // if there isn't arrow then return 0
651 int width = 0;
652 if (!m_subDir.isEmpty()) {
653 width = height() / 2;
654 if (width < 4) {
655 width = 4;
656 }
657 }
658
659 return width;
660}
661
662int KUrlNavigatorButton::textWidth() const
663{
664 QFont adjustedFont(font());
665 adjustedFont.setBold(m_subDir.isEmpty());
666 return QFontMetrics(adjustedFont).size(Qt::TextSingleLine, plainText()).width();
667}
668
669bool KUrlNavigatorButton::isAboveSeparator(int x) const
670{
671 const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
672 return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth() + m_padding);
673}
674
675bool KUrlNavigatorButton::isTextClipped() const
676{
677 // Ignore padding when resizing, so text doesnt go under it
678 int availableWidth = width() - arrowWidth() - m_padding;
679
680 return textWidth() >= availableWidth;
681}
682
683void KUrlNavigatorButton::updateMinimumWidth()
684{
685 const int oldMinWidth = minimumWidth();
686
687 int minWidth = sizeHint().width();
688 if (minWidth < 10) {
689 minWidth = 10;
690 } else if (minWidth > 150) {
691 // don't let an overlong path name waste all the URL navigator space
692 minWidth = 150;
693 }
694 if (oldMinWidth != minWidth) {
695 setMinimumWidth(minWidth);
696 }
697}
698
699void KUrlNavigatorButton::initMenu(KUrlNavigatorMenu *menu, int startIndex)
700{
701 connect(menu, &KUrlNavigatorMenu::mouseButtonClicked, this, &KUrlNavigatorButton::slotMenuActionClicked);
702 connect(menu, &KUrlNavigatorMenu::urlsDropped, this, &KUrlNavigatorButton::slotUrlsDropped);
703
704 // So that triggering a menu item with the keyboard works
705 connect(menu, &QMenu::triggered, this, [this](QAction *act) {
706 slotMenuActionClicked(act, Qt::LeftButton);
707 });
708
709 menu->setLayoutDirection(Qt::LeftToRight);
710
711 const int maxIndex = startIndex + 30; // Don't show more than 30 items in a menu
712 const int subDirsSize = m_subDirs.size();
713 const int lastIndex = std::min(subDirsSize - 1, maxIndex);
714 for (int i = startIndex; i <= lastIndex; ++i) {
715 const auto &[subDirName, subDirDisplayName] = m_subDirs[i];
716 QString text = KStringHandler::csqueeze(subDirDisplayName, 60);
717 text.replace(QLatin1Char('&'), QLatin1String("&&"));
718 QAction *action = new QAction(text, this);
719 if (m_subDir == subDirName) {
720 QFont font(action->font());
721 font.setBold(true);
722 action->setFont(font);
723 }
724 action->setData(i);
725 menu->addAction(action);
726 }
727 if (subDirsSize > maxIndex) {
728 // If too much items are shown, move them into a sub menu
729 menu->addSeparator();
730 KUrlNavigatorMenu *subDirsMenu = new KUrlNavigatorMenu(menu);
731 subDirsMenu->setTitle(i18nc("@action:inmenu", "More"));
732 initMenu(subDirsMenu, maxIndex);
733 menu->addMenu(subDirsMenu);
734 }
735}
736
737} // namespace KDEPrivate
738
739#include "moc_kurlnavigatorbutton_p.cpp"
The base class for all jobs.
Definition job_base.h:45
@ IncludeHidden
Include hidden files in the listing.
Definition listjob.h:35
void entries(KIO::Job *job, const KIO::UDSEntryList &list)
This signal emits the entry found by the job while listing.
A KIO job that retrieves information about a file or directory.
Definition statjob.h:26
Universal Directory Service.
Definition udsentry.h:79
QString stringValue(uint field) const
Definition udsentry.cpp:365
@ UDS_DISPLAY_NAME
If set, contains the label to display instead of the 'real name' in UDS_NAME.
Definition udsentry.h:272
@ UDS_NAME
Filename - as displayed in directory listings etc.
Definition udsentry.h:224
@ UDS_ICON_NAME
Name of the icon, that should be used for displaying.
Definition udsentry.h:211
int error() const
void result(KJob *job)
Widget that allows to navigate through the paths of an URL.
bool showHiddenFolders() const
Returns whether to show hidden folders in the subdirectories popup.
bool sortHiddenFoldersLast() const
Returns whether to sort hidden folders in the subdirectories popup last.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT QString displayName(Akonadi::ETMCalendar *calendar, const Akonadi::Collection &collection)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
void update(Part *part, const QByteArray &data, qint64 dataSize)
KIOCORE_EXPORT QString iconNameForUrl(const QUrl &url)
Return the icon name for a URL.
Definition global.cpp:188
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
Find all details for one file or directory.
Definition statjob.cpp:203
KIOCORE_EXPORT ListJob * listDir(const QUrl &url, JobFlags flags=DefaultFlags, ListJob::ListFlags listFlags=ListJob::ListFlag::IncludeHidden)
List the contents of url, which is assumed to be a directory.
Definition listjob.cpp:239
KIOCORE_EXPORT QUrl upUrl(const QUrl &url)
This function is useful to implement the "Up" button in a file manager for example.
Definition global.cpp:238
QList< UDSEntry > UDSEntryList
A directory listing is a list of UDSEntry instances.
Definition udsentry.h:379
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
Definition job_base.h:251
QString name(const QVariant &location)
KCOREADDONS_EXPORT QString csqueeze(const QString &str, int maxlen=40)
QVariant data() const const
void setData(const QVariant &data)
void setAlpha(int alpha)
QSize size(int flags, const QString &text, int tabStops, int *tabArray) const const
QIcon fromTheme(const QString &name)
void triggered(QAction *action)
void setBrush(const QBrush &brush)
int x() const const
QPoint center() const const
int right() const const
QPoint topLeft() const const
QPoint topRight() const const
bool contains(const QSet< T > &other) const const
int width() const const
const QChar at(qsizetype position) const const
bool isEmpty() const const
qsizetype length() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
void resize(qsizetype newSize, QChar fillChar)
qsizetype size() const const
PE_IndicatorArrowRight
void initFrom(const QWidget *widget)
AlignVCenter
CaseInsensitive
Key_Enter
NoModifier
LeftToRight
LeftButton
TextShowMnemonic
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void timeout()
QString path(ComponentFormattingOptions options) const const
void setPath(const QString &path, ParsingMode mode)
int toInt(bool *ok) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 11 2025 11:51:43 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.