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

KDE's Doxygen guidelines are available online.