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

KDE's Doxygen guidelines are available online.