KIO

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

KDE's Doxygen guidelines are available online.