File indexing completed on 2023-10-01 04:05:52
0001 /* 0002 SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at> 0003 SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kurlnavigatorbutton_p.h" 0009 0010 #include "../utils_p.h" 0011 #include "kurlnavigator.h" 0012 #include "kurlnavigatormenu_p.h" 0013 #include <kio/listjob.h> 0014 #include <kio/statjob.h> 0015 0016 #include <KLocalizedString> 0017 #include <KStringHandler> 0018 0019 #include <QCollator> 0020 #include <QKeyEvent> 0021 #include <QMimeData> 0022 #include <QPainter> 0023 #include <QStyleOption> 0024 #include <QTimer> 0025 0026 namespace KDEPrivate 0027 { 0028 QPointer<KUrlNavigatorMenu> KUrlNavigatorButton::m_subDirsMenu; 0029 0030 KUrlNavigatorButton::KUrlNavigatorButton(const QUrl &url, KUrlNavigator *parent) 0031 : KUrlNavigatorButtonBase(parent) 0032 , m_hoverArrow(false) 0033 , m_pendingTextChange(false) 0034 , m_replaceButton(false) 0035 , m_showMnemonic(false) 0036 , m_wheelSteps(0) 0037 , m_url(url) 0038 , m_subDir() 0039 , m_openSubDirsTimer(nullptr) 0040 , m_subDirsJob(nullptr) 0041 { 0042 setAcceptDrops(true); 0043 setUrl(url); 0044 setMouseTracking(true); 0045 0046 m_openSubDirsTimer = new QTimer(this); 0047 m_openSubDirsTimer->setSingleShot(true); 0048 m_openSubDirsTimer->setInterval(300); 0049 connect(m_openSubDirsTimer, &QTimer::timeout, this, &KUrlNavigatorButton::startSubDirsJob); 0050 0051 connect(this, &QAbstractButton::pressed, this, &KUrlNavigatorButton::requestSubDirs); 0052 } 0053 0054 KUrlNavigatorButton::~KUrlNavigatorButton() 0055 { 0056 } 0057 0058 void KUrlNavigatorButton::setUrl(const QUrl &url) 0059 { 0060 m_url = url; 0061 0062 // Doing a text-resolving with KIO::stat() for all non-local 0063 // URLs leads to problems for protocols where a limit is given for 0064 // the number of parallel connections. A black-list 0065 // is given where KIO::stat() should not be used: 0066 static const QSet<QString> protocolBlacklist = QSet<QString>{ 0067 QStringLiteral("nfs"), 0068 QStringLiteral("fish"), 0069 QStringLiteral("ftp"), 0070 QStringLiteral("sftp"), 0071 QStringLiteral("smb"), 0072 QStringLiteral("webdav"), 0073 QStringLiteral("mtp"), 0074 }; 0075 0076 const bool startTextResolving = m_url.isValid() && !m_url.isLocalFile() && !protocolBlacklist.contains(m_url.scheme()); 0077 0078 if (startTextResolving) { 0079 m_pendingTextChange = true; 0080 KIO::StatJob *job = KIO::stat(m_url, KIO::HideProgressInfo); 0081 connect(job, &KJob::result, this, &KUrlNavigatorButton::statFinished); 0082 Q_EMIT startedTextResolving(); 0083 } else { 0084 setText(m_url.fileName().replace(QLatin1Char('&'), QLatin1String("&&"))); 0085 } 0086 } 0087 0088 QUrl KUrlNavigatorButton::url() const 0089 { 0090 return m_url; 0091 } 0092 0093 void KUrlNavigatorButton::setText(const QString &text) 0094 { 0095 QString adjustedText = text; 0096 if (adjustedText.isEmpty()) { 0097 adjustedText = m_url.scheme(); 0098 } 0099 // Assure that the button always consists of one line 0100 adjustedText.remove(QLatin1Char('\n')); 0101 0102 KUrlNavigatorButtonBase::setText(adjustedText); 0103 updateMinimumWidth(); 0104 0105 // Assure that statFinished() does not overwrite a text that has been 0106 // set by a client of the URL navigator button 0107 m_pendingTextChange = false; 0108 } 0109 0110 void KUrlNavigatorButton::setActiveSubDirectory(const QString &subDir) 0111 { 0112 m_subDir = subDir; 0113 0114 // We use a different (bold) font on active, so the size hint changes 0115 updateGeometry(); 0116 update(); 0117 } 0118 0119 QString KUrlNavigatorButton::activeSubDirectory() const 0120 { 0121 return m_subDir; 0122 } 0123 0124 QSize KUrlNavigatorButton::sizeHint() const 0125 { 0126 QFont adjustedFont(font()); 0127 adjustedFont.setBold(m_subDir.isEmpty()); 0128 // the minimum size is textWidth + arrowWidth() + 2 * BorderWidth; for the 0129 // preferred size we add the BorderWidth 2 times again for having an uncluttered look 0130 const int width = QFontMetrics(adjustedFont).size(Qt::TextSingleLine, plainText()).width() + arrowWidth() + 4 * BorderWidth; 0131 return QSize(width, KUrlNavigatorButtonBase::sizeHint().height()); 0132 } 0133 0134 void KUrlNavigatorButton::setShowMnemonic(bool show) 0135 { 0136 if (m_showMnemonic != show) { 0137 m_showMnemonic = show; 0138 update(); 0139 } 0140 } 0141 0142 bool KUrlNavigatorButton::showMnemonic() const 0143 { 0144 return m_showMnemonic; 0145 } 0146 0147 void KUrlNavigatorButton::paintEvent(QPaintEvent *event) 0148 { 0149 Q_UNUSED(event); 0150 0151 QPainter painter(this); 0152 0153 QFont adjustedFont(font()); 0154 adjustedFont.setBold(m_subDir.isEmpty()); 0155 painter.setFont(adjustedFont); 0156 0157 int buttonWidth = width(); 0158 int preferredWidth = sizeHint().width(); 0159 if (preferredWidth < minimumWidth()) { 0160 preferredWidth = minimumWidth(); 0161 } 0162 if (buttonWidth > preferredWidth) { 0163 buttonWidth = preferredWidth; 0164 } 0165 const int buttonHeight = height(); 0166 0167 const QColor fgColor = foregroundColor(); 0168 drawHoverBackground(&painter); 0169 0170 int textLeft = 0; 0171 int textWidth = buttonWidth; 0172 0173 const bool leftToRight = (layoutDirection() == Qt::LeftToRight); 0174 0175 if (!m_subDir.isEmpty()) { 0176 // draw arrow 0177 const int arrowSize = arrowWidth(); 0178 const int arrowX = leftToRight ? (buttonWidth - arrowSize) - BorderWidth : BorderWidth; 0179 const int arrowY = (buttonHeight - arrowSize) / 2; 0180 0181 QStyleOption option; 0182 option.initFrom(this); 0183 option.rect = QRect(arrowX, arrowY, arrowSize, arrowSize); 0184 option.palette = palette(); 0185 option.palette.setColor(QPalette::Text, fgColor); 0186 option.palette.setColor(QPalette::WindowText, fgColor); 0187 option.palette.setColor(QPalette::ButtonText, fgColor); 0188 0189 if (m_hoverArrow) { 0190 // highlight the background of the arrow to indicate that the directories 0191 // popup can be opened by a mouse click 0192 QColor hoverColor = palette().color(QPalette::HighlightedText); 0193 hoverColor.setAlpha(96); 0194 painter.setPen(Qt::NoPen); 0195 painter.setBrush(hoverColor); 0196 0197 int hoverX = arrowX; 0198 if (!leftToRight) { 0199 hoverX -= BorderWidth; 0200 } 0201 painter.drawRect(QRect(hoverX, 0, arrowSize + BorderWidth, buttonHeight)); 0202 } 0203 0204 if (leftToRight) { 0205 style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &option, &painter, this); 0206 } else { 0207 style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &option, &painter, this); 0208 textLeft += arrowSize + 2 * BorderWidth; 0209 } 0210 0211 textWidth -= arrowSize + 2 * BorderWidth; 0212 } 0213 0214 painter.setPen(fgColor); 0215 const bool clipped = isTextClipped(); 0216 const QRect textRect(textLeft, 0, textWidth, buttonHeight); 0217 if (clipped) { 0218 QColor bgColor = fgColor; 0219 bgColor.setAlpha(0); 0220 QLinearGradient gradient(textRect.topLeft(), textRect.topRight()); 0221 if (leftToRight) { 0222 gradient.setColorAt(0.8, fgColor); 0223 gradient.setColorAt(1.0, bgColor); 0224 } else { 0225 gradient.setColorAt(0.0, bgColor); 0226 gradient.setColorAt(0.2, fgColor); 0227 } 0228 0229 QPen pen; 0230 pen.setBrush(QBrush(gradient)); 0231 painter.setPen(pen); 0232 } 0233 0234 int textFlags = clipped ? Qt::AlignVCenter : Qt::AlignCenter; 0235 if (m_showMnemonic) { 0236 textFlags |= Qt::TextShowMnemonic; 0237 painter.drawText(textRect, textFlags, text()); 0238 } else { 0239 painter.drawText(textRect, textFlags, plainText()); 0240 } 0241 } 0242 0243 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0244 void KUrlNavigatorButton::enterEvent(QEnterEvent *event) 0245 #else 0246 void KUrlNavigatorButton::enterEvent(QEvent *event) 0247 #endif 0248 { 0249 KUrlNavigatorButtonBase::enterEvent(event); 0250 0251 // if the text is clipped due to a small window width, the text should 0252 // be shown as tooltip 0253 if (isTextClipped()) { 0254 setToolTip(plainText()); 0255 } 0256 } 0257 0258 void KUrlNavigatorButton::leaveEvent(QEvent *event) 0259 { 0260 KUrlNavigatorButtonBase::leaveEvent(event); 0261 setToolTip(QString()); 0262 0263 if (m_hoverArrow) { 0264 m_hoverArrow = false; 0265 update(); 0266 } 0267 } 0268 0269 void KUrlNavigatorButton::keyPressEvent(QKeyEvent *event) 0270 { 0271 switch (event->key()) { 0272 case Qt::Key_Enter: 0273 case Qt::Key_Return: 0274 Q_EMIT navigatorButtonActivated(m_url, Qt::LeftButton, event->modifiers()); 0275 break; 0276 case Qt::Key_Down: 0277 case Qt::Key_Space: 0278 startSubDirsJob(); 0279 break; 0280 default: 0281 KUrlNavigatorButtonBase::keyPressEvent(event); 0282 } 0283 } 0284 0285 void KUrlNavigatorButton::dropEvent(QDropEvent *event) 0286 { 0287 if (event->mimeData()->hasUrls()) { 0288 setDisplayHintEnabled(DraggedHint, true); 0289 0290 Q_EMIT urlsDroppedOnNavButton(m_url, event); 0291 0292 setDisplayHintEnabled(DraggedHint, false); 0293 update(); 0294 } 0295 } 0296 0297 void KUrlNavigatorButton::dragEnterEvent(QDragEnterEvent *event) 0298 { 0299 if (event->mimeData()->hasUrls()) { 0300 setDisplayHintEnabled(DraggedHint, true); 0301 event->acceptProposedAction(); 0302 0303 update(); 0304 } 0305 } 0306 0307 void KUrlNavigatorButton::dragMoveEvent(QDragMoveEvent *event) 0308 { 0309 QRect rect = event->answerRect(); 0310 if (isAboveArrow(rect.center().x())) { 0311 m_hoverArrow = true; 0312 update(); 0313 0314 if (m_subDirsMenu == nullptr) { 0315 requestSubDirs(); 0316 } else if (m_subDirsMenu->parent() != this) { 0317 m_subDirsMenu->close(); 0318 m_subDirsMenu->deleteLater(); 0319 m_subDirsMenu = nullptr; 0320 0321 requestSubDirs(); 0322 } 0323 } else { 0324 if (m_openSubDirsTimer->isActive()) { 0325 cancelSubDirsRequest(); 0326 } 0327 m_subDirsMenu->deleteLater(); 0328 m_subDirsMenu = nullptr; 0329 m_hoverArrow = false; 0330 update(); 0331 } 0332 } 0333 0334 void KUrlNavigatorButton::dragLeaveEvent(QDragLeaveEvent *event) 0335 { 0336 KUrlNavigatorButtonBase::dragLeaveEvent(event); 0337 0338 m_hoverArrow = false; 0339 setDisplayHintEnabled(DraggedHint, false); 0340 update(); 0341 } 0342 0343 void KUrlNavigatorButton::mousePressEvent(QMouseEvent *event) 0344 { 0345 if (isAboveArrow(event->x()) && (event->button() == Qt::LeftButton)) { 0346 // the mouse is pressed above the [>] button 0347 startSubDirsJob(); 0348 } 0349 KUrlNavigatorButtonBase::mousePressEvent(event); 0350 } 0351 0352 void KUrlNavigatorButton::mouseReleaseEvent(QMouseEvent *event) 0353 { 0354 if (!isAboveArrow(event->x()) || (event->button() != Qt::LeftButton)) { 0355 // the mouse has been released above the text area and not 0356 // above the [>] button 0357 Q_EMIT navigatorButtonActivated(m_url, event->button(), event->modifiers()); 0358 cancelSubDirsRequest(); 0359 } 0360 KUrlNavigatorButtonBase::mouseReleaseEvent(event); 0361 } 0362 0363 void KUrlNavigatorButton::mouseMoveEvent(QMouseEvent *event) 0364 { 0365 KUrlNavigatorButtonBase::mouseMoveEvent(event); 0366 0367 const bool hoverArrow = isAboveArrow(event->x()); 0368 if (hoverArrow != m_hoverArrow) { 0369 m_hoverArrow = hoverArrow; 0370 update(); 0371 } 0372 } 0373 0374 void KUrlNavigatorButton::wheelEvent(QWheelEvent *event) 0375 { 0376 if (event->angleDelta().y() != 0) { 0377 m_wheelSteps = event->angleDelta().y() / 120; 0378 m_replaceButton = true; 0379 startSubDirsJob(); 0380 } 0381 0382 KUrlNavigatorButtonBase::wheelEvent(event); 0383 } 0384 0385 void KUrlNavigatorButton::requestSubDirs() 0386 { 0387 if (!m_openSubDirsTimer->isActive() && (m_subDirsJob == nullptr)) { 0388 m_openSubDirsTimer->start(); 0389 } 0390 } 0391 0392 void KUrlNavigatorButton::startSubDirsJob() 0393 { 0394 if (m_subDirsJob != nullptr) { 0395 return; 0396 } 0397 0398 const QUrl url = m_replaceButton ? KIO::upUrl(m_url) : m_url; 0399 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent()); 0400 Q_ASSERT(urlNavigator); 0401 m_subDirsJob = KIO::listDir(url, KIO::HideProgressInfo, urlNavigator->showHiddenFolders()); 0402 m_subDirs.clear(); // just to be ++safe 0403 0404 connect(m_subDirsJob, &KIO::ListJob::entries, this, &KUrlNavigatorButton::addEntriesToSubDirs); 0405 0406 if (m_replaceButton) { 0407 connect(m_subDirsJob, &KJob::result, this, &KUrlNavigatorButton::replaceButton); 0408 } else { 0409 connect(m_subDirsJob, &KJob::result, this, &KUrlNavigatorButton::openSubDirsMenu); 0410 } 0411 } 0412 0413 void KUrlNavigatorButton::addEntriesToSubDirs(KIO::Job *job, const KIO::UDSEntryList &entries) 0414 { 0415 Q_ASSERT(job == m_subDirsJob); 0416 Q_UNUSED(job); 0417 0418 for (const KIO::UDSEntry &entry : entries) { 0419 if (entry.isDir()) { 0420 const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME); 0421 QString displayName = entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME); 0422 if (displayName.isEmpty()) { 0423 displayName = name; 0424 } 0425 if (name != QLatin1String(".") && name != QLatin1String("..")) { 0426 m_subDirs.push_back({name, displayName}); 0427 } 0428 } 0429 } 0430 } 0431 0432 void KUrlNavigatorButton::slotUrlsDropped(QAction *action, QDropEvent *event) 0433 { 0434 const int result = action->data().toInt(); 0435 QUrl url(m_url); 0436 url.setPath(Utils::concatPaths(url.path(), m_subDirs.at(result).name)); 0437 Q_EMIT urlsDroppedOnNavButton(url, event); 0438 } 0439 0440 void KUrlNavigatorButton::slotMenuActionClicked(QAction *action, Qt::MouseButton button) 0441 { 0442 const int result = action->data().toInt(); 0443 QUrl url(m_url); 0444 url.setPath(Utils::concatPaths(url.path(), m_subDirs.at(result).name)); 0445 Q_EMIT navigatorButtonActivated(url, button, Qt::NoModifier); 0446 } 0447 0448 void KUrlNavigatorButton::statFinished(KJob *job) 0449 { 0450 if (m_pendingTextChange) { 0451 m_pendingTextChange = false; 0452 0453 const KIO::UDSEntry entry = static_cast<KIO::StatJob *>(job)->statResult(); 0454 QString name = entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME); 0455 if (name.isEmpty()) { 0456 name = m_url.fileName(); 0457 } 0458 setText(name); 0459 0460 Q_EMIT finishedTextResolving(); 0461 } 0462 } 0463 0464 /** 0465 * Helper struct for sorting folder names 0466 */ 0467 struct FolderNameNaturalLessThan { 0468 FolderNameNaturalLessThan(bool sortHiddenLast) 0469 : m_sortHiddenLast(sortHiddenLast) 0470 { 0471 m_collator.setCaseSensitivity(Qt::CaseInsensitive); 0472 m_collator.setNumericMode(true); 0473 } 0474 0475 bool operator()(const KUrlNavigatorButton::SubDirInfo &a, const KUrlNavigatorButton::SubDirInfo &b) 0476 { 0477 if (m_sortHiddenLast) { 0478 const bool isHiddenA = a.name.startsWith(QLatin1Char('.')); 0479 const bool isHiddenB = b.name.startsWith(QLatin1Char('.')); 0480 if (isHiddenA && !isHiddenB) { 0481 return false; 0482 } 0483 if (!isHiddenA && isHiddenB) { 0484 return true; 0485 } 0486 } 0487 return m_collator.compare(a.name, b.name) < 0; 0488 } 0489 0490 private: 0491 QCollator m_collator; 0492 bool m_sortHiddenLast; 0493 }; 0494 0495 void KUrlNavigatorButton::openSubDirsMenu(KJob *job) 0496 { 0497 Q_ASSERT(job == m_subDirsJob); 0498 m_subDirsJob = nullptr; 0499 0500 if (job->error() || m_subDirs.empty()) { 0501 // clear listing 0502 return; 0503 } 0504 0505 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent()); 0506 Q_ASSERT(urlNavigator); 0507 FolderNameNaturalLessThan less(urlNavigator->showHiddenFolders() && urlNavigator->sortHiddenFoldersLast()); 0508 std::sort(m_subDirs.begin(), m_subDirs.end(), less); 0509 setDisplayHintEnabled(PopupActiveHint, true); 0510 update(); // ensure the button is drawn highlighted 0511 0512 if (m_subDirsMenu != nullptr) { 0513 m_subDirsMenu->close(); 0514 m_subDirsMenu->deleteLater(); 0515 m_subDirsMenu = nullptr; 0516 } 0517 0518 m_subDirsMenu = new KUrlNavigatorMenu(this); 0519 initMenu(m_subDirsMenu, 0); 0520 0521 const bool leftToRight = (layoutDirection() == Qt::LeftToRight); 0522 const int popupX = leftToRight ? width() - arrowWidth() - BorderWidth : 0; 0523 const QPoint popupPos = parentWidget()->mapToGlobal(geometry().bottomLeft() + QPoint(popupX, 0)); 0524 0525 QPointer<QObject> guard(this); 0526 0527 m_subDirsMenu->exec(popupPos); 0528 0529 // If 'this' has been deleted in the menu's nested event loop, we have to return 0530 // immediately because any access to a member variable might cause a crash. 0531 if (!guard) { 0532 return; 0533 } 0534 0535 m_subDirs.clear(); 0536 delete m_subDirsMenu; 0537 m_subDirsMenu = nullptr; 0538 0539 setDisplayHintEnabled(PopupActiveHint, false); 0540 } 0541 0542 void KUrlNavigatorButton::replaceButton(KJob *job) 0543 { 0544 Q_ASSERT(job == m_subDirsJob); 0545 m_subDirsJob = nullptr; 0546 m_replaceButton = false; 0547 0548 if (job->error() || m_subDirs.empty()) { 0549 return; 0550 } 0551 0552 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent()); 0553 Q_ASSERT(urlNavigator); 0554 FolderNameNaturalLessThan less(urlNavigator->showHiddenFolders() && urlNavigator->sortHiddenFoldersLast()); 0555 std::sort(m_subDirs.begin(), m_subDirs.end(), less); 0556 0557 // Get index of the directory that is shown currently in the button 0558 const QString currentDir = m_url.fileName(); 0559 int currentIndex = 0; 0560 const int subDirsCount = m_subDirs.size(); 0561 while (currentIndex < subDirsCount) { 0562 if (m_subDirs[currentIndex].name == currentDir) { 0563 break; 0564 } 0565 ++currentIndex; 0566 } 0567 0568 // Adjust the index by respecting the wheel steps and 0569 // trigger a replacing of the button content 0570 int targetIndex = currentIndex - m_wheelSteps; 0571 if (targetIndex < 0) { 0572 targetIndex = 0; 0573 } else if (targetIndex >= subDirsCount) { 0574 targetIndex = subDirsCount - 1; 0575 } 0576 0577 QUrl url(KIO::upUrl(m_url)); 0578 url.setPath(Utils::concatPaths(url.path(), m_subDirs[targetIndex].name)); 0579 Q_EMIT navigatorButtonActivated(url, Qt::LeftButton, Qt::NoModifier); 0580 0581 m_subDirs.clear(); 0582 } 0583 0584 void KUrlNavigatorButton::cancelSubDirsRequest() 0585 { 0586 m_openSubDirsTimer->stop(); 0587 if (m_subDirsJob != nullptr) { 0588 m_subDirsJob->kill(); 0589 m_subDirsJob = nullptr; 0590 } 0591 } 0592 0593 QString KUrlNavigatorButton::plainText() const 0594 { 0595 // Replace all "&&" by '&' and remove all single 0596 // '&' characters 0597 const QString source = text(); 0598 const int sourceLength = source.length(); 0599 0600 QString dest; 0601 dest.resize(sourceLength); 0602 0603 int sourceIndex = 0; 0604 int destIndex = 0; 0605 while (sourceIndex < sourceLength) { 0606 if (source.at(sourceIndex) == QLatin1Char('&')) { 0607 ++sourceIndex; 0608 if (sourceIndex >= sourceLength) { 0609 break; 0610 } 0611 } 0612 dest[destIndex] = source.at(sourceIndex); 0613 ++sourceIndex; 0614 ++destIndex; 0615 } 0616 0617 dest.resize(destIndex); 0618 0619 return dest; 0620 } 0621 0622 int KUrlNavigatorButton::arrowWidth() const 0623 { 0624 // if there isn't arrow then return 0 0625 int width = 0; 0626 if (!m_subDir.isEmpty()) { 0627 width = height() / 2; 0628 if (width < 4) { 0629 width = 4; 0630 } 0631 } 0632 0633 return width; 0634 } 0635 0636 bool KUrlNavigatorButton::isAboveArrow(int x) const 0637 { 0638 const bool leftToRight = (layoutDirection() == Qt::LeftToRight); 0639 return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth()); 0640 } 0641 0642 bool KUrlNavigatorButton::isTextClipped() const 0643 { 0644 int availableWidth = width() - 2 * BorderWidth; 0645 if (!m_subDir.isEmpty()) { 0646 availableWidth -= arrowWidth() - BorderWidth; 0647 } 0648 0649 QFont adjustedFont(font()); 0650 adjustedFont.setBold(m_subDir.isEmpty()); 0651 return QFontMetrics(adjustedFont).size(Qt::TextSingleLine, plainText()).width() >= availableWidth; 0652 } 0653 0654 void KUrlNavigatorButton::updateMinimumWidth() 0655 { 0656 const int oldMinWidth = minimumWidth(); 0657 0658 int minWidth = sizeHint().width(); 0659 if (minWidth < 40) { 0660 minWidth = 40; 0661 } else if (minWidth > 150) { 0662 // don't let an overlong path name waste all the URL navigator space 0663 minWidth = 150; 0664 } 0665 if (oldMinWidth != minWidth) { 0666 setMinimumWidth(minWidth); 0667 } 0668 } 0669 0670 void KUrlNavigatorButton::initMenu(KUrlNavigatorMenu *menu, int startIndex) 0671 { 0672 connect(menu, &KUrlNavigatorMenu::mouseButtonClicked, this, &KUrlNavigatorButton::slotMenuActionClicked); 0673 connect(menu, &KUrlNavigatorMenu::urlsDropped, this, &KUrlNavigatorButton::slotUrlsDropped); 0674 0675 // So that triggering a menu item with the keyboard works 0676 connect(menu, &QMenu::triggered, this, [this](QAction *act) { 0677 slotMenuActionClicked(act, Qt::LeftButton); 0678 }); 0679 0680 menu->setLayoutDirection(Qt::LeftToRight); 0681 0682 const int maxIndex = startIndex + 30; // Don't show more than 30 items in a menu 0683 const int subDirsSize = m_subDirs.size(); 0684 const int lastIndex = std::min(subDirsSize - 1, maxIndex); 0685 for (int i = startIndex; i <= lastIndex; ++i) { 0686 const auto &[subDirName, subDirDisplayName] = m_subDirs[i]; 0687 QString text = KStringHandler::csqueeze(subDirDisplayName, 60); 0688 text.replace(QLatin1Char('&'), QLatin1String("&&")); 0689 QAction *action = new QAction(text, this); 0690 if (m_subDir == subDirName) { 0691 QFont font(action->font()); 0692 font.setBold(true); 0693 action->setFont(font); 0694 } 0695 action->setData(i); 0696 menu->addAction(action); 0697 } 0698 if (subDirsSize > maxIndex) { 0699 // If too much items are shown, move them into a sub menu 0700 menu->addSeparator(); 0701 KUrlNavigatorMenu *subDirsMenu = new KUrlNavigatorMenu(menu); 0702 subDirsMenu->setTitle(i18nc("@action:inmenu", "More")); 0703 initMenu(subDirsMenu, maxIndex); 0704 menu->addMenu(subDirsMenu); 0705 } 0706 } 0707 0708 } // namespace KDEPrivate 0709 0710 #include "moc_kurlnavigatorbutton_p.cpp"