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>
31 : KUrlNavigatorButtonBase(parent)
33 , m_pendingTextChange(false)
34 , m_replaceButton(false)
35 , m_showMnemonic(false)
39 , m_openSubDirsTimer(nullptr)
40 , m_subDirsJob(nullptr)
44 setMouseTracking(
true);
46 m_openSubDirsTimer =
new QTimer(
this);
47 m_openSubDirsTimer->setSingleShot(
true);
48 m_openSubDirsTimer->setInterval(300);
54KUrlNavigatorButton::~KUrlNavigatorButton()
58void KUrlNavigatorButton::setUrl(
const QUrl &url)
67 QStringLiteral(
"nfs"),
68 QStringLiteral(
"fish"),
69 QStringLiteral(
"ftp"),
70 QStringLiteral(
"sftp"),
71 QStringLiteral(
"smb"),
72 QStringLiteral(
"webdav"),
73 QStringLiteral(
"mtp"),
76 const bool startTextResolving = m_url.isValid() && !m_url.isLocalFile() && !protocolBlacklist.
contains(m_url.scheme());
78 if (startTextResolving) {
79 m_pendingTextChange =
true;
82 Q_EMIT startedTextResolving();
88QUrl KUrlNavigatorButton::url()
const
93void KUrlNavigatorButton::setText(
const QString &text)
97 adjustedText = m_url.scheme();
102 KUrlNavigatorButtonBase::setText(adjustedText);
103 updateMinimumWidth();
107 m_pendingTextChange =
false;
110void KUrlNavigatorButton::setActiveSubDirectory(
const QString &subDir)
119QString KUrlNavigatorButton::activeSubDirectory()
const
124QSize KUrlNavigatorButton::sizeHint()
const
126 QFont adjustedFont(font());
127 adjustedFont.setBold(m_subDir.isEmpty());
131 return QSize(width, KUrlNavigatorButtonBase::sizeHint().height());
134void KUrlNavigatorButton::setShowMnemonic(
bool show)
136 if (m_showMnemonic != show) {
137 m_showMnemonic = show;
142bool KUrlNavigatorButton::showMnemonic()
const
144 return m_showMnemonic;
147void KUrlNavigatorButton::paintEvent(
QPaintEvent *event)
153 QFont adjustedFont(font());
154 adjustedFont.setBold(m_subDir.isEmpty());
155 painter.setFont(adjustedFont);
157 int buttonWidth = width();
158 int preferredWidth = sizeHint().width();
159 if (preferredWidth < minimumWidth()) {
160 preferredWidth = minimumWidth();
162 if (buttonWidth > preferredWidth) {
163 buttonWidth = preferredWidth;
165 const int buttonHeight = height();
167 const QColor fgColor = foregroundColor();
168 drawHoverBackground(&painter);
171 int textWidth = buttonWidth;
175 if (!m_subDir.isEmpty()) {
177 const int arrowSize = arrowWidth();
178 const int arrowX = leftToRight ? (buttonWidth - arrowSize) - BorderWidth : BorderWidth;
179 const int arrowY = (buttonHeight - arrowSize) / 2;
183 option.rect =
QRect(arrowX, arrowY, arrowSize, arrowSize);
184 option.palette = palette();
195 painter.setBrush(hoverColor);
199 hoverX -= BorderWidth;
201 painter.drawRect(
QRect(hoverX, 0, arrowSize + BorderWidth, buttonHeight));
208 textLeft += arrowSize + 2 * BorderWidth;
211 textWidth -= arrowSize + 2 * BorderWidth;
214 painter.setPen(fgColor);
215 const bool clipped = isTextClipped();
216 const QRect textRect(textLeft, 0, textWidth, buttonHeight);
222 gradient.setColorAt(0.8, fgColor);
223 gradient.setColorAt(1.0, bgColor);
225 gradient.setColorAt(0.0, bgColor);
226 gradient.setColorAt(0.2, fgColor);
235 if (m_showMnemonic) {
237 painter.drawText(textRect, textFlags, text());
239 painter.drawText(textRect, textFlags, plainText());
243void KUrlNavigatorButton::enterEvent(
QEnterEvent *event)
245 KUrlNavigatorButtonBase::enterEvent(event);
249 if (isTextClipped()) {
250 setToolTip(plainText());
254void KUrlNavigatorButton::leaveEvent(
QEvent *event)
256 KUrlNavigatorButtonBase::leaveEvent(event);
260 m_hoverArrow =
false;
265void KUrlNavigatorButton::keyPressEvent(
QKeyEvent *event)
267 switch (
event->key()) {
277 KUrlNavigatorButtonBase::keyPressEvent(event);
281void KUrlNavigatorButton::dropEvent(
QDropEvent *event)
283 if (
event->mimeData()->hasUrls()) {
284 setDisplayHintEnabled(DraggedHint,
true);
286 Q_EMIT urlsDroppedOnNavButton(m_url, event);
288 setDisplayHintEnabled(DraggedHint,
false);
295 if (
event->mimeData()->hasUrls()) {
296 setDisplayHintEnabled(DraggedHint,
true);
297 event->acceptProposedAction();
305 QRect rect =
event->answerRect();
306 if (isAboveArrow(rect.
center().
x())) {
310 if (m_subDirsMenu ==
nullptr) {
312 }
else if (m_subDirsMenu->parent() !=
this) {
313 m_subDirsMenu->close();
314 m_subDirsMenu->deleteLater();
315 m_subDirsMenu =
nullptr;
320 if (m_openSubDirsTimer->isActive()) {
321 cancelSubDirsRequest();
323 m_subDirsMenu->deleteLater();
324 m_subDirsMenu =
nullptr;
325 m_hoverArrow =
false;
332 KUrlNavigatorButtonBase::dragLeaveEvent(event);
334 m_hoverArrow =
false;
335 setDisplayHintEnabled(DraggedHint,
false);
339void KUrlNavigatorButton::mousePressEvent(
QMouseEvent *event)
345 KUrlNavigatorButtonBase::mousePressEvent(event);
348void KUrlNavigatorButton::mouseReleaseEvent(
QMouseEvent *event)
353 Q_EMIT navigatorButtonActivated(m_url,
event->button(),
event->modifiers());
354 cancelSubDirsRequest();
356 KUrlNavigatorButtonBase::mouseReleaseEvent(event);
359void KUrlNavigatorButton::mouseMoveEvent(
QMouseEvent *event)
361 KUrlNavigatorButtonBase::mouseMoveEvent(event);
363 const bool hoverArrow = isAboveArrow(qRound(
event->position().x()));
364 if (hoverArrow != m_hoverArrow) {
365 m_hoverArrow = hoverArrow;
370void KUrlNavigatorButton::wheelEvent(
QWheelEvent *event)
372 if (
event->angleDelta().y() != 0) {
373 m_wheelSteps =
event->angleDelta().y() / 120;
374 m_replaceButton =
true;
378 KUrlNavigatorButtonBase::wheelEvent(event);
381void KUrlNavigatorButton::requestSubDirs()
383 if (!m_openSubDirsTimer->isActive() && (m_subDirsJob ==
nullptr)) {
384 m_openSubDirsTimer->start();
388void KUrlNavigatorButton::startSubDirsJob()
390 if (m_subDirsJob !=
nullptr) {
395 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
396 Q_ASSERT(urlNavigator);
403 if (m_replaceButton) {
412 Q_ASSERT(job == m_subDirsJob);
431 const int result = action->
data().
toInt();
433 url.
setPath(Utils::concatPaths(url.
path(), m_subDirs.
at(result).name));
434 Q_EMIT urlsDroppedOnNavButton(url, event);
439 const int result = action->
data().
toInt();
441 url.
setPath(Utils::concatPaths(url.
path(), m_subDirs.
at(result).name));
445void KUrlNavigatorButton::statFinished(
KJob *job)
447 if (m_pendingTextChange) {
448 m_pendingTextChange =
false;
453 name = m_url.fileName();
457 Q_EMIT finishedTextResolving();
464struct FolderNameNaturalLessThan {
465 FolderNameNaturalLessThan(
bool sortHiddenLast)
466 : m_sortHiddenLast(sortHiddenLast)
469 m_collator.setNumericMode(
true);
472 bool operator()(
const KUrlNavigatorButton::SubDirInfo &a,
const KUrlNavigatorButton::SubDirInfo &b)
474 if (m_sortHiddenLast) {
475 const bool isHiddenA = a.name.startsWith(
QLatin1Char(
'.'));
476 const bool isHiddenB = b.name.startsWith(
QLatin1Char(
'.'));
477 if (isHiddenA && !isHiddenB) {
480 if (!isHiddenA && isHiddenB) {
484 return m_collator.compare(a.name, b.name) < 0;
489 bool m_sortHiddenLast;
492void KUrlNavigatorButton::openSubDirsMenu(
KJob *job)
494 Q_ASSERT(job == m_subDirsJob);
495 m_subDirsJob =
nullptr;
497 if (job->
error() || m_subDirs.empty()) {
502 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
503 Q_ASSERT(urlNavigator);
505 std::sort(m_subDirs.begin(), m_subDirs.end(), less);
506 setDisplayHintEnabled(PopupActiveHint,
true);
509 if (m_subDirsMenu !=
nullptr) {
510 m_subDirsMenu->close();
511 m_subDirsMenu->deleteLater();
512 m_subDirsMenu =
nullptr;
515 m_subDirsMenu =
new KUrlNavigatorMenu(
this);
516 initMenu(m_subDirsMenu, 0);
519 const int popupX = leftToRight ? width() - arrowWidth() - BorderWidth : 0;
520 const QPoint popupPos = parentWidget()->mapToGlobal(geometry().bottomLeft() +
QPoint(popupX, 0));
524 m_subDirsMenu->exec(popupPos);
533 delete m_subDirsMenu;
534 m_subDirsMenu =
nullptr;
536 setDisplayHintEnabled(PopupActiveHint,
false);
539void KUrlNavigatorButton::replaceButton(
KJob *job)
541 Q_ASSERT(job == m_subDirsJob);
542 m_subDirsJob =
nullptr;
543 m_replaceButton =
false;
545 if (job->
error() || m_subDirs.empty()) {
549 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
550 Q_ASSERT(urlNavigator);
552 std::sort(m_subDirs.begin(), m_subDirs.end(), less);
555 const QString currentDir = m_url.fileName();
556 int currentIndex = 0;
557 const int subDirsCount = m_subDirs.
size();
558 while (currentIndex < subDirsCount) {
559 if (m_subDirs[currentIndex].name == currentDir) {
567 int targetIndex = currentIndex - m_wheelSteps;
568 if (targetIndex < 0) {
570 }
else if (targetIndex >= subDirsCount) {
571 targetIndex = subDirsCount - 1;
575 url.
setPath(Utils::concatPaths(url.
path(), m_subDirs[targetIndex].name));
581void KUrlNavigatorButton::cancelSubDirsRequest()
583 m_openSubDirsTimer->stop();
584 if (m_subDirsJob !=
nullptr) {
585 m_subDirsJob->kill();
586 m_subDirsJob =
nullptr;
590QString KUrlNavigatorButton::plainText()
const
595 const int sourceLength = source.
length();
598 dest.
resize(sourceLength);
602 while (sourceIndex < sourceLength) {
605 if (sourceIndex >= sourceLength) {
609 dest[destIndex] = source.
at(sourceIndex);
619int KUrlNavigatorButton::arrowWidth()
const
623 if (!m_subDir.isEmpty()) {
624 width = height() / 2;
633bool KUrlNavigatorButton::isAboveArrow(
int x)
const
636 return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth());
639bool KUrlNavigatorButton::isTextClipped()
const
641 int availableWidth = width() - 2 * BorderWidth;
642 if (!m_subDir.isEmpty()) {
643 availableWidth -= arrowWidth() - BorderWidth;
646 QFont adjustedFont(font());
647 adjustedFont.setBold(m_subDir.isEmpty());
651void KUrlNavigatorButton::updateMinimumWidth()
653 const int oldMinWidth = minimumWidth();
655 int minWidth = sizeHint().width();
658 }
else if (minWidth > 150) {
662 if (oldMinWidth != minWidth) {
663 setMinimumWidth(minWidth);
667void KUrlNavigatorButton::initMenu(KUrlNavigatorMenu *menu,
int startIndex)
669 connect(menu, &KUrlNavigatorMenu::mouseButtonClicked,
this, &KUrlNavigatorButton::slotMenuActionClicked);
670 connect(menu, &KUrlNavigatorMenu::urlsDropped,
this, &KUrlNavigatorButton::slotUrlsDropped);
679 const int maxIndex = startIndex + 30;
680 const int subDirsSize = m_subDirs.size();
681 const int lastIndex = std::min(subDirsSize - 1, maxIndex);
682 for (
int i = startIndex; i <= lastIndex; ++i) {
683 const auto &[subDirName, subDirDisplayName] = m_subDirs[i];
687 if (m_subDir == subDirName) {
693 menu->addAction(action);
695 if (subDirsSize > maxIndex) {
697 menu->addSeparator();
698 KUrlNavigatorMenu *subDirsMenu =
new KUrlNavigatorMenu(menu);
699 subDirsMenu->setTitle(
i18nc(
"@action:inmenu",
"More"));
700 initMenu(subDirsMenu, maxIndex);
701 menu->addMenu(subDirsMenu);
707#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.
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)
A namespace for KIO globals.
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.
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
QString name(StandardShortcut id)
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
void setBrush(const QBrush &brush)
QPoint center() 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