File indexing completed on 2024-03-24 03:58:16
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 void KUrlNavigatorButton::enterEvent(QEnterEvent *event) 0244 { 0245 KUrlNavigatorButtonBase::enterEvent(event); 0246 0247 // if the text is clipped due to a small window width, the text should 0248 // be shown as tooltip 0249 if (isTextClipped()) { 0250 setToolTip(plainText()); 0251 } 0252 } 0253 0254 void KUrlNavigatorButton::leaveEvent(QEvent *event) 0255 { 0256 KUrlNavigatorButtonBase::leaveEvent(event); 0257 setToolTip(QString()); 0258 0259 if (m_hoverArrow) { 0260 m_hoverArrow = false; 0261 update(); 0262 } 0263 } 0264 0265 void KUrlNavigatorButton::keyPressEvent(QKeyEvent *event) 0266 { 0267 switch (event->key()) { 0268 case Qt::Key_Enter: 0269 case Qt::Key_Return: 0270 Q_EMIT navigatorButtonActivated(m_url, Qt::LeftButton, event->modifiers()); 0271 break; 0272 case Qt::Key_Down: 0273 case Qt::Key_Space: 0274 startSubDirsJob(); 0275 break; 0276 default: 0277 KUrlNavigatorButtonBase::keyPressEvent(event); 0278 } 0279 } 0280 0281 void KUrlNavigatorButton::dropEvent(QDropEvent *event) 0282 { 0283 if (event->mimeData()->hasUrls()) { 0284 setDisplayHintEnabled(DraggedHint, true); 0285 0286 Q_EMIT urlsDroppedOnNavButton(m_url, event); 0287 0288 setDisplayHintEnabled(DraggedHint, false); 0289 update(); 0290 } 0291 } 0292 0293 void KUrlNavigatorButton::dragEnterEvent(QDragEnterEvent *event) 0294 { 0295 if (event->mimeData()->hasUrls()) { 0296 setDisplayHintEnabled(DraggedHint, true); 0297 event->acceptProposedAction(); 0298 0299 update(); 0300 } 0301 } 0302 0303 void KUrlNavigatorButton::dragMoveEvent(QDragMoveEvent *event) 0304 { 0305 QRect rect = event->answerRect(); 0306 if (isAboveArrow(rect.center().x())) { 0307 m_hoverArrow = true; 0308 update(); 0309 0310 if (m_subDirsMenu == nullptr) { 0311 requestSubDirs(); 0312 } else if (m_subDirsMenu->parent() != this) { 0313 m_subDirsMenu->close(); 0314 m_subDirsMenu->deleteLater(); 0315 m_subDirsMenu = nullptr; 0316 0317 requestSubDirs(); 0318 } 0319 } else { 0320 if (m_openSubDirsTimer->isActive()) { 0321 cancelSubDirsRequest(); 0322 } 0323 m_subDirsMenu->deleteLater(); 0324 m_subDirsMenu = nullptr; 0325 m_hoverArrow = false; 0326 update(); 0327 } 0328 } 0329 0330 void KUrlNavigatorButton::dragLeaveEvent(QDragLeaveEvent *event) 0331 { 0332 KUrlNavigatorButtonBase::dragLeaveEvent(event); 0333 0334 m_hoverArrow = false; 0335 setDisplayHintEnabled(DraggedHint, false); 0336 update(); 0337 } 0338 0339 void KUrlNavigatorButton::mousePressEvent(QMouseEvent *event) 0340 { 0341 if (isAboveArrow(qRound(event->position().x())) && (event->button() == Qt::LeftButton)) { 0342 // the mouse is pressed above the [>] button 0343 startSubDirsJob(); 0344 } 0345 KUrlNavigatorButtonBase::mousePressEvent(event); 0346 } 0347 0348 void KUrlNavigatorButton::mouseReleaseEvent(QMouseEvent *event) 0349 { 0350 if (!isAboveArrow(qRound(event->position().x())) || (event->button() != Qt::LeftButton)) { 0351 // the mouse has been released above the text area and not 0352 // above the [>] button 0353 Q_EMIT navigatorButtonActivated(m_url, event->button(), event->modifiers()); 0354 cancelSubDirsRequest(); 0355 } 0356 KUrlNavigatorButtonBase::mouseReleaseEvent(event); 0357 } 0358 0359 void KUrlNavigatorButton::mouseMoveEvent(QMouseEvent *event) 0360 { 0361 KUrlNavigatorButtonBase::mouseMoveEvent(event); 0362 0363 const bool hoverArrow = isAboveArrow(qRound(event->position().x())); 0364 if (hoverArrow != m_hoverArrow) { 0365 m_hoverArrow = hoverArrow; 0366 update(); 0367 } 0368 } 0369 0370 void KUrlNavigatorButton::wheelEvent(QWheelEvent *event) 0371 { 0372 if (event->angleDelta().y() != 0) { 0373 m_wheelSteps = event->angleDelta().y() / 120; 0374 m_replaceButton = true; 0375 startSubDirsJob(); 0376 } 0377 0378 KUrlNavigatorButtonBase::wheelEvent(event); 0379 } 0380 0381 void KUrlNavigatorButton::requestSubDirs() 0382 { 0383 if (!m_openSubDirsTimer->isActive() && (m_subDirsJob == nullptr)) { 0384 m_openSubDirsTimer->start(); 0385 } 0386 } 0387 0388 void KUrlNavigatorButton::startSubDirsJob() 0389 { 0390 if (m_subDirsJob != nullptr) { 0391 return; 0392 } 0393 0394 const QUrl url = m_replaceButton ? KIO::upUrl(m_url) : m_url; 0395 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent()); 0396 Q_ASSERT(urlNavigator); 0397 m_subDirsJob = 0398 KIO::listDir(url, KIO::HideProgressInfo, urlNavigator->showHiddenFolders() ? KIO::ListJob::ListFlag::IncludeHidden : KIO::ListJob::ListFlags{}); 0399 m_subDirs.clear(); // just to be ++safe 0400 0401 connect(m_subDirsJob, &KIO::ListJob::entries, this, &KUrlNavigatorButton::addEntriesToSubDirs); 0402 0403 if (m_replaceButton) { 0404 connect(m_subDirsJob, &KJob::result, this, &KUrlNavigatorButton::replaceButton); 0405 } else { 0406 connect(m_subDirsJob, &KJob::result, this, &KUrlNavigatorButton::openSubDirsMenu); 0407 } 0408 } 0409 0410 void KUrlNavigatorButton::addEntriesToSubDirs(KIO::Job *job, const KIO::UDSEntryList &entries) 0411 { 0412 Q_ASSERT(job == m_subDirsJob); 0413 Q_UNUSED(job); 0414 0415 for (const KIO::UDSEntry &entry : entries) { 0416 if (entry.isDir()) { 0417 const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME); 0418 QString displayName = entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME); 0419 if (displayName.isEmpty()) { 0420 displayName = name; 0421 } 0422 if (name != QLatin1String(".") && name != QLatin1String("..")) { 0423 m_subDirs.push_back({name, displayName}); 0424 } 0425 } 0426 } 0427 } 0428 0429 void KUrlNavigatorButton::slotUrlsDropped(QAction *action, QDropEvent *event) 0430 { 0431 const int result = action->data().toInt(); 0432 QUrl url(m_url); 0433 url.setPath(Utils::concatPaths(url.path(), m_subDirs.at(result).name)); 0434 Q_EMIT urlsDroppedOnNavButton(url, event); 0435 } 0436 0437 void KUrlNavigatorButton::slotMenuActionClicked(QAction *action, Qt::MouseButton button) 0438 { 0439 const int result = action->data().toInt(); 0440 QUrl url(m_url); 0441 url.setPath(Utils::concatPaths(url.path(), m_subDirs.at(result).name)); 0442 Q_EMIT navigatorButtonActivated(url, button, Qt::NoModifier); 0443 } 0444 0445 void KUrlNavigatorButton::statFinished(KJob *job) 0446 { 0447 if (m_pendingTextChange) { 0448 m_pendingTextChange = false; 0449 0450 const KIO::UDSEntry entry = static_cast<KIO::StatJob *>(job)->statResult(); 0451 QString name = entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME); 0452 if (name.isEmpty()) { 0453 name = m_url.fileName(); 0454 } 0455 setText(name); 0456 0457 Q_EMIT finishedTextResolving(); 0458 } 0459 } 0460 0461 /** 0462 * Helper struct for sorting folder names 0463 */ 0464 struct FolderNameNaturalLessThan { 0465 FolderNameNaturalLessThan(bool sortHiddenLast) 0466 : m_sortHiddenLast(sortHiddenLast) 0467 { 0468 m_collator.setCaseSensitivity(Qt::CaseInsensitive); 0469 m_collator.setNumericMode(true); 0470 } 0471 0472 bool operator()(const KUrlNavigatorButton::SubDirInfo &a, const KUrlNavigatorButton::SubDirInfo &b) 0473 { 0474 if (m_sortHiddenLast) { 0475 const bool isHiddenA = a.name.startsWith(QLatin1Char('.')); 0476 const bool isHiddenB = b.name.startsWith(QLatin1Char('.')); 0477 if (isHiddenA && !isHiddenB) { 0478 return false; 0479 } 0480 if (!isHiddenA && isHiddenB) { 0481 return true; 0482 } 0483 } 0484 return m_collator.compare(a.name, b.name) < 0; 0485 } 0486 0487 private: 0488 QCollator m_collator; 0489 bool m_sortHiddenLast; 0490 }; 0491 0492 void KUrlNavigatorButton::openSubDirsMenu(KJob *job) 0493 { 0494 Q_ASSERT(job == m_subDirsJob); 0495 m_subDirsJob = nullptr; 0496 0497 if (job->error() || m_subDirs.empty()) { 0498 // clear listing 0499 return; 0500 } 0501 0502 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent()); 0503 Q_ASSERT(urlNavigator); 0504 FolderNameNaturalLessThan less(urlNavigator->showHiddenFolders() && urlNavigator->sortHiddenFoldersLast()); 0505 std::sort(m_subDirs.begin(), m_subDirs.end(), less); 0506 setDisplayHintEnabled(PopupActiveHint, true); 0507 update(); // ensure the button is drawn highlighted 0508 0509 if (m_subDirsMenu != nullptr) { 0510 m_subDirsMenu->close(); 0511 m_subDirsMenu->deleteLater(); 0512 m_subDirsMenu = nullptr; 0513 } 0514 0515 m_subDirsMenu = new KUrlNavigatorMenu(this); 0516 initMenu(m_subDirsMenu, 0); 0517 0518 const bool leftToRight = (layoutDirection() == Qt::LeftToRight); 0519 const int popupX = leftToRight ? width() - arrowWidth() - BorderWidth : 0; 0520 const QPoint popupPos = parentWidget()->mapToGlobal(geometry().bottomLeft() + QPoint(popupX, 0)); 0521 0522 QPointer<QObject> guard(this); 0523 0524 m_subDirsMenu->exec(popupPos); 0525 0526 // If 'this' has been deleted in the menu's nested event loop, we have to return 0527 // immediately because any access to a member variable might cause a crash. 0528 if (!guard) { 0529 return; 0530 } 0531 0532 m_subDirs.clear(); 0533 delete m_subDirsMenu; 0534 m_subDirsMenu = nullptr; 0535 0536 setDisplayHintEnabled(PopupActiveHint, false); 0537 } 0538 0539 void KUrlNavigatorButton::replaceButton(KJob *job) 0540 { 0541 Q_ASSERT(job == m_subDirsJob); 0542 m_subDirsJob = nullptr; 0543 m_replaceButton = false; 0544 0545 if (job->error() || m_subDirs.empty()) { 0546 return; 0547 } 0548 0549 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent()); 0550 Q_ASSERT(urlNavigator); 0551 FolderNameNaturalLessThan less(urlNavigator->showHiddenFolders() && urlNavigator->sortHiddenFoldersLast()); 0552 std::sort(m_subDirs.begin(), m_subDirs.end(), less); 0553 0554 // Get index of the directory that is shown currently in the button 0555 const QString currentDir = m_url.fileName(); 0556 int currentIndex = 0; 0557 const int subDirsCount = m_subDirs.size(); 0558 while (currentIndex < subDirsCount) { 0559 if (m_subDirs[currentIndex].name == currentDir) { 0560 break; 0561 } 0562 ++currentIndex; 0563 } 0564 0565 // Adjust the index by respecting the wheel steps and 0566 // trigger a replacing of the button content 0567 int targetIndex = currentIndex - m_wheelSteps; 0568 if (targetIndex < 0) { 0569 targetIndex = 0; 0570 } else if (targetIndex >= subDirsCount) { 0571 targetIndex = subDirsCount - 1; 0572 } 0573 0574 QUrl url(KIO::upUrl(m_url)); 0575 url.setPath(Utils::concatPaths(url.path(), m_subDirs[targetIndex].name)); 0576 Q_EMIT navigatorButtonActivated(url, Qt::LeftButton, Qt::NoModifier); 0577 0578 m_subDirs.clear(); 0579 } 0580 0581 void KUrlNavigatorButton::cancelSubDirsRequest() 0582 { 0583 m_openSubDirsTimer->stop(); 0584 if (m_subDirsJob != nullptr) { 0585 m_subDirsJob->kill(); 0586 m_subDirsJob = nullptr; 0587 } 0588 } 0589 0590 QString KUrlNavigatorButton::plainText() const 0591 { 0592 // Replace all "&&" by '&' and remove all single 0593 // '&' characters 0594 const QString source = text(); 0595 const int sourceLength = source.length(); 0596 0597 QString dest; 0598 dest.resize(sourceLength); 0599 0600 int sourceIndex = 0; 0601 int destIndex = 0; 0602 while (sourceIndex < sourceLength) { 0603 if (source.at(sourceIndex) == QLatin1Char('&')) { 0604 ++sourceIndex; 0605 if (sourceIndex >= sourceLength) { 0606 break; 0607 } 0608 } 0609 dest[destIndex] = source.at(sourceIndex); 0610 ++sourceIndex; 0611 ++destIndex; 0612 } 0613 0614 dest.resize(destIndex); 0615 0616 return dest; 0617 } 0618 0619 int KUrlNavigatorButton::arrowWidth() const 0620 { 0621 // if there isn't arrow then return 0 0622 int width = 0; 0623 if (!m_subDir.isEmpty()) { 0624 width = height() / 2; 0625 if (width < 4) { 0626 width = 4; 0627 } 0628 } 0629 0630 return width; 0631 } 0632 0633 bool KUrlNavigatorButton::isAboveArrow(int x) const 0634 { 0635 const bool leftToRight = (layoutDirection() == Qt::LeftToRight); 0636 return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth()); 0637 } 0638 0639 bool KUrlNavigatorButton::isTextClipped() const 0640 { 0641 int availableWidth = width() - 2 * BorderWidth; 0642 if (!m_subDir.isEmpty()) { 0643 availableWidth -= arrowWidth() - BorderWidth; 0644 } 0645 0646 QFont adjustedFont(font()); 0647 adjustedFont.setBold(m_subDir.isEmpty()); 0648 return QFontMetrics(adjustedFont).size(Qt::TextSingleLine, plainText()).width() >= availableWidth; 0649 } 0650 0651 void KUrlNavigatorButton::updateMinimumWidth() 0652 { 0653 const int oldMinWidth = minimumWidth(); 0654 0655 int minWidth = sizeHint().width(); 0656 if (minWidth < 40) { 0657 minWidth = 40; 0658 } else if (minWidth > 150) { 0659 // don't let an overlong path name waste all the URL navigator space 0660 minWidth = 150; 0661 } 0662 if (oldMinWidth != minWidth) { 0663 setMinimumWidth(minWidth); 0664 } 0665 } 0666 0667 void KUrlNavigatorButton::initMenu(KUrlNavigatorMenu *menu, int startIndex) 0668 { 0669 connect(menu, &KUrlNavigatorMenu::mouseButtonClicked, this, &KUrlNavigatorButton::slotMenuActionClicked); 0670 connect(menu, &KUrlNavigatorMenu::urlsDropped, this, &KUrlNavigatorButton::slotUrlsDropped); 0671 0672 // So that triggering a menu item with the keyboard works 0673 connect(menu, &QMenu::triggered, this, [this](QAction *act) { 0674 slotMenuActionClicked(act, Qt::LeftButton); 0675 }); 0676 0677 menu->setLayoutDirection(Qt::LeftToRight); 0678 0679 const int maxIndex = startIndex + 30; // Don't show more than 30 items in a menu 0680 const int subDirsSize = m_subDirs.size(); 0681 const int lastIndex = std::min(subDirsSize - 1, maxIndex); 0682 for (int i = startIndex; i <= lastIndex; ++i) { 0683 const auto &[subDirName, subDirDisplayName] = m_subDirs[i]; 0684 QString text = KStringHandler::csqueeze(subDirDisplayName, 60); 0685 text.replace(QLatin1Char('&'), QLatin1String("&&")); 0686 QAction *action = new QAction(text, this); 0687 if (m_subDir == subDirName) { 0688 QFont font(action->font()); 0689 font.setBold(true); 0690 action->setFont(font); 0691 } 0692 action->setData(i); 0693 menu->addAction(action); 0694 } 0695 if (subDirsSize > maxIndex) { 0696 // If too much items are shown, move them into a sub menu 0697 menu->addSeparator(); 0698 KUrlNavigatorMenu *subDirsMenu = new KUrlNavigatorMenu(menu); 0699 subDirsMenu->setTitle(i18nc("@action:inmenu", "More")); 0700 initMenu(subDirsMenu, maxIndex); 0701 menu->addMenu(subDirsMenu); 0702 } 0703 } 0704 0705 } // namespace KDEPrivate 0706 0707 #include "moc_kurlnavigatorbutton_p.cpp"