KIO

kfilewidget.cpp
1// -*- c++ -*-
2/*
3 This file is part of the KDE libraries
4 SPDX-FileCopyrightText: 1997, 1998 Richard Moore <rich@kde.org>
5 SPDX-FileCopyrightText: 1998 Stephan Kulow <coolo@kde.org>
6 SPDX-FileCopyrightText: 1998 Daniel Grana <grana@ie.iwi.unibe.ch>
7 SPDX-FileCopyrightText: 1999, 2000, 2001, 2002, 2003 Carsten Pfeiffer <pfeiffer@kde.org>
8 SPDX-FileCopyrightText: 2003 Clarence Dang <dang@kde.org>
9 SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org>
10 SPDX-FileCopyrightText: 2008 Rafael Fernández López <ereslibre@kde.org>
11
12 SPDX-License-Identifier: LGPL-2.0-or-later
13*/
14
15#include "kfilewidget.h"
16
17#include "../utils_p.h"
18#include "kfilebookmarkhandler_p.h"
19#include "kfileplacesmodel.h"
20#include "kfileplacesview.h"
21#include "kfilepreviewgenerator.h"
22#include "kfilewidgetdocktitlebar_p.h"
23#include "kurlcombobox.h"
24#include "kurlnavigator.h"
25
26#include <config-kiofilewidgets.h>
27
28#include <defaults-kfile.h>
29#include <kdiroperator.h>
30#include <kfilefiltercombo.h>
31#include <kfileitemdelegate.h>
32#include <kio/job.h>
33#include <kio/jobuidelegate.h>
34#include <kio/statjob.h>
35#include <kprotocolmanager.h>
36#include <krecentdirs.h>
37#include <krecentdocument.h>
38#include <kurlauthorized.h>
39#include <kurlcompletion.h>
40
41#include <KActionMenu>
42#include <KConfigGroup>
43#include <KDirLister>
44#include <KFileItem>
45#include <KFilePlacesModel>
46#include <KIconLoader>
47#include <KJobWidgets>
48#include <KLocalizedString>
49#include <KMessageBox>
50#include <KMessageWidget>
51#include <KSharedConfig>
52#include <KShell>
53#include <KStandardAction>
54
55#include <QAbstractProxyModel>
56#include <QApplication>
57#include <QCheckBox>
58#include <QDebug>
59#include <QDockWidget>
60#include <QFormLayout>
61#include <QHelpEvent>
62#include <QIcon>
63#include <QLabel>
64#include <QLayout>
65#include <QLineEdit>
66#include <QLoggingCategory>
67#include <QMenu>
68#include <QMimeDatabase>
69#include <QPushButton>
70#include <QScreen>
71#include <QSplitter>
72#include <QStandardPaths>
73#include <QTimer>
74#include <QToolBar>
75
76#include <algorithm>
77#include <array>
78
79Q_DECLARE_LOGGING_CATEGORY(KIO_KFILEWIDGETS_FW)
80Q_LOGGING_CATEGORY(KIO_KFILEWIDGETS_FW, "kf.kio.kfilewidgets.kfilewidget", QtInfoMsg)
81
82class KFileWidgetPrivate
83{
84public:
85 explicit KFileWidgetPrivate(KFileWidget *qq)
86 : q(qq)
87 {
88 }
89
90 ~KFileWidgetPrivate()
91 {
92 delete m_bookmarkHandler; // Should be deleted before m_ops!
93 // Must be deleted before m_ops, otherwise the unit test crashes due to the
94 // connection to the QDockWidget::visibilityChanged signal, which may get
95 // emitted after this object is destroyed
96 delete m_placesDock;
97 delete m_ops;
98 }
99
100 QSize screenSize() const
101 {
102 return q->parentWidget() ? q->parentWidget()->screen()->availableGeometry().size() //
103 : QGuiApplication::primaryScreen()->availableGeometry().size();
104 }
105
106 void initDirOpWidgets();
107 void initToolbar();
108 void initZoomWidget();
109 void initLocationWidget();
110 void initFilterWidget();
111 void updateLocationWhatsThis();
112 void updateAutoSelectExtension();
113 void initPlacesPanel();
114 void setPlacesViewSplitterSizes();
115 void initGUI();
116 void readViewConfig();
117 void writeViewConfig();
118 void setNonExtSelection();
119 void setLocationText(const QUrl &);
120 void setLocationText(const QList<QUrl> &);
121 void appendExtension(QUrl &url);
122 void updateLocationEditExtension(const QString &);
123 QString findMatchingFilter(const QString &filter, const QString &filename) const;
124 void updateFilter();
125 void updateFilterText();
126 /**
127 * Parses the string "line" for files. If line doesn't contain any ", the
128 * whole line will be interpreted as one file. If the number of " is odd,
129 * an empty list will be returned. Otherwise, all items enclosed in " "
130 * will be returned as correct urls.
131 */
132 QList<QUrl> tokenize(const QString &line) const;
133 /**
134 * Reads the recent used files and inserts them into the location combobox
135 */
136 void readRecentFiles();
137 /**
138 * Saves the entries from the location combobox.
139 */
140 void saveRecentFiles();
141 /**
142 * called when an item is highlighted/selected in multiselection mode.
143 * handles setting the m_locationEdit.
144 */
145 void multiSelectionChanged();
146
147 /**
148 * Returns the absolute version of the URL specified in m_locationEdit.
149 */
150 QUrl getCompleteUrl(const QString &) const;
151
152 /**
153 * Asks for overwrite confirmation using a KMessageBox and returns
154 * true if the user accepts.
155 *
156 */
157 bool toOverwrite(const QUrl &);
158
159 // private slots
160 void slotLocationChanged(const QString &);
161 void urlEntered(const QUrl &);
162 void enterUrl(const QUrl &);
163 void enterUrl(const QString &);
164 void locationAccepted(const QString &);
165 void slotFilterChanged();
166 void fileHighlighted(const KFileItem &);
167 void fileSelected(const KFileItem &);
168 void slotLoadingFinished();
169 void togglePlacesPanel(bool show, QObject *sender = nullptr);
170 void toggleBookmarks(bool);
171 void slotAutoSelectExtClicked();
172 void placesViewSplitterMoved(int, int);
173 void activateUrlNavigator();
174
175 enum ZoomState {
176 ZoomOut,
177 ZoomIn,
178 };
179 void changeIconsSize(ZoomState zoom);
180 void slotDirOpIconSizeChanged(int size);
181 void slotIconSizeSliderMoved(int);
182 void slotIconSizeChanged(int);
183 void slotViewDoubleClicked(const QModelIndex &);
184 void slotViewKeyEnterReturnPressed();
185
186 void addToRecentDocuments();
187
188 QString locationEditCurrentText() const;
189
190 /**
191 * KIO::NetAccess::mostLocalUrl local replacement.
192 * This method won't show any progress dialogs for stating, since
193 * they are very annoying when stating.
194 */
195 QUrl mostLocalUrl(const QUrl &url);
196
197 void setInlinePreviewShown(bool show);
198
199 KFileWidget *const q;
200
201 // the last selected url
202 QUrl m_url;
203
204 // now following all kind of widgets, that I need to rebuild
205 // the geometry management
206 QBoxLayout *m_boxLayout = nullptr;
207 QFormLayout *m_lafBox = nullptr;
208
209 QLabel *m_locationLabel = nullptr;
210 QWidget *m_opsWidget = nullptr;
211 QVBoxLayout *m_opsWidgetLayout = nullptr;
212
213 QLabel *m_filterLabel = nullptr;
214 KUrlNavigator *m_urlNavigator = nullptr;
215 KMessageWidget *m_messageWidget = nullptr;
216 QPushButton *m_okButton = nullptr;
217 QPushButton *m_cancelButton = nullptr;
218 QDockWidget *m_placesDock = nullptr;
219 KFilePlacesView *m_placesView = nullptr;
220 QSplitter *m_placesViewSplitter = nullptr;
221 // caches the places view width. This value will be updated when the splitter
222 // is moved. This allows us to properly set a value when the dialog itself
223 // is resized
224 int m_placesViewWidth = -1;
225
226 QWidget *m_labeledCustomWidget = nullptr;
227 QWidget *m_bottomCustomWidget = nullptr;
228
229 // Automatically Select Extension stuff
230 QCheckBox *m_autoSelectExtCheckBox = nullptr;
231 QString m_extension; // current extension for this filter
232
233 QList<QUrl> m_urlList; // the list of selected urls
234
235 KFileWidget::OperationMode m_operationMode = KFileWidget::Opening;
236
237 // The file class used for KRecentDirs
238 QString m_fileClass;
239
240 KFileBookmarkHandler *m_bookmarkHandler = nullptr;
241
242 KActionMenu *m_bookmarkButton = nullptr;
243
244 QToolBar *m_toolbar = nullptr;
245 KUrlComboBox *m_locationEdit = nullptr;
246 KDirOperator *m_ops = nullptr;
247 KFileFilterCombo *m_filterWidget = nullptr;
248 QTimer m_filterDelayTimer;
249
250 KFilePlacesModel *m_model = nullptr;
251
252 // whether or not the _user_ has checked the above box
253 bool m_autoSelectExtChecked = false;
254
255 // indicates if the location edit should be kept or cleared when changing
256 // directories
257 bool m_keepLocation = false;
258
259 // the KDirOperators view is set in KFileWidget::show(), so to avoid
260 // setting it again and again, we have this nice little boolean :)
261 bool m_hasView = false;
262
263 bool m_hasDefaultFilter = false; // necessary for the m_operationMode
264 bool m_inAccept = false; // true between beginning and end of accept()
265 bool m_confirmOverwrite = false;
266 bool m_differentHierarchyLevelItemsEntered = false;
267
268 const std::array<short, 8> m_stdIconSizes = {
275 256,
276 512,
277 };
278
279 QSlider *m_iconSizeSlider = nullptr;
280 QAction *m_zoomOutAction = nullptr;
281 QAction *m_zoomInAction = nullptr;
282
283 // The group which stores app-specific settings. These settings are recent
284 // files and urls. Visual settings (view mode, sorting criteria...) are not
285 // app-specific and are stored in kdeglobals
286 KConfigGroup m_configGroup;
287 KConfigGroup m_stateConfigGroup;
288
289 KToggleAction *m_toggleBookmarksAction = nullptr;
290 KToggleAction *m_togglePlacesPanelAction = nullptr;
291};
292
293Q_GLOBAL_STATIC(QUrl, lastDirectory) // to set the start path
294
295// returns true if the string contains "<a>:/" sequence, where <a> is at least 2 alpha chars
296static bool containsProtocolSection(const QString &string)
297{
298 int len = string.length();
299 static const char prot[] = ":/";
300 for (int i = 0; i < len;) {
301 i = string.indexOf(QLatin1String(prot), i);
302 if (i == -1) {
303 return false;
304 }
305 int j = i - 1;
306 for (; j >= 0; j--) {
307 const QChar &ch(string[j]);
308 if (ch.toLatin1() == 0 || !ch.isLetter()) {
309 break;
310 }
311 if (ch.isSpace() && (i - j - 1) >= 2) {
312 return true;
313 }
314 }
315 if (j < 0 && i >= 2) {
316 return true; // at least two letters before ":/"
317 }
318 i += 3; // skip : and / and one char
319 }
320 return false;
321}
322
323// this string-to-url conversion function handles relative paths, full paths and URLs
324// without the http-prepending that QUrl::fromUserInput does.
325static QUrl urlFromString(const QString &str)
326{
327 if (Utils::isAbsoluteLocalPath(str)) {
328 return QUrl::fromLocalFile(str);
329 }
330 QUrl url(str);
331 if (url.isRelative()) {
332 url.clear();
333 url.setPath(str);
334 }
335 return url;
336}
337
338KFileWidget::KFileWidget(const QUrl &_startDir, QWidget *parent)
339 : QWidget(parent)
340 , d(new KFileWidgetPrivate(this))
341{
342 QUrl startDir(_startDir);
343 // qDebug() << "startDir" << startDir;
344 QString filename;
345
346 d->m_okButton = new QPushButton(this);
347 KGuiItem::assign(d->m_okButton, KStandardGuiItem::ok());
348 d->m_okButton->setDefault(true);
349 d->m_cancelButton = new QPushButton(this);
350 KGuiItem::assign(d->m_cancelButton, KStandardGuiItem::cancel());
351 // The dialog shows them
352 d->m_okButton->hide();
353 d->m_cancelButton->hide();
354
355 d->initDirOpWidgets();
356
357 // Resolve this now so that a 'kfiledialog:' URL, if specified,
358 // does not get inserted into the urlNavigator history.
359 d->m_url = getStartUrl(startDir, d->m_fileClass, filename);
360 startDir = d->m_url;
361
362 const auto operatorActions = d->m_ops->allActions();
363 for (QAction *action : operatorActions) {
364 addAction(action);
365 }
366
367 QAction *goToNavigatorAction = new QAction(this);
368
369 connect(goToNavigatorAction, &QAction::triggered, this, [this]() {
370 d->activateUrlNavigator();
371 });
372
373 goToNavigatorAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_L));
374
375 addAction(goToNavigatorAction);
376
377 KUrlComboBox *pathCombo = d->m_urlNavigator->editor();
378 KUrlCompletion *pathCompletionObj = new KUrlCompletion(KUrlCompletion::DirCompletion);
379 pathCombo->setCompletionObject(pathCompletionObj);
380 pathCombo->setAutoDeleteCompletionObject(true);
381
382 connect(d->m_urlNavigator, &KUrlNavigator::urlChanged, this, [this](const QUrl &url) {
383 d->enterUrl(url);
384 });
385 connect(d->m_urlNavigator, &KUrlNavigator::returnPressed, d->m_ops, qOverload<>(&QWidget::setFocus));
386
387 // Location, "Name:", line-edit and label
388 d->initLocationWidget();
389
390 // "Filter:" line-edit and label
391 d->initFilterWidget();
392
393 // the Automatically Select Extension checkbox
394 // (the text, visibility etc. is set in updateAutoSelectExtension(), which is called by readConfig())
395 d->m_autoSelectExtCheckBox = new QCheckBox(this);
396 connect(d->m_autoSelectExtCheckBox, &QCheckBox::clicked, this, [this]() {
397 d->slotAutoSelectExtClicked();
398 });
399
400 d->initGUI(); // activate GM
401
402 // read our configuration
403 KSharedConfig::Ptr config = KSharedConfig::openConfig();
404 config->reparseConfiguration(); // grab newly added dirs by other processes (#403524)
405 d->m_configGroup = KConfigGroup(config, ConfigGroup);
406
407 d->m_stateConfigGroup = KSharedConfig::openStateConfig()->group(ConfigGroup);
408
409 // migrate existing recent files/urls from main config to state config
410 if (d->m_configGroup.hasKey(RecentURLs)) {
411 d->m_stateConfigGroup.writeEntry(RecentURLs, d->m_configGroup.readEntry(RecentURLs));
412 d->m_configGroup.revertToDefault(RecentURLs);
413 }
414
415 if (d->m_configGroup.hasKey(RecentFiles)) {
416 d->m_stateConfigGroup.writeEntry(RecentFiles, d->m_configGroup.readEntry(RecentFiles));
417 d->m_configGroup.revertToDefault(RecentFiles);
418 }
419
420 d->readViewConfig();
421 d->readRecentFiles();
422
423 d->m_ops->action(KDirOperator::ShowPreview)->setChecked(d->m_ops->isInlinePreviewShown());
424 d->slotDirOpIconSizeChanged(d->m_ops->iconSize());
425
426 KFilePreviewGenerator *pg = d->m_ops->previewGenerator();
427 if (pg) {
428 d->m_ops->action(KDirOperator::ShowPreview)->setChecked(pg->isPreviewShown());
429 }
430
431 // getStartUrl() above will have resolved the startDir parameter into
432 // a directory and file name in the two cases: (a) where it is a
433 // special "kfiledialog:" URL, or (b) where it is a plain file name
434 // only without directory or protocol. For any other startDir
435 // specified, it is not possible to resolve whether there is a file name
436 // present just by looking at the URL; the only way to be sure is
437 // to stat it.
438 bool statRes = false;
439 if (filename.isEmpty()) {
440 KIO::StatJob *statJob = KIO::stat(startDir, KIO::HideProgressInfo);
441 KJobWidgets::setWindow(statJob, this);
442 statRes = statJob->exec();
443 // qDebug() << "stat of" << startDir << "-> statRes" << statRes << "isDir" << statJob->statResult().isDir();
444 if (!statRes || !statJob->statResult().isDir()) {
445 filename = startDir.fileName();
447 // qDebug() << "statJob -> startDir" << startDir << "filename" << filename;
448 }
449 }
450
451 d->m_ops->setUrl(startDir, true);
452 d->m_urlNavigator->setLocationUrl(startDir);
453 if (d->m_placesView) {
454 d->m_placesView->setUrl(startDir);
455 }
456
457 // We have a file name either explicitly specified, or have checked that
458 // we could stat it and it is not a directory. Set it.
459 if (!filename.isEmpty()) {
460 QLineEdit *lineEdit = d->m_locationEdit->lineEdit();
461 // qDebug() << "selecting filename" << filename;
462 if (statRes) {
463 d->setLocationText(QUrl(filename));
464 } else {
465 lineEdit->setText(filename);
466 // Preserve this filename when clicking on the view (cf fileHighlighted)
467 lineEdit->setModified(true);
468 }
469 lineEdit->selectAll();
470 }
471
472 d->m_locationEdit->setFocus();
473
474 const QAction *showHiddenAction = d->m_ops->action(KDirOperator::ShowHiddenFiles);
475 Q_ASSERT(showHiddenAction);
476 d->m_urlNavigator->setShowHiddenFolders(showHiddenAction->isChecked());
477 connect(showHiddenAction, &QAction::toggled, this, [this](bool checked) {
478 d->m_urlNavigator->setShowHiddenFolders(checked);
479 });
480
481 const QAction *hiddenFilesLastAction = d->m_ops->action(KDirOperator::SortHiddenFilesLast);
482 Q_ASSERT(hiddenFilesLastAction);
483 d->m_urlNavigator->setSortHiddenFoldersLast(hiddenFilesLastAction->isChecked());
484 connect(hiddenFilesLastAction, &QAction::toggled, this, [this](bool checked) {
485 d->m_urlNavigator->setSortHiddenFoldersLast(checked);
486 });
487}
488
490{
491 KSharedConfig::Ptr config = KSharedConfig::openConfig();
492 config->sync();
493 d->m_ops->removeEventFilter(this);
494 d->m_locationEdit->removeEventFilter(this);
495}
496
498{
499 d->m_locationLabel->setText(text);
500}
501
502void KFileWidget::setFilters(const QList<KFileFilter> &filters, const KFileFilter &activeFilter)
503{
504 d->m_ops->clearFilter();
505 d->m_filterWidget->setFilters(filters, activeFilter);
506 d->m_ops->updateDir();
507 d->m_hasDefaultFilter = false;
508 d->m_filterWidget->setEditable(true);
509 d->updateFilterText();
510
511 d->updateAutoSelectExtension();
512}
513
515{
516 return d->m_filterWidget->currentFilter();
517}
518
520{
521 d->m_filterWidget->setFilters({}, KFileFilter());
522 d->m_ops->clearFilter();
523 d->m_hasDefaultFilter = false;
524 d->m_filterWidget->setEditable(true);
525
526 d->updateAutoSelectExtension();
527}
528
530{
531 d->m_ops->setPreviewWidget(w);
532 d->m_ops->clearHistory();
533 d->m_hasView = true;
534}
535
536QUrl KFileWidgetPrivate::getCompleteUrl(const QString &_url) const
537{
538 // qDebug() << "got url " << _url;
539
540 const QString url = KShell::tildeExpand(_url);
541 QUrl u;
542
543 if (Utils::isAbsoluteLocalPath(url)) {
544 u = QUrl::fromLocalFile(url);
545 } else {
546 QUrl relativeUrlTest(m_ops->url());
547 relativeUrlTest.setPath(Utils::concatPaths(relativeUrlTest.path(), url));
548 if (!m_ops->dirLister()->findByUrl(relativeUrlTest).isNull() || !KProtocolInfo::isKnownProtocol(relativeUrlTest)) {
549 u = relativeUrlTest;
550 } else {
551 // Try to preserve URLs if they have a scheme (for example,
552 // "https://example.com/foo.txt") and otherwise resolve relative
553 // paths to absolute ones (e.g. "foo.txt" -> "file:///tmp/foo.txt").
554 u = QUrl(url);
555 if (u.isRelative()) {
556 u = relativeUrlTest;
557 }
558 }
559 }
560
561 return u;
562}
563
565{
566 int fontSize = fontMetrics().height();
567 const QSize goodSize(48 * fontSize, 30 * fontSize);
568 const QSize scrnSize = d->screenSize();
569 const QSize minSize(scrnSize / 2);
570 const QSize maxSize(scrnSize * qreal(0.9));
571 return (goodSize.expandedTo(minSize).boundedTo(maxSize));
572}
573
574static QString relativePathOrUrl(const QUrl &baseUrl, const QUrl &url);
575
576/**
577 * Escape the given Url so that is fit for use in the selected list of file. This
578 * mainly handles double quote (") characters. These are used to separate entries
579 * in the list, however, if `"` appears in the filename (or path), this will be
580 * escaped as `\"`. Later, the tokenizer is able to understand the difference
581 * and do the right thing
582 */
583static QString escapeDoubleQuotes(QString &&path);
584
585// Called by KFileDialog
587{
588 // qDebug() << "slotOk\n";
589
590 const QString locationEditCurrentText(KShell::tildeExpand(d->locationEditCurrentText()));
591
592 QList<QUrl> locationEditCurrentTextList(d->tokenize(locationEditCurrentText));
593 KFile::Modes mode = d->m_ops->mode();
594
595 // if there is nothing to do, just return from here
596 if (locationEditCurrentTextList.isEmpty()) {
597 return;
598 }
599
600 // Make sure that one of the modes was provided
601 if (!((mode & KFile::File) || (mode & KFile::Directory) || (mode & KFile::Files))) {
602 mode |= KFile::File;
603 // qDebug() << "No mode() provided";
604 }
605
606 // Clear the list as we are going to refill it
607 d->m_urlList.clear();
608
609 const bool directoryMode = (mode & KFile::Directory);
610 const bool onlyDirectoryMode = directoryMode && !(mode & KFile::File) && !(mode & KFile::Files);
611
612 // if we are on file mode, and the list of provided files/folder is greater than one, inform
613 // the user about it
614 if (locationEditCurrentTextList.count() > 1) {
615 if (mode & KFile::File) {
616 KMessageBox::error(this, i18n("You can only select one file"), i18n("More than one file provided"));
617 return;
618 }
619
620 /**
621 * Logic of the next part of code (ends at "end multi relative urls").
622 *
623 * We allow for instance to be at "/" and insert '"home/foo/bar.txt" "boot/grub/menu.lst"'.
624 * Why we need to support this ? Because we provide tree views, which aren't plain.
625 *
626 * Now, how does this logic work. It will get the first element on the list (with no filename),
627 * following the previous example say "/home/foo" and set it as the top most url.
628 *
629 * After this, it will iterate over the rest of items and check if this URL (topmost url)
630 * contains the url being iterated.
631 *
632 * As you might have guessed it will do "/home/foo" against "/boot/grub" (again stripping
633 * filename), and a false will be returned. Then we upUrl the top most url, resulting in
634 * "/home" against "/boot/grub", what will again return false, so we upUrl again. Now we
635 * have "/" against "/boot/grub", what returns true for us, so we can say that the closest
636 * common ancestor of both is "/".
637 *
638 * This example has been written for 2 urls, but this works for any number of urls.
639 */
640 if (!d->m_differentHierarchyLevelItemsEntered) { // avoid infinite recursion. running this
641 int start = 0;
642 QUrl topMostUrl;
643 KIO::StatJob *statJob = nullptr;
644 bool res = false;
645
646 // we need to check for a valid first url, so in theory we only iterate one time over
647 // this loop. However it can happen that the user did
648 // "home/foo/nonexistantfile" "boot/grub/menu.lst", so we look for a good first
649 // candidate.
650 while (!res && start < locationEditCurrentTextList.count()) {
651 topMostUrl = locationEditCurrentTextList.at(start);
652 statJob = KIO::stat(topMostUrl, KIO::HideProgressInfo);
653 KJobWidgets::setWindow(statJob, this);
654 res = statJob->exec();
655 start++;
656 }
657
658 Q_ASSERT(statJob);
659
660 // if this is not a dir, strip the filename. after this we have an existent and valid
661 // dir (we stated correctly the file).
662 if (!statJob->statResult().isDir()) {
663 topMostUrl = topMostUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash);
664 }
665
666 // now the funny part. for the rest of filenames, go and look for the closest ancestor
667 // of all them.
668 for (int i = start; i < locationEditCurrentTextList.count(); ++i) {
669 QUrl currUrl = locationEditCurrentTextList.at(i);
670 KIO::StatJob *statJob = KIO::stat(currUrl, KIO::HideProgressInfo);
671 KJobWidgets::setWindow(statJob, this);
672 int res = statJob->exec();
673 if (res) {
674 // again, we don't care about filenames
675 if (!statJob->statResult().isDir()) {
677 }
678
679 // iterate while this item is contained on the top most url
680 while (!topMostUrl.matches(currUrl, QUrl::StripTrailingSlash) && !topMostUrl.isParentOf(currUrl)) {
681 topMostUrl = KIO::upUrl(topMostUrl);
682 }
683 }
684 }
685
686 // now recalculate all paths for them being relative in base of the top most url
687 QStringList stringList;
688 stringList.reserve(locationEditCurrentTextList.count());
689 for (int i = 0; i < locationEditCurrentTextList.count(); ++i) {
690 Q_ASSERT(topMostUrl.isParentOf(locationEditCurrentTextList[i]));
691 QString relativePath = relativePathOrUrl(topMostUrl, locationEditCurrentTextList[i]);
692 stringList << escapeDoubleQuotes(std::move(relativePath));
693 }
694
695 d->m_ops->setUrl(topMostUrl, true);
696 const bool signalsBlocked = d->m_locationEdit->lineEdit()->blockSignals(true);
697 d->m_locationEdit->lineEdit()->setText(QStringLiteral("\"%1\"").arg(stringList.join(QStringLiteral("\" \""))));
698 d->m_locationEdit->lineEdit()->blockSignals(signalsBlocked);
699
700 d->m_differentHierarchyLevelItemsEntered = true;
701 slotOk();
702 return;
703 }
704 /**
705 * end multi relative urls
706 */
707 } else if (!locationEditCurrentTextList.isEmpty()) {
708 // if we are on file or files mode, and we have an absolute url written by
709 // the user:
710 // * convert it to relative and call slotOk again if the protocol supports listing.
711 // * use the full url if the protocol doesn't support listing
712 // This is because when using a protocol that supports listing we want to show the directory
713 // the user just opened/saved from the next time they open the dialog, it makes sense usability wise.
714 // If the protocol doesn't support listing (i.e. http:// ) the user would end up with the dialog
715 // showing an "empty directory" which is bad usability wise.
716 if (!locationEditCurrentText.isEmpty() && !onlyDirectoryMode
717 && (Utils::isAbsoluteLocalPath(locationEditCurrentText) || containsProtocolSection(locationEditCurrentText))) {
718 QUrl url = urlFromString(locationEditCurrentText);
720 QString fileName;
721 if (d->m_operationMode == Opening) {
723 KJobWidgets::setWindow(statJob, this);
724 int res = statJob->exec();
725 if (res) {
726 if (!statJob->statResult().isDir()) {
727 fileName = url.fileName();
728 url = url.adjusted(QUrl::RemoveFilename); // keeps trailing slash
729 } else {
730 Utils::appendSlashToPath(url);
731 }
732 }
733 } else {
734 const QUrl directory = url.adjusted(QUrl::RemoveFilename);
735 // Check if the folder exists
736 KIO::StatJob *statJob = KIO::stat(directory, KIO::HideProgressInfo);
737 KJobWidgets::setWindow(statJob, this);
738 int res = statJob->exec();
739 if (res) {
740 if (statJob->statResult().isDir()) {
742 fileName = url.fileName();
744 }
745 }
746 }
747 d->m_ops->setUrl(url, true);
748 const bool signalsBlocked = d->m_locationEdit->lineEdit()->blockSignals(true);
749 d->m_locationEdit->lineEdit()->setText(fileName);
750 d->m_locationEdit->lineEdit()->blockSignals(signalsBlocked);
751 slotOk();
752 return;
753 } else {
754 locationEditCurrentTextList = {url};
755 }
756 }
757 }
758
759 // restore it
760 d->m_differentHierarchyLevelItemsEntered = false;
761
762 // locationEditCurrentTextList contains absolute paths
763 // this is the general loop for the File and Files mode. Obviously we know
764 // that the File mode will iterate only one time here
765 QList<QUrl>::ConstIterator it = locationEditCurrentTextList.constBegin();
766 bool filesInList = false;
767 while (it != locationEditCurrentTextList.constEnd()) {
768 QUrl url(*it);
769
770 if (d->m_operationMode == Saving && !directoryMode) {
771 d->appendExtension(url);
772 }
773
774 d->m_url = url;
776 KJobWidgets::setWindow(statJob, this);
777 int res = statJob->exec();
778
779 if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("open"), QUrl(), url)) {
780 QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, d->m_url.toDisplayString());
781 KMessageBox::error(this, msg);
782 return;
783 }
784
785 // if we are on local mode, make sure we haven't got a remote base url
786 if ((mode & KFile::LocalOnly) && !d->mostLocalUrl(d->m_url).isLocalFile()) {
787 KMessageBox::error(this, i18n("You can only select local files"), i18n("Remote files not accepted"));
788 return;
789 }
790
791 const auto &supportedSchemes = d->m_model->supportedSchemes();
792 if (!supportedSchemes.isEmpty() && !supportedSchemes.contains(d->m_url.scheme())) {
794 i18np("The selected URL uses an unsupported scheme. "
795 "Please use the following scheme: %2",
796 "The selected URL uses an unsupported scheme. "
797 "Please use one of the following schemes: %2",
800 i18n("Unsupported URL scheme"));
801 return;
802 }
803
804 // if we are given a folder when not on directory mode, let's get into it
805 if (res && !directoryMode && statJob->statResult().isDir()) {
806 // check if we were given more than one folder, in that case we don't know to which one
807 // cd
808 ++it;
809 while (it != locationEditCurrentTextList.constEnd()) {
810 QUrl checkUrl(*it);
811 KIO::StatJob *checkStatJob = KIO::stat(checkUrl, KIO::HideProgressInfo);
812 KJobWidgets::setWindow(checkStatJob, this);
813 bool res = checkStatJob->exec();
814 if (res && checkStatJob->statResult().isDir()) {
816 i18n("More than one folder has been selected and this dialog does not accept folders, so it is not possible to decide "
817 "which one to enter. Please select only one folder to list it."),
818 i18n("More than one folder provided"));
819 return;
820 } else if (res) {
821 filesInList = true;
822 }
823 ++it;
824 }
825 if (filesInList) {
827 this,
828 i18n("At least one folder and one file has been selected. Selected files will be ignored and the selected folder will be listed"),
829 i18n("Files and folders selected"));
830 }
831 d->m_ops->setUrl(url, true);
832 const bool signalsBlocked = d->m_locationEdit->lineEdit()->blockSignals(true);
833 d->m_locationEdit->lineEdit()->setText(QString());
834 d->m_locationEdit->lineEdit()->blockSignals(signalsBlocked);
835 return;
836 } else if (res && onlyDirectoryMode && !statJob->statResult().isDir()) {
837 // if we are given a file when on directory only mode, reject it
838 return;
839 } else if (!(mode & KFile::ExistingOnly) || res) {
840 // if we don't care about ExistingOnly flag, add the file even if
841 // it doesn't exist. If we care about it, don't add it to the list
842 if (!onlyDirectoryMode || (res && statJob->statResult().isDir())) {
843 d->m_urlList << url;
844 }
845 filesInList = true;
846 } else {
847 KMessageBox::error(this, i18n("The file \"%1\" could not be found", url.toDisplayString(QUrl::PreferLocalFile)), i18n("Cannot open file"));
848 return; // do not emit accepted() if we had ExistingOnly flag and stat failed
849 }
850
851 if ((d->m_operationMode == Saving) && d->m_confirmOverwrite && !d->toOverwrite(url)) {
852 return;
853 }
854
855 ++it;
856 }
857
858 // if we have reached this point and we didn't return before, that is because
859 // we want this dialog to be accepted
861}
862
863void KFileWidget::accept()
864{
865 d->m_inAccept = true;
866
867 *lastDirectory() = d->m_ops->url();
868 if (!d->m_fileClass.isEmpty()) {
869 KRecentDirs::add(d->m_fileClass, d->m_ops->url().toString());
870 }
871
872 // clear the topmost item, we insert it as full path later on as item 1
873 d->m_locationEdit->setItemText(0, QString());
874
875 const QList<QUrl> list = selectedUrls();
876 int atmost = d->m_locationEdit->maxItems(); // don't add more items than necessary
877 for (const auto &url : list) {
878 if (atmost-- == 0) {
879 break;
880 }
881
882 // we strip the last slash (-1) because KUrlComboBox does that as well
883 // when operating in file-mode. If we wouldn't , dupe-finding wouldn't
884 // work.
885 const QString file = url.toDisplayString(QUrl::StripTrailingSlash | QUrl::PreferLocalFile);
886
887 // remove dupes
888 for (int i = 1; i < d->m_locationEdit->count(); ++i) {
889 if (d->m_locationEdit->itemText(i) == file) {
890 d->m_locationEdit->removeItem(i--);
891 break;
892 }
893 }
894 // FIXME I don't think this works correctly when the KUrlComboBox has some default urls.
895 // KUrlComboBox should provide a function to add an url and rotate the existing ones, keeping
896 // track of maxItems, and we shouldn't be able to insert items as we please.
897 d->m_locationEdit->insertItem(1, file);
898 }
899
900 d->writeViewConfig();
901 d->saveRecentFiles();
902
903 d->addToRecentDocuments();
904
905 if (!(mode() & KFile::Files)) { // single selection
906 Q_EMIT fileSelected(d->m_url);
907 }
908
909 d->m_ops->close();
910}
911
912void KFileWidgetPrivate::fileHighlighted(const KFileItem &i)
913{
914 if ((m_locationEdit->hasFocus() && !m_locationEdit->currentText().isEmpty())) { // don't disturb
915 return;
916 }
917
918 if (!i.isNull() && i.isDir() && !(m_ops->mode() & KFile::Directory)) {
919 return;
920 }
921
922 const bool modified = m_locationEdit->lineEdit()->isModified();
923
924 if (!(m_ops->mode() & KFile::Files)) {
925 if (i.isNull()) {
926 if (!modified) {
927 setLocationText(QUrl());
928 }
929 return;
930 }
931
932 m_url = i.url();
933
934 if (!m_locationEdit->hasFocus()) { // don't disturb while editing
935 setLocationText(m_url);
936 }
937
938 Q_EMIT q->fileHighlighted(m_url);
939 } else {
940 multiSelectionChanged();
941 Q_EMIT q->selectionChanged();
942 }
943
944 m_locationEdit->lineEdit()->setModified(false);
945
946 // When saving, and when double-click mode is being used, highlight the
947 // filename after a file is single-clicked so the user has a chance to quickly
948 // rename it if desired
949 // Note that double-clicking will override this and overwrite regardless of
950 // single/double click mouse setting (see slotViewDoubleClicked() )
951 if (m_operationMode == KFileWidget::Saving) {
952 m_locationEdit->setFocus();
953 }
954}
955
956void KFileWidgetPrivate::fileSelected(const KFileItem &i)
957{
958 if (!i.isNull() && i.isDir()) {
959 return;
960 }
961
962 if (!(m_ops->mode() & KFile::Files)) {
963 if (i.isNull()) {
964 setLocationText(QUrl());
965 return;
966 }
967 setLocationText(i.targetUrl());
968 } else {
969 multiSelectionChanged();
970 Q_EMIT q->selectionChanged();
971 }
972
973 // Same as above in fileHighlighted(), but for single-click mode
974 if (m_operationMode == KFileWidget::Saving) {
975 m_locationEdit->setFocus();
976 } else {
977 q->slotOk();
978 }
979}
980
981// I know it's slow to always iterate thru the whole filelist
982// (d->m_ops->selectedItems()), but what can we do?
983void KFileWidgetPrivate::multiSelectionChanged()
984{
985 if (m_locationEdit->hasFocus() && !m_locationEdit->currentText().isEmpty()) { // don't disturb
986 return;
987 }
988
989 const KFileItemList list = m_ops->selectedItems();
990
991 if (list.isEmpty()) {
992 setLocationText(QUrl());
993 return;
994 }
995
996 setLocationText(list.targetUrlList());
997}
998
999void KFileWidgetPrivate::setLocationText(const QUrl &url)
1000{
1001 // Block m_locationEdit signals as setCurrentItem() will cause textChanged() to get
1002 // emitted, so slotLocationChanged() will be called. Make sure we don't clear the
1003 // KDirOperator's view-selection in there
1004 const QSignalBlocker blocker(m_locationEdit);
1005
1006 if (!url.isEmpty()) {
1007 if (!url.isRelative()) {
1008 const QUrl directory = url.adjusted(QUrl::RemoveFilename);
1009 if (!directory.path().isEmpty()) {
1010 q->setUrl(directory, false);
1011 } else {
1012 q->setUrl(url, false);
1013 }
1014 }
1015 m_locationEdit->setEditText(escapeDoubleQuotes(url.fileName()));
1016 } else if (!m_locationEdit->lineEdit()->text().isEmpty()) {
1017 m_locationEdit->clearEditText();
1018 }
1019
1020 if (m_operationMode == KFileWidget::Saving) {
1021 setNonExtSelection();
1022 }
1023}
1024
1025static QString relativePathOrUrl(const QUrl &baseUrl, const QUrl &url)
1026{
1027 if (baseUrl.isParentOf(url)) {
1028 const QString basePath(QDir::cleanPath(baseUrl.path()));
1029 QString relPath(QDir::cleanPath(url.path()));
1030 relPath.remove(0, basePath.length());
1031 if (relPath.startsWith(QLatin1Char('/'))) {
1032 relPath.remove(0, 1);
1033 }
1034 return relPath;
1035 } else {
1036 return url.toDisplayString();
1037 }
1038}
1039
1040static QString escapeDoubleQuotes(QString &&path)
1041{
1042 // First escape the escape character that we are using
1043 path.replace(QStringLiteral("\\"), QStringLiteral("\\\\"));
1044 // Second, escape the quotes
1045 path.replace(QStringLiteral("\""), QStringLiteral("\\\""));
1046 return path;
1047}
1048
1049void KFileWidgetPrivate::initDirOpWidgets()
1050{
1051 m_opsWidget = new QWidget(q);
1052 m_opsWidgetLayout = new QVBoxLayout(m_opsWidget);
1053 m_opsWidgetLayout->setContentsMargins(0, 0, 0, 0);
1054 m_opsWidgetLayout->setSpacing(0);
1055
1056 m_model = new KFilePlacesModel(q);
1057
1058 // Don't pass "startDir" (KFileWidget constructor 1st arg) to the
1059 // KUrlNavigator at this stage: it may also contain a file name which
1060 // should not get inserted in that form into the old-style navigation
1061 // bar history. Wait until the KIO::stat has been done later.
1062 //
1063 // The stat cannot be done before this point, bug 172678.
1064 m_urlNavigator = new KUrlNavigator(m_model, QUrl(), m_opsWidget); // d->m_toolbar);
1065 m_urlNavigator->setPlacesSelectorVisible(false);
1066
1068 0,
1071
1072 m_messageWidget = new KMessageWidget(q);
1073 m_messageWidget->setMessageType(KMessageWidget::Error);
1074 m_messageWidget->hide();
1075
1076 auto topSeparator = new QFrame(q);
1077 topSeparator->setFrameStyle(QFrame::HLine);
1078
1079 m_ops = new KDirOperator(QUrl(), m_opsWidget);
1080 m_ops->installEventFilter(q);
1081 m_ops->setObjectName(QStringLiteral("KFileWidget::ops"));
1082 m_ops->setIsSaving(m_operationMode == KFileWidget::Saving);
1084 m_ops->showOpenWithActions(true);
1086
1087 auto bottomSparator = new QFrame(q);
1088 bottomSparator->setFrameStyle(QFrame::HLine);
1089
1090 q->connect(m_ops, &KDirOperator::urlEntered, q, [this](const QUrl &url) {
1091 urlEntered(url);
1092 });
1093 q->connect(m_ops, &KDirOperator::fileHighlighted, q, [this](const KFileItem &item) {
1094 fileHighlighted(item);
1095 });
1096 q->connect(m_ops, &KDirOperator::fileSelected, q, [this](const KFileItem &item) {
1097 fileSelected(item);
1098 });
1099 q->connect(m_ops, &KDirOperator::finishedLoading, q, [this]() {
1100 slotLoadingFinished();
1101 });
1102 q->connect(m_ops, &KDirOperator::keyEnterReturnPressed, q, [this]() {
1103 slotViewKeyEnterReturnPressed();
1104 });
1105 q->connect(m_ops, &KDirOperator::renamingFinished, q, [this](const QList<QUrl> &urls) {
1106 // Update file names in location text field after renaming selected files
1107 q->setSelectedUrls(urls);
1108 });
1109
1110 q->connect(m_ops, &KDirOperator::viewChanged, q, [](QAbstractItemView *newView) {
1111 newView->setProperty("_breeze_borders_sides", QVariant::fromValue(QFlags{Qt::TopEdge | Qt::BottomEdge}));
1112 });
1113
1114 m_ops->dirLister()->setAutoErrorHandlingEnabled(false);
1115 q->connect(m_ops->dirLister(), &KDirLister::jobError, q, [this](KIO::Job *job) {
1116 m_messageWidget->setText(job->errorString());
1117 m_messageWidget->animatedShow();
1118 });
1119
1120 m_ops->setupMenu(KDirOperator::SortActions | KDirOperator::FileActions | KDirOperator::ViewActions);
1121
1122 initToolbar();
1123
1124 m_opsWidgetLayout->addWidget(m_toolbar);
1125 m_opsWidgetLayout->addWidget(m_urlNavigator);
1126 m_opsWidgetLayout->addWidget(m_messageWidget);
1127 m_opsWidgetLayout->addWidget(topSeparator);
1128 m_opsWidgetLayout->addWidget(m_ops);
1129 m_opsWidgetLayout->addWidget(bottomSparator);
1130}
1131
1132void KFileWidgetPrivate::initZoomWidget()
1133{
1134 m_iconSizeSlider = new QSlider(q);
1136 m_iconSizeSlider->setMinimumWidth(40);
1137 m_iconSizeSlider->setOrientation(Qt::Horizontal);
1138 m_iconSizeSlider->setMinimum(0);
1139 m_iconSizeSlider->setMaximum(m_stdIconSizes.size() - 1);
1140 m_iconSizeSlider->setSingleStep(1);
1141 m_iconSizeSlider->setPageStep(1);
1142 m_iconSizeSlider->setTickPosition(QSlider::TicksBelow);
1143
1144 q->connect(m_iconSizeSlider, &QAbstractSlider::valueChanged, q, [this](int step) {
1145 slotIconSizeChanged(m_stdIconSizes[step]);
1146 });
1147
1148 q->connect(m_iconSizeSlider, &QAbstractSlider::sliderMoved, q, [this](int step) {
1149 slotIconSizeSliderMoved(m_stdIconSizes[step]);
1150 });
1151
1152 q->connect(m_ops, &KDirOperator::currentIconSizeChanged, q, [this](int iconSize) {
1153 slotDirOpIconSizeChanged(iconSize);
1154 });
1155
1156 m_zoomOutAction = KStandardAction::create(
1158 q,
1159 [this]() {
1160 changeIconsSize(ZoomOut);
1161 },
1162 q);
1163
1164 q->addAction(m_zoomOutAction);
1165
1166 m_zoomInAction = KStandardAction::create(
1168 q,
1169 [this]() {
1170 changeIconsSize(ZoomIn);
1171 },
1172 q);
1173
1174 q->addAction(m_zoomInAction);
1175}
1176
1177void KFileWidgetPrivate::initToolbar()
1178{
1179 m_toolbar = new QToolBar(m_opsWidget);
1180 m_toolbar->setObjectName(QStringLiteral("KFileWidget::toolbar"));
1181 m_toolbar->setMovable(false);
1182
1183 // add nav items to the toolbar
1184 //
1185 // NOTE: The order of the button icons here differs from that
1186 // found in the file manager and web browser, but has been discussed
1187 // and agreed upon on the kde-core-devel mailing list:
1188 //
1189 // http://lists.kde.org/?l=kde-core-devel&m=116888382514090&w=2
1190
1191 m_ops->action(KDirOperator::Up)
1192 ->setWhatsThis(i18n("<qt>Click this button to enter the parent folder.<br /><br />"
1193 "For instance, if the current location is file:/home/konqi clicking this "
1194 "button will take you to file:/home.</qt>"));
1195
1196 m_ops->action(KDirOperator::Back)->setWhatsThis(i18n("Click this button to move backwards one step in the browsing history."));
1197 m_ops->action(KDirOperator::Forward)->setWhatsThis(i18n("Click this button to move forward one step in the browsing history."));
1198
1199 m_ops->action(KDirOperator::Reload)->setWhatsThis(i18n("Click this button to reload the contents of the current location."));
1201 m_ops->action(KDirOperator::NewFolder)->setWhatsThis(i18n("Click this button to create a new folder."));
1202
1203 m_togglePlacesPanelAction = new KToggleAction(i18n("Show Places Panel"), q);
1204 q->addAction(m_togglePlacesPanelAction);
1205 m_togglePlacesPanelAction->setShortcut(QKeySequence(Qt::Key_F9));
1206 q->connect(m_togglePlacesPanelAction, &QAction::toggled, q, [this](bool show) {
1207 togglePlacesPanel(show);
1208 });
1209
1210 m_toggleBookmarksAction = new KToggleAction(i18n("Show Bookmarks Button"), q);
1211 q->addAction(m_toggleBookmarksAction);
1212 q->connect(m_toggleBookmarksAction, &QAction::toggled, q, [this](bool show) {
1213 toggleBookmarks(show);
1214 });
1215
1216 // Build the settings menu
1217 KActionMenu *menu = new KActionMenu(QIcon::fromTheme(QStringLiteral("configure")), i18n("Options"), q);
1218 q->addAction(menu);
1219 menu->setWhatsThis(
1220 i18n("<qt>This is the preferences menu for the file dialog. "
1221 "Various options can be accessed from this menu including: <ul>"
1222 "<li>how files are sorted in the list</li>"
1223 "<li>types of view, including icon and list</li>"
1224 "<li>showing of hidden files</li>"
1225 "<li>the Places panel</li>"
1226 "<li>file previews</li>"
1227 "<li>separating folders from files</li></ul></qt>"));
1228
1229 menu->addAction(m_ops->action(KDirOperator::AllowExpansionInDetailsView));
1230 menu->addSeparator();
1232 menu->addAction(m_togglePlacesPanelAction);
1233 menu->addAction(m_toggleBookmarksAction);
1235
1238
1239 m_bookmarkButton = new KActionMenu(QIcon::fromTheme(QStringLiteral("bookmarks")), i18n("Bookmarks"), q);
1240 m_bookmarkButton->setPopupMode(QToolButton::InstantPopup);
1241 q->addAction(m_bookmarkButton);
1242 m_bookmarkButton->setWhatsThis(
1243 i18n("<qt>This button allows you to bookmark specific locations. "
1244 "Click on this button to open the bookmark menu where you may add, "
1245 "edit or select a bookmark.<br /><br />"
1246 "These bookmarks are specific to the file dialog, but otherwise operate "
1247 "like bookmarks elsewhere in KDE.</qt>"));
1248
1249 QWidget *midSpacer = new QWidget(q);
1251
1252 m_toolbar->addAction(m_ops->action(KDirOperator::Back));
1253 m_toolbar->addAction(m_ops->action(KDirOperator::Forward));
1254 m_toolbar->addAction(m_ops->action(KDirOperator::Up));
1255 m_toolbar->addAction(m_ops->action(KDirOperator::Reload));
1256 m_toolbar->addSeparator();
1257 m_toolbar->addAction(m_ops->action(KDirOperator::ViewIconsView));
1258 m_toolbar->addAction(m_ops->action(KDirOperator::ViewCompactView));
1259 m_toolbar->addAction(m_ops->action(KDirOperator::ViewDetailsView));
1260 m_toolbar->addSeparator();
1261 m_toolbar->addAction(m_ops->action(KDirOperator::ShowPreview));
1262 m_toolbar->addAction(m_ops->action(KDirOperator::SortMenu));
1263 m_toolbar->addAction(m_bookmarkButton);
1264
1265 m_toolbar->addWidget(midSpacer);
1266
1267 initZoomWidget();
1268 m_toolbar->addAction(m_zoomOutAction);
1269 m_toolbar->addWidget(m_iconSizeSlider);
1270 m_toolbar->addAction(m_zoomInAction);
1271 m_toolbar->addSeparator();
1272
1273 m_toolbar->addAction(m_ops->action(KDirOperator::NewFolder));
1274 m_toolbar->addAction(menu);
1275
1277 m_toolbar->setMovable(false);
1278}
1279
1280void KFileWidgetPrivate::initLocationWidget()
1281{
1282 m_locationLabel = new QLabel(i18n("&Name:"), q);
1283 m_locationEdit = new KUrlComboBox(KUrlComboBox::Files, true, q);
1284 m_locationEdit->installEventFilter(q);
1285 // Properly let the dialog be resized (to smaller). Otherwise we could have
1286 // huge dialogs that can't be resized to smaller (it would be as big as the longest
1287 // item in this combo box). (ereslibre)
1289 q->connect(m_locationEdit, &KUrlComboBox::editTextChanged, q, [this](const QString &text) {
1290 slotLocationChanged(text);
1291 });
1292
1293 updateLocationWhatsThis();
1294 m_locationLabel->setBuddy(m_locationEdit);
1295
1296 KUrlCompletion *fileCompletionObj = new KUrlCompletion(KUrlCompletion::FileCompletion);
1297 m_locationEdit->setCompletionObject(fileCompletionObj);
1298 m_locationEdit->setAutoDeleteCompletionObject(true);
1299
1300 q->connect(m_locationEdit, &KUrlComboBox::returnPressed, q, [this](const QString &text) {
1301 locationAccepted(text);
1302 });
1303}
1304
1305void KFileWidgetPrivate::initFilterWidget()
1306{
1307 m_filterLabel = new QLabel(q);
1308 m_filterWidget = new KFileFilterCombo(q);
1310 updateFilterText();
1311 // Properly let the dialog be resized (to smaller). Otherwise we could have
1312 // huge dialogs that can't be resized to smaller (it would be as big as the longest
1313 // item in this combo box). (ereslibre)
1315 m_filterLabel->setBuddy(m_filterWidget);
1316 q->connect(m_filterWidget, &KFileFilterCombo::filterChanged, q, [this]() {
1317 slotFilterChanged();
1318 });
1319
1320 m_filterDelayTimer.setSingleShot(true);
1321 m_filterDelayTimer.setInterval(300);
1322 q->connect(m_filterWidget, &QComboBox::editTextChanged, &m_filterDelayTimer, qOverload<>(&QTimer::start));
1323 q->connect(&m_filterDelayTimer, &QTimer::timeout, q, [this]() {
1324 slotFilterChanged();
1325 });
1326}
1327
1328void KFileWidgetPrivate::setLocationText(const QList<QUrl> &urlList)
1329{
1330 // Block m_locationEdit signals as setCurrentItem() will cause textChanged() to get
1331 // emitted, so slotLocationChanged() will be called. Make sure we don't clear the
1332 // KDirOperator's view-selection in there
1333 const QSignalBlocker blocker(m_locationEdit);
1334
1335 const QUrl baseUrl = m_ops->url();
1336
1337 if (urlList.count() > 1) {
1338 QString urls;
1339 for (const QUrl &url : urlList) {
1340 urls += QStringLiteral("\"%1\" ").arg(escapeDoubleQuotes(relativePathOrUrl(baseUrl, url)));
1341 }
1342 urls.chop(1);
1343 m_locationEdit->setEditText(urls);
1344 } else if (urlList.count() == 1) {
1345 const auto url = urlList[0];
1346 m_locationEdit->setEditText(escapeDoubleQuotes(relativePathOrUrl(baseUrl, url)));
1347 } else if (!m_locationEdit->lineEdit()->text().isEmpty()) {
1348 m_locationEdit->clearEditText();
1349 }
1350
1351 if (m_operationMode == KFileWidget::Saving) {
1352 setNonExtSelection();
1353 }
1354}
1355
1356void KFileWidgetPrivate::updateLocationWhatsThis()
1357{
1358 const QString autocompletionWhatsThisText = i18n(
1359 "<qt>While typing in the text area, you may be presented "
1360 "with possible matches. "
1361 "This feature can be controlled by clicking with the right mouse button "
1362 "and selecting a preferred mode from the <b>Text Completion</b> menu.</qt>");
1363
1364 QString whatsThisText;
1365 if (m_operationMode == KFileWidget::Saving) {
1366 whatsThisText = QLatin1String("<qt>") + i18n("This is the name to save the file as.") + autocompletionWhatsThisText;
1367 } else if (m_ops->mode() & KFile::Files) {
1368 whatsThisText = QLatin1String("<qt>")
1369 + i18n("This is the list of files to open. More than "
1370 "one file can be specified by listing several "
1371 "files, separated by spaces.")
1372 + autocompletionWhatsThisText;
1373 } else {
1374 whatsThisText = QLatin1String("<qt>") + i18n("This is the name of the file to open.") + autocompletionWhatsThisText;
1375 }
1376
1377 m_locationLabel->setWhatsThis(whatsThisText);
1378 m_locationEdit->setWhatsThis(whatsThisText);
1379}
1380
1381void KFileWidgetPrivate::initPlacesPanel()
1382{
1383 if (m_placesDock) {
1384 return;
1385 }
1386
1387 m_placesDock = new QDockWidget(i18nc("@title:window", "Places"), q);
1389 m_placesDock->setTitleBarWidget(new KDEPrivate::KFileWidgetDockTitleBar(m_placesDock));
1390
1391 m_placesView = new KFilePlacesView(m_placesDock);
1392 m_placesView->setModel(m_model);
1394
1395 m_placesView->setObjectName(QStringLiteral("url bar"));
1396 QObject::connect(m_placesView, &KFilePlacesView::urlChanged, q, [this](const QUrl &url) {
1397 enterUrl(url);
1398 });
1399
1400 QObject::connect(qobject_cast<KFilePlacesModel *>(m_placesView->model()), &KFilePlacesModel::errorMessage, q, [this](const QString &errorMessage) {
1401 m_messageWidget->setText(errorMessage);
1402 m_messageWidget->animatedShow();
1403 });
1404
1405 // need to set the current url of the urlbar manually (not via urlEntered()
1406 // here, because the initial url of KDirOperator might be the same as the
1407 // one that will be set later (and then urlEntered() won't be emitted).
1408 // TODO: KDE5 ### REMOVE THIS when KDirOperator's initial URL (in the c'tor) is gone.
1409 m_placesView->setUrl(m_url);
1410
1411 m_placesDock->setWidget(m_placesView);
1412 m_placesViewSplitter->insertWidget(0, m_placesDock);
1413
1414 // initialize the size of the splitter
1415 m_placesViewWidth = m_configGroup.readEntry(SpeedbarWidth, m_placesView->sizeHint().width());
1416
1417 // Needed for when the dialog is shown with the places panel initially hidden
1418 setPlacesViewSplitterSizes();
1419
1420 QObject::connect(m_placesDock, &QDockWidget::visibilityChanged, q, [this](bool visible) {
1421 togglePlacesPanel(visible, m_placesDock);
1422 });
1423}
1424
1425void KFileWidgetPrivate::setPlacesViewSplitterSizes()
1426{
1427 if (m_placesViewWidth > 0) {
1428 QList<int> sizes = m_placesViewSplitter->sizes();
1429 sizes[0] = m_placesViewWidth;
1430 sizes[1] = q->width() - m_placesViewWidth - m_placesViewSplitter->handleWidth();
1431 m_placesViewSplitter->setSizes(sizes);
1432 }
1433}
1434
1435void KFileWidgetPrivate::initGUI()
1436{
1437 delete m_boxLayout; // deletes all sub layouts
1438
1439 m_boxLayout = new QVBoxLayout(q);
1440 m_boxLayout->setContentsMargins(0, 0, 0, 0); // no additional margin to the already existing
1441
1442 m_placesViewSplitter = new QSplitter(q);
1444 m_placesViewSplitter->setChildrenCollapsible(false);
1445 m_boxLayout->addWidget(m_placesViewSplitter);
1446
1447 QObject::connect(m_placesViewSplitter, &QSplitter::splitterMoved, q, [this](int pos, int index) {
1448 placesViewSplitterMoved(pos, index);
1449 });
1450 m_placesViewSplitter->insertWidget(0, m_opsWidget);
1451
1452 m_lafBox = new QFormLayout();
1453 m_lafBox->setSpacing(q->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing));
1454 m_lafBox->setContentsMargins(q->style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
1455 q->style()->pixelMetric(QStyle::PM_LayoutTopMargin),
1456 q->style()->pixelMetric(QStyle::PM_LayoutRightMargin),
1457 0);
1458
1459 m_lafBox->addRow(m_locationLabel, m_locationEdit);
1460 m_lafBox->addRow(m_filterLabel, m_filterWidget);
1461 // Add the "Automatically Select Extension" checkbox
1462 m_lafBox->addWidget(m_autoSelectExtCheckBox);
1463
1464 m_opsWidgetLayout->addLayout(m_lafBox);
1465
1466 auto hbox = new QHBoxLayout();
1467 hbox->setSpacing(q->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing));
1468 hbox->setContentsMargins(q->style()->pixelMetric(QStyle::PM_LayoutTopMargin),
1469 q->style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
1470 q->style()->pixelMetric(QStyle::PM_LayoutRightMargin),
1471 q->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
1472
1473 hbox->addStretch(2);
1474 hbox->addWidget(m_okButton);
1475 hbox->addWidget(m_cancelButton);
1476
1477 m_opsWidgetLayout->addLayout(hbox);
1478
1479 q->setTabOrder(m_ops, m_autoSelectExtCheckBox);
1480 q->setTabOrder(m_autoSelectExtCheckBox, m_locationEdit);
1481 q->setTabOrder(m_locationEdit, m_filterWidget);
1482 q->setTabOrder(m_filterWidget, m_okButton);
1483 q->setTabOrder(m_okButton, m_cancelButton);
1484 q->setTabOrder(m_cancelButton, m_urlNavigator);
1485 q->setTabOrder(m_urlNavigator, m_ops);
1486}
1487
1488void KFileWidgetPrivate::slotFilterChanged()
1489{
1490 m_filterDelayTimer.stop();
1491
1492 KFileFilter filter = m_filterWidget->currentFilter();
1493
1494 m_ops->clearFilter();
1495
1496 if (!filter.mimePatterns().isEmpty()) {
1497 QStringList types = filter.mimePatterns();
1498 types.prepend(QStringLiteral("inode/directory"));
1499 m_ops->setMimeFilter(types);
1500 }
1501
1502 const auto filePatterns = filter.filePatterns();
1503 const bool hasRegExSyntax = std::any_of(filePatterns.constBegin(), filePatterns.constEnd(), [](const QString &filter) {
1504 return filter.contains(QLatin1Char('*')) || filter.contains(QLatin1Char('?')) || filter.contains(QLatin1Char('['));
1505 });
1506
1507 if (hasRegExSyntax) {
1508 m_ops->setNameFilter(filter.filePatterns().join(QLatin1Char(' ')));
1509 } else {
1510 m_ops->setNameFilter(QLatin1Char('*') + filePatterns.join(QLatin1Char('*')) + QLatin1Char('*'));
1511 }
1512
1513 updateAutoSelectExtension();
1514
1515 m_ops->updateDir();
1516
1517 Q_EMIT q->filterChanged(filter);
1518}
1519
1520void KFileWidget::setUrl(const QUrl &url, bool clearforward)
1521{
1522 // qDebug();
1523
1524 d->m_ops->setUrl(url, clearforward);
1525}
1526
1527// Protected
1528void KFileWidgetPrivate::urlEntered(const QUrl &url)
1529{
1530 // qDebug();
1531
1532 KUrlComboBox *pathCombo = m_urlNavigator->editor();
1533 if (pathCombo->count() != 0) { // little hack
1534 pathCombo->setUrl(url);
1535 }
1536
1537 bool blocked = m_locationEdit->blockSignals(true);
1538 if (m_keepLocation) {
1539 const QUrl currentUrl = urlFromString(locationEditCurrentText());
1540 // iconNameForUrl will get the icon or fallback to a generic one
1541 m_locationEdit->setItemIcon(0, QIcon::fromTheme(KIO::iconNameForUrl(currentUrl)));
1542 // Preserve the text when clicking on the view (cf fileHighlighted)
1543 m_locationEdit->lineEdit()->setModified(true);
1544 }
1545
1546 m_locationEdit->blockSignals(blocked);
1547
1548 m_urlNavigator->setLocationUrl(url);
1549
1550 // is triggered in ctor before completion object is set
1551 KUrlCompletion *completion = dynamic_cast<KUrlCompletion *>(m_locationEdit->completionObject());
1552 if (completion) {
1553 completion->setDir(url);
1554 }
1555
1556 if (m_placesView) {
1557 m_placesView->setUrl(url);
1558 }
1559
1560 m_messageWidget->hide();
1561}
1562
1563void KFileWidgetPrivate::locationAccepted(const QString &url)
1564{
1565 Q_UNUSED(url);
1566 // qDebug();
1567 q->slotOk();
1568}
1569
1570void KFileWidgetPrivate::enterUrl(const QUrl &url)
1571{
1572 // qDebug();
1573
1574 // append '/' if needed: url combo does not add it
1575 // tokenize() expects it because it uses QUrl::adjusted(QUrl::RemoveFilename)
1576 QUrl u(url);
1577 Utils::appendSlashToPath(u);
1578 q->setUrl(u);
1579
1580 // We need to check window()->focusWidget() instead of m_locationEdit->hasFocus
1581 // because when the window is showing up m_locationEdit
1582 // may still not have focus but it'll be the one that will have focus when the window
1583 // gets it and we don't want to steal its focus either
1584 if (q->window()->focusWidget() != m_locationEdit) {
1585 m_ops->setFocus();
1586 }
1587}
1588
1589void KFileWidgetPrivate::enterUrl(const QString &url)
1590{
1591 // qDebug();
1592
1593 enterUrl(urlFromString(KUrlCompletion::replacedPath(url, true, true)));
1594}
1595
1596bool KFileWidgetPrivate::toOverwrite(const QUrl &url)
1597{
1598 // qDebug();
1599
1601 KJobWidgets::setWindow(statJob, q);
1602 bool res = statJob->exec();
1603
1604 if (res) {
1606 i18n("The file \"%1\" already exists. Do you wish to overwrite it?", url.fileName()),
1607 i18n("Overwrite File?"),
1610 QString(),
1612
1613 if (ret != KMessageBox::Continue) {
1614 m_locationEdit->setFocus();
1615 setNonExtSelection();
1616
1617 return false;
1618 }
1619 return true;
1620 }
1621
1622 return true;
1623}
1624
1626{
1627 // Honor protocols that do not support directory listing
1628 if (!url.isRelative() && !KProtocolManager::supportsListing(url)) {
1629 return;
1630 }
1631 d->setLocationText(url);
1632}
1633
1635{
1636 if (urls.isEmpty()) {
1637 return;
1638 }
1639
1640 // Honor protocols that do not support directory listing
1641 if (!urls[0].isRelative() && !KProtocolManager::supportsListing(urls[0])) {
1642 return;
1643 }
1644 d->setLocationText(urls);
1645}
1646
1647void KFileWidgetPrivate::slotLoadingFinished()
1648{
1649 const QString currentText = m_locationEdit->currentText();
1650 if (currentText.isEmpty()) {
1651 return;
1652 }
1653
1654 m_ops->blockSignals(true);
1655 QUrl u(m_ops->url());
1656 if (currentText.startsWith(QLatin1Char('/'))) {
1657 u.setPath(currentText);
1658 } else {
1659 u.setPath(Utils::concatPaths(m_ops->url().path(), currentText));
1660 }
1661 m_ops->setCurrentItem(u);
1662 m_ops->blockSignals(false);
1663}
1664
1665void KFileWidgetPrivate::slotLocationChanged(const QString &text)
1666{
1667 // qDebug();
1668
1669 m_locationEdit->lineEdit()->setModified(true);
1670
1671 if (text.isEmpty() && m_ops->view()) {
1672 m_ops->view()->clearSelection();
1673 }
1674
1675 if (!m_locationEdit->lineEdit()->text().isEmpty()) {
1676 const QList<QUrl> urlList(tokenize(text));
1677 m_ops->setCurrentItems(urlList);
1678 }
1679
1680 updateFilter();
1681}
1682
1684{
1685 // qDebug();
1686
1687 if (d->m_inAccept) {
1688 return d->m_url;
1689 } else {
1690 return QUrl();
1691 }
1692}
1693
1695{
1696 // qDebug();
1697
1698 QList<QUrl> list;
1699 if (d->m_inAccept) {
1700 if (d->m_ops->mode() & KFile::Files) {
1701 list = d->m_urlList;
1702 } else {
1703 list.append(d->m_url);
1704 }
1705 }
1706 return list;
1707}
1708
1709QList<QUrl> KFileWidgetPrivate::tokenize(const QString &line) const
1710{
1711 qCDebug(KIO_KFILEWIDGETS_FW) << "Tokenizing:" << line;
1712
1713 QList<QUrl> urls;
1714 QUrl baseUrl(m_ops->url().adjusted(QUrl::RemoveFilename));
1715 Utils::appendSlashToPath(baseUrl);
1716
1717 // A helper that creates, validates and appends a new url based
1718 // on the given filename.
1719 auto addUrl = [baseUrl, &urls](const QString &partial_name) {
1720 if (partial_name.trimmed().isEmpty()) {
1721 return;
1722 }
1723
1724 // url could be absolute
1725 QUrl partial_url(partial_name);
1726 if (!partial_url.isValid()
1727 || partial_url.isRelative()
1728 // the text might look like a url scheme but not be a real one
1729 || (!partial_url.scheme().isEmpty() && (!partial_name.contains(QStringLiteral("://")) || !KProtocolInfo::isKnownProtocol(partial_url.scheme())))) {
1730 // We have to use setPath here, so that something like "test#file"
1731 // isn't interpreted to have path "test" and fragment "file".
1732 partial_url.clear();
1733 partial_url.setPath(partial_name);
1734 }
1735
1736 // This returns QUrl(partial_name) for absolute URLs.
1737 // Otherwise, returns the concatenated url.
1738 if (partial_url.isRelative() || baseUrl.isParentOf(partial_url)) {
1739 partial_url = baseUrl.resolved(partial_url);
1740 }
1741
1742 if (partial_url.isValid()) {
1743 urls.append(partial_url);
1744 } else {
1745 // This can happen in the first quote! (ex: ' "something here"')
1746 qCDebug(KIO_KFILEWIDGETS_FW) << "Discarding Invalid" << partial_url;
1747 }
1748 };
1749
1750 // An iterative approach here where we toggle the "escape" flag
1751 // if we hit `\`. If we hit `"` and the escape flag is false,
1752 // we split
1753 QString partial_name;
1754 bool escape = false;
1755 for (int i = 0; i < line.length(); i++) {
1756 const QChar ch = line[i];
1757
1758 // Handle any character previously escaped
1759 if (escape) {
1760 partial_name += ch;
1761 escape = false;
1762 continue;
1763 }
1764
1765 // Handle escape start
1766 if (ch.toLatin1() == '\\') {
1767 escape = true;
1768 continue;
1769 }
1770
1771 // Handle UNESCAPED quote (") since the above ifs are
1772 // dealing with the escaped ones
1773 if (ch.toLatin1() == '"') {
1774 addUrl(partial_name);
1775 partial_name.clear();
1776 continue;
1777 }
1778
1779 // Any other character just append
1780 partial_name += ch;
1781 }
1782
1783 // Handle the last item which is buffered in partial_name. This is
1784 // required for single-file selection dialogs since the name will not
1785 // be wrapped in quotes
1786 if (!partial_name.isEmpty()) {
1787 addUrl(partial_name);
1788 partial_name.clear();
1789 }
1790
1791 return urls;
1792}
1793
1795{
1796 // qDebug();
1797
1798 if (d->m_inAccept) {
1799 const QUrl url = d->mostLocalUrl(d->m_url);
1800 if (url.isLocalFile()) {
1801 return url.toLocalFile();
1802 } else {
1803 KMessageBox::error(const_cast<KFileWidget *>(this), i18n("You can only select local files."), i18n("Remote Files Not Accepted"));
1804 }
1805 }
1806 return QString();
1807}
1808
1810{
1811 // qDebug();
1812
1813 QStringList list;
1814
1815 if (d->m_inAccept) {
1816 if (d->m_ops->mode() & KFile::Files) {
1817 const QList<QUrl> urls = d->m_urlList;
1818 for (const auto &u : urls) {
1819 const QUrl url = d->mostLocalUrl(u);
1820 if (url.isLocalFile()) {
1821 list.append(url.toLocalFile());
1822 }
1823 }
1824 }
1825
1826 else { // single-selection mode
1827 if (d->m_url.isLocalFile()) {
1828 list.append(d->m_url.toLocalFile());
1829 }
1830 }
1831 }
1832
1833 return list;
1834}
1835
1837{
1838 return d->m_ops->url();
1839}
1840
1841void KFileWidget::resizeEvent(QResizeEvent *event)
1842{
1844
1845 if (d->m_placesDock) {
1846 // we don't want our places dock actually changing size when we resize
1847 // and qt doesn't make it easy to enforce such a thing with QSplitter
1848 d->setPlacesViewSplitterSizes();
1849 }
1850}
1851
1852void KFileWidget::showEvent(QShowEvent *event)
1853{
1854 if (!d->m_hasView) { // delayed view-creation
1855 Q_ASSERT(d);
1856 Q_ASSERT(d->m_ops);
1857 d->m_ops->setViewMode(KFile::Default);
1858 d->m_hasView = true;
1859
1860 connect(d->m_ops->view(), &QAbstractItemView::doubleClicked, this, [this](const QModelIndex &index) {
1861 d->slotViewDoubleClicked(index);
1862 });
1863 }
1864 d->m_ops->clearHistory();
1865
1867}
1868
1869bool KFileWidget::eventFilter(QObject *watched, QEvent *event)
1870{
1871 const bool res = QWidget::eventFilter(watched, event);
1872
1873 QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>(event);
1874 if (!keyEvent) {
1875 return res;
1876 }
1877
1878 const auto type = event->type();
1879 const auto key = keyEvent->key();
1880
1881 if (watched == d->m_ops && type == QEvent::KeyPress && (key == Qt::Key_Return || key == Qt::Key_Enter)) {
1882 // ignore return events from the KDirOperator
1883 // they are not needed, activated is used to handle this case
1884 event->accept();
1885 return true;
1886 }
1887
1888 return res;
1889}
1890
1892{
1893 // qDebug();
1894
1895 d->m_ops->setMode(m);
1896 if (d->m_ops->dirOnlyMode()) {
1897 d->m_filterWidget->setDefaultFilter(KFileFilter(i18n("All Folders"), {QStringLiteral("*")}, {}));
1898 } else {
1899 d->m_filterWidget->setDefaultFilter(KFileFilter(i18n("All Files"), {QStringLiteral("*")}, {}));
1900 }
1901
1902 d->updateAutoSelectExtension();
1903}
1904
1906{
1907 return d->m_ops->mode();
1908}
1909
1910void KFileWidgetPrivate::readViewConfig()
1911{
1912 m_ops->setViewConfig(m_configGroup);
1913 m_ops->readConfig(m_configGroup);
1914 KUrlComboBox *combo = m_urlNavigator->editor();
1915
1917 (KCompletion::CompletionMode)m_configGroup.readEntry(PathComboCompletionMode, static_cast<int>(KCompletion::CompletionPopup));
1918 if (cm != KCompletion::CompletionPopup) {
1919 combo->setCompletionMode(cm);
1920 }
1921
1922 cm = (KCompletion::CompletionMode)m_configGroup.readEntry(LocationComboCompletionMode, static_cast<int>(KCompletion::CompletionPopup));
1923 if (cm != KCompletion::CompletionPopup) {
1924 m_locationEdit->setCompletionMode(cm);
1925 }
1926
1927 // Show or don't show the places panel
1928 togglePlacesPanel(m_configGroup.readEntry(ShowSpeedbar, true));
1929
1930 // show or don't show the bookmarks
1931 toggleBookmarks(m_configGroup.readEntry(ShowBookmarks, false));
1932
1933 // does the user want Automatically Select Extension?
1934 m_autoSelectExtChecked = m_configGroup.readEntry(AutoSelectExtChecked, DefaultAutoSelectExtChecked);
1935 updateAutoSelectExtension();
1936
1937 // should the URL navigator use the breadcrumb navigation?
1938 m_urlNavigator->setUrlEditable(!m_configGroup.readEntry(BreadcrumbNavigation, true));
1939
1940 // should the URL navigator show the full path?
1941 m_urlNavigator->setShowFullPath(m_configGroup.readEntry(ShowFullPath, false));
1942
1943 int w1 = q->minimumSize().width();
1944 int w2 = m_toolbar->sizeHint().width();
1945 if (w1 < w2) {
1946 q->setMinimumWidth(w2);
1947 }
1948}
1949
1950void KFileWidgetPrivate::writeViewConfig()
1951{
1952 // these settings are global settings; ALL instances of the file dialog
1953 // should reflect them.
1954 // There is no way to tell KFileOperator::writeConfig() to write to
1955 // kdeglobals so we write settings to a temporary config group then copy
1956 // them all to kdeglobals
1958 KConfigGroup tmpGroup(&tmp, ConfigGroup);
1959
1960 KUrlComboBox *pathCombo = m_urlNavigator->editor();
1961 // saveDialogSize( tmpGroup, KConfigGroup::Persistent | KConfigGroup::Global );
1962 tmpGroup.writeEntry(PathComboCompletionMode, static_cast<int>(pathCombo->completionMode()));
1963 tmpGroup.writeEntry(LocationComboCompletionMode, static_cast<int>(m_locationEdit->completionMode()));
1964
1965 const bool showPlacesPanel = m_placesDock && !m_placesDock->isHidden();
1966 tmpGroup.writeEntry(ShowSpeedbar, showPlacesPanel);
1967 if (m_placesViewWidth > 0) {
1968 tmpGroup.writeEntry(SpeedbarWidth, m_placesViewWidth);
1969 }
1970
1971 tmpGroup.writeEntry(ShowBookmarks, m_bookmarkHandler != nullptr);
1972 tmpGroup.writeEntry(AutoSelectExtChecked, m_autoSelectExtChecked);
1973 tmpGroup.writeEntry(BreadcrumbNavigation, !m_urlNavigator->isUrlEditable());
1974 tmpGroup.writeEntry(ShowFullPath, m_urlNavigator->showFullPath());
1975
1976 m_ops->writeConfig(tmpGroup);
1977
1978 // Copy saved settings to kdeglobals
1979 tmpGroup.copyTo(&m_configGroup, KConfigGroup::Persistent | KConfigGroup::Global);
1980}
1981
1982void KFileWidgetPrivate::readRecentFiles()
1983{
1984 // qDebug();
1985
1986 const bool oldState = m_locationEdit->blockSignals(true);
1987 m_locationEdit->setMaxItems(m_configGroup.readEntry(RecentFilesNumber, DefaultRecentURLsNumber));
1988 m_locationEdit->setUrls(m_stateConfigGroup.readPathEntry(RecentFiles, QStringList()), KUrlComboBox::RemoveBottom);
1989 m_locationEdit->setCurrentIndex(-1);
1990 m_locationEdit->blockSignals(oldState);
1991
1992 KUrlComboBox *combo = m_urlNavigator->editor();
1993 combo->setUrls(m_stateConfigGroup.readPathEntry(RecentURLs, QStringList()), KUrlComboBox::RemoveTop);
1994 combo->setMaxItems(m_configGroup.readEntry(RecentURLsNumber, DefaultRecentURLsNumber));
1995 combo->setUrl(m_ops->url());
1996 // since we delayed this moment, initialize the directory of the completion object to
1997 // our current directory (that was very probably set on the constructor)
1998 KUrlCompletion *completion = dynamic_cast<KUrlCompletion *>(m_locationEdit->completionObject());
1999 if (completion) {
2000 completion->setDir(m_ops->url());
2001 }
2002}
2003
2004void KFileWidgetPrivate::saveRecentFiles()
2005{
2006 // qDebug();
2007 m_stateConfigGroup.writePathEntry(RecentFiles, m_locationEdit->urls());
2008
2009 KUrlComboBox *pathCombo = m_urlNavigator->editor();
2010 m_stateConfigGroup.writePathEntry(RecentURLs, pathCombo->urls());
2011}
2012
2014{
2015 return d->m_okButton;
2016}
2017
2019{
2020 return d->m_cancelButton;
2021}
2022
2023// Called by KFileDialog
2024void KFileWidget::slotCancel()
2025{
2026 d->writeViewConfig();
2027 d->m_ops->close();
2028}
2029
2031{
2032 d->m_keepLocation = keep;
2033}
2034
2036{
2037 return d->m_keepLocation;
2038}
2039
2041{
2042 // qDebug();
2043
2044 d->m_operationMode = mode;
2045 d->m_keepLocation = (mode == Saving);
2046 d->m_filterWidget->setEditable(!d->m_hasDefaultFilter || mode != Saving);
2047 if (mode == Opening) {
2048 // don't use KStandardGuiItem::open() here which has trailing ellipsis!
2049 d->m_okButton->setText(i18n("&Open"));
2050 d->m_okButton->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
2051 // hide the new folder actions...usability team says they shouldn't be in open file dialog
2052 d->m_ops->action(KDirOperator::NewFolder)->setEnabled(false);
2053 d->m_toolbar->removeAction(d->m_ops->action(KDirOperator::NewFolder));
2054 } else if (mode == Saving) {
2055 KGuiItem::assign(d->m_okButton, KStandardGuiItem::save());
2056 d->setNonExtSelection();
2057 } else {
2058 KGuiItem::assign(d->m_okButton, KStandardGuiItem::ok());
2059 }
2060 d->updateLocationWhatsThis();
2061 d->updateAutoSelectExtension();
2062
2063 if (d->m_ops) {
2064 d->m_ops->setIsSaving(mode == Saving);
2065 }
2066 d->updateFilterText();
2067}
2068
2070{
2071 return d->m_operationMode;
2072}
2073
2074void KFileWidgetPrivate::slotAutoSelectExtClicked()
2075{
2076 // qDebug() << "slotAutoSelectExtClicked(): "
2077 // << m_autoSelectExtCheckBox->isChecked() << endl;
2078
2079 // whether the _user_ wants it on/off
2080 m_autoSelectExtChecked = m_autoSelectExtCheckBox->isChecked();
2081
2082 // update the current filename's extension
2083 updateLocationEditExtension(m_extension /* extension hasn't changed */);
2084}
2085
2086void KFileWidgetPrivate::placesViewSplitterMoved(int pos, int index)
2087{
2088 // qDebug();
2089
2090 // we need to record the size of the splitter when the splitter changes size
2091 // so we can keep the places box the right size!
2092 if (m_placesDock && index == 1) {
2093 m_placesViewWidth = pos;
2094 // qDebug() << "setting m_lafBox minwidth to" << m_placesViewWidth;
2095 }
2096}
2097
2098void KFileWidgetPrivate::activateUrlNavigator()
2099{
2100 // qDebug();
2101
2102 QLineEdit *lineEdit = m_urlNavigator->editor()->lineEdit();
2103
2104 // If the text field currently has focus and everything is selected,
2105 // pressing the keyboard shortcut returns the whole thing to breadcrumb mode
2106 if (m_urlNavigator->isUrlEditable() && lineEdit->hasFocus() && lineEdit->selectedText() == lineEdit->text()) {
2107 m_urlNavigator->setUrlEditable(false);
2108 } else {
2109 m_urlNavigator->setUrlEditable(true);
2110 m_urlNavigator->setFocus();
2111 lineEdit->selectAll();
2112 }
2113}
2114
2115void KFileWidgetPrivate::slotDirOpIconSizeChanged(int size)
2116{
2117 auto beginIt = m_stdIconSizes.cbegin();
2118 auto endIt = m_stdIconSizes.cend();
2119 auto it = std::lower_bound(beginIt, endIt, size);
2120 const int sliderStep = it != endIt ? it - beginIt : 0;
2121 m_iconSizeSlider->setValue(sliderStep);
2122 m_zoomOutAction->setDisabled(it == beginIt);
2123 m_zoomInAction->setDisabled(it == (endIt - 1));
2124}
2125
2126void KFileWidgetPrivate::changeIconsSize(ZoomState zoom)
2127{
2128 int step = m_iconSizeSlider->value();
2129
2130 if (zoom == ZoomOut) {
2131 if (step == 0) {
2132 return;
2133 }
2134 --step;
2135 } else { // ZoomIn
2136 if (step == static_cast<int>(m_stdIconSizes.size() - 1)) {
2137 return;
2138 }
2139 ++step;
2140 }
2141
2142 m_iconSizeSlider->setValue(step);
2143 slotIconSizeSliderMoved(m_stdIconSizes[step]);
2144}
2145
2146void KFileWidgetPrivate::slotIconSizeChanged(int _value)
2147{
2148 m_ops->setIconSize(_value);
2149 m_iconSizeSlider->setToolTip(i18n("Icon size: %1 pixels", _value));
2150}
2151
2152void KFileWidgetPrivate::slotIconSizeSliderMoved(int size)
2153{
2154 // Force this to be called in case this slot is called first on the
2155 // slider move.
2156 slotIconSizeChanged(size);
2157
2158 QPoint global(m_iconSizeSlider->rect().topLeft());
2159 global.ry() += m_iconSizeSlider->height() / 2;
2160 QHelpEvent toolTipEvent(QEvent::ToolTip, QPoint(0, 0), m_iconSizeSlider->mapToGlobal(global));
2161 QApplication::sendEvent(m_iconSizeSlider, &toolTipEvent);
2162}
2163
2164void KFileWidgetPrivate::slotViewDoubleClicked(const QModelIndex &index)
2165{
2166 // double clicking to save should only work on files
2167 if (m_operationMode == KFileWidget::Saving && index.isValid() && m_ops->selectedItems().constFirst().isFile()) {
2168 q->slotOk();
2169 }
2170}
2171
2172void KFileWidgetPrivate::slotViewKeyEnterReturnPressed()
2173{
2174 // an enter/return event occurred in the view
2175 // when we are saving one file and there is no selection in the view (otherwise we get an activated event)
2176 if (m_operationMode == KFileWidget::Saving && (m_ops->mode() & KFile::File) && m_ops->selectedItems().isEmpty()) {
2177 q->slotOk();
2178 }
2179}
2180
2181static QString getExtensionFromPatternList(const QStringList &patternList)
2182{
2183 // qDebug();
2184
2185 QString ret;
2186 // qDebug() << "\tgetExtension " << patternList;
2187
2188 QStringList::ConstIterator patternListEnd = patternList.end();
2189 for (QStringList::ConstIterator it = patternList.begin(); it != patternListEnd; ++it) {
2190 // qDebug() << "\t\ttry: \'" << (*it) << "\'";
2191
2192 // is this pattern like "*.BMP" rather than useless things like:
2193 //
2194 // README
2195 // *.
2196 // *.*
2197 // *.JP*G
2198 // *.JP?
2199 // *.[Jj][Pp][Gg]
2200 if ((*it).startsWith(QLatin1String("*.")) && (*it).length() > 2 && (*it).indexOf(QLatin1Char('*'), 2) < 0 && (*it).indexOf(QLatin1Char('?'), 2) < 0
2201 && (*it).indexOf(QLatin1Char('['), 2) < 0 && (*it).indexOf(QLatin1Char(']'), 2) < 0) {
2202 ret = (*it).mid(1);
2203 break;
2204 }
2205 }
2206
2207 return ret;
2208}
2209
2210static QString stripUndisplayable(const QString &string)
2211{
2212 QString ret = string;
2213
2214 ret.remove(QLatin1Char(':'));
2216
2217 return ret;
2218}
2219
2220// QString KFileWidget::currentFilterExtension()
2221//{
2222// return d->m_extension;
2223//}
2224
2225void KFileWidgetPrivate::updateAutoSelectExtension()
2226{
2227 if (!m_autoSelectExtCheckBox) {
2228 return;
2229 }
2230
2231 QMimeDatabase db;
2232 //
2233 // Figure out an extension for the Automatically Select Extension thing
2234 // (some Windows users apparently don't know what to do when confronted
2235 // with a text file called "COPYING" but do know what to do with
2236 // COPYING.txt ...)
2237 //
2238
2239 // qDebug() << "Figure out an extension: ";
2240 QString lastExtension = m_extension;
2241 m_extension.clear();
2242
2243 // Automatically Select Extension is only valid if the user is _saving_ a _file_
2244 if ((m_operationMode == KFileWidget::Saving) && (m_ops->mode() & KFile::File)) {
2245 //
2246 // Get an extension from the filter
2247 //
2248
2249 KFileFilter fileFilter = m_filterWidget->currentFilter();
2250 if (!fileFilter.isEmpty()) {
2251 // if the currently selected filename already has an extension which
2252 // is also included in the currently allowed extensions, keep it
2253 // otherwise use the default extension
2254 QString currentExtension = db.suffixForFileName(locationEditCurrentText());
2255 if (currentExtension.isEmpty()) {
2256 currentExtension = locationEditCurrentText().section(QLatin1Char('.'), -1, -1);
2257 }
2258 // qDebug() << "filter:" << filter << "locationEdit:" << locationEditCurrentText() << "currentExtension:" << currentExtension;
2259
2260 QString defaultExtension;
2261 QStringList extensionList;
2262
2263 // e.g. "*.cpp"
2264 if (!fileFilter.filePatterns().isEmpty()) {
2265 extensionList = fileFilter.filePatterns();
2266 defaultExtension = getExtensionFromPatternList(extensionList);
2267 }
2268 // e.g. "text/html"
2269 else if (!fileFilter.mimePatterns().isEmpty()) {
2270 QMimeType mime = db.mimeTypeForName(fileFilter.mimePatterns().first());
2271 if (mime.isValid()) {
2272 extensionList = mime.globPatterns();
2273 defaultExtension = mime.preferredSuffix();
2274 if (!defaultExtension.isEmpty()) {
2275 defaultExtension.prepend(QLatin1Char('.'));
2276 }
2277 }
2278 }
2279
2280 if ((!currentExtension.isEmpty() && extensionList.contains(QLatin1String("*.") + currentExtension))
2281 || (!fileFilter.mimePatterns().isEmpty() && fileFilter.mimePatterns().first() == QLatin1String("application/octet-stream"))) {
2282 m_extension = QLatin1Char('.') + currentExtension;
2283 } else {
2284 m_extension = defaultExtension;
2285 }
2286
2287 // qDebug() << "List:" << extensionList << "auto-selected extension:" << m_extension;
2288 }
2289
2290 //
2291 // GUI: checkbox
2292 //
2293
2294 QString whatsThisExtension;
2295 if (!m_extension.isEmpty()) {
2296 // remember: sync any changes to the string with below
2297 m_autoSelectExtCheckBox->setText(i18n("Automatically select filename e&xtension (%1)", m_extension));
2298 whatsThisExtension = i18n("the extension <b>%1</b>", m_extension);
2299
2300 m_autoSelectExtCheckBox->setEnabled(true);
2301 m_autoSelectExtCheckBox->setChecked(m_autoSelectExtChecked);
2302 } else {
2303 // remember: sync any changes to the string with above
2304 m_autoSelectExtCheckBox->setText(i18n("Automatically select filename e&xtension"));
2305 whatsThisExtension = i18n("a suitable extension");
2306
2307 m_autoSelectExtCheckBox->setChecked(false);
2308 m_autoSelectExtCheckBox->setEnabled(false);
2309 }
2310
2311 const QString locationLabelText = stripUndisplayable(m_locationLabel->text());
2312 m_autoSelectExtCheckBox->setWhatsThis(QLatin1String("<qt>")
2313 + i18n("This option enables some convenient features for "
2314 "saving files with extensions:<br />"
2315 "<ol>"
2316 "<li>Any extension specified in the <b>%1</b> text "
2317 "area will be updated if you change the file type "
2318 "to save in.<br />"
2319 "<br /></li>"
2320 "<li>If no extension is specified in the <b>%2</b> "
2321 "text area when you click "
2322 "<b>Save</b>, %3 will be added to the end of the "
2323 "filename (if the filename does not already exist). "
2324 "This extension is based on the file type that you "
2325 "have chosen to save in.<br />"
2326 "<br />"
2327 "If you do not want KDE to supply an extension for the "
2328 "filename, you can either turn this option off or you "
2329 "can suppress it by adding a period (.) to the end of "
2330 "the filename (the period will be automatically "
2331 "removed)."
2332 "</li>"
2333 "</ol>"
2334 "If unsure, keep this option enabled as it makes your "
2335 "files more manageable.",
2336 locationLabelText,
2337 locationLabelText,
2338 whatsThisExtension)
2339 + QLatin1String("</qt>"));
2340
2341 m_autoSelectExtCheckBox->show();
2342
2343 // update the current filename's extension
2344 updateLocationEditExtension(lastExtension);
2345 }
2346 // Automatically Select Extension not valid
2347 else {
2348 m_autoSelectExtCheckBox->setChecked(false);
2349 m_autoSelectExtCheckBox->hide();
2350 }
2351}
2352
2353// Updates the extension of the filename specified in d->m_locationEdit if the
2354// Automatically Select Extension feature is enabled.
2355// (this prevents you from accidentally saving "file.kwd" as RTF, for example)
2356void KFileWidgetPrivate::updateLocationEditExtension(const QString &lastExtension)
2357{
2358 if (!m_autoSelectExtCheckBox->isChecked() || m_extension.isEmpty()) {
2359 return;
2360 }
2361
2362 const QString urlStr = locationEditCurrentText();
2363 if (urlStr.isEmpty()) {
2364 return;
2365 }
2366
2367 const int fileNameOffset = urlStr.lastIndexOf(QLatin1Char('/')) + 1;
2368 QStringView fileName = QStringView(urlStr).mid(fileNameOffset);
2369
2370 const int dot = fileName.lastIndexOf(QLatin1Char('.'));
2371 const int len = fileName.length();
2372 if (dot > 0 && // has an extension already and it's not a hidden file
2373 // like ".hidden" (but we do accept ".hidden.ext")
2374 dot != len - 1 // and not deliberately suppressing extension
2375 ) {
2376 const QUrl url = getCompleteUrl(urlStr);
2377 // qDebug() << "updateLocationEditExtension (" << url << ")";
2378 // exists?
2380 KJobWidgets::setWindow(statJob, q);
2381 bool result = statJob->exec();
2382 if (result) {
2383 // qDebug() << "\tfile exists";
2384
2385 if (statJob->statResult().isDir()) {
2386 // qDebug() << "\tisDir - won't alter extension";
2387 return;
2388 }
2389
2390 // --- fall through ---
2391 }
2392
2393 //
2394 // try to get rid of the current extension
2395 //
2396
2397 // catch "double extensions" like ".tar.gz"
2398 if (!lastExtension.isEmpty() && fileName.endsWith(lastExtension)) {
2399 fileName.chop(lastExtension.length());
2400 } else if (!m_extension.isEmpty() && fileName.endsWith(m_extension)) {
2401 fileName.chop(m_extension.length());
2402 } else { // can only handle "single extensions"
2403 fileName.truncate(dot);
2404 }
2405
2406 // add extension
2407 const QString newText = QStringView(urlStr).left(fileNameOffset) + fileName + m_extension;
2408 if (newText != locationEditCurrentText()) {
2409 const int idx = m_locationEdit->currentIndex();
2410 if (idx == -1) {
2411 m_locationEdit->setEditText(newText);
2412 } else {
2413 m_locationEdit->setItemText(idx, newText);
2414 }
2415 m_locationEdit->lineEdit()->setModified(true);
2416 }
2417 }
2418}
2419
2420QString KFileWidgetPrivate::findMatchingFilter(const QString &filter, const QString &filename) const
2421{
2422 // e.g.: '*.foo *.bar|Foo type' -> '*.foo', '*.bar'
2423 const QStringList patterns = filter.left(filter.indexOf(QLatin1Char('|'))).split(QLatin1Char(' '), Qt::SkipEmptyParts);
2424
2426 for (const QString &p : patterns) {
2428 if (rx.match(filename).hasMatch()) {
2429 return p;
2430 }
2431 }
2432 return QString();
2433}
2434
2435// Updates the filter if the extension of the filename specified in d->m_locationEdit is changed
2436// (this prevents you from accidentally saving "file.kwd" as RTF, for example)
2437void KFileWidgetPrivate::updateFilter()
2438{
2439 if ((m_operationMode == KFileWidget::Saving) && (m_ops->mode() & KFile::File)) {
2440 QString urlStr = locationEditCurrentText();
2441 if (urlStr.isEmpty()) {
2442 return;
2443 }
2444
2445 QMimeDatabase db;
2447
2448 bool matchesCurrentFilter = [this, urlMimeType, urlStr] {
2449 const KFileFilter filter = m_filterWidget->currentFilter();
2450 if (filter.mimePatterns().contains(urlMimeType.name())) {
2451 return true;
2452 }
2453
2454 QString filename = urlStr.mid(urlStr.lastIndexOf(QLatin1Char('/')) + 1); // only filename
2455
2456 const auto filePatterns = filter.filePatterns();
2457 const bool hasMatch = std::any_of(filePatterns.cbegin(), filePatterns.cend(), [filename](const QString &pattern) {
2458 QRegularExpression rx(QRegularExpression::wildcardToRegularExpression(pattern));
2459
2460 return rx.match(filename).hasMatch();
2461 });
2462 return hasMatch;
2463 }();
2464
2465 if (matchesCurrentFilter) {
2466 return;
2467 }
2468
2469 const auto filters = m_filterWidget->filters();
2470
2471 auto filterIt = std::find_if(filters.cbegin(), filters.cend(), [urlStr, urlMimeType](const KFileFilter &filter) {
2472 if (filter.mimePatterns().contains(urlMimeType.name())) {
2473 return true;
2474 }
2475
2476 QString filename = urlStr.mid(urlStr.lastIndexOf(QLatin1Char('/')) + 1); // only filename
2477 // accept any match to honor the user's selection; see later code handling the "*" match
2478
2479 const auto filePatterns = filter.filePatterns();
2480 const bool hasMatch = std::any_of(filePatterns.cbegin(), filePatterns.cend(), [filename](const QString &pattern) {
2481 // never match the catch-all filter
2482 if (pattern == QLatin1String("*")) {
2483 return false;
2484 }
2485
2487
2488 return rx.match(filename).hasMatch();
2489 });
2490
2491 return hasMatch;
2492 });
2493
2494 if (filterIt != filters.cend()) {
2495 m_filterWidget->setCurrentFilter(*filterIt);
2496 }
2497 }
2498}
2499
2500// applies only to a file that doesn't already exist
2501void KFileWidgetPrivate::appendExtension(QUrl &url)
2502{
2503 // qDebug();
2504
2505 if (!m_autoSelectExtCheckBox->isChecked() || m_extension.isEmpty()) {
2506 return;
2507 }
2508
2509 QString fileName = url.fileName();
2510 if (fileName.isEmpty()) {
2511 return;
2512 }
2513
2514 // qDebug() << "appendExtension(" << url << ")";
2515
2516 const int len = fileName.length();
2517 const int dot = fileName.lastIndexOf(QLatin1Char('.'));
2518
2519 const bool suppressExtension = (dot == len - 1);
2520 const bool unspecifiedExtension = !fileName.endsWith(m_extension);
2521
2522 // don't KIO::Stat if unnecessary
2523 if (!(suppressExtension || unspecifiedExtension)) {
2524 return;
2525 }
2526
2527 // exists?
2529 KJobWidgets::setWindow(statJob, q);
2530 bool res = statJob->exec();
2531 if (res) {
2532 // qDebug() << "\tfile exists - won't append extension";
2533 return;
2534 }
2535
2536 // suppress automatically append extension?
2537 if (suppressExtension) {
2538 //
2539 // Strip trailing dot
2540 // This allows lazy people to have m_autoSelectExtCheckBox->isChecked
2541 // but don't want a file extension to be appended
2542 // e.g. "README." will make a file called "README"
2543 //
2544 // If you really want a name like "README.", then type "README.."
2545 // and the trailing dot will be removed (or just stop being lazy and
2546 // turn off this feature so that you can type "README.")
2547 //
2548 // qDebug() << "\tstrip trailing dot";
2549 QString path = url.path();
2550 path.chop(1);
2551 url.setPath(path);
2552 }
2553 // evilmatically append extension :) if the user hasn't specified one
2554 else if (unspecifiedExtension) {
2555 // qDebug() << "\tappending extension \'" << m_extension << "\'...";
2556 url = url.adjusted(QUrl::RemoveFilename); // keeps trailing slash
2557 url.setPath(url.path() + fileName + m_extension);
2558 // qDebug() << "\tsaving as \'" << url << "\'";
2559 }
2560}
2561
2562// adds the selected files/urls to 'recent documents'
2563void KFileWidgetPrivate::addToRecentDocuments()
2564{
2565 int m = m_ops->mode();
2566 int atmost = KRecentDocument::maximumItems();
2567 // don't add more than we need. KRecentDocument::add() is pretty slow
2568
2569 if (m & KFile::LocalOnly) {
2570 const QStringList files = q->selectedFiles();
2571 QStringList::ConstIterator it = files.begin();
2572 for (; it != files.end() && atmost > 0; ++it) {
2574 atmost--;
2575 }
2576 }
2577
2578 else { // urls
2579 const QList<QUrl> urls = q->selectedUrls();
2581 for (; it != urls.end() && atmost > 0; ++it) {
2582 if ((*it).isValid()) {
2584 atmost--;
2585 }
2586 }
2587 }
2588}
2589
2591{
2592 return d->m_locationEdit;
2593}
2594
2596{
2597 return d->m_filterWidget;
2598}
2599
2600void KFileWidgetPrivate::togglePlacesPanel(bool show, QObject *sender)
2601{
2602 if (show) {
2603 initPlacesPanel();
2604 m_placesDock->show();
2605
2606 // check to see if they have a home item defined, if not show the home button
2607 QUrl homeURL;
2608 homeURL.setPath(QDir::homePath());
2609 KFilePlacesModel *model = static_cast<KFilePlacesModel *>(m_placesView->model());
2610 for (int rowIndex = 0; rowIndex < model->rowCount(); rowIndex++) {
2611 QModelIndex index = model->index(rowIndex, 0);
2612 QUrl url = model->url(index);
2613
2614 if (homeURL.matches(url, QUrl::StripTrailingSlash)) {
2615 m_toolbar->removeAction(m_ops->action(KDirOperator::Home));
2616 break;
2617 }
2618 }
2619 } else {
2620 if (sender == m_placesDock && m_placesDock && m_placesDock->isVisibleTo(q)) {
2621 // we didn't *really* go away! the dialog was simply hidden or
2622 // we changed virtual desktops or ...
2623 return;
2624 }
2625
2626 if (m_placesDock) {
2627 m_placesDock->hide();
2628 }
2629
2630 QAction *homeAction = m_ops->action(KDirOperator::Home);
2631 QAction *reloadAction = m_ops->action(KDirOperator::Reload);
2632 if (!m_toolbar->actions().contains(homeAction)) {
2633 m_toolbar->insertAction(reloadAction, homeAction);
2634 }
2635 }
2636
2637 m_togglePlacesPanelAction->setChecked(show);
2638
2639 // if we don't show the places panel, at least show the places menu
2640 m_urlNavigator->setPlacesSelectorVisible(!show);
2641}
2642
2643void KFileWidgetPrivate::toggleBookmarks(bool show)
2644{
2645 if (show) {
2646 if (m_bookmarkHandler) {
2647 return;
2648 }
2649 m_bookmarkHandler = new KFileBookmarkHandler(q);
2650 q->connect(m_bookmarkHandler, &KFileBookmarkHandler::openUrl, q, [this](const QString &path) {
2651 enterUrl(path);
2652 });
2653 m_bookmarkButton->setMenu(m_bookmarkHandler->menu());
2654 } else if (m_bookmarkHandler) {
2655 m_bookmarkButton->setMenu(nullptr);
2656 delete m_bookmarkHandler;
2657 m_bookmarkHandler = nullptr;
2658 }
2659
2660 if (m_bookmarkButton) {
2661 m_bookmarkButton->setVisible(show);
2662 }
2663
2664 m_toggleBookmarksAction->setChecked(show);
2665}
2666
2667// static, overloaded
2668QUrl KFileWidget::getStartUrl(const QUrl &startDir, QString &recentDirClass)
2669{
2670 QString fileName; // result discarded
2671 return getStartUrl(startDir, recentDirClass, fileName);
2672}
2673
2674// static, overloaded
2675QUrl KFileWidget::getStartUrl(const QUrl &startDir, QString &recentDirClass, QString &fileName)
2676{
2677 recentDirClass.clear();
2678 fileName.clear();
2679 QUrl ret;
2680
2681 bool useDefaultStartDir = startDir.isEmpty();
2682 if (!useDefaultStartDir) {
2683 if (startDir.scheme() == QLatin1String("kfiledialog")) {
2684 // The startDir URL with this protocol may be in the format:
2685 // directory() fileName()
2686 // 1. kfiledialog:///keyword "/" keyword
2687 // 2. kfiledialog:///keyword?global "/" keyword
2688 // 3. kfiledialog:///keyword/ "/" keyword
2689 // 4. kfiledialog:///keyword/?global "/" keyword
2690 // 5. kfiledialog:///keyword/filename /keyword filename
2691 // 6. kfiledialog:///keyword/filename?global /keyword filename
2692
2693 QString keyword;
2695 QString urlFile = startDir.fileName();
2696 if (urlDir == QLatin1String("/")) { // '1'..'4' above
2697 keyword = urlFile;
2698 fileName.clear();
2699 } else { // '5' or '6' above
2700 keyword = urlDir.mid(1);
2701 fileName = urlFile;
2702 }
2703
2704 const QLatin1String query(":%1");
2705 recentDirClass = query.arg(keyword);
2706
2707 ret = QUrl::fromLocalFile(KRecentDirs::dir(recentDirClass));
2708 } else { // not special "kfiledialog" URL
2709 // "foo.png" only gives us a file name, the default start dir will be used.
2710 // "file:foo.png" (from KHTML/webkit, due to fromPath()) means the same
2711 // (and is the reason why we don't just use QUrl::isRelative()).
2712
2713 // In all other cases (startDir contains a directory path, or has no
2714 // fileName for us anyway, such as smb://), startDir is indeed a dir url.
2715
2716 if (!startDir.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path().isEmpty() || startDir.fileName().isEmpty()) {
2717 // can use start directory
2718 ret = startDir; // will be checked by stat later
2719 // If we won't be able to list it (e.g. http), then use default
2721 useDefaultStartDir = true;
2722 fileName = startDir.fileName();
2723 }
2724 } else { // file name only
2725 fileName = startDir.fileName();
2726 useDefaultStartDir = true;
2727 }
2728 }
2729 }
2730
2731 if (useDefaultStartDir) {
2732 if (lastDirectory()->isEmpty()) {
2735 // if there is no docpath set (== home dir), we prefer the current
2736 // directory over it. We also prefer the homedir when our CWD is
2737 // different from our homedirectory or when the document dir
2738 // does not exist
2739 if (lastDirectory()->adjusted(QUrl::StripTrailingSlash) == home.adjusted(QUrl::StripTrailingSlash) //
2741 || !QDir(lastDirectory()->toLocalFile()).exists()) {
2742 *lastDirectory() = QUrl::fromLocalFile(QDir::currentPath());
2743 }
2744 }
2745 ret = *lastDirectory();
2746 }
2747
2748 // qDebug() << "for" << startDir << "->" << ret << "recentDirClass" << recentDirClass << "fileName" << fileName;
2749 return ret;
2750}
2751
2752void KFileWidget::setStartDir(const QUrl &directory)
2753{
2754 if (directory.isValid()) {
2755 *lastDirectory() = directory;
2756 }
2757}
2758
2759void KFileWidgetPrivate::setNonExtSelection()
2760{
2761 // Enhanced rename: Don't highlight the file extension.
2762 QString filename = locationEditCurrentText();
2763 QMimeDatabase db;
2764 QString extension = db.suffixForFileName(filename);
2765
2766 if (!extension.isEmpty()) {
2767 m_locationEdit->lineEdit()->setSelection(0, filename.length() - extension.length() - 1);
2768 } else {
2769 int lastDot = filename.lastIndexOf(QLatin1Char('.'));
2770 if (lastDot > 0) {
2771 m_locationEdit->lineEdit()->setSelection(0, lastDot);
2772 } else {
2773 m_locationEdit->lineEdit()->selectAll();
2774 }
2775 }
2776}
2777
2778// Sets the filter text to "File type" if the dialog is saving and a MIME type
2779// filter has been set; otherwise, the text is "Filter:"
2780void KFileWidgetPrivate::updateFilterText()
2781{
2782 QString label;
2783 QString whatsThisText;
2784
2785 if (m_operationMode == KFileWidget::Saving && !m_filterWidget->currentFilter().mimePatterns().isEmpty()) {
2786 label = i18n("&File type:");
2787 whatsThisText = i18n("<qt>This is the file type selector. It is used to select the format that the file will be saved as.</qt>");
2788 } else {
2789 label = i18n("&Filter:");
2790 whatsThisText = i18n(
2791 "<qt>This is the filter to apply to the file list. "
2792 "File names that do not match the filter will not be shown.<p>"
2793 "You may select from one of the preset filters in the "
2794 "drop down menu, or you may enter a custom filter "
2795 "directly into the text area.</p><p>"
2796 "Wildcards such as * and ? are allowed.</p></qt>");
2797 }
2798
2799 if (m_filterLabel) {
2800 m_filterLabel->setText(label);
2801 m_filterLabel->setWhatsThis(whatsThisText);
2802 }
2803 if (m_filterWidget) {
2804 m_filterWidget->setWhatsThis(whatsThisText);
2805 }
2806}
2807
2809{
2810 delete d->m_bottomCustomWidget;
2811 d->m_bottomCustomWidget = widget;
2812
2813 // add it to the dialog, below the filter list box.
2814
2815 // Change the parent so that this widget is a child of the main widget
2816 d->m_bottomCustomWidget->setParent(this);
2817
2818 d->m_opsWidgetLayout->addWidget(d->m_bottomCustomWidget);
2819
2820 // FIXME: This should adjust the tab orders so that the custom widget
2821 // comes after the Cancel button. The code appears to do this, but the result
2822 // somehow screws up the tab order of the file path combo box. Not a major
2823 // problem, but ideally the tab order with a custom widget should be
2824 // the same as the order without one.
2825 setTabOrder(d->m_cancelButton, d->m_bottomCustomWidget);
2826 setTabOrder(d->m_bottomCustomWidget, d->m_urlNavigator);
2827}
2828
2830{
2831 delete d->m_labeledCustomWidget;
2832 d->m_labeledCustomWidget = widget;
2833
2834 QLabel *label = new QLabel(text, this);
2835 label->setAlignment(Qt::AlignRight);
2836 d->m_lafBox->addRow(label, widget);
2837}
2838
2840{
2841 return d->m_ops;
2842}
2843
2844#if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(6, 3)
2846{
2847 d->m_configGroup = group;
2848 d->readViewConfig();
2849 d->readRecentFiles();
2850}
2851#endif
2852
2853QString KFileWidgetPrivate::locationEditCurrentText() const
2854{
2855 return QDir::fromNativeSeparators(m_locationEdit->currentText());
2856}
2857
2858QUrl KFileWidgetPrivate::mostLocalUrl(const QUrl &url)
2859{
2860 if (url.isLocalFile()) {
2861 return url;
2862 }
2863
2865 KJobWidgets::setWindow(statJob, q);
2866 bool res = statJob->exec();
2867
2868 if (!res) {
2869 return url;
2870 }
2871
2873 if (!path.isEmpty()) {
2874 QUrl newUrl;
2875 newUrl.setPath(path);
2876 return newUrl;
2877 }
2878
2879 return url;
2880}
2881
2882void KFileWidgetPrivate::setInlinePreviewShown(bool show)
2883{
2884 m_ops->setInlinePreviewShown(show);
2885}
2886
2888{
2889 d->m_confirmOverwrite = enable;
2890}
2891
2893{
2894 d->setInlinePreviewShown(show);
2895}
2896
2898{
2899 int fontSize = fontMetrics().height();
2900 QSize goodSize(48 * fontSize, 30 * fontSize);
2901 const QSize scrnSize = d->screenSize();
2902 QSize minSize(scrnSize / 2);
2903 QSize maxSize(scrnSize * qreal(0.9));
2904 return (goodSize.expandedTo(minSize).boundedTo(maxSize));
2905}
2906
2907void KFileWidget::setViewMode(KFile::FileView mode)
2908{
2909 d->m_ops->setViewMode(mode);
2910 d->m_hasView = true;
2911}
2912
2914{
2915 d->m_model->setSupportedSchemes(schemes);
2916 d->m_ops->setSupportedSchemes(schemes);
2917 d->m_urlNavigator->setSupportedSchemes(schemes);
2918}
2919
2921{
2922 return d->m_model->supportedSchemes();
2923}
2924
2925#include "moc_kfilewidget.cpp"
void setPopupMode(QToolButton::ToolButtonPopupMode popupMode)
void addAction(QAction *action)
void returnPressed(const QString &text)
virtual void setCompletionMode(KCompletion::CompletionMode mode)
KCompletion * completionObject(bool handleSignals=true)
void setAutoDeleteCompletionObject(bool autoDelete)
KCompletion::CompletionMode completionMode() const
void writePathEntry(const char *Key, const QString &path, WriteConfigFlags pFlags=Normal)
QString readPathEntry(const char *key, const QString &aDefault) const
QString readEntry(const char *key, const char *aDefault=nullptr) const
void jobError(KIO::Job *job)
Emitted if listing a directory fails with an error.
KFileItem findByUrl(const QUrl &url) const
Find an item by its URL.
void setAutoErrorHandlingEnabled(bool enable)
Enable or disable auto error handling.
This widget works as a network transparent filebrowser.
void setIconSize(int value)
Notifies that the icons size should change.
void keyEnterReturnPressed()
Triggered when the user hit Enter/Return.
KFile::Modes mode() const
void setInlinePreviewShown(bool show)
Forces the inline previews to be shown or hidden, depending on show.
void renamingFinished(const QList< QUrl > &urls)
Emitted when renaming selected files has finished.
KFileItemList selectedItems() const
void setMimeFilter(const QStringList &mimetypes)
Sets a list of MIME types as filter.
void showOpenWithActions(bool enable)
Call with true to add open-with actions to items in the view.
KDirLister * dirLister() const
void updateSelectionDependentActions()
Enables/disables actions that are selection dependent.
virtual void readConfig(const KConfigGroup &configGroup)
Reads the default settings for a view, i.e. the default KFile::FileView.
void setCurrentItem(const QUrl &url)
Clears the current selection and attempts to set url the current url file.
void setNameFilter(const QString &filter)
Sets a filter like "*.cpp *.h *.o".
QAbstractItemView * view() const
QUrl url() const
void setupMenu(int whichActions)
Sets up the action menu.
virtual void writeConfig(KConfigGroup &configGroup)
Saves the current settings like sorting, simple or detailed view.
@ Up
Changes to the parent directory.
@ Home
Changes to the user's home directory.
@ ShowHiddenFiles
shows hidden files
@ ShowPreviewPanel
shows a preview next to the fileview
@ Forward
Goes forward in the history.
@ NewFolder
Opens a dialog box to create a directory.
@ SortMenu
An ActionMenu containing all sort-options.
@ SortHiddenFilesLast
Sorts hidden files last.
@ Reload
Reloads the current directory.
@ Back
Goes back to the previous directory.
void clearFilter()
Clears both the namefilter and MIME type filter, so that all files and directories will be shown.
void fileHighlighted(const KFileItem &item)
Emitted when a file is highlighted or generally the selection changes in multiselection mode.
void updateDir()
to update the view after changing the settings
void setIsSaving(bool isSaving)
If the system is set up to trigger items on single click, if isSaving is true, we will force to doubl...
void setNewFileMenuSelectDirWhenAlreadyExist(bool selectOnDirExists)
Setting this to true will make a directory get selected when trying to create a new one that has the ...
void viewChanged(QAbstractItemView *newView)
Emitted whenever the current fileview is changed, either by an explicit call to setView() or by the u...
QAction * action(KDirOperator::Action action) const
Obtain a given action from the KDirOperator's set of actions.
void currentIconSizeChanged(int size)
Will notify that the icon size has changed.
void setCurrentItems(const QList< QUrl > &urls)
Clears the current selection and attempts to set urls the current url files.
virtual void setViewConfig(KConfigGroup &configGroup)
Sets the config object and the to be used group in KDirOperator.
File filter combo box.
KFileFilter currentFilter() const
The currently selected/active filter.
QList< KFileFilter > filters() const
The current filters.
void filterChanged()
This signal is emitted whenever the filter has been changed.
Encapsulates rules to filter a list of files.
Definition kfilefilter.h:29
QStringList filePatterns() const
List of file name patterns that are included by this filter.
bool isEmpty() const
Whether the filer is empty, i.e. matches all files.
QStringList mimePatterns() const
List of MIME types that are included by this filter;.
List of KFileItems, which adds a few helper methods to QList<KFileItem>.
Definition kfileitem.h:630
A KFileItem is a generic class to handle a file, local or remote.
Definition kfileitem.h:36
bool isNull() const
Return true if default-constructed.
This class is a list view model.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
Get the children model index for the given row and column.
Q_INVOKABLE QUrl url(const QModelIndex &index) const
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Get the number of rows for a model index.
void errorMessage(const QString &message)
message An error message explaining what went wrong.
This class allows to display a KFilePlacesModel.
Generates previews for files of an item view.
File selector widget.
Definition kfilewidget.h:49
KDirOperator * dirOperator()
void setMode(KFile::Modes m)
Sets the mode of the dialog.
QString selectedFile() const
Returns the full path of the selected file in the local filesystem.
void setUrl(const QUrl &url, bool clearforward=true)
Sets the directory to view.
void accepted()
Emitted by slotOk() (directly or asynchronously) once everything has been done.
void setSelectedUrls(const QList< QUrl > &urls)
Sets a list of URLs as preselected.
QPushButton * okButton() const
void fileSelected(const QUrl &)
Emitted when the user selects a file.
void setFilters(const QList< KFileFilter > &filters, const KFileFilter &activeFilter=KFileFilter())
Set the filters to be used.
OperationMode operationMode() const
~KFileWidget() override
Destructor.
KUrlComboBox * locationEdit() const
KFileFilter currentFilter() const
Returns the current filter as entered by the user or one of the predefined set via setFilters().
QList< QUrl > selectedUrls() const
void fileHighlighted(const QUrl &)
Emitted when the user highlights a file.
void readConfig(KConfigGroup &group)
reads the configuration for this widget from the given config group
void setLocationLabel(const QString &text)
Sets the text to be displayed in front of the selection.
KFile::Modes mode() const
Returns the mode of the filedialog.
void slotOk()
Called when clicking ok (when this widget is used in KFileDialog) Might or might not call accept().
void setConfirmOverwrite(bool enable)
Sets whether the user should be asked for confirmation when an overwrite might occur.
KFileFilterCombo * filterWidget() const
void setSupportedSchemes(const QStringList &schemes)
Set the URL schemes that the file widget should allow navigating to.
void setKeepLocation(bool keep)
Sets whether the filename/url should be kept when changing directories.
QStringList selectedFiles() const
Returns a list of all selected local files.
bool keepsLocation() const
void setOperationMode(OperationMode)
Sets the operational mode of the filedialog to Saving, Opening or Other.
QSize sizeHint() const override
Reimplemented.
QSize dialogSizeHint() const
Provides a size hint, useful for dialogs that embed the widget.
void setInlinePreviewShown(bool show)
Forces the inline previews to be shown or hidden, depending on show.
void setSelectedUrl(const QUrl &url)
Sets the URL to preselect to url.
QStringList supportedSchemes() const
Returns the URL schemes that the file widget should allow navigating to.
QUrl selectedUrl() const
void selectionChanged()
Emitted when the user highlights one or more files in multiselection mode.
OperationMode
Defines some default behavior of the filedialog.
QPushButton * cancelButton() const
void setCustomWidget(QWidget *widget)
Set a custom widget that should be added to the file dialog.
void setViewMode(KFile::FileView mode)
Sets how the view should be displayed.
void setPreviewWidget(KPreviewWidgetBase *w)
Adds a preview widget and enters the preview mode.
KFileWidget(const QUrl &startDir, QWidget *parent=nullptr)
Constructs a file selector widget.
QUrl baseUrl() const
void clearFilter()
Clears any MIME type or name filter.
static void setStartDir(const QUrl &directory)
static QUrl getStartUrl(const QUrl &startDir, QString &recentDirClass)
This method implements the logic to determine the user's default directory to be listed.
static void assign(QPushButton *button, const KGuiItem &item)
The base class for all jobs.
A KIO job that retrieves information about a file or directory.
const UDSEntry & statResult() const
Result of the stat operation.
Definition statjob.cpp:80
QString stringValue(uint field) const
Definition udsentry.cpp:365
@ UDS_LOCAL_PATH
A local file path if the KIO worker display files sitting on the local filesystem (but in another hie...
Definition udsentry.h:227
bool isDir() const
Definition udsentry.cpp:375
bool exec()
static QString removeAcceleratorMarker(const QString &label)
void animatedShow()
void setMessageType(KMessageWidget::MessageType type)
void setText(const QString &text)
Abstract baseclass for all preview widgets which shall be used via KFileDialog::setPreviewWidget(cons...
static bool isKnownProtocol(const QUrl &url)
Returns whether a protocol is installed that is able to handle url.
static bool supportsListing(const QUrl &url)
Returns whether the protocol can list files/objects.
static int maximumItems()
Returns the maximum amount of recent document entries allowed.
static void add(const QUrl &url)
Add a new item to the Recent Document menu.
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
static KSharedConfig::Ptr openStateConfig(const QString &fileName=QString())
This combobox shows a number of recent URLs/directories, as well as some default directories.
void setUrls(const QStringList &urls)
Inserts urls into the combobox below the "default urls" (see addDefaultUrl).
void setUrl(const QUrl &url)
Sets the current url.
void setCompletionObject(KCompletion *compObj, bool hsig=true) override
Reimplemented from KComboBox (from KCompletion)
void setMaxItems(int)
Sets how many items should be handled and displayed by the combobox.
This class does completion of URLs including user directories (~user) and environment variables.
QString replacedPath(const QString &text) const
Replaces username and/or environment variables, depending on the current settings and returns the fil...
Widget that allows to navigate through the paths of an URL.
void setShowFullPath(bool show)
Shows the full path of the URL even if a place represents a part of the URL.
void setPlacesSelectorVisible(bool visible)
Sets the places selector visible, if visible is true.
void setLocationUrl(const QUrl &url)
Sets the location to url.
KUrlComboBox * editor() const
void setUrlEditable(bool editable)
Allows to edit the URL of the navigation bar if editable is true, and sets the focus accordingly.
bool showFullPath() const
bool isUrlEditable() const
void returnPressed()
This signal is emitted when the Return or Enter key is pressed.
void urlChanged(const QUrl &url)
Is emitted, if the location URL has been changed e.
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT QString iconNameForUrl(const QUrl &url)
Return the icon name for a URL.
Definition global.cpp:184
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
Find all details for one file or directory.
Definition statjob.cpp:203
KIOCORE_EXPORT QString buildErrorString(int errorCode, const QString &errorText)
Returns a translated error message for errorCode using the additional error information provided by e...
Definition job_error.cpp:31
KIOCORE_EXPORT QUrl upUrl(const QUrl &url)
This function is useful to implement the "Up" button in a file manager for example.
Definition global.cpp:234
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
Definition job_base.h:251
void setWindow(QObject *job, QWidget *widget)
QString path(const QString &relativePath)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
void information(QWidget *parent, const QString &text, const QString &title=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
KIOCORE_EXPORT QString dir(const QString &fileClass)
Returns the most recently used directory associated with this file-class.
KIOCORE_EXPORT QStringList list(const QString &fileClass)
Returns a list of directories associated with this file-class.
KIOCORE_EXPORT void add(const QString &fileClass, const QString &directory)
Associates directory with fileClass.
KCOREADDONS_EXPORT QString tildeExpand(const QString &path)
QAction * create(StandardAction id, const QObject *recvr, const char *slot, QObject *parent)
KGuiItem overwrite()
KGuiItem cancel()
KGuiItem save()
QString label(StandardShortcut id)
const QList< QKeySequence > & completion()
const QList< QKeySequence > & createFolder()
bool authorizeUrlAction(const QString &action, const QUrl &baseURL, const QUrl &destURL)
Returns whether a certain URL related action is authorized.
bool isChecked() const const
void clicked(bool checked)
void setText(const QString &text)
void doubleClicked(const QModelIndex &index)
QAbstractItemModel * model() const const
void setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy)
void setMaximum(int)
void setMinimum(int)
void setOrientation(Qt::Orientation)
void setPageStep(int)
void setSingleStep(int)
void sliderMoved(int value)
void setValue(int)
void valueChanged(int value)
bool isChecked() const const
QMenu * menu() const const
void setDisabled(bool b)
void setMenu(QMenu *menu)
void setShortcut(const QKeySequence &shortcut)
void setShortcuts(QKeySequence::StandardKey key)
void toggled(bool checked)
void triggered(bool checked)
void setVisible(bool)
void setWhatsThis(const QString &what)
void addLayout(QLayout *layout, int stretch)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
virtual void setSpacing(int spacing) override
char toLatin1() const const
AdjustToContentsOnFirstShow
void clearEditText()
void setCurrentIndex(int index)
void editTextChanged(const QString &text)
QLineEdit * lineEdit() const const
void setEditText(const QString &text)
void setItemIcon(int index, const QIcon &icon)
void setItemText(int index, const QString &text)
void setSizeAdjustPolicy(SizeAdjustPolicy policy)
bool sendEvent(QObject *receiver, QEvent *event)
QString cleanPath(const QString &path)
QString currentPath()
QString fromNativeSeparators(const QString &pathName)
QString homePath()
void setFeatures(DockWidgetFeatures features)
void setTitleBarWidget(QWidget *widget)
void setWidget(QWidget *widget)
void visibilityChanged(bool visible)
int height() const const
void addRow(QLayout *layout)
virtual void setSpacing(int spacing) override
QIcon fromTheme(const QString &name)
void setBuddy(QWidget *buddy)
void addWidget(QWidget *w)
void setContentsMargins(const QMargins &margins)
void setModified(bool)
void selectAll()
void setSelection(int start, int length)
void setText(const QString &)
typedef ConstIterator
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
iterator begin()
const_iterator constBegin() const const
const_iterator constEnd() const const
const T & constFirst() const const
bool contains(const AT &value) const const
qsizetype count() const const
iterator end()
T & first()
bool isEmpty() const const
void prepend(parameter_type value)
void reserve(qsizetype size)
qsizetype size() const const
void aboutToShow()
QMimeType mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const const
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
QString suffixForFileName(const QString &fileName) const const
bool isValid() const const
bool isValid() const const
Q_EMITQ_EMIT
bool blockSignals(bool block)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual bool eventFilter(QObject *watched, QEvent *event)
void installEventFilter(QObject *filterObj)
void setObjectName(QAnyStringView name)
bool setProperty(const char *name, QVariant &&value)
bool signalsBlocked() const const
QRegularExpressionMatch match(QStringView subjectView, qsizetype offset, MatchType matchType, MatchOptions matchOptions) const const
void setPattern(const QString &pattern)
QString wildcardToRegularExpression(QStringView pattern, WildcardConversionOptions options)
bool hasMatch() const const
QSize boundedTo(const QSize &otherSize) const const
QSize expandedTo(const QSize &otherSize) const const
int width() const const
void setTickPosition(TickPosition position)
void setChildrenCollapsible(bool)
void insertWidget(int index, QWidget *widget)
void setSizes(const QList< int > &list)
QList< int > sizes() const const
void splitterMoved(int pos, int index)
QString writableLocation(StandardLocation type)
QString arg(Args &&... args) const const
void chop(qsizetype n)
void clear()
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QString & prepend(QChar ch)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const
QStringView left(qsizetype length) const const
QStringView mid(qsizetype start, qsizetype length) const const
void chop(qsizetype length)
bool endsWith(QChar ch) const const
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs) const const
qsizetype length() const const
void truncate(qsizetype length)
PM_LayoutLeftMargin
virtual int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const const=0
AlignRight
Horizontal
ScrollBarAlwaysOff
SkipEmptyParts
ToolButtonIconOnly
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
void keyEvent(KeyAction action, QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier, int delay)
void setInterval(int msec)
void setSingleShot(bool singleShot)
void start()
void stop()
void timeout()
QAction * addSeparator()
QAction * addWidget(QWidget *widget)
void setMovable(bool movable)
void setToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle)
RemoveFilename
QUrl adjusted(FormattingOptions options) const const
QString fileName(ComponentFormattingOptions options) const const
QUrl fromLocalFile(const QString &localFile)
bool isEmpty() const const
bool isLocalFile() const const
bool isParentOf(const QUrl &childUrl) const const
bool isRelative() const const
bool isValid() const const
bool matches(const QUrl &url, FormattingOptions options) const const
QString path(ComponentFormattingOptions options) const const
QUrl resolved(const QUrl &relative) const const
QString scheme() const const
void setPath(const QString &path, ParsingMode mode)
QString toDisplayString(FormattingOptions options) const const
QString toLocalFile() const const
QVariant fromValue(T &&value)
QList< QAction * > actions() const const
QAction * addAction(const QIcon &icon, const QString &text)
void setEnabled(bool)
virtual bool event(QEvent *event) override
bool hasFocus() const const
QFontMetrics fontMetrics() const const
void hide()
void insertAction(QAction *before, QAction *action)
bool isHidden() const const
bool isVisibleTo(const QWidget *ancestor) const const
QPoint mapToGlobal(const QPoint &pos) const const
void setMinimumWidth(int minw)
QWidget * parentWidget() const const
void removeAction(QAction *action)
virtual void resizeEvent(QResizeEvent *event)
QScreen * screen() const const
void setContentsMargins(const QMargins &margins)
void setFocus()
void setTabOrder(QWidget *first, QWidget *second)
void show()
virtual void showEvent(QShowEvent *event)
void setSizePolicy(QSizePolicy)
QStyle * style() const const
void setToolTip(const QString &)
void setWhatsThis(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:54:08 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.