KIO

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

KDE's Doxygen guidelines are available online.