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

KDE's Doxygen guidelines are available online.