34 #include <QtCore/QCoreApplication>
35 #include <QtCore/QMutableStringListIterator>
36 #include <QtCore/QRegExp>
37 #include <QtCore/QTimer>
38 #include <QtCore/QDir>
39 #include <QtCore/QDirIterator>
40 #include <QtCore/QFile>
41 #include <QtCore/QTextIStream>
42 #include <QtCore/QThread>
43 #include <QtGui/QActionEvent>
55 #include <sys/types.h>
61 #include <sys/param.h>
75 #define MODE_EXE (S_IXUSR | S_IXGRP | S_IXOTH)
80 class CompletionThread;
86 class 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();
113 bool only_exe =
false,
114 bool only_dir =
false,
115 bool no_hidden =
false,
116 bool stat_files =
true);
120 bool only_exe =
false,
121 bool no_hidden =
false);
128 void setListedUrl(
int compl_type ,
131 bool no_hidden =
false);
133 bool isListedUrl(
int compl_type ,
136 bool no_hidden =
false);
144 bool url_auto_completion;
148 bool popup_append_slash;
170 bool list_urls_only_exe;
171 bool list_urls_no_hidden;
174 CompletionThread* userListThread;
175 CompletionThread* dirListThread;
183 class 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;
202 class 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;
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));
236 const bool m_complete_url;
239 KUrlCompletionPrivate* m_receiver;
241 bool m_terminationRequested;
249 class 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));
274 class DirectoryListThread :
public CompletionThread
277 DirectoryListThread(KUrlCompletionPrivate* receiver,
283 bool appendSlashToDir) :
284 CompletionThread(receiver),
289 m_noHidden(noHidden),
290 m_appendSlashToDir(appendSlashToDir)
301 bool m_appendSlashToDir;
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)) {
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);
376 KUrlCompletionPrivate::~KUrlCompletionPrivate()
379 userListThread->requestTermination();
381 dirListThread->requestTermination();
389 class KUrlCompletionPrivate::MyURL
393 MyURL(
const MyURL& url);
401 return m_kurl.protocol();
421 void filter(
bool replace_user_dir,
bool replace_env);
431 KUrlCompletionPrivate::MyURL::MyURL(
const QString& _url,
const QString& cwd)
436 KUrlCompletionPrivate::MyURL::MyURL(
const MyURL& _url)
437 : m_kurl(_url.m_kurl)
440 m_isURL = _url.m_isURL;
443 void KUrlCompletionPrivate::MyURL::init(
const QString& _url,
const QString& cwd)
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);
485 KUrlCompletionPrivate::MyURL::~MyURL()
489 void KUrlCompletionPrivate::MyURL::filter(
bool replace_user_dir,
bool replace_env)
510 d(new KUrlCompletionPrivate(this))
523 void 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;
571 d->replace_env = replace;
576 return d->replace_home;
581 d->replace_home = replace;
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))
660 QString 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;
700 void KUrlCompletionPrivate::setListedUrl(
int complType,
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;
712 bool KUrlCompletionPrivate::isListedUrl(
int complType,
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;
731 bool KUrlCompletionPrivate::isAutoCompletion()
743 bool 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);
766 *pMatch = finished();
776 extern char** environ;
779 bool 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();
822 bool KUrlCompletionPrivate::exeCompletion(
const KUrlCompletionPrivate::MyURL& url,
QString* pMatch)
824 if (url.protocol() != QLatin1String(
"file"))
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);
883 bool KUrlCompletionPrivate::fileCompletion(
const KUrlCompletionPrivate::MyURL& url,
QString* pMatch)
885 if (url.protocol() != QLatin1String(
"file"))
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()) {
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)) {
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();
965 bool 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();
1042 void KUrlCompletionPrivate::addMatches(
const QStringList& matchList)
1044 q->insertItems(matchList);
1059 QString KUrlCompletionPrivate::listDirectories(
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);
1125 void KUrlCompletionPrivate::listUrls(
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);
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);
1224 void 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"
An alternative URL (If different from the caption).
KUrlCompletion()
Constructs a KUrlCompletion object in FileCompletion mode.
static QString unescape(const QString &text)
virtual bool replaceHome() const
Returns whether ~username is completed and whether ~username is replaced internally with the user's h...
QString directory(const DirectoryOptions &options=IgnoreTrailingSlash) const
Universal Directory Service.
static bool expandEnv(QString &)
virtual void setMode(Mode mode)
Changes the completion mode: exe or file completion.
virtual bool isRunning() const
Check whether asynchronous completion is in progress.
virtual void customEvent(QEvent *e)
A ListJob is allows you to get the get the content of a directory.
Hide progress information dialog, i.e.
Mode
Determines how completion is done.
virtual Mode mode() const
Returns the completion mode: exe or file completion (default FileCompletion).
QString toLocalFile(AdjustPathOption trailing=LeaveTrailingSlash) const
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
bool run(const KUrl &_url, bool _is_local)
Invokes the default action for the desktop entry.
KSharedConfigPtr config()
void setPath(const QString &path)
ListJob * listDir(const KUrl &url, JobFlags flags=DefaultFlags, bool includeHidden=true)
List the contents of url, which is assumed to be a directory.
virtual QString makeCompletion(const QString &text)
Finds completions to the given text.
long long numberValue(uint field, long long defaultValue=0) const
virtual bool replaceEnv() const
Checks whether environment variables are completed and whether they are replaced internally while fin...
bool authorizeUrlAction(const QString &action, const KUrl &baseUrl, const KUrl &destUrl)
void postProcessMatch(QString *match) const
QString stringValue(uint field) const
virtual void setDir(const QString &dir)
Sets the current directory (used as base for completion).
static bool isLocalProtocol(const QString &protocol)
static QString protocolClass(const QString &protocol)
static bool expandTilde(QString &)
virtual ~KUrlCompletion()
Destructs the KUrlCompletion object.
void setFileName(const QString &_txt)
QString dir(const QString &fileClass)
Returns the most recently used directory accociated with this file-class.
QString replacedPath(const QString &text) const
Replaces username and/or environment variables, depending on the current settings and returns the fil...
Access permissions (part of the mode returned by stat)
QString fileName(const DirectoryOptions &options=IgnoreTrailingSlash) const
virtual void setReplaceEnv(bool replace)
Enables/disables completion and replacement (internally) of environment variables in URLs...
Filename - as displayed in directory listings etc.
The base class for all jobs.
CopyJob * copy(const KUrl &src, const KUrl &dest, JobFlags flags=DefaultFlags)
Copy a file or directory src into the destination dest, which can be a file (including the final file...
virtual QString dir() const
Returns the current directory, as it was given in setDir.
QString url(AdjustPathOption trailing=LeaveTrailingSlash) const
static bool supportsListing(const KUrl &url)
Returns whether the protocol can list files/objects.
void postProcessMatches(QStringList *matches) const
virtual void setReplaceHome(bool replace)
Enables/disables completion of ~username and replacement (internally) of ~username with the user's ho...
QString prettyUrl(AdjustPathOption trailing=LeaveTrailingSlash) const
This class does completion of URLs including user directories (~user) and environment variables...
virtual void stop()
Stops asynchronous completion.