34#include <QtCore/QCoreApplication>
35#include <QtCore/QMutableStringListIterator>
36#include <QtCore/QRegExp>
37#include <QtCore/QTimer>
39#include <QtCore/QDirIterator>
40#include <QtCore/QFile>
41#include <QtCore/QTextIStream>
42#include <QtCore/QThread>
43#include <QtGui/QActionEvent>
71static QString
unescape(
const QString& text);
75#define MODE_EXE (S_IXUSR | S_IXGRP | S_IXOTH)
80class CompletionThread;
86class KUrlCompletionPrivate
91 url_auto_completion(true),
96 ~KUrlCompletionPrivate();
99 void _k_slotIOFinished(
KJob*);
102 bool userCompletion(
const MyURL& url, QString* match);
103 bool envCompletion(
const MyURL& url, QString* match);
104 bool exeCompletion(
const MyURL& url, QString* match);
105 bool fileCompletion(
const MyURL& url, QString* match);
106 bool urlCompletion(
const MyURL& url, QString* match);
108 bool isAutoCompletion();
111 QString listDirectories(
const QStringList&,
113 bool only_exe =
false,
114 bool only_dir =
false,
115 bool no_hidden =
false,
116 bool stat_files =
true);
119 const QString& filter = QString(),
120 bool only_exe =
false,
121 bool no_hidden =
false);
123 void addMatches(
const QStringList&);
128 void setListedUrl(
int compl_type ,
129 const QString& dir = QString(),
130 const QString& filter = QString(),
131 bool no_hidden =
false);
133 bool isListedUrl(
int compl_type ,
134 const QString& dir = QString(),
135 const QString& filter = QString(),
136 bool no_hidden =
false);
144 bool url_auto_completion;
148 bool popup_append_slash;
151 QString last_path_listed;
152 QString last_file_listed;
153 QString last_prepend;
170 bool list_urls_only_exe;
171 bool list_urls_no_hidden;
172 QString list_urls_filter;
174 CompletionThread* userListThread;
175 CompletionThread* dirListThread;
183class CompletionMatchEvent :
public QEvent
186 CompletionMatchEvent(CompletionThread* thread) :
187 QEvent(uniqueType()),
188 m_completionThread(thread)
191 CompletionThread* completionThread()
const {
192 return m_completionThread;
194 static Type uniqueType() {
195 return Type(User + 61080);
199 CompletionThread* m_completionThread;
202class CompletionThread :
public QThread
205 CompletionThread(KUrlCompletionPrivate* receiver) :
207 m_prepend(receiver->prepend),
208 m_complete_url(receiver->complete_url),
209 m_receiver(receiver),
210 m_terminationRequested(false)
214 void requestTermination() {
215 m_terminationRequested =
true;
217 QStringList matches()
const {
222 void addMatch(
const QString& match) {
223 m_matches.append(match);
225 bool terminationRequested()
const {
226 return m_terminationRequested;
229 if (!m_terminationRequested)
230 qApp->postEvent(m_receiver->q,
new CompletionMatchEvent(
this));
235 const QString m_prepend;
236 const bool m_complete_url;
239 KUrlCompletionPrivate* m_receiver;
240 QStringList m_matches;
241 bool m_terminationRequested;
249class UserListThread :
public CompletionThread
252 UserListThread(KUrlCompletionPrivate* receiver) :
253 CompletionThread(receiver)
258 static const QChar tilde =
'~';
261 assert(m_prepend.isEmpty());
263 while ((pw = ::getpwent()) && !terminationRequested())
264 addMatch(tilde + QString::fromLocal8Bit(pw->pw_name));
268 addMatch(QString(tilde));
274class DirectoryListThread :
public CompletionThread
277 DirectoryListThread(KUrlCompletionPrivate* receiver,
278 const QStringList& dirList,
279 const QString& filter,
283 bool appendSlashToDir) :
284 CompletionThread(receiver),
289 m_noHidden(noHidden),
290 m_appendSlashToDir(appendSlashToDir)
296 QStringList m_dirList;
301 bool m_appendSlashToDir;
304void DirectoryListThread::run()
320 QStringList::ConstIterator
end = m_dirList.constEnd();
321 for (QStringList::ConstIterator it = m_dirList.constBegin();
322 it != end && !terminationRequested();
330 QString path = QDir::currentPath();
331 QDir::setCurrent(*it);
333 QDir::Filters iterator_filter = (m_noHidden ? QDir::Filter(0) : QDir::Hidden) | QDir::Readable | QDir::NoDotAndDotDot;
336 iterator_filter |= (QDir::Dirs | QDir::Files | QDir::Executable);
338 iterator_filter |= QDir::Dirs;
340 iterator_filter |= (QDir::Dirs | QDir::Files);
342 QDirIterator current_dir_iterator(*it, iterator_filter);
344 while (current_dir_iterator.hasNext()) {
345 current_dir_iterator.next();
347 QFileInfo file_info = current_dir_iterator.fileInfo();
348 const QString file_name = file_info.fileName();
352 if (m_filter.isEmpty() || file_name.startsWith(m_filter)) {
354 QString toAppend = file_name;
356 if (m_appendSlashToDir && file_info.isDir())
357 toAppend.append(QLatin1Char(
'/'));
359 if (m_complete_url) {
361 url.addPath(toAppend);
362 addMatch(url.prettyUrl());
364 addMatch(m_prepend + toAppend);
370 QDir::setCurrent(path);
376KUrlCompletionPrivate::~KUrlCompletionPrivate()
379 userListThread->requestTermination();
381 dirListThread->requestTermination();
389class KUrlCompletionPrivate::MyURL
392 MyURL(
const QString& url,
const QString& cwd);
393 MyURL(
const MyURL& url);
400 QString protocol()
const {
404 QString
dir()
const {
407 QString file()
const {
412 QString url()
const {
421 void filter(
bool replace_user_dir,
bool replace_env);
424 void init(
const QString& url,
const QString& cwd);
431KUrlCompletionPrivate::MyURL::MyURL(
const QString& _url,
const QString& cwd)
436KUrlCompletionPrivate::MyURL::MyURL(
const MyURL& _url)
437 : m_kurl(_url.m_kurl)
440 m_isURL = _url.m_isURL;
443void KUrlCompletionPrivate::MyURL::init(
const QString& _url,
const QString& cwd)
449 QString url_copy = _url;
452 if (url_copy.startsWith(QLatin1Char(
'#'))) {
453 if (url_copy.length() > 1 && url_copy.at(1) == QLatin1Char(
'#'))
454 url_copy.replace(0, 2, QLatin1String(
"info:"));
456 url_copy.replace(0, 1, QLatin1String(
"man:"));
460 QRegExp protocol_regex = QRegExp(
"^(?![A-Za-z]:)[^/\\s\\\\]*:");
464 if (protocol_regex.indexIn(url_copy) == 0) {
465 m_kurl =
KUrl(url_copy);
469 if (!QDir::isRelativePath(url_copy) ||
470 url_copy.startsWith(QLatin1Char(
'~')) ||
471 url_copy.startsWith(QLatin1Char(
'$'))) {
473 m_kurl.setPath(url_copy);
476 m_kurl =
KUrl(url_copy);
479 m_kurl.addPath(url_copy);
485KUrlCompletionPrivate::MyURL::~MyURL()
489void KUrlCompletionPrivate::MyURL::filter(
bool replace_user_dir,
bool replace_env)
491 QString d =
dir() + file();
510 d(new KUrlCompletionPrivate(this))
523void KUrlCompletionPrivate::init()
525 cwd = QDir::homePath();
529 last_no_hidden =
false;
537 url_auto_completion = cg.readEntry(
"alwaysAutoComplete",
true);
538 popup_append_slash = cg.readEntry(
"popupAppendSlash",
true);
539 onlyLocalProto = cg.readEntry(
"LocalProtocolsOnly",
false);
541 q->setIgnoreCase(
true);
566 return d->replace_env;
576 return d->replace_home;
593 KUrlCompletionPrivate::MyURL url(text, d->cwd);
595 d->compl_text = text;
599 int toRemove = url.file().length() - url.kurl().query().length();
600 if (url.kurl().hasRef())
601 toRemove += url.kurl().ref().length() + 1;
602 d->prepend = text.left(text.length() - toRemove);
603 d->complete_url = url.isURL();
609 if (d->replace_env && d->envCompletion(url, &aMatch))
614 if (d->replace_home && d->userCompletion(url, &aMatch))
618 url.filter(d->replace_home, d->replace_env);
628 if (d->exeCompletion(url, &aMatch))
634 if (d->urlCompletion(url, &aMatch))
639 if (d->fileCompletion(url, &aMatch))
644 if (d->urlCompletion(url, &aMatch))
660QString KUrlCompletionPrivate::finished()
662 if (last_compl_type ==
CTInfo)
663 return q->KCompletion::makeCompletion(compl_text.toLower());
665 return q->KCompletion::makeCompletion(compl_text);
676 return d->list_job || (d->dirListThread && !d->dirListThread->isFinished());
691 if (d->dirListThread) {
692 d->dirListThread->requestTermination();
693 d->dirListThread = 0;
700void KUrlCompletionPrivate::setListedUrl(
int complType,
701 const QString& directory,
702 const QString& filter,
705 last_compl_type = complType;
706 last_path_listed = directory;
707 last_file_listed = filter;
708 last_no_hidden = (int) no_hidden;
709 last_prepend = prepend;
712bool KUrlCompletionPrivate::isListedUrl(
int complType,
713 const QString& directory,
714 const QString& filter,
717 return last_compl_type == complType
718 && (last_path_listed == directory
719 || (directory.isEmpty() && last_path_listed.isEmpty()))
720 && (filter.startsWith (last_file_listed)
721 || (filter.isEmpty() && last_file_listed.isEmpty()))
722 && last_no_hidden == (int) no_hidden
723 && last_prepend == prepend;
731bool KUrlCompletionPrivate::isAutoCompletion()
743bool KUrlCompletionPrivate::userCompletion(
const KUrlCompletionPrivate::MyURL& url, QString* pMatch)
745 if (url.protocol() != QLatin1String(
"file")
746 || !url.dir().isEmpty()
747 || !url.file().startsWith(QLatin1Char(
'~')))
750 if (!isListedUrl(
CTUser)) {
754 if (!userListThread) {
755 userListThread =
new UserListThread(
this);
756 userListThread->start();
761 userListThread->wait(200);
762 const QStringList l = userListThread->matches();
766 *pMatch = finished();
776extern char** environ;
779bool KUrlCompletionPrivate::envCompletion(
const KUrlCompletionPrivate::MyURL& url, QString* pMatch)
781 if (url.file().isEmpty() || url.file().at(0) != QLatin1Char(
'$'))
784 if (!isListedUrl(
CTEnv)) {
788 char** env = environ;
790 QString dollar = QLatin1String(
"$");
795 QString s = QString::fromLocal8Bit(*env);
797 int pos = s.indexOf(QLatin1Char(
'='));
803 l.append(prepend + dollar + s.left(pos));
813 *pMatch = finished();
822bool KUrlCompletionPrivate::exeCompletion(
const KUrlCompletionPrivate::MyURL& url, QString* pMatch)
824 if (url.protocol() != QLatin1String(
"file"))
827 QString directory =
unescape(url.dir());
838 if (!url.file().isEmpty()) {
840 dirList = QString::fromLocal8Bit(qgetenv(
"PATH")).split(
841 KPATH_SEPARATOR, QString::SkipEmptyParts);
843 QStringList::Iterator it = dirList.begin();
845 for (; it != dirList.end(); ++it)
846 it->append(QLatin1Char(
'/'));
847 }
else if (!QDir::isRelativePath(directory)) {
849 dirList.append(directory);
850 }
else if (!directory.isEmpty() && !cwd.isEmpty()) {
852 dirList.append(cwd + QLatin1Char(
'/') + directory);
856 bool no_hidden_files = url.file().isEmpty() || url.file().at(0) != QLatin1Char(
'.');
860 if (!isListedUrl(
CTExe, directory, url.file(), no_hidden_files)) {
864 setListedUrl(
CTExe, directory, url.file(), no_hidden_files);
866 *pMatch = listDirectories(dirList, url.file(),
true,
false, no_hidden_files);
867 }
else if (!q->isRunning()) {
868 *pMatch = finished();
871 setListedUrl(
CTExe, directory, url.file(), no_hidden_files);
883bool KUrlCompletionPrivate::fileCompletion(
const KUrlCompletionPrivate::MyURL& url, QString* pMatch)
885 if (url.protocol() != QLatin1String(
"file"))
888 QString directory =
unescape(url.dir());
890 if (url.url().length() && url.url().at(0) == QLatin1Char(
'.')) {
891 if (url.url().length() == 1) {
896 }
else if (url.url().length() == 2 && url.url().at(1) == QLatin1Char(
'.')) {
897 *pMatch = QLatin1String(
"..");
912 if (!QDir::isRelativePath(directory)) {
914 dirList.append(directory);
915 }
else if (!cwd.isEmpty()) {
917 QString dirToAdd = cwd;
918 if (!directory.isEmpty()) {
919 if (!cwd.endsWith(
'/'))
920 dirToAdd.append(QLatin1Char(
'/'));
921 dirToAdd.append(directory);
923 dirList.append(dirToAdd);
927 bool no_hidden_files = !url.file().startsWith(QLatin1Char(
'.'));
931 if (!isListedUrl(
CTFile, directory, QString(), no_hidden_files)) {
935 setListedUrl(
CTFile, directory, QString(), no_hidden_files);
938 bool append_slash = (popup_append_slash
944 *pMatch = listDirectories(dirList, QString(),
false, only_dir, no_hidden_files,
946 }
else if (!q->isRunning()) {
947 *pMatch = finished();
962 return (KProtocolInfo::protocolClass(protocol) == QLatin1String(
":local"));
965bool KUrlCompletionPrivate::urlCompletion(
const KUrlCompletionPrivate::MyURL& url, QString* pMatch)
972 KUrl url_dir = url.kurl();
973 if (url_dir.isRelative() && !cwd.isEmpty()) {
974 const KUrl url_cwd (cwd);
976 url_dir =
KUrl(url_cwd, url_dir.
url());
980 if (!url_dir.isValid())
986 if (url_dir.host().isEmpty())
994 if (isAutoCompletion() && !url_auto_completion)
1018 url_list.append(url_dir);
1020 listUrls(url_list, QString(),
false);
1023 }
else if (!q->isRunning()) {
1024 *pMatch = finished();
1042void KUrlCompletionPrivate::addMatches(
const QStringList& matchList)
1044 q->insertItems(matchList);
1059QString KUrlCompletionPrivate::listDirectories(
1060 const QStringList& dirList,
1061 const QString& filter,
1065 bool append_slash_to_dir)
1067 assert(!q->isRunning());
1069 if (qgetenv(
"KURLCOMPLETION_LOCAL_KIO").isEmpty()) {
1076 dirListThread->requestTermination();
1080 QStringList::ConstIterator
end = dirList.constEnd();
1081 for (QStringList::ConstIterator it = dirList.constBegin();
1090 dirListThread =
new DirectoryListThread(
this, dirs, filter, only_exe, only_dir,
1091 no_hidden, append_slash_to_dir);
1092 dirListThread->start();
1093 dirListThread->wait(200);
1094 addMatches(dirListThread->matches());
1104 QStringList::ConstIterator it = dirList.constBegin();
1105 QStringList::ConstIterator
end = dirList.constEnd();
1107 for (; it !=
end; ++it) {
1108 url_list.append(
KUrl(*it));
1111 listUrls(url_list, filter, only_exe, no_hidden);
1125void KUrlCompletionPrivate::listUrls(
1127 const QString& filter,
1131 assert(list_urls.isEmpty());
1132 assert(list_job == 0L);
1135 list_urls_filter = filter;
1136 list_urls_only_exe = only_exe;
1137 list_urls_no_hidden = no_hidden;
1146 _k_slotIOFinished(0);
1156 QStringList matchList;
1158 KIO::UDSEntryList::ConstIterator it = entries.constBegin();
1159 const KIO::UDSEntryList::ConstIterator
end = entries.constEnd();
1161 QString filter = list_urls_filter;
1163 int filter_len = filter.length();
1167 for (; it !=
end; ++it) {
1172 if (!url.isEmpty()) {
1181 if ((!entry_name.isEmpty() && entry_name.at(0) == QLatin1Char(
'.')) &&
1182 (list_urls_no_hidden ||
1183 entry_name.length() == 1 ||
1184 (entry_name.length() == 2 && entry_name.at(1) == QLatin1Char(
'.'))))
1187 const bool isDir = entry.
isDir();
1192 if (filter_len == 0 || entry_name.left(filter_len) == filter) {
1194 QString toAppend = entry_name;
1197 toAppend.append(QLatin1Char(
'/'));
1199 if (!list_urls_only_exe ||
1204 url.addPath(toAppend);
1205 matchList.append(url.prettyUrl());
1207 matchList.append(prepend + toAppend);
1213 addMatches(matchList);
1224void KUrlCompletionPrivate::_k_slotIOFinished(
KJob* job)
1226 assert(job == list_job); Q_UNUSED(job)
1228 if (list_urls.isEmpty()) {
1236 KUrl kurl(list_urls.takeFirst());
1243 list_job->addMetaData(
"no-auth-prompt",
"true");
1247 q->connect(list_job,
1248 SIGNAL(result(
KJob*)),
1249 SLOT(_k_slotIOFinished(
KJob*)));
1251 q->connect(list_job,
1272 if (!pMatch->isEmpty()) {
1276 if (d->last_compl_type ==
CTFile
1277 && pMatch->at(pMatch->length() - 1) != QLatin1Char(
'/')) {
1280 if (pMatch->startsWith(QLatin1String(
"file:")))
1288 DWORD dwAttr = GetFileAttributesW((LPCWSTR)
copy.utf16());
1289 if (dwAttr == INVALID_FILE_ATTRIBUTES) {
1290 kDebug() <<
"Could not get file attribs ( "
1294 }
else if ((dwAttr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
1295 pMatch->append(QLatin1Char(
'/'));
1297 if (QDir::isRelativePath(
copy))
1298 copy.prepend(d->cwd + QLatin1Char(
'/'));
1302 KDE_struct_stat sbuff;
1304 QByteArray file = QFile::encodeName(
copy);
1306 if (KDE_stat(file.data(), &sbuff) == 0) {
1307 if (S_ISDIR(sbuff.st_mode))
1308 pMatch->append(QLatin1Char(
'/'));
1333 if (e->type() == CompletionMatchEvent::uniqueType()) {
1335 CompletionMatchEvent* matchEvent =
static_cast<CompletionMatchEvent*
>(e);
1337 matchEvent->completionThread()->wait();
1339 if (!d->isListedUrl(
CTUser)) {
1342 d->addMatches(matchEvent->completionThread()->matches());
1347 if (d->userListThread == matchEvent->completionThread())
1348 d->userListThread = 0;
1350 if (d->dirListThread == matchEvent->completionThread())
1351 d->dirListThread = 0;
1353 delete matchEvent->completionThread();
1363 KUrlCompletionPrivate::MyURL url(text, QString());
1364 if (!url.kurl().isLocalFile())
1367 url.filter(replaceHome, replaceEnv);
1368 return url.dir() + url.file();
1374 return replacedPath(text, d->replace_home, d->replace_env);
1393 bool expanded =
false;
1395 while ((pos = text.indexOf(QLatin1Char(
'$'), pos)) != -1) {
1399 if (pos > 0 && text.at(pos - 1) == QLatin1Char(
'\\')) {
1407 int pos2 = text.indexOf(QLatin1Char(
' '), pos + 1);
1408 int pos_tmp = text.indexOf(QLatin1Char(
'/'), pos + 1);
1410 if (pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2))
1414 pos2 = text.length();
1420 int len = pos2 - pos;
1421 QString key = text.mid(pos + 1, len - 1);
1423 QString::fromLocal8Bit(qgetenv(key.toLocal8Bit()));
1425 if (!value.isEmpty()) {
1427 text.replace(pos, len, value);
1428 pos = pos + value.length();
1447 if (text.isEmpty() || (text.at(0) != QLatin1Char(
'~')))
1450 bool expanded =
false;
1454 int pos2 = text.indexOf(QLatin1Char(
' '), 1);
1455 int pos_tmp = text.indexOf(QLatin1Char(
'/'), 1);
1457 if (pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2))
1461 pos2 = text.length();
1467 QString user = text.mid(1, pos2 - 1);
1472 if (user.isEmpty()) {
1473 dir = QDir::homePath();
1478 struct passwd* pw = ::getpwnam(user.toLocal8Bit());
1481 dir = QFile::decodeName(pw->pw_dir);
1486 if (!dir.isEmpty()) {
1488 text.replace(0, pos2, dir);
1505 for (
int pos = 0; pos < text.length(); pos++)
1506 if (text.at(pos) != QLatin1Char(
'\\'))
1507 result.insert(result.length(), text.at(pos));
1512#include "kurlcompletion.moc"
The base class for all jobs.
A ListJob is allows you to get the get the content of a directory.
Universal Directory Service.
QString stringValue(uint field) const
long long numberValue(uint field, long long defaultValue=0) const
@ UDS_URL
An alternative URL (If different from the caption).
@ UDS_NAME
Filename - as displayed in directory listings etc.
@ UDS_ACCESS
Access permissions (part of the mode returned by stat)
static bool supportsListing(const KUrl &url)
Returns whether the protocol can list files/objects.
This class does completion of URLs including user directories (~user) and environment variables.
virtual void setMode(Mode mode)
Changes the completion mode: exe or file completion.
virtual QString dir() const
Returns the current directory, as it was given in setDir.
virtual ~KUrlCompletion()
Destructs the KUrlCompletion object.
virtual bool replaceEnv() const
Checks whether environment variables are completed and whether they are replaced internally while fin...
virtual void stop()
Stops asynchronous completion.
virtual bool replaceHome() const
Returns whether ~username is completed and whether ~username is replaced internally with the user's h...
virtual void customEvent(QEvent *e)
virtual void setReplaceHome(bool replace)
Enables/disables completion of ~username and replacement (internally) of ~username with the user's ho...
KUrlCompletion()
Constructs a KUrlCompletion object in FileCompletion mode.
virtual void setReplaceEnv(bool replace)
Enables/disables completion and replacement (internally) of environment variables in URLs.
void postProcessMatch(QString *match) const
Mode
Determines how completion is done.
virtual QString makeCompletion(const QString &text)
Finds completions to the given text.
virtual Mode mode() const
Returns the completion mode: exe or file completion (default FileCompletion).
virtual void setDir(const QString &dir)
Sets the current directory (used as base for completion).
virtual bool isRunning() const
Check whether asynchronous completion is in progress.
void postProcessMatches(QStringList *matches) const
QString replacedPath(const QString &text) const
Replaces username and/or environment variables, depending on the current settings and returns the fil...
QString prettyUrl(AdjustPathOption trailing=LeaveTrailingSlash) const
QString url(AdjustPathOption trailing=LeaveTrailingSlash) const
void setFileName(const QString &_txt)
QString directory(const DirectoryOptions &options=IgnoreTrailingSlash) const
void setPath(const QString &path)
QString fileName(const DirectoryOptions &options=IgnoreTrailingSlash) const
QString toLocalFile(AdjustPathOption trailing=LeaveTrailingSlash) const
static bool isLocalProtocol(const QString &protocol)
static bool expandEnv(QString &)
static QString unescape(const QString &text)
static bool expandTilde(QString &)
bool authorizeUrlAction(const QString &action, const KUrl &baseUrl, const KUrl &destUrl)
bool run(const KUrl &_url, bool _is_local)
Invokes the default action for the desktop entry.
KSharedConfigPtr config()
ListJob * listDir(const KUrl &url, JobFlags flags=DefaultFlags, bool includeHidden=true)
List the contents of url, which is assumed to be a directory.
@ HideProgressInfo
Hide progress information dialog, i.e.
QString dir(const QString &fileClass)
Returns the most recently used directory accociated with this file-class.
KAction * replace(const QObject *recvr, const char *slot, QObject *parent)
KAction * copy(const QObject *recvr, const char *slot, QObject *parent)