KIO

kurlcompletion.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 2000 David Smith <dsmith@algonet.se>
4  SPDX-FileCopyrightText: 2004 Scott Wheeler <wheeler@kde.org>
5 
6  This class was inspired by a previous KUrlCompletion by
7  SPDX-FileContributor: Henner Zeller <zeller@think.de>
8 
9  SPDX-License-Identifier: LGPL-2.0-or-later
10 */
11 
12 #include "kurlcompletion.h"
13 #include "../utils_p.h"
14 #include <assert.h>
15 #include <limits.h>
16 #include <stdlib.h>
17 
18 #include <QCollator>
19 #include <QDebug>
20 #include <QDir>
21 #include <QDirIterator>
22 #include <QFile>
23 #include <QMimeDatabase>
24 #include <QMutex>
25 #include <QProcessEnvironment>
26 #include <QRegularExpression>
27 #include <QThread>
28 #include <QUrl>
29 #include <qplatformdefs.h> // QT_LSTAT, QT_STAT, QT_STATBUF
30 
31 #include <KConfig>
32 #include <KConfigGroup>
33 #include <KSharedConfig>
34 #include <KUser>
35 
36 #include <kio/listjob.h>
37 #include <kio_widgets_debug.h>
38 #include <kioglobal_p.h>
39 #include <kprotocolmanager.h>
40 #include <kurlauthorized.h>
41 
42 #include <time.h>
43 
44 #ifdef Q_OS_WIN
45 #include <qt_windows.h>
46 #else
47 #include <pwd.h>
48 #include <sys/param.h>
49 #endif
50 
51 static bool expandTilde(QString &);
52 static bool expandEnv(QString &);
53 
54 static QString unescape(const QString &text);
55 
56 // Permission mask for files that are executable by
57 // user, group or other
58 static constexpr mode_t s_modeExe = S_IXUSR | S_IXGRP | S_IXOTH;
59 
60 // Constants for types of completion
61 enum ComplType { CTNone = 0, CTEnv, CTUser, CTMan, CTExe, CTFile, CTUrl, CTInfo };
62 
63 class CompletionThread;
64 
65 // Ensure that we don't end up with "//".
66 static void addPathToUrl(QUrl &url, const QString &relPath)
67 {
68  url.setPath(Utils::concatPaths(url.path(), relPath));
69 }
70 
71 static QBasicAtomicInt s_waitDuration = Q_BASIC_ATOMIC_INITIALIZER(-1);
72 
73 static int initialWaitDuration()
74 {
75  if (s_waitDuration.loadRelaxed() == -1) {
76  const QByteArray envVar = qgetenv("KURLCOMPLETION_WAIT");
77  if (envVar.isEmpty()) {
78  s_waitDuration = 200; // default: 200 ms
79  } else {
80  s_waitDuration = envVar.toInt();
81  }
82  }
83  return s_waitDuration;
84 }
85 
86 // For local paths we use our custom comparer function that ignores the trailing slash character
87 static void sortLocalPaths(QStringList &list)
88 {
89  QCollator c;
91  std::sort(list.begin(), list.end(), [c](const QString &a, const QString &b) {
92  return c.compare(a.endsWith(QStringLiteral("/")) ? a.chopped(1) : a, b.endsWith(QStringLiteral("/")) ? b.chopped(1) : b) < 0;
93  });
94 }
95 
96 ///////////////////////////////////////////////////////
97 ///////////////////////////////////////////////////////
98 // KUrlCompletionPrivate
99 //
100 class KUrlCompletionPrivate
101 {
102 public:
103  explicit KUrlCompletionPrivate(KUrlCompletion *qq, KUrlCompletion::Mode m)
104  : q(qq)
105  , cwd(QUrl::fromLocalFile(QDir::homePath()))
106  , mode(m)
107  {
108  // Read settings
109  KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("URLCompletion"));
110  url_auto_completion = cg.readEntry("alwaysAutoComplete", true);
111  popup_append_slash = cg.readEntry("popupAppendSlash", true);
112  onlyLocalProto = cg.readEntry("LocalProtocolsOnly", false);
113 
114  q->setIgnoreCase(true);
115  }
116 
117  ~KUrlCompletionPrivate();
118 
119  void slotEntries(KIO::Job *, const KIO::UDSEntryList &);
120  void slotIOFinished(KJob *);
121  void slotCompletionThreadDone(QThread *thread, const QStringList &matches);
122 
123  class MyURL;
124  bool userCompletion(const MyURL &url, QString *match);
125  bool envCompletion(const MyURL &url, QString *match);
126  bool exeCompletion(const MyURL &url, QString *match);
127  bool fileCompletion(const MyURL &url, QString *match);
128  bool urlCompletion(const MyURL &url, QString *match);
129 
130  bool isAutoCompletion();
131 
132  // List the next dir in m_dirs
133  QString listDirectories(const QStringList &, const QString &, bool only_exe = false, bool only_dir = false, bool no_hidden = false, bool stat_files = true);
134 
135  void listUrls(const QList<QUrl> &urls, const QString &filter = QString(), bool only_exe = false, bool no_hidden = false);
136 
137  void addMatches(const QStringList &);
138  QString finished();
139 
140  void init();
141 
142  void setListedUrl(ComplType compl_type, const QString &dir = QString(), const QString &filter = QString(), bool no_hidden = false);
143 
144  bool isListedUrl(ComplType compl_type, const QString &dir = QString(), const QString &filter = QString(), bool no_hidden = false);
145 
146  KUrlCompletion *const q;
147  QList<QUrl> list_urls;
148 
149  bool onlyLocalProto = false;
150 
151  // urlCompletion() in Auto/Popup mode?
152  bool url_auto_completion = true;
153 
154  // Append '/' to directories in Popup mode?
155  // Doing that stat's all files and is slower
156  bool popup_append_slash = true;
157 
158  // Keep track of currently listed files to avoid reading them again
159  bool last_no_hidden = false;
160  QString last_path_listed;
161  QString last_file_listed;
162  QString last_prepend;
163  ComplType last_compl_type = CTNone;
164 
165  QUrl cwd; // "current directory" = base dir for completion
166 
167  KUrlCompletion::Mode mode = KUrlCompletion::FileCompletion;
168  bool replace_env = true;
169  bool replace_home = true;
170  bool complete_url; // if true completing a URL (i.e. 'prepend' is a URL), otherwise a path
171 
172  KIO::ListJob *list_job = nullptr; // kio job to list directories
173 
174  QString prepend; // text to prepend to listed items
175  QString compl_text; // text to pass on to KCompletion
176 
177  // Filters for files read with kio
178  bool list_urls_only_exe; // true = only list executables
179  bool list_urls_no_hidden;
180  QString list_urls_filter; // filter for listed files
181 
182  CompletionThread *userListThread = nullptr;
183  CompletionThread *dirListThread = nullptr;
184 
185  QStringList mimeTypeFilters;
186 };
187 
188 class CompletionThread : public QThread
189 {
190  Q_OBJECT
191 protected:
192  CompletionThread(KUrlCompletionPrivate *receiver)
193  : QThread()
194  , m_prepend(receiver->prepend)
195  , m_complete_url(receiver->complete_url)
196  , m_terminationRequested(false)
197  {
198  }
199 
200 public:
201  void requestTermination()
202  {
203  if (!isFinished()) {
204  qCDebug(KIO_WIDGETS) << "stopping thread" << this;
205  }
206  m_terminationRequested.storeRelaxed(true);
207  wait();
208  }
209 
210  QStringList matches() const
211  {
212  QMutexLocker locker(&m_mutex);
213  return m_matches;
214  }
215 
216 Q_SIGNALS:
217  void completionThreadDone(QThread *thread, const QStringList &matches);
218 
219 protected:
220  void addMatch(const QString &match)
221  {
222  QMutexLocker locker(&m_mutex);
223  m_matches.append(match);
224  }
225  bool terminationRequested() const
226  {
227  return m_terminationRequested.loadRelaxed();
228  }
229  void done()
230  {
231  if (!terminationRequested()) {
232  qCDebug(KIO_WIDGETS) << "done, emitting signal with" << m_matches.count() << "matches";
233  Q_EMIT completionThreadDone(this, m_matches);
234  }
235  }
236 
237  const QString m_prepend;
238  const bool m_complete_url; // if true completing a URL (i.e. 'm_prepend' is a URL), otherwise a path
239 
240 private:
241  mutable QMutex m_mutex; // protects m_matches
242  QStringList m_matches; // written by secondary thread, read by the matches() method
243  QAtomicInt m_terminationRequested; // used as a bool
244 };
245 
246 /**
247  * A simple thread that fetches a list of tilde-completions and returns this
248  * to the caller via the completionThreadDone signal.
249  */
250 
251 class UserListThread : public CompletionThread
252 {
253  Q_OBJECT
254 public:
255  UserListThread(KUrlCompletionPrivate *receiver)
256  : CompletionThread(receiver)
257  {
258  }
259 
260 protected:
261  void run() override
262  {
263 #ifndef Q_OS_ANDROID
264  const QChar tilde = QLatin1Char('~');
265 
266  // we don't need to handle prepend here, right? ~user is always at pos 0
267  assert(m_prepend.isEmpty());
268 #ifndef Q_OS_WIN
269  struct passwd *pw;
270  ::setpwent();
271  while ((pw = ::getpwent()) && !terminationRequested()) {
272  addMatch(tilde + QString::fromLocal8Bit(pw->pw_name));
273  }
274  ::endpwent();
275 #else
276  // TODO: add KUser::allUserNames() with a std::function<bool()> shouldTerminate parameter
277  // currently terminationRequested is ignored on Windows
278  const QStringList allUsers = KUser::allUserNames();
279  for (const QString &s : allUsers) {
280  addMatch(tilde + s);
281  }
282 #endif
283  addMatch(QString(tilde));
284 #endif
285  done();
286  }
287 };
288 
289 class DirectoryListThread : public CompletionThread
290 {
291  Q_OBJECT
292 public:
293  DirectoryListThread(KUrlCompletionPrivate *receiver,
294  const QStringList &dirList,
295  const QString &filter,
296  const QStringList &mimeTypeFilters,
297  bool onlyExe,
298  bool onlyDir,
299  bool noHidden,
300  bool appendSlashToDir)
301  : CompletionThread(receiver)
302  , m_dirList(dirList)
303  , m_filter(filter)
304  , m_mimeTypeFilters(mimeTypeFilters)
305  , m_onlyExe(onlyExe)
306  , m_onlyDir(onlyDir)
307  , m_noHidden(noHidden)
308  , m_appendSlashToDir(appendSlashToDir)
309  {
310  }
311 
312  void run() override;
313 
314 private:
315  QStringList m_dirList;
316  QString m_filter;
317  QStringList m_mimeTypeFilters;
318  bool m_onlyExe;
319  bool m_onlyDir;
320  bool m_noHidden;
321  bool m_appendSlashToDir;
322 };
323 
324 void DirectoryListThread::run()
325 {
326  // qDebug() << "Entered DirectoryListThread::run(), m_filter=" << m_filter << ", m_onlyExe=" << m_onlyExe << ", m_onlyDir=" << m_onlyDir << ",
327  // m_appendSlashToDir=" << m_appendSlashToDir << ", m_dirList.size()=" << m_dirList.size();
328 
329  QDir::Filters iterator_filter = (m_noHidden ? QDir::Filter(0) : QDir::Hidden) | QDir::Readable | QDir::NoDotAndDotDot;
330  if (m_onlyExe) {
331  iterator_filter |= (QDir::Dirs | QDir::Files | QDir::Executable);
332  } else if (m_onlyDir) {
333  iterator_filter |= QDir::Dirs;
334  } else {
335  iterator_filter |= (QDir::Dirs | QDir::Files);
336  }
337 
338  QMimeDatabase mimeTypes;
339 
340  for (const QString &dir : std::as_const(m_dirList)) {
341  if (terminationRequested()) {
342  break;
343  }
344 
345  // qDebug() << "Scanning directory" << dir;
346 
347  QDirIterator current_dir_iterator(dir, iterator_filter);
348 
349  while (current_dir_iterator.hasNext() && !terminationRequested()) {
350  current_dir_iterator.next();
351 
352  QFileInfo item_info = current_dir_iterator.fileInfo();
353  QString item_name = item_info.fileName();
354 
355  // qDebug() << "Found" << file_name;
356 
357  if (!m_filter.isEmpty() && !item_name.startsWith(m_filter)) {
358  continue;
359  }
360 
361  if (!m_mimeTypeFilters.isEmpty() && !item_info.isDir()) {
362  auto mimeType = mimeTypes.mimeTypeForFile(item_info);
363  if (!m_mimeTypeFilters.contains(mimeType.name())) {
364  continue;
365  }
366  }
367 
368  // Add '/' to directories
369  if (m_appendSlashToDir && item_info.isDir()) {
370  Utils::appendSlash(item_name);
371  }
372 
373  if (m_complete_url) {
374  QUrl url(m_prepend);
375  addPathToUrl(url, item_name);
376  addMatch(url.toDisplayString());
377  } else {
378  item_name.prepend(m_prepend);
379  addMatch(item_name);
380  }
381  }
382  }
383 
384  done();
385 }
386 
387 KUrlCompletionPrivate::~KUrlCompletionPrivate()
388 {
389 }
390 
391 ///////////////////////////////////////////////////////
392 ///////////////////////////////////////////////////////
393 // MyURL - wrapper for QUrl with some different functionality
394 //
395 
396 class KUrlCompletionPrivate::MyURL
397 {
398 public:
399  MyURL(const QString &url, const QUrl &cwd);
400  MyURL(const MyURL &url);
401  ~MyURL();
402 
403  QUrl kurl() const
404  {
405  return m_kurl;
406  }
407 
408  bool isLocalFile() const
409  {
410  return m_kurl.isLocalFile();
411  }
412  QString scheme() const
413  {
414  return m_kurl.scheme();
415  }
416  // The directory with a trailing '/'
417  QString dir() const
418  {
419  return m_kurl.adjusted(QUrl::RemoveFilename).path();
420  }
421  QString file() const
422  {
423  return m_kurl.fileName();
424  }
425 
426  // The initial, unparsed, url, as a string.
427  QString url() const
428  {
429  return m_url;
430  }
431 
432  // Is the initial string a URL, or just a path (whether absolute or relative)
433  bool isURL() const
434  {
435  return m_isURL;
436  }
437 
438  void filter(bool replace_user_dir, bool replace_env);
439 
440 private:
441  void init(const QString &url, const QUrl &cwd);
442 
443  QUrl m_kurl;
444  QString m_url;
445  bool m_isURL;
446 };
447 
448 KUrlCompletionPrivate::MyURL::MyURL(const QString &_url, const QUrl &cwd)
449 {
450  init(_url, cwd);
451 }
452 
453 KUrlCompletionPrivate::MyURL::MyURL(const MyURL &_url)
454  : m_kurl(_url.m_kurl)
455 {
456  m_url = _url.m_url;
457  m_isURL = _url.m_isURL;
458 }
459 
460 void KUrlCompletionPrivate::MyURL::init(const QString &_url, const QUrl &cwd)
461 {
462  // Save the original text
463  m_url = _url;
464 
465  // Non-const copy
466  QString url_copy = _url;
467 
468  // Special shortcuts for "man:" and "info:"
469  if (url_copy.startsWith(QLatin1Char('#'))) {
470  if (url_copy.length() > 1 && url_copy.at(1) == QLatin1Char('#')) {
471  url_copy.replace(0, 2, QStringLiteral("info:"));
472  } else {
473  url_copy.replace(0, 1, QStringLiteral("man:"));
474  }
475  }
476 
477  // Look for a protocol in 'url'
478  const QRegularExpression protocol_regex(QStringLiteral("^(?![A-Za-z]:)[^/\\s\\\\]*:"));
479 
480  // Assume "file:" or whatever is given by 'cwd' if there is
481  // no protocol. (QUrl does this only for absolute paths)
482  if (protocol_regex.match(url_copy).hasMatch()) {
483  m_kurl = QUrl(url_copy);
484  m_isURL = true;
485  } else { // relative path or ~ or $something
486  m_isURL = false;
487  if (Utils::isAbsoluteLocalPath(url_copy) || url_copy.startsWith(QLatin1Char('~')) || url_copy.startsWith(QLatin1Char('$'))) {
488  m_kurl = QUrl::fromLocalFile(url_copy);
489  } else {
490  // Relative path
491  if (cwd.isEmpty()) {
492  m_kurl = QUrl(url_copy);
493  } else {
494  m_kurl = cwd;
495  m_kurl.setPath(Utils::concatPaths(m_kurl.path(), url_copy));
496  }
497  }
498  }
499 }
500 
501 KUrlCompletionPrivate::MyURL::~MyURL()
502 {
503 }
504 
505 void KUrlCompletionPrivate::MyURL::filter(bool replace_user_dir, bool replace_env)
506 {
507  QString d = dir() + file();
508  if (replace_user_dir) {
509  expandTilde(d);
510  }
511  if (replace_env) {
512  expandEnv(d);
513  }
514  m_kurl.setPath(d);
515 }
516 
517 ///////////////////////////////////////////////////////
518 ///////////////////////////////////////////////////////
519 // KUrlCompletion
520 //
521 
523  : KUrlCompletion(FileCompletion)
524 {
525 }
526 
528  : KCompletion()
529  , d(new KUrlCompletionPrivate(this, _mode))
530 {
531 }
532 
534 {
535  stop();
536 }
537 
538 void KUrlCompletion::setDir(const QUrl &dir)
539 {
540  d->cwd = dir;
541 }
542 
544 {
545  return d->cwd;
546 }
547 
549 {
550  return d->mode;
551 }
552 
554 {
555  d->mode = _mode;
556 }
557 
559 {
560  return d->replace_env;
561 }
562 
564 {
565  d->replace_env = replace;
566 }
567 
569 {
570  return d->replace_home;
571 }
572 
574 {
575  d->replace_home = replace;
576 }
577 
578 /*
579  * makeCompletion()
580  *
581  * Entry point for file name completion
582  */
584 {
585  qCDebug(KIO_WIDGETS) << text << "d->cwd=" << d->cwd;
586 
587  KUrlCompletionPrivate::MyURL url(text, d->cwd);
588 
589  d->compl_text = text;
590 
591  // Set d->prepend to the original URL, with the filename [and ref/query] stripped.
592  // This is what gets prepended to the directory-listing matches.
593  if (url.isURL()) {
594  QUrl directoryUrl(url.kurl());
595  directoryUrl.setQuery(QString());
596  directoryUrl.setFragment(QString());
597  directoryUrl.setPath(url.dir());
598  d->prepend = directoryUrl.toString();
599  } else {
600  d->prepend = text.left(text.length() - url.file().length());
601  }
602 
603  d->complete_url = url.isURL();
604 
605  // We use our custom sorter function if we are completing local paths
606  setSorterFunction(!d->complete_url ? sortLocalPaths : nullptr);
607 
608  // If we typed an exact path to a directory, we block autosuggestion
609  // or else it would autosuggest the first child dir.
610  setShouldAutoSuggest(d->complete_url || (url.dir() != text));
611 
612  QString aMatch;
613 
614  // Environment variables
615  //
616  if (d->replace_env && d->envCompletion(url, &aMatch)) {
617  return aMatch;
618  }
619 
620  // User directories
621  //
622  if (d->replace_home && d->userCompletion(url, &aMatch)) {
623  return aMatch;
624  }
625 
626  // Replace user directories and variables
627  url.filter(d->replace_home, d->replace_env);
628 
629  // qDebug() << "Filtered: proto=" << url.scheme()
630  // << ", dir=" << url.dir()
631  // << ", file=" << url.file()
632  // << ", kurl url=" << *url.kurl();
633 
634  if (d->mode == ExeCompletion) {
635  // Executables
636  //
637  if (d->exeCompletion(url, &aMatch)) {
638  return aMatch;
639  }
640 
641  // KRun can run "man:" and "info:" etc. so why not treat them
642  // as executables...
643 
644  if (d->urlCompletion(url, &aMatch)) {
645  return aMatch;
646  }
647  } else {
648  // Local files, directories
649  //
650  if (d->fileCompletion(url, &aMatch)) {
651  return aMatch;
652  }
653 
654  // All other...
655  //
656  if (d->urlCompletion(url, &aMatch)) {
657  return aMatch;
658  }
659  }
660 
661  d->setListedUrl(CTNone);
662  stop();
663 
664  return QString();
665 }
666 
667 /*
668  * finished
669  *
670  * Go on and call KCompletion.
671  * Called when all matches have been added
672  */
673 QString KUrlCompletionPrivate::finished()
674 {
675  if (last_compl_type == CTInfo) {
676  return q->KCompletion::makeCompletion(compl_text.toLower());
677  } else {
678  return q->KCompletion::makeCompletion(compl_text);
679  }
680 }
681 
682 /*
683  * isRunning
684  *
685  * Return true if either a KIO job or a thread is running
686  */
688 {
689  return d->list_job || (d->dirListThread && !d->dirListThread->isFinished()) || (d->userListThread && !d->userListThread->isFinished());
690 }
691 
692 /*
693  * stop
694  *
695  * Stop and delete a running KIO job or the DirLister
696  */
698 {
699  if (d->list_job) {
700  d->list_job->kill();
701  d->list_job = nullptr;
702  }
703 
704  if (d->dirListThread) {
705  d->dirListThread->requestTermination();
706  delete d->dirListThread;
707  d->dirListThread = nullptr;
708  }
709 
710  if (d->userListThread) {
711  d->userListThread->requestTermination();
712  delete d->userListThread;
713  d->userListThread = nullptr;
714  }
715 }
716 
717 /*
718  * Keep track of the last listed directory
719  */
720 void KUrlCompletionPrivate::setListedUrl(ComplType complType, const QString &directory, const QString &filter, bool no_hidden)
721 {
722  last_compl_type = complType;
723  last_path_listed = directory;
724  last_file_listed = filter;
725  last_no_hidden = no_hidden;
726  last_prepend = prepend;
727 }
728 
729 bool KUrlCompletionPrivate::isListedUrl(ComplType complType, const QString &directory, const QString &filter, bool no_hidden)
730 {
731  /* clang-format off */
732  return last_compl_type == complType
733  && (last_path_listed == directory || (directory.isEmpty() && last_path_listed.isEmpty()))
734  && (filter.startsWith(last_file_listed) || (filter.isEmpty() && last_file_listed.isEmpty()))
735  && last_no_hidden == no_hidden
736  && last_prepend == prepend; // e.g. relative path vs absolute
737  /* clang-format on */
738 }
739 
740 /*
741  * isAutoCompletion
742  *
743  * Returns true if completion mode is Auto or Popup
744  */
745 bool KUrlCompletionPrivate::isAutoCompletion()
746 {
747  /* clang-format off */
748  return q->completionMode() == KCompletion::CompletionAuto
749  || q->completionMode() == KCompletion::CompletionPopup
750  || q->completionMode() == KCompletion::CompletionMan
751  || q->completionMode() == KCompletion::CompletionPopupAuto;
752  /* clang-format on */
753 }
754 //////////////////////////////////////////////////
755 //////////////////////////////////////////////////
756 // User directories
757 //
758 
759 bool KUrlCompletionPrivate::userCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
760 {
761  if (url.scheme() != QLatin1String("file") || !url.dir().isEmpty() || !url.file().startsWith(QLatin1Char('~')) || !prepend.isEmpty()) {
762  return false;
763  }
764 
765  if (!isListedUrl(CTUser)) {
766  q->stop();
767  q->clear();
768  setListedUrl(CTUser);
769 
770  Q_ASSERT(!userListThread); // caller called stop()
771  userListThread = new UserListThread(this);
772  QObject::connect(userListThread, &CompletionThread::completionThreadDone, q, [this](QThread *thread, const QStringList &matches) {
773  slotCompletionThreadDone(thread, matches);
774  });
775  userListThread->start();
776 
777  // If the thread finishes quickly make sure that the results
778  // are added to the first matching case.
779 
780  userListThread->wait(initialWaitDuration());
781  const QStringList l = userListThread->matches();
782  addMatches(l);
783  }
784  *pMatch = finished();
785  return true;
786 }
787 
788 /////////////////////////////////////////////////////
789 /////////////////////////////////////////////////////
790 // Environment variables
791 //
792 
793 bool KUrlCompletionPrivate::envCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
794 {
795  if (url.file().isEmpty() || url.file().at(0) != QLatin1Char('$')) {
796  return false;
797  }
798 
799  if (!isListedUrl(CTEnv)) {
800  q->stop();
801  q->clear();
802 
804  const QStringList keys = env.keys();
805 
806  QStringList l;
807  l.reserve(keys.size());
808  for (const QString &key : keys) {
809  l.append(prepend + QLatin1Char('$') + key);
810  }
811 
812  addMatches(l);
813  }
814 
815  setListedUrl(CTEnv);
816 
817  *pMatch = finished();
818  return true;
819 }
820 
821 //////////////////////////////////////////////////
822 //////////////////////////////////////////////////
823 // Executables
824 //
825 
826 bool KUrlCompletionPrivate::exeCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
827 {
828  if (!url.isLocalFile()) {
829  return false;
830  }
831 
832  QString directory = unescape(url.dir()); // remove escapes
833 
834  // Find directories to search for completions, either
835  //
836  // 1. complete path given in url
837  // 2. current directory (d->cwd)
838  // 3. $PATH
839  // 4. no directory at all
840 
841  QStringList dirList;
842 
843  if (!url.file().isEmpty()) {
844  // $PATH
846 
847  QStringList::Iterator it = dirList.begin();
848 
849  for (; it != dirList.end(); ++it) {
850  it->append(QLatin1Char('/'));
851  }
852  } else if (Utils::isAbsoluteLocalPath(directory)) {
853  // complete path in url
854  dirList.append(directory);
855  } else if (!directory.isEmpty() && !cwd.isEmpty()) {
856  // current directory
857  dirList.append(cwd.toLocalFile() + QLatin1Char('/') + directory);
858  }
859 
860  // No hidden files unless the user types "."
861  bool no_hidden_files = url.file().isEmpty() || url.file().at(0) != QLatin1Char('.');
862 
863  // List files if needed
864  //
865  if (!isListedUrl(CTExe, directory, url.file(), no_hidden_files)) {
866  q->stop();
867  q->clear();
868 
869  setListedUrl(CTExe, directory, url.file(), no_hidden_files);
870 
871  *pMatch = listDirectories(dirList, url.file(), true, false, no_hidden_files);
872  } else {
873  *pMatch = finished();
874  }
875 
876  return true;
877 }
878 
879 //////////////////////////////////////////////////
880 //////////////////////////////////////////////////
881 // Local files
882 //
883 
884 bool KUrlCompletionPrivate::fileCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
885 {
886  if (!url.isLocalFile()) {
887  return false;
888  }
889 
890  QString directory = unescape(url.dir());
891 
892  if (url.url() == QLatin1String("..")) {
893  *pMatch = QStringLiteral("..");
894  return true;
895  }
896 
897  // qDebug() << "fileCompletion" << url << "dir=" << dir;
898 
899  // Find directories to search for completions, either
900  //
901  // 1. complete path given in url
902  // 2. current directory (d->cwd)
903  // 3. no directory at all
904 
905  QStringList dirList;
906 
907  if (Utils::isAbsoluteLocalPath(directory)) {
908  // complete path in url
909  dirList.append(directory);
910  } else if (!cwd.isEmpty()) {
911  // current directory
912  QString dirToAdd = cwd.toLocalFile();
913  if (!directory.isEmpty()) {
914  Utils::appendSlash(dirToAdd);
915  dirToAdd += directory;
916  }
917  dirList.append(dirToAdd);
918  }
919 
920  // No hidden files unless the user types "."
921  bool no_hidden_files = !url.file().startsWith(QLatin1Char('.'));
922 
923  // List files if needed
924  //
925  if (!isListedUrl(CTFile, directory, QString(), no_hidden_files)) {
926  q->stop();
927  q->clear();
928 
929  setListedUrl(CTFile, directory, QString(), no_hidden_files);
930 
931  // Append '/' to directories in Popup mode?
932  bool append_slash =
933  (popup_append_slash && (q->completionMode() == KCompletion::CompletionPopup || q->completionMode() == KCompletion::CompletionPopupAuto));
934 
935  bool only_dir = (mode == KUrlCompletion::DirCompletion);
936 
937  *pMatch = listDirectories(dirList, QString(), false, only_dir, no_hidden_files, append_slash);
938  } else {
939  *pMatch = finished();
940  }
941 
942  return true;
943 }
944 
945 //////////////////////////////////////////////////
946 //////////////////////////////////////////////////
947 // URLs not handled elsewhere...
948 //
949 
950 static bool isLocalProtocol(const QString &protocol)
951 {
952  return (KProtocolInfo::protocolClass(protocol) == QLatin1String(":local"));
953 }
954 
955 bool KUrlCompletionPrivate::urlCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
956 {
957  // qDebug() << *url.kurl();
958  if (onlyLocalProto && isLocalProtocol(url.scheme())) {
959  return false;
960  }
961 
962  // Use d->cwd as base url in case url is not absolute
963  QUrl url_dir = url.kurl();
964  if (url_dir.isRelative() && !cwd.isEmpty()) {
965  // Create an URL with the directory to be listed
966  url_dir = cwd.resolved(url_dir);
967  }
968 
969  // url is malformed
970  if (!url_dir.isValid() || url.scheme().isEmpty()) {
971  return false;
972  }
973 
974  // non local urls
975  if (!isLocalProtocol(url.scheme())) {
976  // url does not specify host
977  if (url_dir.host().isEmpty()) {
978  return false;
979  }
980 
981  // url does not specify a valid directory
983  return false;
984  }
985 
986  // automatic completion is disabled
987  if (isAutoCompletion() && !url_auto_completion) {
988  return false;
989  }
990  }
991 
992  // url handler doesn't support listing
993  if (!KProtocolManager::supportsListing(url_dir)) {
994  return false;
995  }
996 
997  // Remove escapes
998  const QString directory = unescape(url_dir.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path());
999  url_dir.setPath(directory);
1000 
1001  // List files if needed
1002  //
1003  if (!isListedUrl(CTUrl, directory, url.file())) {
1004  q->stop();
1005  q->clear();
1006 
1007  setListedUrl(CTUrl, directory, QString());
1008 
1009  QList<QUrl> url_list;
1010  url_list.append(url_dir);
1011 
1012  listUrls(url_list, QString(), false);
1013 
1014  pMatch->clear();
1015  } else if (!q->isRunning()) {
1016  *pMatch = finished();
1017  } else {
1018  pMatch->clear();
1019  }
1020 
1021  return true;
1022 }
1023 
1024 //////////////////////////////////////////////////
1025 //////////////////////////////////////////////////
1026 // Directory and URL listing
1027 //
1028 
1029 /*
1030  * addMatches
1031  *
1032  * Called to add matches to KCompletion
1033  */
1034 void KUrlCompletionPrivate::addMatches(const QStringList &matchList)
1035 {
1036  q->insertItems(matchList);
1037 }
1038 
1039 /*
1040  * listDirectories
1041  *
1042  * List files starting with 'filter' in the given directories,
1043  * either using DirLister or listURLs()
1044  *
1045  * In either case, addMatches() is called with the listed
1046  * files, and eventually finished() when the listing is done
1047  *
1048  * Returns the match if available, or QString() if
1049  * DirLister timed out or using kio
1050  */
1051 QString KUrlCompletionPrivate::listDirectories(const QStringList &dirList,
1052  const QString &filter,
1053  bool only_exe,
1054  bool only_dir,
1055  bool no_hidden,
1056  bool append_slash_to_dir)
1057 {
1058  assert(!q->isRunning());
1059 
1060  if (qEnvironmentVariableIsEmpty("KURLCOMPLETION_LOCAL_KIO")) {
1061  qCDebug(KIO_WIDGETS) << "Listing directories:" << dirList << "with filter=" << filter << "using thread";
1062 
1063  // Don't use KIO
1064 
1065  QStringList dirs;
1066 
1068  for (QStringList::ConstIterator it = dirList.constBegin(); it != end; ++it) {
1069  QUrl url = QUrl::fromLocalFile(*it);
1070  if (KUrlAuthorized::authorizeUrlAction(QStringLiteral("list"), QUrl(), url)) {
1071  dirs.append(*it);
1072  }
1073  }
1074 
1075  Q_ASSERT(!dirListThread); // caller called stop()
1076  dirListThread = new DirectoryListThread(this, dirs, filter, mimeTypeFilters, only_exe, only_dir, no_hidden, append_slash_to_dir);
1077  QObject::connect(dirListThread, &CompletionThread::completionThreadDone, q, [this](QThread *thread, const QStringList &matches) {
1078  slotCompletionThreadDone(thread, matches);
1079  });
1080  dirListThread->start();
1081  dirListThread->wait(initialWaitDuration());
1082  qCDebug(KIO_WIDGETS) << "Adding initial matches:" << dirListThread->matches();
1083  addMatches(dirListThread->matches());
1084 
1085  return finished();
1086  }
1087 
1088  // Use KIO
1089  // qDebug() << "Listing (listDirectories):" << dirList << "with KIO";
1090 
1091  QList<QUrl> url_list;
1092 
1093  QStringList::ConstIterator it = dirList.constBegin();
1095 
1096  url_list.reserve(dirList.size());
1097  for (; it != end; ++it) {
1098  url_list.append(QUrl(*it));
1099  }
1100 
1101  listUrls(url_list, filter, only_exe, no_hidden);
1102  // Will call addMatches() and finished()
1103 
1104  return QString();
1105 }
1106 
1107 /*
1108  * listURLs
1109  *
1110  * Use KIO to list the given urls
1111  *
1112  * addMatches() is called with the listed files
1113  * finished() is called when the listing is done
1114  */
1115 void KUrlCompletionPrivate::listUrls(const QList<QUrl> &urls, const QString &filter, bool only_exe, bool no_hidden)
1116 {
1117  assert(list_urls.isEmpty());
1118  assert(list_job == nullptr);
1119 
1120  list_urls = urls;
1121  list_urls_filter = filter;
1122  list_urls_only_exe = only_exe;
1123  list_urls_no_hidden = no_hidden;
1124 
1125  // qDebug() << "Listing URLs:" << *urls[0] << ",...";
1126 
1127  // Start it off by calling slotIOFinished
1128  //
1129  // This will start a new list job as long as there
1130  // are urls in d->list_urls
1131  //
1132  slotIOFinished(nullptr);
1133 }
1134 
1135 /*
1136  * slotEntries
1137  *
1138  * Receive files listed by KIO and call addMatches()
1139  */
1140 void KUrlCompletionPrivate::slotEntries(KIO::Job *, const KIO::UDSEntryList &entries)
1141 {
1142  QStringList matchList;
1143 
1144  const QString filter = list_urls_filter;
1145  const int filter_len = filter.length();
1146 
1147  // Iterate over all files
1148  for (const auto &entry : entries) {
1149  const QString udsUrl = entry.stringValue(KIO::UDSEntry::UDS_URL);
1150 
1151  QString entry_name;
1152  if (!udsUrl.isEmpty()) {
1153  // qDebug() << "url:" << url;
1154  entry_name = QUrl(udsUrl).fileName();
1155  } else {
1156  entry_name = entry.stringValue(KIO::UDSEntry::UDS_NAME);
1157  }
1158 
1159  // This can happen with kdeconnect://deviceId as a completion for kdeconnect:/,
1160  // there's no fileName [and the UDS_NAME is unrelated, can't use that].
1161  // This code doesn't support completing hostnames anyway (see addPathToUrl below).
1162  if (entry_name.isEmpty()) {
1163  continue;
1164  }
1165 
1166  if (entry_name.at(0) == QLatin1Char('.')
1167  && (list_urls_no_hidden || entry_name.length() == 1 || (entry_name.length() == 2 && entry_name.at(1) == QLatin1Char('.')))) {
1168  continue;
1169  }
1170 
1171  const bool isDir = entry.isDir();
1172 
1173  if (mode == KUrlCompletion::DirCompletion && !isDir) {
1174  continue;
1175  }
1176 
1177  if (filter_len != 0 && QStringView(entry_name).left(filter_len) != filter) {
1178  continue;
1179  }
1180 
1181  if (!mimeTypeFilters.isEmpty() && !isDir && !mimeTypeFilters.contains(entry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE))) {
1182  continue;
1183  }
1184 
1185  QString toAppend = entry_name;
1186 
1187  if (isDir) {
1188  toAppend.append(QLatin1Char('/'));
1189  }
1190 
1191  if (!list_urls_only_exe || (entry.numberValue(KIO::UDSEntry::UDS_ACCESS) & s_modeExe) // true if executable
1192  ) {
1193  if (complete_url) {
1194  QUrl url(prepend);
1195  addPathToUrl(url, toAppend);
1196  matchList.append(url.toDisplayString());
1197  } else {
1198  matchList.append(prepend + toAppend);
1199  }
1200  }
1201  }
1202 
1203  addMatches(matchList);
1204 }
1205 
1206 /*
1207  * slotIOFinished
1208  *
1209  * Called when a KIO job is finished.
1210  *
1211  * Start a new list job if there are still urls in
1212  * list_urls, otherwise call finished()
1213  */
1214 void KUrlCompletionPrivate::slotIOFinished(KJob *job)
1215 {
1216  assert(job == list_job);
1217  Q_UNUSED(job)
1218 
1219  if (list_urls.isEmpty()) {
1220  list_job = nullptr;
1221 
1222  finished(); // will call KCompletion::makeCompletion()
1223 
1224  } else {
1225  QUrl kurl(list_urls.takeFirst());
1226 
1227  // list_urls.removeAll( kurl );
1228 
1229  // qDebug() << "Start KIO::listDir" << kurl;
1230 
1231  list_job = KIO::listDir(kurl, KIO::HideProgressInfo);
1232  list_job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true"));
1233 
1234  assert(list_job);
1235 
1236  q->connect(list_job, &KJob::result, q, [this](KJob *job) {
1237  slotIOFinished(job);
1238  });
1239 
1240  q->connect(list_job, &KIO::ListJob::entries, q, [this](KIO::Job *job, const KIO::UDSEntryList &list) {
1241  slotEntries(job, list);
1242  });
1243  }
1244 }
1245 
1246 ///////////////////////////////////////////////////
1247 ///////////////////////////////////////////////////
1248 
1249 /*
1250  * postProcessMatch, postProcessMatches
1251  *
1252  * Called by KCompletion before emitting match() and matches()
1253  *
1254  * Append '/' to directories for file completion. This is
1255  * done here to avoid stat()'ing a lot of files
1256  */
1257 void KUrlCompletion::postProcessMatch(QString *pMatch) const
1258 {
1259  // qDebug() << *pMatch;
1260 
1261  if (!pMatch->isEmpty() && pMatch->startsWith(QLatin1String("file:"))) {
1262  // Add '/' to directories in file completion mode
1263  // unless it has already been done
1264  if (d->last_compl_type == CTFile && pMatch->at(pMatch->length() - 1) != QLatin1Char('/')) {
1265  QString copy = QUrl(*pMatch).toLocalFile();
1266  expandTilde(copy);
1267  expandEnv(copy);
1268  if (!Utils::isAbsoluteLocalPath(copy)) {
1269  copy.prepend(d->cwd.toLocalFile() + QLatin1Char('/'));
1270  }
1271 
1272  // qDebug() << "stat'ing" << copy;
1273 
1274  QByteArray file = QFile::encodeName(copy);
1275 
1276  QT_STATBUF sbuff;
1277  if (QT_STAT(file.constData(), &sbuff) == 0) {
1278  if (Utils::isDirMask(sbuff.st_mode)) {
1279  pMatch->append(QLatin1Char('/'));
1280  }
1281  } else {
1282  // qDebug() << "Could not stat file" << copy;
1283  }
1284  }
1285  }
1286 }
1287 
1288 void KUrlCompletion::postProcessMatches(QStringList * /*matches*/) const
1289 {
1290  // Maybe '/' should be added to directories here as in
1291  // postProcessMatch() but it would slow things down
1292  // when there are a lot of matches...
1293 }
1294 
1295 void KUrlCompletion::postProcessMatches(KCompletionMatches * /*matches*/) const
1296 {
1297  // Maybe '/' should be added to directories here as in
1298  // postProcessMatch() but it would slow things down
1299  // when there are a lot of matches...
1300 }
1301 
1302 void KUrlCompletionPrivate::slotCompletionThreadDone(QThread *thread, const QStringList &matches)
1303 {
1304  if (thread != userListThread && thread != dirListThread) {
1305  qCDebug(KIO_WIDGETS) << "got" << matches.count() << "outdated matches";
1306  return;
1307  }
1308 
1309  qCDebug(KIO_WIDGETS) << "got" << matches.count() << "matches at end of thread";
1310  q->setItems(matches);
1311 
1312  if (userListThread == thread) {
1313  thread->wait();
1314  delete thread;
1315  userListThread = nullptr;
1316  } else if (dirListThread == thread) {
1317  thread->wait();
1318  delete thread;
1319  dirListThread = nullptr;
1320  }
1321  finished(); // will call KCompletion::makeCompletion()
1322 }
1323 
1324 // static
1325 QString KUrlCompletion::replacedPath(const QString &text, bool replaceHome, bool replaceEnv)
1326 {
1327  if (text.isEmpty()) {
1328  return text;
1329  }
1330 
1331  KUrlCompletionPrivate::MyURL url(text, QUrl()); // no need to replace something of our current cwd
1332  if (!url.kurl().isLocalFile()) {
1333  return text;
1334  }
1335 
1336  url.filter(replaceHome, replaceEnv);
1337  return url.dir() + url.file();
1338 }
1339 
1341 {
1342  return replacedPath(text, d->replace_home, d->replace_env);
1343 }
1344 
1346 {
1347  d->mimeTypeFilters = mimeTypeFilters;
1348 }
1349 
1351 {
1352  return d->mimeTypeFilters;
1353 }
1354 
1355 /////////////////////////////////////////////////////////
1356 /////////////////////////////////////////////////////////
1357 // Static functions
1358 
1359 /*
1360  * expandEnv
1361  *
1362  * Expand environment variables in text. Escaped '$' are ignored.
1363  * Return true if expansion was made.
1364  */
1365 static bool expandEnv(QString &text)
1366 {
1367  // Find all environment variables beginning with '$'
1368  //
1369  int pos = 0;
1370 
1371  bool expanded = false;
1372 
1373  while ((pos = text.indexOf(QLatin1Char('$'), pos)) != -1) {
1374  // Skip escaped '$'
1375  //
1376  if (pos > 0 && text.at(pos - 1) == QLatin1Char('\\')) {
1377  pos++;
1378  }
1379  // Variable found => expand
1380  //
1381  else {
1382  // Find the end of the variable = next '/' or ' '
1383  //
1384  int pos2 = text.indexOf(QLatin1Char(' '), pos + 1);
1385  int pos_tmp = text.indexOf(QLatin1Char('/'), pos + 1);
1386 
1387  if (pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2)) {
1388  pos2 = pos_tmp;
1389  }
1390 
1391  if (pos2 == -1) {
1392  pos2 = text.length();
1393  }
1394 
1395  // Replace if the variable is terminated by '/' or ' '
1396  // and defined
1397  //
1398  if (pos2 >= 0) {
1399  const int len = pos2 - pos;
1400  const QStringView key = QStringView(text).mid(pos + 1, len - 1);
1401  const QString value = QString::fromLocal8Bit(qgetenv(key.toLocal8Bit().constData()));
1402 
1403  if (!value.isEmpty()) {
1404  expanded = true;
1405  text.replace(pos, len, value);
1406  pos = pos + value.length();
1407  } else {
1408  pos = pos2;
1409  }
1410  }
1411  }
1412  }
1413 
1414  return expanded;
1415 }
1416 
1417 /*
1418  * expandTilde
1419  *
1420  * Replace "~user" with the users home directory
1421  * Return true if expansion was made.
1422  */
1423 static bool expandTilde(QString &text)
1424 {
1425  if (text.isEmpty() || (text.at(0) != QLatin1Char('~'))) {
1426  return false;
1427  }
1428 
1429  bool expanded = false;
1430 
1431  // Find the end of the user name = next '/' or ' '
1432  //
1433  int pos2 = text.indexOf(QLatin1Char(' '), 1);
1434  int pos_tmp = text.indexOf(QLatin1Char('/'), 1);
1435 
1436  if (pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2)) {
1437  pos2 = pos_tmp;
1438  }
1439 
1440  if (pos2 == -1) {
1441  pos2 = text.length();
1442  }
1443 
1444  // Replace ~user if the user name is terminated by '/' or ' '
1445  //
1446  if (pos2 >= 0) {
1447  QString userName = text.mid(1, pos2 - 1);
1448  QString dir;
1449 
1450  // A single ~ is replaced with $HOME
1451  //
1452  if (userName.isEmpty()) {
1453  dir = QDir::homePath();
1454  }
1455  // ~user is replaced with the dir from passwd
1456  //
1457  else {
1458  KUser user(userName);
1459  dir = user.homeDir();
1460  }
1461 
1462  if (!dir.isEmpty()) {
1463  expanded = true;
1464  text.replace(0, pos2, dir);
1465  }
1466  }
1467 
1468  return expanded;
1469 }
1470 
1471 /*
1472  * unescape
1473  *
1474  * Remove escapes and return the result in a new string
1475  *
1476  */
1477 static QString unescape(const QString &text)
1478 {
1479  QString result;
1480  result.reserve(text.size());
1481 
1482  for (const QChar ch : text) {
1483  if (ch != QLatin1Char('\\')) {
1484  result.append(ch);
1485  }
1486  }
1487 
1488  return result;
1489 }
1490 
1491 #include "kurlcompletion.moc"
1492 #include "moc_kurlcompletion.cpp"
virtual QUrl dir() const
Returns the current directory, as it was given in setDir.
Q_OBJECTQ_OBJECT
void append(const T &value)
virtual bool replaceHome() const
Returns whether ~username is completed and whether ~username is replaced internally with the user's h...
bool isFinished() const const
QFuture< T > run(Function function,...)
int size() const const
CaseSensitive
bool isDir() const const
Q_EMITQ_EMIT
void result(KJob *job)
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
int count(const T &value) const const
QByteArray encodeName(const QString &fileName)
QTextStream & left(QTextStream &stream)
virtual bool replaceEnv() const
Checks whether environment variables are completed and whether they are replaced internally while fin...
void clear()
QStringView mid(qsizetype start) const const
QString & prepend(QChar ch)
QProcessEnvironment systemEnvironment()
bool wait(QDeadlineTimer deadline)
QByteArray toLocal8Bit() const const
void entries(KIO::Job *job, const KIO::UDSEntryList &list)
This signal emits the entry found by the job while listing.
KCALUTILS_EXPORT QString mimeType()
QList::const_iterator constBegin() const const
typedef Filters
void setSorterFunction(SorterFunction sortFunc)
void setShouldAutoSuggest(bool shouldAutosuggest)
QString homePath()
void reserve(int size)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
Returns a list of directories associated with this file-class.
Definition: krecentdirs.cpp:33
QStringList mimeTypeFilters() const
Returns the MIME type filters for the file dialog.
bool authorizeUrlAction(const QString &action, const QUrl &baseURL, const QUrl &destURL)
Returns whether a certain URL related action is authorized.
~KUrlCompletion() override
Destructs the KUrlCompletion object.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
@ UDS_NAME
Filename - as displayed in directory listings etc.
Definition: udsentry.h:224
virtual void setReplaceHome(bool replace)
Enables/disables completion of ~username and replacement (internally) of ~username with the user's ho...
void reserve(int alloc)
bool isValid() const const
virtual Mode mode() const
Returns the completion mode: exe or file completion (default FileCompletion).
int size() const const
QAction * copy(const QObject *recvr, const char *slot, QObject *parent)
QString toString(QUrl::FormattingOptions options) const const
QString fromLocal8Bit(const char *str, int size)
QStringList keys() const const
Completion of a single URL.
bool isEmpty() const const
@ UDS_URL
An alternative URL (If different from the caption).
Definition: udsentry.h:251
RemoveFilename
SkipEmptyParts
bool isEmpty() const const
QUrl fromLocalFile(const QString &localFile)
QString fileName(QUrl::ComponentFormattingOptions options) const const
int length() const const
QString toDisplayString(QUrl::FormattingOptions options) const const
Mode
Determines how completion is done.
QFuture< void > filter(Sequence &sequence, KeepFunctor filterFunction)
QString toLocalFile() const const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
static QStringList allUserNames(uint maxCount=KCOREADDONS_UINT_MAX)
QUrl resolved(const QUrl &relative) const const
virtual void setReplaceEnv(bool replace)
Enables/disables completion and replacement (internally) of environment variables in URLs.
QString fileName() const const
QString makeCompletion(const QString &text) override
Finds completions to the given text.
QString & replace(int position, int n, QChar after)
virtual void setDir(const QUrl &dir)
Sets the current directory (used as base for completion).
void setQuery(const QString &query, QUrl::ParsingMode mode)
Q_SIGNALSQ_SIGNALS
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
typedef ConstIterator
virtual void setMode(Mode mode)
Changes the completion mode: exe or file completion.
KUrlCompletion()
Constructs a KUrlCompletion object in FileCompletion mode.
KIOCORE_EXPORT ListJob * listDir(const QUrl &url, JobFlags flags=DefaultFlags, ListJob::ListFlags listFlags=ListJob::ListFlag::IncludeHidden)
List the contents of url, which is assumed to be a directory.
Definition: listjob.cpp:239
QString host(QUrl::ComponentFormattingOptions options) const const
bool isRelative() const const
int toInt(bool *ok, int base) const const
typedef Iterator
bool isEmpty() const const
KIOCORE_EXPORT QString dir(const QString &fileClass)
Returns the most recently used directory associated with this file-class.
Definition: krecentdirs.cpp:41
QList::const_iterator constEnd() const const
QString path(QUrl::ComponentFormattingOptions options) const const
virtual void stop()
Stops asynchronous completion.
const char * constData() const const
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
QString left(int n) const const
static bool supportsListing(const QUrl &url)
Returns whether the protocol can list files/objects.
void setPath(const QString &path, QUrl::ParsingMode mode)
bool isLocalFile() const const
QUrl adjusted(QUrl::FormattingOptions options) const const
const QChar at(int position) const const
void setFragment(const QString &fragment, QUrl::ParsingMode mode)
QMimeType mimeTypeForFile(const QString &fileName, QMimeDatabase::MatchMode mode) const const
QList::iterator begin()
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
Definition: job_base.h:251
@ UDS_ACCESS
Access permissions (part of the mode returned by stat)
Definition: udsentry.h:232
virtual bool isRunning() const
Check whether asynchronous completion is in progress.
QChar listSeparator()
QList::iterator end()
@ UDS_MIME_TYPE
A MIME type; the KIO worker should set it if it's known.
Definition: udsentry.h:253
QString replacedPath(const QString &text) const
Replaces username and/or environment variables, depending on the current settings and returns the fil...
void setMimeTypeFilters(const QStringList &mimeTypes)
Sets the MIME type filters for the file dialog.
QString mid(int position, int n) const const
void setCaseSensitivity(Qt::CaseSensitivity sensitivity)
const QList< QKeySequence > & end()
QString & append(QChar ch)
static QString protocolClass(const QString &protocol)
Returns the protocol class for the specified protocol.
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Thu Feb 15 2024 03:51:26 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.