• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KIO

kurlcompletion.cpp

Go to the documentation of this file.
00001 /* -*- indent-tabs-mode: t; tab-width: 4; c-basic-offset:4 -*-
00002 
00003    This file is part of the KDE libraries
00004    Copyright (C) 2000 David Smith <dsmith@algonet.se>
00005    Copyright (C) 2004 Scott Wheeler <wheeler@kde.org>
00006 
00007    This class was inspired by a previous KUrlCompletion by
00008    Henner Zeller <zeller@think.de>
00009 
00010    This library is free software; you can redistribute it and/or
00011    modify it under the terms of the GNU Library General Public
00012    License as published by the Free Software Foundation; either
00013    version 2 of the License, or (at your option) any later version.
00014 
00015    This library is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018    Library General Public License for more details.
00019 
00020    You should have received a copy of the GNU Library General Public License
00021    along with this library; see the file COPYING.LIB.   If not, write to
00022    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00023    Boston, MA 02110-1301, USA.
00024 */
00025 
00026 #include "kurlcompletion.h"
00027 
00028 #include <config.h>
00029 
00030 #include <stdlib.h>
00031 #include <assert.h>
00032 #include <limits.h>
00033 
00034 #include <QtCore/QMutableStringListIterator>
00035 #include <QtCore/QRegExp>
00036 #include <QtCore/QTimer>
00037 #include <QtCore/QDir>
00038 #include <QtCore/QFile>
00039 #include <QtCore/QTextIStream>
00040 #include <QtCore/QThread>
00041 #include <QtGui/QActionEvent>
00042 
00043 #include <kapplication.h>
00044 #include <kauthorized.h>
00045 #include <kdebug.h>
00046 #include <kurl.h>
00047 #include <kio/job.h>
00048 #include <kprotocolmanager.h>
00049 #include <kconfig.h>
00050 #include <kglobal.h>
00051 #include <kde_file.h>
00052 
00053 #include <sys/types.h>
00054 #include <dirent.h>
00055 #include <unistd.h>
00056 #include <sys/stat.h>
00057 #include <pwd.h>
00058 #include <time.h>
00059 #include <sys/param.h>
00060 #include <kconfiggroup.h>
00061 
00062 static bool expandTilde(QString &);
00063 static bool expandEnv(QString &);
00064 
00065 static QString unescape(const QString &text);
00066 
00067 // Permission mask for files that are executable by
00068 // user, group or other
00069 #define MODE_EXE (S_IXUSR | S_IXGRP | S_IXOTH)
00070 
00071 // Constants for types of completion
00072 enum ComplType {CTNone=0, CTEnv, CTUser, CTMan, CTExe, CTFile, CTUrl, CTInfo};
00073 
00074 class CompletionThread;
00075 
00078 // KUrlCompletionPrivate
00079 //
00080 class KUrlCompletionPrivate
00081 {
00082 public:
00083     KUrlCompletionPrivate(KUrlCompletion *parent)
00084     : q(parent),
00085       url_auto_completion(true),
00086       userListThread(0),
00087       dirListThread(0)
00088   {
00089   }
00090 
00091     ~KUrlCompletionPrivate();
00092 
00093     void _k_slotEntries( KIO::Job*, const KIO::UDSEntryList& );
00094     void _k_slotIOFinished( KJob* );
00095 
00096     class MyURL;
00097     bool userCompletion(const MyURL &url, QString *match);
00098     bool envCompletion(const MyURL &url, QString *match);
00099     bool exeCompletion(const MyURL &url, QString *match);
00100     bool fileCompletion(const MyURL &url, QString *match);
00101     bool urlCompletion(const MyURL &url, QString *match);
00102 
00103     bool isAutoCompletion();
00104 
00105     // List the next dir in m_dirs
00106     QString listDirectories(const QStringList &,
00107                             const QString &,
00108                             bool only_exe = false,
00109                             bool only_dir = false,
00110                             bool no_hidden = false,
00111                             bool stat_files = true);
00112 
00113     void listUrls( const QList<KUrl *> &urls,
00114                        const QString &filter = QString(),
00115                        bool only_exe = false,
00116                        bool no_hidden = false );
00117 
00118     void addMatches( const QStringList & );
00119     QString finished();
00120 
00121     void init();
00122 
00123     void setListedUrl(int compl_type /* enum ComplType */,
00124                       const QString& dir = QString(),
00125                       const QString& filter = QString(),
00126                       bool no_hidden = false );
00127 
00128     bool isListedUrl( int compl_type /* enum ComplType */,
00129                       const QString& dir = QString(),
00130                       const QString& filter = QString(),
00131                       bool no_hidden = false );
00132 
00133   KUrlCompletion *q;
00134     QList<KUrl*> list_urls;
00135 
00136     bool onlyLocalProto;
00137 
00138     // urlCompletion() in Auto/Popup mode?
00139     bool url_auto_completion;
00140 
00141     // Append '/' to directories in Popup mode?
00142     // Doing that stat's all files and is slower
00143     bool popup_append_slash;
00144 
00145     // Keep track of currently listed files to avoid reading them again
00146     QString last_path_listed;
00147     QString last_file_listed;
00148     QString last_prepend;
00149     int last_compl_type;
00150     int last_no_hidden;
00151 
00152     QString cwd; // "current directory" = base dir for completion
00153 
00154     KUrlCompletion::Mode mode; // ExeCompletion, FileCompletion, DirCompletion
00155     bool replace_env;
00156     bool replace_home;
00157     bool complete_url; // if true completing a URL (i.e. 'prepend' is a URL), otherwise a path
00158 
00159     KIO::ListJob *list_job; // kio job to list directories
00160 
00161     QString prepend; // text to prepend to listed items
00162     QString compl_text; // text to pass on to KCompletion
00163 
00164     // Filters for files read with  kio
00165     bool list_urls_only_exe; // true = only list executables
00166     bool list_urls_no_hidden;
00167     QString list_urls_filter; // filter for listed files
00168 
00169     CompletionThread *userListThread;
00170     CompletionThread *dirListThread;
00171 };
00172 
00178 class CompletionMatchEvent : public QEvent
00179 {
00180 public:
00181     CompletionMatchEvent( CompletionThread *thread ) :
00182         QEvent( uniqueType() ),
00183         m_completionThread( thread )
00184     {}
00185 
00186     CompletionThread *completionThread() const { return m_completionThread; }
00187     static Type uniqueType() { return Type(User + 61080); }
00188 
00189 private:
00190     CompletionThread *m_completionThread;
00191 };
00192 
00193 class CompletionThread : public QThread
00194 {
00195 protected:
00196     CompletionThread( KUrlCompletionPrivate *receiver ) :
00197         QThread(),
00198         m_prepend( receiver->prepend ),
00199         m_complete_url( receiver->complete_url ),
00200         m_receiver( receiver ),
00201         m_terminationRequested( false )
00202     {}
00203 
00204 public:
00205     void requestTermination() { m_terminationRequested = true; }
00206     QStringList matches() const { return m_matches; }
00207 
00208 protected:
00209     void addMatch( const QString &match ) { m_matches.append( match ); }
00210     bool terminationRequested() const { return m_terminationRequested; }
00211     void done()
00212     {
00213         if ( !m_terminationRequested )
00214             qApp->postEvent( m_receiver->q, new CompletionMatchEvent( this ) );
00215         else
00216             deleteLater();
00217     }
00218 
00219     const QString m_prepend;
00220     const bool m_complete_url; // if true completing a URL (i.e. 'm_prepend' is a URL), otherwise a path
00221 
00222 private:
00223     KUrlCompletionPrivate *m_receiver;
00224     QStringList m_matches;
00225     bool m_terminationRequested;
00226 };
00227 
00233 class UserListThread : public CompletionThread
00234 {
00235 public:
00236     UserListThread( KUrlCompletionPrivate *receiver ) :
00237         CompletionThread( receiver )
00238     {}
00239 
00240 protected:
00241     virtual void run()
00242     {
00243         static const QChar tilde = '~';
00244 
00245         // we don't need to handle prepend here, right? ~user is always at pos 0
00246         assert(m_prepend.isEmpty());
00247         struct passwd *pw;
00248         while ( ( pw = ::getpwent() ) && !terminationRequested() )
00249             addMatch( tilde + QString::fromLocal8Bit( pw->pw_name ) );
00250 
00251         ::endpwent();
00252 
00253         addMatch( QString( tilde ) );
00254 
00255         done();
00256     }
00257 };
00258 
00259 class DirectoryListThread : public CompletionThread
00260 {
00261 public:
00262     DirectoryListThread( KUrlCompletionPrivate *receiver,
00263                          const QStringList &dirList,
00264                          const QString &filter,
00265                          bool onlyExe,
00266                          bool onlyDir,
00267                          bool noHidden,
00268                          bool appendSlashToDir ) :
00269         CompletionThread( receiver ),
00270         m_dirList( dirList ),
00271         m_filter(  filter  ),
00272         m_onlyExe( onlyExe ),
00273         m_onlyDir( onlyDir ),
00274         m_noHidden( noHidden ),
00275         m_appendSlashToDir( appendSlashToDir )
00276     {}
00277 
00278     virtual void run();
00279 
00280 private:
00281     QStringList m_dirList;
00282     QString m_filter;
00283     bool m_onlyExe;
00284     bool m_onlyDir;
00285     bool m_noHidden;
00286     bool m_appendSlashToDir;
00287 };
00288 
00289 void DirectoryListThread::run()
00290 {
00291     // Thread safety notes:
00292     //
00293     // There very possibly may be thread safety issues here, but I've done a check
00294     // of all of the things that would seem to be problematic.  Here are a few
00295     // things that I have checked to be safe here (some used indirectly):
00296     //
00297     // QDir::currentPath(), QDir::setCurrent(), QFile::decodeName(), QFile::encodeName()
00298     // QString::fromLocal8Bit(), QString::toLocal8Bit(), QTextCodec::codecForLocale()
00299     //
00300     // Also see (for POSIX functions):
00301     // http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html
00302 
00303     DIR *dir = 0;
00304 
00305     for ( QStringList::ConstIterator it = m_dirList.begin();
00306           it != m_dirList.end() && !terminationRequested();
00307           ++it )
00308     {
00309         // Open the next directory
00310 
00311         if ( !dir ) {
00312             dir = ::opendir( QFile::encodeName( *it ) );
00313             if ( ! dir ) {
00314                 kDebug() << "Failed to open dir:" << *it;
00315                 return;
00316             }
00317         }
00318 
00319         // A trick from KIO that helps performance by a little bit:
00320         // chdir to the directory so we won't have to deal with full paths
00321         // with stat()
00322 
00323         QString path = QDir::currentPath();
00324         QDir::setCurrent( *it );
00325 
00326         // Loop through all directory entries
00327         // Solaris and IRIX dirent structures do not allocate space for d_name. On
00328         // systems that do (HP-UX, Linux, Tru64 UNIX), we overallocate space but
00329         // that's ok.
00330 #ifndef HAVE_READDIR_R
00331         KDE_struct_dirent *dirEntry = 0;
00332         while ( !terminationRequested() &&
00333                 (dirEntry = KDE_readdir( dir)))
00334 #else
00335         struct dirent *dirPosition = (struct dirent *) malloc( sizeof( struct dirent ) + MAXPATHLEN + 1 );
00336         struct dirent *dirEntry = 0;
00337         while ( !terminationRequested() &&
00338                 ::readdir_r( dir, dirPosition, &dirEntry ) == 0 && dirEntry )
00339 #endif
00340 
00341         {
00342             // Skip hidden files if m_noHidden is true
00343 
00344             if ( dirEntry->d_name[0] == '.' && m_noHidden )
00345                 continue;
00346 
00347             // Skip "."
00348 
00349             if ( dirEntry->d_name[0] == '.' && dirEntry->d_name[1] == '\0' )
00350                 continue;
00351 
00352             // Skip ".."
00353 
00354             if ( dirEntry->d_name[0] == '.' && dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0' )
00355                 continue;
00356 
00357             QString file = QFile::decodeName( dirEntry->d_name );
00358 
00359             if ( m_filter.isEmpty() || file.startsWith( m_filter ) ) {
00360 
00361                 QString toAppend = m_complete_url ? QUrl::toPercentEncoding(file) : file;
00362 
00363                 if ( m_onlyExe || m_onlyDir || m_appendSlashToDir ) {
00364                     KDE_struct_stat sbuff;
00365 
00366                     if ( KDE_stat( dirEntry->d_name, &sbuff ) == 0 ) {
00367 
00368                         // Verify executable
00369 
00370                         if ( m_onlyExe && ( sbuff.st_mode & MODE_EXE ) == 0 )
00371                             continue;
00372 
00373                         // Verify directory
00374 
00375                         if ( m_onlyDir && !S_ISDIR( sbuff.st_mode ) )
00376                             continue;
00377 
00378                         // Add '/' to directories
00379 
00380                         if ( m_appendSlashToDir && S_ISDIR( sbuff.st_mode ) )
00381                             toAppend.append( QLatin1Char( '/' ) );
00382 
00383                     }
00384                     else {
00385                         kDebug() << "Could not stat file" << file;
00386                         continue;
00387                     }
00388                 }
00389 
00390                 addMatch( m_prepend + toAppend );
00391             }
00392         }
00393 
00394         // chdir to the original directory
00395 
00396         QDir::setCurrent( path );
00397 
00398         ::closedir( dir );
00399         dir = 0;
00400 #ifdef HAVE_READDIR_R
00401         free( dirPosition );
00402 #endif
00403     }
00404 
00405     done();
00406 }
00407 
00408 KUrlCompletionPrivate::~KUrlCompletionPrivate()
00409 {
00410     if ( userListThread )
00411         userListThread->requestTermination();
00412     if ( dirListThread )
00413         dirListThread->requestTermination();
00414 }
00415 
00418 // MyURL - wrapper for KUrl with some different functionality
00419 //
00420 
00421 class KUrlCompletionPrivate::MyURL
00422 {
00423 public:
00424     MyURL(const QString &url, const QString &cwd);
00425     MyURL(const MyURL &url);
00426     ~MyURL();
00427 
00428     KUrl *kurl() const { return m_kurl; }
00429 
00430     QString protocol() const { return m_kurl->protocol(); }
00431     // The directory with a trailing '/'
00432         QString dir() const { return m_kurl->directory(KUrl::AppendTrailingSlash|KUrl::ObeyTrailingSlash); }
00433     QString file() const { return m_kurl->fileName(KUrl::ObeyTrailingSlash); }
00434 
00435     // The initial, unparsed, url, as a string.
00436     QString url() const { return m_url; }
00437 
00438     // Is the initial string a URL, or just a path (whether absolute or relative)
00439     bool isURL() const { return m_isURL; }
00440 
00441     void filter( bool replace_user_dir, bool replace_env );
00442 
00443 private:
00444     void init(const QString &url, const QString &cwd);
00445 
00446     KUrl *m_kurl;
00447     QString m_url;
00448     bool m_isURL;
00449 };
00450 
00451 KUrlCompletionPrivate::MyURL::MyURL(const QString &_url, const QString &cwd)
00452 {
00453     init(_url, cwd);
00454 }
00455 
00456 KUrlCompletionPrivate::MyURL::MyURL(const MyURL &_url)
00457 {
00458     m_kurl = new KUrl( *(_url.m_kurl) );
00459     m_url = _url.m_url;
00460     m_isURL = _url.m_isURL;
00461 }
00462 
00463 void KUrlCompletionPrivate::MyURL::init(const QString &_url, const QString &cwd)
00464 {
00465     // Save the original text
00466     m_url = _url;
00467 
00468     // Non-const copy
00469     QString url_copy = _url;
00470 
00471     // Special shortcuts for "man:" and "info:"
00472     if ( url_copy.startsWith( QLatin1Char('#') ) ) {
00473         if ( url_copy.length() > 1 && url_copy.at(1) == QLatin1Char('#') )
00474             url_copy.replace( 0, 2, QLatin1String("info:") );
00475         else
00476             url_copy.replace( 0, 1, QLatin1String("man:") );
00477     }
00478 
00479     // Look for a protocol in 'url'
00480     QRegExp protocol_regex = QRegExp( "^(?![A-Za-z]:)[^/\\s\\\\]*:" );
00481 
00482     // Assume "file:" or whatever is given by 'cwd' if there is
00483     // no protocol.  (KUrl does this only for absolute paths)
00484     if ( protocol_regex.indexIn( url_copy ) == 0 )
00485     {
00486         m_kurl = new KUrl( url_copy );
00487         m_isURL = true;
00488     }
00489     else // relative path or ~ or $something
00490     {
00491         m_isURL = false;
00492         if ( !QDir::isRelativePath(url_copy) ||
00493              url_copy.startsWith( QLatin1Char('~') ) ||
00494              url_copy.startsWith( QLatin1Char('$') ))
00495         {
00496             m_kurl = new KUrl;
00497             m_kurl->setPath( url_copy );
00498         }
00499         else
00500         {
00501             if ( cwd.isEmpty() ) {
00502                 m_kurl = new KUrl( url_copy );
00503             } else {
00504                 m_kurl = new KUrl( cwd );
00505                 m_kurl->addPath( url_copy );
00506             }
00507         }
00508     }
00509 }
00510 
00511 KUrlCompletionPrivate::MyURL::~MyURL()
00512 {
00513     delete m_kurl;
00514 }
00515 
00516 void KUrlCompletionPrivate::MyURL::filter( bool replace_user_dir, bool replace_env )
00517 {
00518     QString d = dir() + file();
00519     if ( replace_user_dir ) expandTilde( d );
00520     if ( replace_env ) expandEnv( d );
00521     m_kurl->setPath( d );
00522 }
00523 
00526 // KUrlCompletion
00527 //
00528 
00529 KUrlCompletion::KUrlCompletion() : KCompletion(),d(new KUrlCompletionPrivate(this))
00530 {
00531     d->init();
00532 }
00533 
00534 
00535 KUrlCompletion::KUrlCompletion( Mode _mode ) : KCompletion(),d(new KUrlCompletionPrivate(this))
00536 {
00537     d->init();
00538     setMode ( _mode );
00539 }
00540 
00541 KUrlCompletion::~KUrlCompletion()
00542 {
00543     stop();
00544     delete d;
00545 }
00546 
00547 
00548 void KUrlCompletionPrivate::init()
00549 {
00550 
00551     cwd = QDir::homePath();
00552 
00553     replace_home = true;
00554     replace_env = true;
00555     last_no_hidden = false;
00556     last_compl_type = 0;
00557     list_job = 0L;
00558     mode = KUrlCompletion::FileCompletion;
00559 
00560     // Read settings
00561     KConfigGroup cg( KGlobal::config(), "URLCompletion" );
00562 
00563     url_auto_completion = cg.readEntry("alwaysAutoComplete", true);
00564     popup_append_slash = cg.readEntry("popupAppendSlash", true);
00565     onlyLocalProto = cg.readEntry("LocalProtocolsOnly", false);
00566 }
00567 
00568 void KUrlCompletion::setDir(const QString &_dir)
00569 {
00570     d->cwd = _dir;
00571 }
00572 
00573 QString KUrlCompletion::dir() const
00574 {
00575     return d->cwd;
00576 }
00577 
00578 KUrlCompletion::Mode KUrlCompletion::mode() const
00579 {
00580     return d->mode;
00581 }
00582 
00583 void KUrlCompletion::setMode( Mode _mode )
00584 {
00585     d->mode = _mode;
00586 }
00587 
00588 bool KUrlCompletion::replaceEnv() const
00589 {
00590     return d->replace_env;
00591 }
00592 
00593 void KUrlCompletion::setReplaceEnv( bool replace )
00594 {
00595     d->replace_env = replace;
00596 }
00597 
00598 bool KUrlCompletion::replaceHome() const
00599 {
00600     return d->replace_home;
00601 }
00602 
00603 void KUrlCompletion::setReplaceHome( bool replace )
00604 {
00605     d->replace_home = replace;
00606 }
00607 
00608 /*
00609  * makeCompletion()
00610  *
00611  * Entry point for file name completion
00612  */
00613 QString KUrlCompletion::makeCompletion(const QString &text)
00614 {
00615     //kDebug() << text << "d->cwd=" << d->cwd;
00616 
00617   KUrlCompletionPrivate::MyURL url(text, d->cwd);
00618 
00619     d->compl_text = text;
00620 
00621     // Set d->prepend to the original URL, with the filename [and ref/query] stripped.
00622     // This is what gets prepended to the directory-listing matches.
00623     int toRemove = url.file().length() - url.kurl()->query().length();
00624     if ( url.kurl()->hasRef() )
00625         toRemove += url.kurl()->ref().length() + 1;
00626     d->prepend = text.left( text.length() - toRemove );
00627     d->complete_url = url.isURL();
00628 
00629     QString aMatch;
00630 
00631     // Environment variables
00632     //
00633     if ( d->replace_env && d->envCompletion( url, &aMatch ) )
00634         return aMatch;
00635 
00636     // User directories
00637     //
00638     if ( d->replace_home && d->userCompletion( url, &aMatch ) )
00639         return aMatch;
00640 
00641     // Replace user directories and variables
00642     url.filter( d->replace_home, d->replace_env );
00643 
00644     //kDebug() << "Filtered: proto=" << url.protocol()
00645     //          << ", dir=" << url.dir()
00646     //          << ", file=" << url.file()
00647     //          << ", kurl url=" << *url.kurl();
00648 
00649     if ( d->mode == ExeCompletion ) {
00650         // Executables
00651         //
00652         if ( d->exeCompletion( url, &aMatch ) )
00653             return aMatch;
00654 
00655         // KRun can run "man:" and "info:" etc. so why not treat them
00656         // as executables...
00657 
00658         if ( d->urlCompletion( url, &aMatch ) )
00659             return aMatch;
00660     }
00661     else {
00662         // Local files, directories
00663         //
00664         if ( d->fileCompletion( url, &aMatch ) )
00665             return aMatch;
00666 
00667         // All other...
00668         //
00669         if ( d->urlCompletion( url, &aMatch ) )
00670             return aMatch;
00671     }
00672 
00673     d->setListedUrl( CTNone );
00674     stop();
00675 
00676     return QString();
00677 }
00678 
00679 /*
00680  * finished
00681  *
00682  * Go on and call KCompletion.
00683  * Called when all matches have been added
00684  */
00685 QString KUrlCompletionPrivate::finished()
00686 {
00687     if ( last_compl_type == CTInfo )
00688         return q->KCompletion::makeCompletion( compl_text.toLower() );
00689     else
00690         return q->KCompletion::makeCompletion( compl_text );
00691 }
00692 
00693 /*
00694  * isRunning
00695  *
00696  * Return true if either a KIO job or the DirLister
00697  * is running
00698  */
00699 bool KUrlCompletion::isRunning() const
00700 {
00701     return d->list_job || (d->dirListThread && !d->dirListThread->isFinished());
00702 }
00703 
00704 /*
00705  * stop
00706  *
00707  * Stop and delete a running KIO job or the DirLister
00708  */
00709 void KUrlCompletion::stop()
00710 {
00711     if ( d->list_job ) {
00712         d->list_job->kill();
00713         d->list_job = 0L;
00714     }
00715 
00716     while ( !d->list_urls.isEmpty() ) {
00717         delete d->list_urls.takeFirst();
00718     }
00719 
00720     if ( d->dirListThread ) {
00721         d->dirListThread->requestTermination();
00722         d->dirListThread = 0;
00723     }
00724 }
00725 
00726 /*
00727  * Keep track of the last listed directory
00728  */
00729 void KUrlCompletionPrivate::setListedUrl( int complType,
00730                                    const QString& directory,
00731                                    const QString& filter,
00732                                    bool no_hidden )
00733 {
00734     last_compl_type = complType;
00735     last_path_listed = directory;
00736     last_file_listed = filter;
00737     last_no_hidden = (int)no_hidden;
00738     last_prepend = prepend;
00739 }
00740 
00741 bool KUrlCompletionPrivate::isListedUrl( int complType,
00742                                   const QString& directory,
00743                                   const QString& filter,
00744                                   bool no_hidden )
00745 {
00746     return  last_compl_type == complType
00747             && ( last_path_listed == directory
00748                     || (directory.isEmpty() && last_path_listed.isEmpty()) )
00749             && ( filter.startsWith(last_file_listed)
00750                     || (filter.isEmpty() && last_file_listed.isEmpty()) )
00751             && last_no_hidden == (int)no_hidden
00752             && last_prepend == prepend; // e.g. relative path vs absolute
00753 }
00754 
00755 /*
00756  * isAutoCompletion
00757  *
00758  * Returns true if completion mode is Auto or Popup
00759  */
00760 bool KUrlCompletionPrivate::isAutoCompletion()
00761 {
00762     return q->completionMode() == KGlobalSettings::CompletionAuto
00763            || q->completionMode() == KGlobalSettings::CompletionPopup
00764            || q->completionMode() == KGlobalSettings::CompletionMan
00765            || q->completionMode() == KGlobalSettings::CompletionPopupAuto;
00766 }
00769 // User directories
00770 //
00771 
00772 bool KUrlCompletionPrivate::userCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
00773 {
00774     if ( url.protocol() != QLatin1String("file")
00775           || !url.dir().isEmpty()
00776           || !url.file().startsWith( QLatin1Char('~') ) )
00777         return false;
00778 
00779     if ( !isListedUrl( CTUser ) ) {
00780         q->stop();
00781         q->clear();
00782 
00783         if ( !userListThread ) {
00784             userListThread = new UserListThread( this );
00785             userListThread->start();
00786 
00787             // If the thread finishes quickly make sure that the results
00788             // are added to the first matching case.
00789 
00790             userListThread->wait( 200 );
00791             const QStringList l = userListThread->matches();
00792             addMatches( l );
00793         }
00794     }
00795     *pMatch = finished();
00796     return true;
00797 }
00798 
00801 // Environment variables
00802 //
00803 
00804 #ifndef Q_OS_WIN
00805 extern char **environ; // Array of environment variables
00806 #endif
00807 
00808 bool KUrlCompletionPrivate::envCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
00809 {
00810     if ( url.file().isEmpty() || url.file().at(0) != QLatin1Char('$') )
00811         return false;
00812 
00813     if ( !isListedUrl( CTEnv ) ) {
00814         q->stop();
00815         q->clear();
00816 
00817         char **env = environ;
00818 
00819         QString dollar = QLatin1String("$");
00820 
00821         QStringList l;
00822 
00823         while ( *env ) {
00824             QString s = QString::fromLocal8Bit( *env );
00825 
00826             int pos = s.indexOf(QLatin1Char('='));
00827 
00828             if ( pos == -1 )
00829                 pos = s.length();
00830 
00831             if ( pos > 0 )
00832                 l.append( prepend + dollar + s.left(pos) );
00833 
00834             env++;
00835         }
00836 
00837         addMatches( l );
00838     }
00839 
00840     setListedUrl( CTEnv );
00841 
00842     *pMatch = finished();
00843     return true;
00844 }
00845 
00848 // Executables
00849 //
00850 
00851 bool KUrlCompletionPrivate::exeCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
00852 {
00853     if ( url.protocol() != QLatin1String("file") )
00854         return false;
00855 
00856     QString directory = unescape( url.dir() ); // remove escapes
00857 
00858     // Find directories to search for completions, either
00859     //
00860     // 1. complete path given in url
00861     // 2. current directory (d->cwd)
00862     // 3. $PATH
00863     // 4. no directory at all
00864 
00865     QStringList dirList;
00866 
00867     if ( !QDir::isRelativePath(directory) ) {
00868         // complete path in url
00869         dirList.append( directory );
00870     }
00871     else if ( !directory.isEmpty() && !cwd.isEmpty() ) {
00872         // current directory
00873         dirList.append( cwd + QLatin1Char('/') + directory );
00874     }
00875     else if ( !url.file().isEmpty() ) {
00876         // $PATH
00877         dirList = QString::fromLocal8Bit(qgetenv("PATH")).split(
00878                 KPATH_SEPARATOR,QString::SkipEmptyParts);
00879 
00880         QStringList::Iterator it = dirList.begin();
00881 
00882         for ( ; it != dirList.end(); ++it )
00883             it->append(QLatin1Char('/'));
00884     }
00885 
00886     // No hidden files unless the user types "."
00887     bool no_hidden_files = url.file().isEmpty() || url.file().at(0) != QLatin1Char('.');
00888 
00889     // List files if needed
00890     //
00891     if ( !isListedUrl( CTExe, directory, url.file(), no_hidden_files ) )
00892     {
00893         q->stop();
00894         q->clear();
00895 
00896         setListedUrl( CTExe, directory, url.file(), no_hidden_files );
00897 
00898         *pMatch = listDirectories( dirList, url.file(), true, false, no_hidden_files );
00899     }
00900     else if ( !q->isRunning() ) {
00901         *pMatch = finished();
00902     }
00903     else {
00904         if ( dirListThread )
00905             setListedUrl( CTExe, directory, url.file(), no_hidden_files );
00906         *pMatch = QString();
00907     }
00908 
00909     return true;
00910 }
00911 
00914 // Local files
00915 //
00916 
00917 bool KUrlCompletionPrivate::fileCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
00918 {
00919     if ( url.protocol() != QLatin1String("file") )
00920         return false;
00921 
00922     QString directory = unescape( url.dir() );
00923 
00924     if (url.url().length() && url.url().at(0) == QLatin1Char('.'))
00925     {
00926         if (url.url().length() == 1)
00927         {
00928             *pMatch = ( q->completionMode() == KGlobalSettings::CompletionMan )?
00929                     QLatin1String(".") :
00930                     QLatin1String("..");
00931             return true;
00932         }
00933         else if (url.url().length() == 2 && url.url().at(1) == QLatin1Char('.'))
00934         {
00935             *pMatch = QLatin1String("..");
00936             return true;
00937         }
00938     }
00939 
00940     //kDebug() << "fileCompletion" << url << "dir=" << dir;
00941 
00942     // Find directories to search for completions, either
00943     //
00944     // 1. complete path given in url
00945     // 2. current directory (d->cwd)
00946     // 3. no directory at all
00947 
00948     QStringList dirList;
00949 
00950     if ( !QDir::isRelativePath(directory) ) {
00951         // complete path in url
00952         dirList.append( directory );
00953     }
00954     else if ( !cwd.isEmpty() ) {
00955         // current directory
00956         QString dirToAdd = cwd;
00957         if ( !directory.isEmpty() ) {
00958             if ( !cwd.endsWith('/') )
00959                 dirToAdd.append( QLatin1Char('/') );
00960             dirToAdd.append( directory );
00961         }
00962         dirList.append( dirToAdd );
00963     }
00964 
00965     // No hidden files unless the user types "."
00966     bool no_hidden_files = !url.file().startsWith( QLatin1Char('.') );
00967 
00968     // List files if needed
00969     //
00970     if ( !isListedUrl( CTFile, directory, QString(), no_hidden_files ) )
00971     {
00972         q->stop();
00973         q->clear();
00974 
00975         setListedUrl( CTFile, directory, QString(), no_hidden_files );
00976 
00977         // Append '/' to directories in Popup mode?
00978         bool append_slash = ( popup_append_slash
00979             && (q->completionMode() == KGlobalSettings::CompletionPopup ||
00980             q->completionMode() == KGlobalSettings::CompletionPopupAuto ) );
00981 
00982         bool only_dir = ( mode == KUrlCompletion::DirCompletion );
00983 
00984         *pMatch = listDirectories( dirList, QString(), false, only_dir, no_hidden_files,
00985                                   append_slash );
00986     }
00987     else if ( !q->isRunning() ) {
00988         *pMatch = finished();
00989     }
00990     else {
00991         *pMatch = QString();
00992     }
00993 
00994     return true;
00995 }
00996 
00999 // URLs not handled elsewhere...
01000 //
01001 
01002 bool KUrlCompletionPrivate::urlCompletion(const KUrlCompletionPrivate::MyURL &url, QString *pMatch)
01003 {
01004     //kDebug() << *url.kurl();
01005     if (onlyLocalProto && KProtocolInfo::protocolClass(url.protocol()) != QLatin1String(":local"))
01006         return false;
01007 
01008     // Use d->cwd as base url in case url is not absolute
01009     KUrl url_cwd( cwd );
01010 
01011     // Create an URL with the directory to be listed
01012     KUrl url_dir( url_cwd, url.kurl()->url() );
01013 
01014     // Don't try url completion if
01015     // 1. malformed url
01016     // 2. protocol that doesn't have listDir()
01017     // 3. there is no directory (e.g. "ftp://ftp.kd" shouldn't do anything)
01018     // 4. auto or popup completion mode depending on settings
01019 
01020     bool man_or_info = ( url_dir.protocol() == QLatin1String("man")
01021                          || url_dir.protocol() == QLatin1String("info") );
01022 
01023     if ( !url_dir.isValid()
01024          || !KProtocolManager::supportsListing( url_dir )
01025          || ( !man_or_info
01026                      && ( url_dir.directory(KUrl::AppendTrailingSlash|KUrl::ObeyTrailingSlash).isEmpty()
01027                    || ( isAutoCompletion()
01028                         && !url_auto_completion ) ) ) ) {
01029                 return false;
01030         }
01031 
01032     url_dir.setFileName(QString()); // not really nesseccary, but clear the filename anyway...
01033 
01034     // Remove escapes
01035     QString directory = unescape( url_dir.directory(KUrl::AppendTrailingSlash|KUrl::ObeyTrailingSlash) );
01036 
01037     url_dir.setPath( directory );
01038 
01039     // List files if needed
01040     //
01041     if ( !isListedUrl( CTUrl, url_dir.prettyUrl(), url.file() ) )
01042     {
01043         q->stop();
01044         q->clear();
01045 
01046         setListedUrl( CTUrl, url_dir.prettyUrl(), QString() );
01047 
01048         QList<KUrl*> url_list;
01049         url_list.append( new KUrl( url_dir ) );
01050 
01051         listUrls( url_list, QString(), false );
01052 
01053         *pMatch = QString();
01054     }
01055     else if ( !q->isRunning() ) {
01056         *pMatch = finished();
01057     }
01058     else {
01059         *pMatch = QString();
01060     }
01061 
01062     return true;
01063 }
01064 
01067 // Directory and URL listing
01068 //
01069 
01070 /*
01071  * addMatches
01072  *
01073  * Called to add matches to KCompletion
01074  */
01075 void KUrlCompletionPrivate::addMatches( const QStringList &matchList )
01076 {
01077     q->insertItems(matchList);
01078 }
01079 
01080 /*
01081  * listDirectories
01082  *
01083  * List files starting with 'filter' in the given directories,
01084  * either using DirLister or listURLs()
01085  *
01086  * In either case, addMatches() is called with the listed
01087  * files, and eventually finished() when the listing is done
01088  *
01089  * Returns the match if available, or QString() if
01090  * DirLister timed out or using kio
01091  */
01092 QString KUrlCompletionPrivate::listDirectories(
01093         const QStringList &dirList,
01094         const QString &filter,
01095         bool only_exe,
01096         bool only_dir,
01097         bool no_hidden,
01098         bool append_slash_to_dir)
01099 {
01100     assert( !q->isRunning() );
01101 
01102     if ( qgetenv("KURLCOMPLETION_LOCAL_KIO").isEmpty() ) {
01103 
01104         //kDebug() << "Listing (listDirectories):" << dirList << "filter=" << filter << "without KIO";
01105 
01106         // Don't use KIO
01107 
01108         if ( dirListThread )
01109             dirListThread->requestTermination();
01110 
01111         QStringList dirs;
01112 
01113         for ( QStringList::ConstIterator it = dirList.begin();
01114               it != dirList.end();
01115               ++it )
01116         {
01117             KUrl url;
01118             url.setPath(*it);
01119             if ( KAuthorized::authorizeUrlAction( QLatin1String("list"), KUrl(), url ) )
01120                 dirs.append( *it );
01121         }
01122 
01123         dirListThread = new DirectoryListThread( this, dirs, filter, only_exe, only_dir,
01124                                                  no_hidden, append_slash_to_dir );
01125         dirListThread->start();
01126         dirListThread->wait( 200 );
01127         addMatches( dirListThread->matches() );
01128 
01129         return finished();
01130     }
01131 
01132     // Use KIO
01133     //kDebug() << "Listing (listDirectories):" << dirList << "with KIO";
01134 
01135     QList<KUrl*> url_list;
01136 
01137     QStringList::ConstIterator it = dirList.begin();
01138 
01139     for ( ; it != dirList.end(); ++it ) {
01140         url_list.append( new KUrl( *it ) );
01141     }
01142 
01143     listUrls( url_list, filter, only_exe, no_hidden );
01144     // Will call addMatches() and finished()
01145 
01146     return QString();
01147 }
01148 
01149 /*
01150  * listURLs
01151  *
01152  * Use KIO to list the given urls
01153  *
01154  * addMatches() is called with the listed files
01155  * finished() is called when the listing is done
01156  */
01157 void KUrlCompletionPrivate::listUrls(
01158         const QList<KUrl *> &urls,
01159         const QString &filter,
01160         bool only_exe,
01161         bool no_hidden )
01162 {
01163     assert( list_urls.isEmpty() );
01164     assert( list_job == 0L );
01165 
01166     list_urls = urls;
01167     list_urls_filter = filter;
01168     list_urls_only_exe = only_exe;
01169     list_urls_no_hidden = no_hidden;
01170 
01171     //kDebug() << "Listing URLs:" << *urls[0] << ",...";
01172 
01173     // Start it off by calling _k_slotIOFinished
01174     //
01175     // This will start a new list job as long as there
01176     // are urls in d->list_urls
01177     //
01178     _k_slotIOFinished(0);
01179 }
01180 
01181 /*
01182  * _k_slotEntries
01183  *
01184  * Receive files listed by KIO and call addMatches()
01185  */
01186 void KUrlCompletionPrivate::_k_slotEntries(KIO::Job*, const KIO::UDSEntryList& entries)
01187 {
01188     QStringList matchList;
01189 
01190     KIO::UDSEntryList::ConstIterator it = entries.begin();
01191     const KIO::UDSEntryList::ConstIterator end = entries.end();
01192 
01193     QString filter = list_urls_filter;
01194 
01195     int filter_len = filter.length();
01196 
01197     // Iterate over all files
01198     //
01199     for (; it != end; ++it) {
01200         const KIO::UDSEntry& entry = *it;
01201         const QString url = entry.stringValue( KIO::UDSEntry::UDS_URL );
01202 
01203         QString entry_name;
01204         if (!url.isEmpty()) {
01205             // kDebug() << "url:" << url;
01206             entry_name = KUrl(url).fileName();
01207         } else {
01208             entry_name = entry.stringValue( KIO::UDSEntry::UDS_NAME );
01209         }
01210 
01211         // kDebug() << "name:" << name;
01212 
01213         if ( (!entry_name.isEmpty() && entry_name.at(0) == QLatin1Char('.')) &&
01214              ( list_urls_no_hidden ||
01215                 entry_name.length() == 1 ||
01216                   ( entry_name.length() == 2 && entry_name.at(1) == QLatin1Char('.') ) ) )
01217             continue;
01218 
01219         const bool isDir = entry.isDir();
01220 
01221         if ( mode == KUrlCompletion::DirCompletion && !isDir )
01222             continue;
01223 
01224         if ( filter_len == 0 || entry_name.left(filter_len) == filter ) {
01225 
01226             QString toAppend = complete_url ? QUrl::toPercentEncoding(entry_name) : entry_name;
01227 
01228             if (isDir)
01229                 toAppend.append( QLatin1Char( '/' ) );
01230 
01231             if ( !list_urls_only_exe ||
01232                  (entry.numberValue( KIO::UDSEntry::UDS_ACCESS ) & MODE_EXE) // true if executable
01233                 ) {
01234                 matchList.append( prepend + toAppend );
01235             }
01236         }
01237     }
01238 
01239     addMatches( matchList );
01240 }
01241 
01242 /*
01243  * _k_slotIOFinished
01244  *
01245  * Called when a KIO job is finished.
01246  *
01247  * Start a new list job if there are still urls in
01248  * list_urls, otherwise call finished()
01249  */
01250 void KUrlCompletionPrivate::_k_slotIOFinished( KJob * job )
01251 {
01252     assert( job == list_job );
01253 
01254     if ( list_urls.isEmpty() ) {
01255 
01256         list_job = 0L;
01257 
01258         finished(); // will call KCompletion::makeCompletion()
01259 
01260     }
01261     else {
01262 
01263         KUrl *kurl = list_urls.takeFirst();
01264 
01265 //      list_urls.removeAll( kurl );
01266 
01267 //      kDebug() << "Start KIO::listDir" << *kurl;
01268 
01269         list_job = KIO::listDir( *kurl, KIO::HideProgressInfo );
01270         list_job->addMetaData("no-auth-prompt", "true");
01271 
01272         assert( list_job );
01273 
01274         q->connect( list_job,
01275                 SIGNAL(result(KJob*)),
01276                 SLOT(_k_slotIOFinished(KJob*)) );
01277 
01278         q->connect( list_job,
01279                 SIGNAL( entries( KIO::Job*, const KIO::UDSEntryList&)),
01280                 SLOT( _k_slotEntries( KIO::Job*, const KIO::UDSEntryList&)) );
01281 
01282         delete kurl;
01283     }
01284 }
01285 
01288 
01289 /*
01290  * postProcessMatch, postProcessMatches
01291  *
01292  * Called by KCompletion before emitting match() and matches()
01293  *
01294  * Append '/' to directories for file completion. This is
01295  * done here to avoid stat()'ing a lot of files
01296  */
01297 void KUrlCompletion::postProcessMatch( QString *pMatch ) const
01298 {
01299 //  kDebug() << *pMatch;
01300 
01301     if ( !pMatch->isEmpty() ) {
01302 
01303         // Add '/' to directories in file completion mode
01304         // unless it has already been done
01305         if ( d->last_compl_type == CTFile
01306                && pMatch->at( pMatch->length()-1 ) != QLatin1Char('/') )
01307         {
01308             QString copy;
01309 
01310             if ( pMatch->startsWith( QLatin1String("file:") ) )
01311                 copy = KUrl(*pMatch).path();
01312             else
01313                 copy = *pMatch;
01314 
01315             expandTilde( copy );
01316             expandEnv( copy );
01317             if ( QDir::isRelativePath(copy) )
01318                 copy.prepend( d->cwd + QLatin1Char('/') );
01319 
01320 //          kDebug() << "stat'ing" << copy;
01321 
01322             KDE_struct_stat sbuff;
01323 
01324             QByteArray file = QFile::encodeName( copy );
01325 
01326             if ( KDE_stat( file.data(), &sbuff ) == 0 ) {
01327                 if ( S_ISDIR ( sbuff.st_mode ) )
01328                     pMatch->append( QLatin1Char( '/' ) );
01329             }
01330             else {
01331                 kDebug() << "Could not stat file" << copy;
01332             }
01333         }
01334     }
01335 }
01336 
01337 void KUrlCompletion::postProcessMatches( QStringList * /*matches*/ ) const
01338 {
01339     // Maybe '/' should be added to directories here as in
01340     // postProcessMatch() but it would slow things down
01341     // when there are a lot of matches...
01342 }
01343 
01344 void KUrlCompletion::postProcessMatches( KCompletionMatches * /*matches*/ ) const
01345 {
01346     // Maybe '/' should be added to directories here as in
01347     // postProcessMatch() but it would slow things down
01348     // when there are a lot of matches...
01349 }
01350 
01351 void KUrlCompletion::customEvent(QEvent *e)
01352 {
01353     if ( e->type() == CompletionMatchEvent::uniqueType() ) {
01354 
01355         CompletionMatchEvent *matchEvent = static_cast<CompletionMatchEvent *>( e );
01356 
01357         matchEvent->completionThread()->wait();
01358 
01359         if ( !d->isListedUrl( CTUser ) ) {
01360             stop();
01361             clear();
01362             d->addMatches( matchEvent->completionThread()->matches() );
01363         }
01364 
01365         d->setListedUrl( CTUser );
01366 
01367         if ( d->userListThread == matchEvent->completionThread() )
01368             d->userListThread = 0;
01369 
01370         if ( d->dirListThread == matchEvent->completionThread() )
01371             d->dirListThread = 0;
01372 
01373         delete matchEvent->completionThread();
01374     }
01375 }
01376 
01377 // static
01378 QString KUrlCompletion::replacedPath( const QString& text, bool replaceHome, bool replaceEnv )
01379 {
01380     if ( text.isEmpty() )
01381         return text;
01382 
01383     KUrlCompletionPrivate::MyURL url( text, QString() ); // no need to replace something of our current cwd
01384     if ( !url.kurl()->isLocalFile() )
01385         return text;
01386 
01387     url.filter( replaceHome, replaceEnv );
01388     return url.dir() + url.file();
01389 }
01390 
01391 
01392 QString KUrlCompletion::replacedPath( const QString& text ) const
01393 {
01394     return replacedPath( text, d->replace_home, d->replace_env );
01395 }
01396 
01399 // Static functions
01400 
01401 /*
01402  * expandEnv
01403  *
01404  * Expand environment variables in text. Escaped '$' are ignored.
01405  * Return true if expansion was made.
01406  */
01407 static bool expandEnv( QString &text )
01408 {
01409     // Find all environment variables beginning with '$'
01410     //
01411     int pos = 0;
01412 
01413     bool expanded = false;
01414 
01415     while ( (pos = text.indexOf(QLatin1Char('$'), pos)) != -1 ) {
01416 
01417         // Skip escaped '$'
01418         //
01419         if ( pos > 0 && text.at(pos-1) == QLatin1Char('\\') ) {
01420             pos++;
01421         }
01422         // Variable found => expand
01423         //
01424         else {
01425             // Find the end of the variable = next '/' or ' '
01426             //
01427             int pos2 = text.indexOf( QLatin1Char(' '), pos+1 );
01428             int pos_tmp = text.indexOf( QLatin1Char('/'), pos+1 );
01429 
01430             if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
01431                 pos2 = pos_tmp;
01432 
01433             if ( pos2 == -1 )
01434                 pos2 = text.length();
01435 
01436             // Replace if the variable is terminated by '/' or ' '
01437             // and defined
01438             //
01439             if ( pos2 >= 0 ) {
01440                 int len = pos2 - pos;
01441                 QString key = text.mid( pos+1, len-1);
01442                 QString value =
01443                     QString::fromLocal8Bit( qgetenv(key.toLocal8Bit()) );
01444 
01445                 if ( !value.isEmpty() ) {
01446                     expanded = true;
01447                     text.replace( pos, len, value );
01448                     pos = pos + value.length();
01449                 }
01450                 else {
01451                     pos = pos2;
01452                 }
01453             }
01454         }
01455     }
01456 
01457     return expanded;
01458 }
01459 
01460 /*
01461  * expandTilde
01462  *
01463  * Replace "~user" with the users home directory
01464  * Return true if expansion was made.
01465  */
01466 static bool expandTilde(QString &text)
01467 {
01468     if ( text.isEmpty() || ( text.at(0) != QLatin1Char('~') ))
01469         return false;
01470 
01471     bool expanded = false;
01472 
01473     // Find the end of the user name = next '/' or ' '
01474     //
01475     int pos2 = text.indexOf( QLatin1Char(' '), 1 );
01476     int pos_tmp = text.indexOf( QLatin1Char('/'), 1 );
01477 
01478     if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
01479         pos2 = pos_tmp;
01480 
01481     if ( pos2 == -1 )
01482         pos2 = text.length();
01483 
01484     // Replace ~user if the user name is terminated by '/' or ' '
01485     //
01486     if ( pos2 >= 0 ) {
01487 
01488         QString user = text.mid( 1, pos2-1 );
01489         QString dir;
01490 
01491         // A single ~ is replaced with $HOME
01492         //
01493         if ( user.isEmpty() ) {
01494             dir = QDir::homePath();
01495         }
01496         // ~user is replaced with the dir from passwd
01497         //
01498         else {
01499             struct passwd *pw = ::getpwnam( user.toLocal8Bit() );
01500 
01501             if ( pw )
01502                 dir = QFile::decodeName( pw->pw_dir );
01503 
01504             ::endpwent();
01505         }
01506 
01507         if ( !dir.isEmpty() ) {
01508             expanded = true;
01509             text.replace(0, pos2, dir);
01510         }
01511     }
01512 
01513     return expanded;
01514 }
01515 
01516 /*
01517  * unescape
01518  *
01519  * Remove escapes and return the result in a new string
01520  *
01521  */
01522 static QString unescape(const QString &text)
01523 {
01524     QString result;
01525 
01526     for (int pos = 0; pos < text.length(); pos++)
01527         if ( text.at(pos) != QLatin1Char('\\') )
01528             result.insert( result.length(), text.at(pos) );
01529 
01530     return result;
01531 }
01532 
01533 #include "kurlcompletion.moc"

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.6
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal