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