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();
324 m_subDirsMenu->deleteLater();
325 m_subDirsMenu =
nullptr;
327 m_hoverArrow =
false;
334 KUrlNavigatorButtonBase::dragLeaveEvent(event);
336 m_hoverArrow =
false;
337 setDisplayHintEnabled(DraggedHint,
false);
341void KUrlNavigatorButton::mousePressEvent(
QMouseEvent *event)
347 KUrlNavigatorButtonBase::mousePressEvent(event);
350void KUrlNavigatorButton::mouseReleaseEvent(
QMouseEvent *event)
355 Q_EMIT navigatorButtonActivated(m_url,
event->button(),
event->modifiers());
356 cancelSubDirsRequest();
358 KUrlNavigatorButtonBase::mouseReleaseEvent(event);
361void KUrlNavigatorButton::mouseMoveEvent(
QMouseEvent *event)
363 KUrlNavigatorButtonBase::mouseMoveEvent(event);
365 const bool hoverArrow = isAboveArrow(qRound(
event->position().x()));
366 if (hoverArrow != m_hoverArrow) {
367 m_hoverArrow = hoverArrow;
372void KUrlNavigatorButton::wheelEvent(
QWheelEvent *event)
374 if (
event->angleDelta().y() != 0) {
375 m_wheelSteps =
event->angleDelta().y() / 120;
376 m_replaceButton =
true;
380 KUrlNavigatorButtonBase::wheelEvent(event);
383void KUrlNavigatorButton::requestSubDirs()
385 if (!m_openSubDirsTimer->isActive() && (m_subDirsJob ==
nullptr)) {
386 m_openSubDirsTimer->start();
390void KUrlNavigatorButton::startSubDirsJob()
392 if (m_subDirsJob !=
nullptr) {
397 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
398 Q_ASSERT(urlNavigator);
405 if (m_replaceButton) {
414 Q_ASSERT(job == m_subDirsJob);
433 const int result = action->
data().
toInt();
435 url.
setPath(Utils::concatPaths(url.
path(), m_subDirs.
at(result).name));
436 Q_EMIT urlsDroppedOnNavButton(url, event);
441 const int result = action->
data().
toInt();
443 url.
setPath(Utils::concatPaths(url.
path(), m_subDirs.
at(result).name));
447void KUrlNavigatorButton::statFinished(
KJob *job)
449 if (m_pendingTextChange) {
450 m_pendingTextChange =
false;
455 name = m_url.fileName();
459 Q_EMIT finishedTextResolving();
466struct FolderNameNaturalLessThan {
467 FolderNameNaturalLessThan(
bool sortHiddenLast)
468 : m_sortHiddenLast(sortHiddenLast)
471 m_collator.setNumericMode(
true);
474 bool operator()(
const KUrlNavigatorButton::SubDirInfo &a,
const KUrlNavigatorButton::SubDirInfo &b)
476 if (m_sortHiddenLast) {
477 const bool isHiddenA = a.name.startsWith(
QLatin1Char(
'.'));
478 const bool isHiddenB = b.name.startsWith(
QLatin1Char(
'.'));
479 if (isHiddenA && !isHiddenB) {
482 if (!isHiddenA && isHiddenB) {
486 return m_collator.compare(a.name, b.name) < 0;
491 bool m_sortHiddenLast;
494void KUrlNavigatorButton::openSubDirsMenu(
KJob *job)
496 Q_ASSERT(job == m_subDirsJob);
497 m_subDirsJob =
nullptr;
499 if (job->
error() || m_subDirs.empty()) {
504 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
505 Q_ASSERT(urlNavigator);
507 std::sort(m_subDirs.begin(), m_subDirs.end(), less);
508 setDisplayHintEnabled(PopupActiveHint,
true);
511 if (m_subDirsMenu !=
nullptr) {
512 m_subDirsMenu->close();
513 m_subDirsMenu->deleteLater();
514 m_subDirsMenu =
nullptr;
517 m_subDirsMenu =
new KUrlNavigatorMenu(
this);
518 initMenu(m_subDirsMenu, 0);
521 const int popupX = leftToRight ? width() - arrowWidth() - BorderWidth : 0;
522 const QPoint popupPos = parentWidget()->mapToGlobal(geometry().bottomLeft() +
QPoint(popupX, 0));
526 m_subDirsMenu->exec(popupPos);
535 delete m_subDirsMenu;
536 m_subDirsMenu =
nullptr;
538 setDisplayHintEnabled(PopupActiveHint,
false);
541void KUrlNavigatorButton::replaceButton(
KJob *job)
543 Q_ASSERT(job == m_subDirsJob);
544 m_subDirsJob =
nullptr;
545 m_replaceButton =
false;
547 if (job->
error() || m_subDirs.empty()) {
551 const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
552 Q_ASSERT(urlNavigator);
554 std::sort(m_subDirs.begin(), m_subDirs.end(), less);
557 const QString currentDir = m_url.fileName();
558 int currentIndex = 0;
559 const int subDirsCount = m_subDirs.
size();
560 while (currentIndex < subDirsCount) {
561 if (m_subDirs[currentIndex].name == currentDir) {
569 int targetIndex = currentIndex - m_wheelSteps;
570 if (targetIndex < 0) {
572 }
else if (targetIndex >= subDirsCount) {
573 targetIndex = subDirsCount - 1;
577 url.
setPath(Utils::concatPaths(url.
path(), m_subDirs[targetIndex].name));
583void KUrlNavigatorButton::cancelSubDirsRequest()
585 m_openSubDirsTimer->stop();
586 if (m_subDirsJob !=
nullptr) {
587 m_subDirsJob->kill();
588 m_subDirsJob =
nullptr;
592QString KUrlNavigatorButton::plainText()
const
597 const int sourceLength = source.
length();
600 dest.
resize(sourceLength);
604 while (sourceIndex < sourceLength) {
607 if (sourceIndex >= sourceLength) {
611 dest[destIndex] = source.
at(sourceIndex);
621int KUrlNavigatorButton::arrowWidth()
const
625 if (!m_subDir.isEmpty()) {
626 width = height() / 2;
635bool KUrlNavigatorButton::isAboveArrow(
int x)
const
638 return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth());
641bool KUrlNavigatorButton::isTextClipped()
const
643 int availableWidth = width() - 2 * BorderWidth;
644 if (!m_subDir.isEmpty()) {
645 availableWidth -= arrowWidth() - BorderWidth;
648 QFont adjustedFont(font());
649 adjustedFont.setBold(m_subDir.isEmpty());
653void KUrlNavigatorButton::updateMinimumWidth()
655 const int oldMinWidth = minimumWidth();
657 int minWidth = sizeHint().width();
660 }
else if (minWidth > 150) {
664 if (oldMinWidth != minWidth) {
665 setMinimumWidth(minWidth);
669void KUrlNavigatorButton::initMenu(KUrlNavigatorMenu *menu,
int startIndex)
671 connect(menu, &KUrlNavigatorMenu::mouseButtonClicked,
this, &KUrlNavigatorButton::slotMenuActionClicked);
672 connect(menu, &KUrlNavigatorMenu::urlsDropped,
this, &KUrlNavigatorButton::slotUrlsDropped);
681 const int maxIndex = startIndex + 30;
682 const int subDirsSize = m_subDirs.size();
683 const int lastIndex = std::min(subDirsSize - 1, maxIndex);
684 for (
int i = startIndex; i <= lastIndex; ++i) {
685 const auto &[subDirName, subDirDisplayName] = m_subDirs[i];
689 if (m_subDir == subDirName) {
695 menu->addAction(action);
697 if (subDirsSize > maxIndex) {
699 menu->addSeparator();
700 KUrlNavigatorMenu *subDirsMenu =
new KUrlNavigatorMenu(menu);
701 subDirsMenu->setTitle(
i18nc(
"@action:inmenu",
"More"));
702 initMenu(subDirsMenu, maxIndex);
703 menu->addMenu(subDirsMenu);
709#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)
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(StandardAction 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