8#include "kurlnavigatorbutton_p.h"
10#include "../utils_p.h"
11#include "kurlnavigator.h"
12#include "kurlnavigatormenu_p.h"
13#include <kio/listjob.h>
14#include <kio/statjob.h>
16#include <KLocalizedString>
17#include <KStringHandler>
23#include <QStyleOption>
28QPointer<KUrlNavigatorMenu> KUrlNavigatorButton::m_subDirsMenu;
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)
41 , m_openSubDirsTimer(nullptr)
42 , m_subDirsJob(nullptr)
47 setMouseTracking(
true);
49 m_openSubDirsTimer =
new QTimer(
this);
50 m_openSubDirsTimer->setSingleShot(
true);
51 m_openSubDirsTimer->setInterval(300);
57KUrlNavigatorButton::~KUrlNavigatorButton()
61void KUrlNavigatorButton::setUrl(
const QUrl &url)
70 QStringLiteral(
"nfs"),
71 QStringLiteral(
"fish"),
72 QStringLiteral(
"ftp"),
73 QStringLiteral(
"sftp"),
74 QStringLiteral(
"smb"),
75 QStringLiteral(
"webdav"),
76 QStringLiteral(
"mtp"),
79 const bool startTextResolving = m_url.isValid() && !m_url.isLocalFile() && !protocolBlacklist.
contains(m_url.scheme());
81 if (startTextResolving) {
82 m_pendingTextChange =
true;
85 Q_EMIT startedTextResolving();
92QUrl KUrlNavigatorButton::url()
const
97void KUrlNavigatorButton::setText(
const QString &text)
101 adjustedText = m_url.scheme();
106 KUrlNavigatorButtonBase::setText(adjustedText);
107 updateMinimumWidth();
111 m_pendingTextChange =
false;
114void KUrlNavigatorButton::setActiveSubDirectory(
const QString &subDir)
123QString KUrlNavigatorButton::activeSubDirectory()
const
128QSize KUrlNavigatorButton::sizeHint()
const
130 QFont adjustedFont(font());
131 adjustedFont.setBold(m_subDir.isEmpty());
135 const int width = m_padding + textWidth() + arrowWidth() + m_padding;
136 return QSize(width, KUrlNavigatorButtonBase::sizeHint().height());
139void KUrlNavigatorButton::setShowMnemonic(
bool show)
141 if (m_showMnemonic != show) {
142 m_showMnemonic = show;
147bool KUrlNavigatorButton::showMnemonic()
const
149 return m_showMnemonic;
152void KUrlNavigatorButton::setDrawSeparator(
bool draw)
154 if (m_drawSeparator != draw) {
155 m_drawSeparator = draw;
160bool KUrlNavigatorButton::drawSeparator()
const
162 return m_drawSeparator;
165void KUrlNavigatorButton::paintEvent(
QPaintEvent *event)
171 QFont adjustedFont(font());
172 adjustedFont.setBold(m_subDir.isEmpty());
173 painter.setFont(adjustedFont);
175 int buttonWidth = width();
176 int arrowWidth = KUrlNavigatorButton::arrowWidth();
178 int preferredWidth = sizeHint().width();
179 if (preferredWidth < minimumWidth()) {
180 preferredWidth = minimumWidth();
182 if (buttonWidth > preferredWidth) {
183 buttonWidth = preferredWidth;
185 const int buttonHeight = height();
186 const QColor fgColor = foregroundColor();
191 const int textRectWidth = buttonWidth - arrowWidth - m_padding;
193 textRect =
QRect(m_padding, 0, textRectWidth, buttonHeight);
196 textRect =
QRect(m_drawSeparator ? arrowWidth : 0, 0, textRectWidth, buttonHeight);
199 drawHoverBackground(&painter);
202 painter.setPen(fgColor);
203 const bool clipped = isTextClipped();
209 gradient.setFinalStop(
QPoint(gradient.finalStop().x() - m_padding, gradient.finalStop().y()));
210 gradient.setColorAt(0.8, fgColor);
211 gradient.setColorAt(1.0, bgColor);
213 gradient.setStart(
QPoint(gradient.start().x() + m_padding, gradient.start().y()));
214 gradient.setColorAt(0.0, bgColor);
215 gradient.setColorAt(0.2, fgColor);
225 if (m_showMnemonic) {
227 painter.drawText(textRect, textFlags, text());
229 painter.drawText(textRect, textFlags, plainText());
233 if (m_drawSeparator) {
236 option.palette = palette();
242 option.rect =
QRect(textRect.
right(), 0, arrowWidth, buttonHeight);
245 option.rect =
QRect(0, 0, arrowWidth, buttonHeight);
248 if (!m_hoverOverArrow) {
255void KUrlNavigatorButton::enterEvent(
QEnterEvent *event)
257 KUrlNavigatorButtonBase::enterEvent(event);
261 if (isTextClipped()) {
262 setToolTip(plainText());
264 if (!m_hoverOverButton) {
265 m_hoverOverButton =
true;
270void KUrlNavigatorButton::leaveEvent(
QEvent *event)
272 KUrlNavigatorButtonBase::leaveEvent(event);
275 if (m_hoverOverArrow) {
276 m_hoverOverArrow =
false;
279 if (m_hoverOverButton) {
280 m_hoverOverButton =
false;
285void KUrlNavigatorButton::keyPressEvent(
QKeyEvent *event)
287 switch (
event->key()) {
297 KUrlNavigatorButtonBase::keyPressEvent(event);
301void KUrlNavigatorButton::dropEvent(
QDropEvent *event)
303 if (
event->mimeData()->hasUrls()) {
304 setDisplayHintEnabled(DraggedHint,
true);
306 Q_EMIT urlsDroppedOnNavButton(m_url, event);
308 setDisplayHintEnabled(DraggedHint,
false);
315 if (
event->mimeData()->hasUrls()) {
316 setDisplayHintEnabled(DraggedHint,
true);
317 event->acceptProposedAction();
325 QRect rect =
event->answerRect();
327 if (isAboveSeparator(rect.
center().
x())) {
328 m_hoverOverArrow =
true;
331 if (m_subDirsMenu ==
nullptr) {
333 }
else if (m_subDirsMenu->parent() !=
this) {
334 m_subDirsMenu->close();
335 m_subDirsMenu->deleteLater();
336 m_subDirsMenu =
nullptr;
341 if (m_openSubDirsTimer->isActive()) {
342 cancelSubDirsRequest();
345 m_subDirsMenu->deleteLater();
346 m_subDirsMenu =
nullptr;
348 m_hoverOverArrow =
false;
355 KUrlNavigatorButtonBase::dragLeaveEvent(event);
357 m_hoverOverArrow =
false;
358 setDisplayHintEnabled(DraggedHint,
false);
362void KUrlNavigatorButton::mousePressEvent(
QMouseEvent *event)
368 KUrlNavigatorButtonBase::mousePressEvent(event);
371void KUrlNavigatorButton::mouseReleaseEvent(
QMouseEvent *event)
376 Q_EMIT navigatorButtonActivated(m_url,
event->button(),
event->modifiers());
377 cancelSubDirsRequest();
379 KUrlNavigatorButtonBase::mouseReleaseEvent(event);
382void KUrlNavigatorButton::mouseMoveEvent(
QMouseEvent *event)
384 KUrlNavigatorButtonBase::mouseMoveEvent(event);
386 const bool hoverOverIcon = isAboveSeparator(qRound(
event->position().x()));
387 if (hoverOverIcon != m_hoverOverArrow) {
388 m_hoverOverArrow = hoverOverIcon;
393void KUrlNavigatorButton::wheelEvent(
QWheelEvent *event)
395 if (
event->angleDelta().y() != 0) {
396 m_wheelSteps =
event->angleDelta().y() / 120;
397 m_replaceButton =
true;
401 KUrlNavigatorButtonBase::wheelEvent(event);
404void KUrlNavigatorButton::requestSubDirs()
406 if (!m_openSubDirsTimer->isActive() && (m_subDirsJob ==
nullptr)) {
407 m_openSubDirsTimer->start();
411void KUrlNavigatorButton::startSubDirsJob()
413 if (m_subDirsJob !=
nullptr) {
418 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
419 Q_ASSERT(urlNavigator);
426 if (m_replaceButton) {
435 Q_ASSERT(job == m_subDirsJob);
454 const int result = action->
data().
toInt();
456 url.
setPath(Utils::concatPaths(url.
path(), m_subDirs.
at(result).name));
457 Q_EMIT urlsDroppedOnNavButton(url, event);
462 const int result = action->
data().
toInt();
464 url.
setPath(Utils::concatPaths(url.
path(), m_subDirs.
at(result).name));
468void KUrlNavigatorButton::statFinished(
KJob *job)
472 if (m_pendingTextChange) {
473 m_pendingTextChange =
false;
477 name = m_url.fileName();
481 Q_EMIT finishedTextResolving();
493struct FolderNameNaturalLessThan {
494 FolderNameNaturalLessThan(
bool sortHiddenLast)
495 : m_sortHiddenLast(sortHiddenLast)
498 m_collator.setNumericMode(
true);
501 bool operator()(
const KUrlNavigatorButton::SubDirInfo &a,
const KUrlNavigatorButton::SubDirInfo &b)
503 if (m_sortHiddenLast) {
504 const bool isHiddenA = a.name.startsWith(QLatin1Char(
'.'));
505 const bool isHiddenB = b.name.startsWith(QLatin1Char(
'.'));
506 if (isHiddenA && !isHiddenB) {
509 if (!isHiddenA && isHiddenB) {
513 return m_collator.compare(a.name, b.name) < 0;
517 QCollator m_collator;
518 bool m_sortHiddenLast;
521void KUrlNavigatorButton::openSubDirsMenu(
KJob *job)
523 Q_ASSERT(job == m_subDirsJob);
524 m_subDirsJob =
nullptr;
526 if (job->
error() || m_subDirs.empty()) {
531 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
532 Q_ASSERT(urlNavigator);
534 std::sort(m_subDirs.begin(), m_subDirs.end(), less);
535 setDisplayHintEnabled(PopupActiveHint,
true);
538 if (m_subDirsMenu !=
nullptr) {
539 m_subDirsMenu->close();
540 m_subDirsMenu->deleteLater();
541 m_subDirsMenu =
nullptr;
544 m_subDirsMenu =
new KUrlNavigatorMenu(
this);
545 initMenu(m_subDirsMenu, 0);
548 const int popupX = leftToRight ? width() - arrowWidth() : 0;
549 const QPoint popupPos = parentWidget()->mapToGlobal(geometry().bottomLeft() +
QPoint(popupX, 0));
553 m_subDirsMenu->exec(popupPos);
562 delete m_subDirsMenu;
563 m_subDirsMenu =
nullptr;
565 setDisplayHintEnabled(PopupActiveHint,
false);
568void KUrlNavigatorButton::replaceButton(
KJob *job)
570 Q_ASSERT(job == m_subDirsJob);
571 m_subDirsJob =
nullptr;
572 m_replaceButton =
false;
574 if (job->
error() || m_subDirs.empty()) {
578 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
579 Q_ASSERT(urlNavigator);
581 std::sort(m_subDirs.begin(), m_subDirs.end(), less);
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) {
596 int targetIndex = currentIndex - m_wheelSteps;
597 if (targetIndex < 0) {
599 }
else if (targetIndex >= subDirsCount) {
600 targetIndex = subDirsCount - 1;
604 url.
setPath(Utils::concatPaths(url.
path(), m_subDirs[targetIndex].name));
610void KUrlNavigatorButton::cancelSubDirsRequest()
612 m_openSubDirsTimer->stop();
613 if (m_subDirsJob !=
nullptr) {
614 m_subDirsJob->kill();
615 m_subDirsJob =
nullptr;
619QString KUrlNavigatorButton::plainText()
const
624 const int sourceLength = source.
length();
627 dest.
resize(sourceLength);
631 while (sourceIndex < sourceLength) {
634 if (sourceIndex >= sourceLength) {
638 dest[destIndex] = source.
at(sourceIndex);
648int KUrlNavigatorButton::arrowWidth()
const
652 if (!m_subDir.isEmpty()) {
653 width = height() / 2;
662int KUrlNavigatorButton::textWidth()
const
664 QFont adjustedFont(font());
665 adjustedFont.setBold(m_subDir.isEmpty());
669bool KUrlNavigatorButton::isAboveSeparator(
int x)
const
672 return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth() + m_padding);
675bool KUrlNavigatorButton::isTextClipped()
const
678 int availableWidth = width() - arrowWidth() - m_padding;
680 return textWidth() >= availableWidth;
683void KUrlNavigatorButton::updateMinimumWidth()
685 const int oldMinWidth = minimumWidth();
687 int minWidth = sizeHint().width();
690 }
else if (minWidth > 150) {
694 if (oldMinWidth != minWidth) {
695 setMinimumWidth(minWidth);
699void KUrlNavigatorButton::initMenu(KUrlNavigatorMenu *menu,
int startIndex)
701 connect(menu, &KUrlNavigatorMenu::mouseButtonClicked,
this, &KUrlNavigatorButton::slotMenuActionClicked);
702 connect(menu, &KUrlNavigatorMenu::urlsDropped,
this, &KUrlNavigatorButton::slotUrlsDropped);
711 const int maxIndex = startIndex + 30;
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];
719 if (m_subDir == subDirName) {
725 menu->addAction(action);
727 if (subDirsSize > maxIndex) {
729 menu->addSeparator();
730 KUrlNavigatorMenu *subDirsMenu =
new KUrlNavigatorMenu(menu);
731 subDirsMenu->setTitle(
i18nc(
"@action:inmenu",
"More"));
732 initMenu(subDirsMenu, maxIndex);
733 menu->addMenu(subDirsMenu);
739#include "moc_kurlnavigatorbutton_p.cpp"
The base class for all jobs.
@ IncludeHidden
Include hidden files in the listing.
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.
Universal Directory Service.
QString stringValue(uint field) const
@ UDS_DISPLAY_NAME
If set, contains the label to display instead of the 'real name' in UDS_NAME.
@ UDS_NAME
Filename - as displayed in directory listings etc.
@ UDS_ICON_NAME
Name of the icon, that should be used for displaying.
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.
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
Find all details for one file or directory.
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.
KIOCORE_EXPORT QUrl upUrl(const QUrl &url)
This function is useful to implement the "Up" button in a file manager for example.
QList< UDSEntry > UDSEntryList
A directory listing is a list of UDSEntry instances.
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
QString name(const QVariant &location)
KCOREADDONS_EXPORT QString csqueeze(const QString &str, int maxlen=40)
QVariant data() const const
void setData(const QVariant &data)
QSize size(int flags, const QString &text, int tabStops, int *tabArray) const const
QIcon fromTheme(const QString &name)
void setBrush(const QBrush &brush)
QPoint center() const const
QPoint topLeft() const const
QPoint topRight() const const
bool contains(const QSet< T > &other) 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
void initFrom(const QWidget *widget)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QString path(ComponentFormattingOptions options) const const
void setPath(const QString &path, ParsingMode mode)
int toInt(bool *ok) const const