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

KFile

kfilewidget.cpp

Go to the documentation of this file.
00001 // -*- c++ -*-
00002 /* This file is part of the KDE libraries
00003     Copyright (C) 1997, 1998 Richard Moore <rich@kde.org>
00004                   1998 Stephan Kulow <coolo@kde.org>
00005                   1998 Daniel Grana <grana@ie.iwi.unibe.ch>
00006                   1999,2000,2001,2002,2003 Carsten Pfeiffer <pfeiffer@kde.org>
00007                   2003 Clarence Dang <dang@kde.org>
00008                   2007 David Faure <faure@kde.org>
00009                   2008 Rafael Fernández López <ereslibre@kde.org>
00010 
00011     This library is free software; you can redistribute it and/or
00012     modify it under the terms of the GNU Library General Public
00013     License as published by the Free Software Foundation; either
00014     version 2 of the License, or (at your option) any later version.
00015 
00016     This library is distributed in the hope that it will be useful,
00017     but WITHOUT ANY WARRANTY; without even the implied warranty of
00018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019     Library General Public License for more details.
00020 
00021     You should have received a copy of the GNU Library General Public License
00022     along with this library; see the file COPYING.LIB.  If not, write to
00023     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00024     Boston, MA 02110-1301, USA.
00025 */
00026 
00027 #include "kfilewidget.h"
00028 
00029 #include "kfileplacesview.h"
00030 #include "kfileplacesmodel.h"
00031 #include "kfilebookmarkhandler_p.h"
00032 #include "kurlcombobox.h"
00033 #include "kurlnavigator.h"
00034 #include <config-kfile.h>
00035 
00036 #include <kactioncollection.h>
00037 #include <kdiroperator.h>
00038 #include <kdirselectdialog.h>
00039 #include <kfilefiltercombo.h>
00040 #include <kimagefilepreview.h>
00041 #include <kmenu.h>
00042 #include <kmimetype.h>
00043 #include <kpushbutton.h>
00044 #include <krecentdocument.h>
00045 #include <ktoolbar.h>
00046 #include <kurlcompletion.h>
00047 #include <kuser.h>
00048 #include <kprotocolmanager.h>
00049 #include <kio/job.h>
00050 #include <kio/jobuidelegate.h>
00051 #include <kio/netaccess.h>
00052 #include <kio/scheduler.h>
00053 #include <krecentdirs.h>
00054 #include <kdebug.h>
00055 
00056 #include <QtGui/QCheckBox>
00057 #include <QtGui/QLayout>
00058 #include <QtGui/QLabel>
00059 #include <QtGui/QLineEdit>
00060 #include <QtGui/QSplitter>
00061 #include <QtCore/QFSFileEngine>
00062 #include <kshell.h>
00063 #include <kmessagebox.h>
00064 #include <kauthorized.h>
00065 
00066 class KFileWidgetPrivate
00067 {
00068 public:
00069     KFileWidgetPrivate( KFileWidget* q )
00070         : boxLayout(0),
00071           labeledCustomWidget(0),
00072           bottomCustomWidget(0),
00073           speedBarWidth(-1),
00074           inAccept(false),
00075           dummyAdded(false),
00076           q(q)
00077     {
00078     }
00079     void updateLocationWhatsThis();
00080     void updateAutoSelectExtension();
00081     void initSpeedbar();
00082     void initGUI();
00083     void readConfig(const KConfigGroup &configGroup);
00084     void writeConfig(KConfigGroup &configGroup);
00085     void setNonExtSelection();
00086     void setLocationText(const KUrl&);
00087     void setLocationText(const KUrl::List&);
00088     void appendExtension(KUrl &url);
00089     void updateLocationEditExtension(const QString &);
00090     void updateFilter();
00091     void updateSplitterSize();
00092     KUrl::List& parseSelectedUrls();
00099     KUrl::List tokenize(const QString& line) const;
00103     void readRecentFiles( KConfig * );
00107     void saveRecentFiles( KConfig * );
00112     void multiSelectionChanged();
00113 
00117     KUrl getCompleteUrl(const QString&) const;
00118 
00123     void setDummyHistoryEntry(const QString& text, const QPixmap& icon = QPixmap(),
00124                               bool usePreviousPixmapIfNull = true);
00125 
00129     void removeDummyHistoryEntry();
00130 
00131     // private slots
00132     void _k_slotLocationChanged( const QString& );
00133     void _k_urlEntered( const KUrl& );
00134     void _k_enterUrl( const KUrl& );
00135     void _k_enterUrl( const QString& );
00136     void _k_locationAccepted( const QString& );
00137     void _k_locationActivated( const QString& );
00138     void _k_slotFilterChanged();
00139     void _k_fileHighlighted( const KFileItem& );
00140     void _k_fileSelected( const KFileItem& );
00141     void _k_slotStatResult( KJob* );
00142     void _k_slotLoadingFinished();
00143     void _k_fileCompletion( const QString& );
00144     void _k_toggleSpeedbar( bool );
00145     void _k_toggleBookmarks( bool );
00146     void _k_slotAutoSelectExtClicked();
00147     void _k_placesViewSplitterMoved();
00148 
00149     void addToRecentDocuments();
00150 
00151     QString locationEditCurrentText() const;
00152 
00153     // the last selected url
00154     KUrl url;
00155 
00156     // the selected filenames in multiselection mode -- FIXME
00157     QString filenames;
00158 
00159     // the name of the filename set by setSelection
00160     QString selection;
00161 
00162     // now following all kind of widgets, that I need to rebuild
00163     // the geometry management
00164     QBoxLayout *boxLayout;
00165     QGridLayout *lafBox;
00166     QVBoxLayout *vbox;
00167 
00168     QLabel *locationLabel;
00169 
00170     // @deprecated remove in KDE4 -- err, remove what?
00171     QLabel *filterLabel;
00172     KUrlNavigator *urlNavigator;
00173     KPushButton *okButton, *cancelButton;
00174     KFilePlacesView *placesView;
00175     QSplitter *placesViewSplitter;
00176     QWidget *labeledCustomWidget;
00177     QWidget *bottomCustomWidget;
00178 
00179     // Automatically Select Extension stuff
00180     QCheckBox *autoSelectExtCheckBox;
00181     bool autoSelectExtChecked; // whether or not the _user_ has checked the above box
00182     QString extension; // current extension for this filter
00183 
00184     QList<KIO::StatJob*> statJobs;
00185 
00186     KUrl::List urlList; //the list of selected urls
00187 
00188     QStringList mimetypes; //the list of possible mimetypes to save as
00189 
00190     // caches the speed bar width. This value will be updated when the splitter
00191     // is moved. This allows us to properly set a value when the dialog itself
00192     // is resized
00193     int speedBarWidth;
00194 
00195     // indicates if the location edit should be kept or cleared when changing
00196     // directories
00197     bool keepLocation;
00198 
00199     // the KDirOperators view is set in KFileWidget::show(), so to avoid
00200     // setting it again and again, we have this nice little boolean :)
00201     bool hasView;
00202 
00203     bool hasDefaultFilter; // necessary for the operationMode
00204     bool autoDirectoryFollowing;
00205     bool inAccept; // true between beginning and end of accept()
00206     bool dummyAdded; // if the dummy item has been added. This prevents the combo from having a
00207                      // blank item added when loaded
00208 
00209     KFileWidget::OperationMode operationMode;
00210 
00211     // The file class used for KRecentDirs
00212     QString fileClass;
00213 
00214     KFileBookmarkHandler *bookmarkHandler;
00215 
00216     KActionMenu* bookmarkButton;
00217     KConfigGroup *viewConfigGroup;
00218 
00219     KToolBar *toolbar;
00220     KUrlComboBox *locationEdit;
00221     KDirOperator *ops;
00222     KFileFilterCombo *filterWidget;
00223     KFileWidget* q;
00224 
00225     KFilePlacesModel *model;
00226 };
00227 
00228 K_GLOBAL_STATIC(KUrl, lastDirectory) // to set the start path
00229 
00230 static const char autocompletionWhatsThisText[] = I18N_NOOP("<qt>While typing in the text area, you may be presented "
00231                                                   "with possible matches. "
00232                                                   "This feature can be controlled by clicking with the right mouse button "
00233                                                   "and selecting a preferred mode from the <b>Text Completion</b> menu.")  "</qt>";
00234 
00235 // returns true if the string contains "<a>:/" sequence, where <a> is at least 2 alpha chars
00236 static bool containsProtocolSection( const QString& string )
00237 {
00238     int len = string.length();
00239     static const char prot[] = ":/";
00240     for (int i=0; i < len;) {
00241         i = string.indexOf( QLatin1String(prot), i );
00242         if (i == -1)
00243             return false;
00244         int j=i-1;
00245         for (; j >= 0; j--) {
00246             const QChar& ch( string[j] );
00247             if (ch.toAscii() == 0 || !ch.isLetter())
00248                 break;
00249             if (ch.isSpace() && (i-j-1) >= 2)
00250                 return true;
00251         }
00252         if (j < 0 && i >= 2)
00253             return true; // at least two letters before ":/"
00254         i += 3; // skip : and / and one char
00255     }
00256     return false;
00257 }
00258 
00259 KFileWidget::KFileWidget( const KUrl& startDir, QWidget *parent )
00260     : QWidget(parent), KAbstractFileWidget(), d(new KFileWidgetPrivate(this))
00261 {
00262     // TODO move most of this code for the KFileWidgetPrivate constructor
00263     d->keepLocation = false;
00264     d->operationMode = Opening;
00265     d->bookmarkHandler = 0;
00266     d->hasDefaultFilter = false;
00267     d->hasView = false;
00268 
00269     d->okButton = new KPushButton(KStandardGuiItem::ok(), this);
00270     d->okButton->setDefault( true );
00271     d->cancelButton = new KPushButton(KStandardGuiItem::cancel(), this);
00272     // The dialog shows them
00273     d->okButton->hide();
00274     d->cancelButton->hide();
00275 
00276     d->autoSelectExtCheckBox = 0; // delayed loading
00277     d->autoSelectExtChecked = false;
00278     d->placesView = 0; // delayed loading
00279 
00280     d->toolbar = new KToolBar(this, true);
00281     d->toolbar->setObjectName("KFileWidget::toolbar");
00282     d->toolbar->setMovable(false);
00283 
00284     d->model = new KFilePlacesModel(this);
00285     d->urlNavigator = new KUrlNavigator(d->model, startDir, d->toolbar);
00286     d->urlNavigator->setPlacesSelectorVisible(false);
00287 
00288     KUrl u;
00289     KUrlComboBox *pathCombo = d->urlNavigator->editor();
00290 #ifdef Q_WS_WIN
00291     foreach( const QFileInfo &drive,QFSFileEngine::drives() )
00292     {
00293         u.setPath( drive.filePath() );
00294         pathCombo->addDefaultUrl(u,
00295                                  KIO::pixmapForUrl( u, 0, KIconLoader::Small ),
00296                                  i18n("Drive: %1",  u.toLocalFile()));
00297     }
00298 #else
00299     u.setPath(QDir::rootPath());
00300     pathCombo->addDefaultUrl(u,
00301                              KIO::pixmapForUrl(u, 0, KIconLoader::Small),
00302                              u.toLocalFile());
00303 #endif
00304 
00305     u.setPath(QDir::homePath());
00306     pathCombo->addDefaultUrl(u, KIO::pixmapForUrl(u, 0, KIconLoader::Small),
00307                              u.path(KUrl::AddTrailingSlash));
00308 
00309     KUrl docPath;
00310     docPath.setPath( KGlobalSettings::documentPath() );
00311     if ( (u.path(KUrl::AddTrailingSlash) != docPath.path(KUrl::AddTrailingSlash)) &&
00312           QDir(docPath.path(KUrl::AddTrailingSlash)).exists() )
00313     {
00314         pathCombo->addDefaultUrl( docPath,
00315                                   KIO::pixmapForUrl( docPath, 0, KIconLoader::Small ),
00316                                   docPath.path(KUrl::AddTrailingSlash));
00317     }
00318 
00319     u.setPath( KGlobalSettings::desktopPath() );
00320     pathCombo->addDefaultUrl(u,
00321                              KIO::pixmapForUrl(u, 0, KIconLoader::Small),
00322                              u.path(KUrl::AddTrailingSlash));
00323 
00324     d->url = getStartUrl( startDir, d->fileClass );
00325     d->selection = d->url.url();
00326 
00327     // If local, check it exists. If not, go up until it exists.
00328     if ( d->url.isLocalFile() )
00329     {
00330         if ( !QFile::exists( d->url.toLocalFile() ) )
00331         {
00332             d->url = d->url.upUrl();
00333             QDir dir( d->url.toLocalFile() );
00334             while ( !dir.exists() )
00335             {
00336                 d->url = d->url.upUrl();
00337                 dir.setPath( d->url.toLocalFile() );
00338             }
00339         }
00340     }
00341 
00342     d->ops = new KDirOperator(d->url, this );
00343     d->ops->setObjectName( "KFileWidget::ops" );
00344     d->ops->setOnlyDoubleClickSelectsFiles( true );
00345     connect(d->ops, SIGNAL(urlEntered(const KUrl&)),
00346             SLOT(_k_urlEntered(const KUrl&)));
00347     connect(d->ops, SIGNAL(fileHighlighted(const KFileItem &)),
00348             SLOT(_k_fileHighlighted(const KFileItem &)));
00349     connect(d->ops, SIGNAL(fileSelected(const KFileItem &)),
00350             SLOT(_k_fileSelected(const KFileItem &)));
00351     connect(d->ops, SIGNAL(finishedLoading()),
00352             SLOT(_k_slotLoadingFinished()));
00353 
00354     d->ops->setupMenu(KDirOperator::SortActions |
00355                    KDirOperator::FileActions |
00356                    KDirOperator::ViewActions);
00357     KActionCollection *coll = d->ops->actionCollection();
00358 
00359     // add nav items to the toolbar
00360     //
00361     // NOTE:  The order of the button icons here differs from that
00362     // found in the file manager and web browser, but has been discussed
00363     // and agreed upon on the kde-core-devel mailing list:
00364     //
00365     // http://lists.kde.org/?l=kde-core-devel&m=116888382514090&w=2
00366     //
00367     d->toolbar->addAction( coll->action( "up" ) );
00368     coll->action( "up" )->setWhatsThis(i18n("<qt>Click this button to enter the parent folder.<br /><br />"
00369                                             "For instance, if the current location is file:/home/%1 clicking this "
00370                                             "button will take you to file:/home.</qt>",  KUser().loginName() ));
00371 
00372     d->toolbar->addAction( coll->action( "back" ) );
00373     coll->action( "back" )->setWhatsThis(i18n("Click this button to move backwards one step in the browsing history."));
00374     d->toolbar->addAction( coll->action( "forward" ) );
00375     coll->action( "forward" )->setWhatsThis(i18n("Click this button to move forward one step in the browsing history."));
00376 
00377     d->toolbar->addAction( coll->action( "reload" ) );
00378     coll->action( "reload" )->setWhatsThis(i18n("Click this button to reload the contents of the current location."));
00379     coll->action( "mkdir" )->setShortcut( QKeySequence(Qt::Key_F10) );
00380     d->toolbar->addAction( coll->action( "mkdir" ) );
00381     coll->action( "mkdir" )->setWhatsThis(i18n("Click this button to create a new folder."));
00382 
00383     KToggleAction *showSidebarAction =
00384         new KToggleAction(i18n("Show Places Navigation Panel"), this);
00385     coll->addAction("toggleSpeedbar", showSidebarAction);
00386     showSidebarAction->setShortcut( QKeySequence(Qt::Key_F9) );
00387     connect( showSidebarAction, SIGNAL( toggled( bool ) ),
00388              SLOT( _k_toggleSpeedbar( bool )) );
00389 
00390     KToggleAction *showBookmarksAction =
00391         new KToggleAction(i18n("Show Bookmarks"), this);
00392     coll->addAction("toggleBookmarks", showBookmarksAction);
00393     connect( showBookmarksAction, SIGNAL( toggled( bool ) ),
00394              SLOT( _k_toggleBookmarks( bool )) );
00395 
00396     KActionMenu *menu = new KActionMenu( KIcon("configure"), i18n("Options"), this);
00397     coll->addAction("extra menu", menu);
00398     menu->setWhatsThis(i18n("<qt>This is the preferences menu for the file dialog. "
00399                             "Various options can be accessed from this menu including: <ul>"
00400                             "<li>how files are sorted in the list</li>"
00401                             "<li>types of view, including icon and list</li>"
00402                             "<li>showing of hidden files</li>"
00403                             "<li>the Places navigation panel</li>"
00404                             "<li>file previews</li>"
00405                             "<li>separating folders from files</li></ul></qt>"));
00406     menu->addAction( coll->action( "sorting menu" ));
00407     menu->addSeparator();
00408     coll->action( "short view" )->setShortcut( QKeySequence(Qt::Key_F6) );
00409     menu->addAction( coll->action( "short view" ));
00410     coll->action( "detailed view" )->setShortcut( QKeySequence(Qt::Key_F7) );
00411     menu->addAction( coll->action( "detailed view" ));
00412     menu->addSeparator();
00413     coll->action( "show hidden" )->setShortcut( QKeySequence(Qt::Key_F8) );
00414     menu->addAction( coll->action( "show hidden" ));
00415     menu->addAction( showSidebarAction );
00416     menu->addAction( showBookmarksAction );
00417     coll->action( "preview" )->setShortcut( QKeySequence(Qt::Key_F11) );
00418     menu->addAction( coll->action( "preview" ));
00419 
00420     menu->setDelayed( false );
00421     connect( menu->menu(), SIGNAL( aboutToShow() ),
00422              d->ops, SLOT( updateSelectionDependentActions() ));
00423     d->toolbar->addAction( menu );
00424 
00425     d->toolbar->addWidget(d->urlNavigator);
00426 
00427     // FIXME KAction port - add capability
00428     //d->toolbar->setItemAutoSized (PATH_COMBO);
00429     d->toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
00430     d->toolbar->setMovable(false);
00431 
00432     KUrlCompletion *pathCompletionObj = new KUrlCompletion( KUrlCompletion::DirCompletion );
00433     pathCombo->setCompletionObject( pathCompletionObj );
00434     pathCombo->setAutoDeleteCompletionObject( true );
00435 
00436     connect( d->urlNavigator, SIGNAL( urlChanged( const KUrl&  )),
00437              this,  SLOT( _k_enterUrl( const KUrl& ) ));
00438 
00439     QString whatsThisText;
00440 
00441     // the Location label/edit
00442     d->locationLabel = new QLabel(i18n("&Location:"), this);
00443     d->locationEdit = new KUrlComboBox(KUrlComboBox::Files, true, this);
00444     // Properly let the dialog be resized (to smaller). Otherwise we could have
00445     // huge dialogs that can't be resized to smaller (it would be as big as the longest
00446     // item in this combo box). (ereslibre)
00447     d->locationEdit->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
00448     connect( d->locationEdit, SIGNAL( editTextChanged( const QString& ) ),
00449              SLOT( _k_slotLocationChanged( const QString& )) );
00450 
00451     d->updateLocationWhatsThis();
00452     d->locationLabel->setBuddy(d->locationEdit);
00453 
00454     KUrlCompletion *fileCompletionObj = new KUrlCompletion( KUrlCompletion::FileCompletion );
00455     QString dir = d->url.url(KUrl::AddTrailingSlash);
00456 
00457     d->urlNavigator->setUrl( dir );
00458 
00459     fileCompletionObj->setDir( dir );
00460     d->locationEdit->setCompletionObject( fileCompletionObj );
00461     d->locationEdit->setAutoDeleteCompletionObject( true );
00462     connect( fileCompletionObj, SIGNAL( match( const QString& ) ),
00463              SLOT( _k_fileCompletion( const QString& )) );
00464 
00465     connect(d->locationEdit, SIGNAL( returnPressed( const QString&  )),
00466             this,  SLOT( _k_locationAccepted( const QString& ) ));
00467     connect(d->locationEdit, SIGNAL( activated( const QString& )),
00468             this,  SLOT( _k_locationActivated( const QString& ) ));
00469 
00470     // the Filter label/edit
00471     whatsThisText = i18n("<qt>This is the filter to apply to the file list. "
00472                          "File names that do not match the filter will not be shown.<p>"
00473                          "You may select from one of the preset filters in the "
00474                          "drop down menu, or you may enter a custom filter "
00475                          "directly into the text area.</p><p>"
00476                          "Wildcards such as * and ? are allowed.</p></qt>");
00477     d->filterLabel = new QLabel(i18n("&Filter:"), this);
00478     d->filterLabel->setWhatsThis(whatsThisText);
00479     d->filterWidget = new KFileFilterCombo(this);
00480     // Properly let the dialog be resized (to smaller). Otherwise we could have
00481     // huge dialogs that can't be resized to smaller (it would be as big as the longest
00482     // item in this combo box). (ereslibre)
00483     d->filterWidget->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
00484     d->filterWidget->setWhatsThis(whatsThisText);
00485     d->filterLabel->setBuddy(d->filterWidget);
00486     connect(d->filterWidget, SIGNAL(filterChanged()), SLOT(_k_slotFilterChanged()));
00487 
00488     // the Automatically Select Extension checkbox
00489     // (the text, visibility etc. is set in updateAutoSelectExtension(), which is called by readConfig())
00490     d->autoSelectExtCheckBox = new QCheckBox (this);
00491     d->autoSelectExtCheckBox->setStyleSheet(QString("QCheckBox { padding-top: %1px; }").arg(KDialog::spacingHint()));
00492     connect(d->autoSelectExtCheckBox, SIGNAL(clicked()), SLOT(_k_slotAutoSelectExtClicked()));
00493 
00494     d->initGUI(); // activate GM
00495 
00496     KSharedConfig::Ptr config = KGlobal::config();
00497     d->readRecentFiles(config.data());
00498 
00499     d->viewConfigGroup=new KConfigGroup(config,ConfigGroup);
00500     d->ops->setViewConfig(*d->viewConfigGroup);
00501     d->readConfig(* d->viewConfigGroup);
00502     setSelection(d->selection);
00503     d->locationEdit->setFocus();
00504 }
00505 
00506 KFileWidget::~KFileWidget()
00507 {
00508     KSharedConfig::Ptr config = KGlobal::config();
00509 
00510     config->sync();
00511 
00512     delete d->bookmarkHandler; // Should be deleted before ops!
00513     delete d->ops;
00514     delete d->viewConfigGroup;
00515     delete d;
00516 }
00517 
00518 void KFileWidget::setLocationLabel(const QString& text)
00519 {
00520     d->locationLabel->setText(text);
00521 }
00522 
00523 void KFileWidget::setFilter(const QString& filter)
00524 {
00525     int pos = filter.indexOf('/');
00526 
00527     // Check for an un-escaped '/', if found
00528     // interpret as a MIME filter.
00529 
00530     if (pos > 0 && filter[pos - 1] != '\\') {
00531         QStringList filters = filter.split(" ", QString::SkipEmptyParts); //QStringList::split( " ", filter );
00532         setMimeFilter( filters );
00533         return;
00534     }
00535 
00536     // Strip the escape characters from
00537     // escaped '/' characters.
00538 
00539     QString copy (filter);
00540     for (pos = 0; (pos = copy.indexOf("\\/", pos)) != -1; ++pos)
00541         copy.remove(pos, 1);
00542 
00543     d->ops->clearFilter();
00544     d->filterWidget->setFilter(copy);
00545     d->ops->setNameFilter(d->filterWidget->currentFilter());
00546     d->hasDefaultFilter = false;
00547     d->filterWidget->setEditable( true );
00548 
00549     d->updateAutoSelectExtension ();
00550 }
00551 
00552 QString KFileWidget::currentFilter() const
00553 {
00554     return d->filterWidget->currentFilter();
00555 }
00556 
00557 void KFileWidget::setMimeFilter( const QStringList& mimeTypes,
00558                                  const QString& defaultType )
00559 {
00560     d->mimetypes = mimeTypes;
00561     d->filterWidget->setMimeFilter( mimeTypes, defaultType );
00562 
00563     QStringList types = d->filterWidget->currentFilter().split(" ",QString::SkipEmptyParts); //QStringList::split(" ", d->filterWidget->currentFilter());
00564     types.append( QLatin1String( "inode/directory" ));
00565     d->ops->clearFilter();
00566     d->ops->setMimeFilter( types );
00567     d->hasDefaultFilter = !defaultType.isEmpty();
00568     d->filterWidget->setEditable( !d->hasDefaultFilter ||
00569                                d->operationMode != Saving );
00570 
00571     d->updateAutoSelectExtension ();
00572 }
00573 
00574 void KFileWidget::clearFilter()
00575 {
00576     d->mimetypes.clear();
00577     d->filterWidget->setFilter( QString() );
00578     d->ops->clearFilter();
00579     d->hasDefaultFilter = false;
00580     d->filterWidget->setEditable( true );
00581 
00582     d->updateAutoSelectExtension ();
00583 }
00584 
00585 QString KFileWidget::currentMimeFilter() const
00586 {
00587     int i = d->filterWidget->currentIndex();
00588     if (d->filterWidget->showsAllTypes())
00589         i--;
00590 
00591     if ((i >= 0) && (i < (int) d->mimetypes.count()))
00592         return d->mimetypes[i];
00593     return QString(); // The "all types" item has no mimetype
00594 }
00595 
00596 KMimeType::Ptr KFileWidget::currentFilterMimeType()
00597 {
00598     return KMimeType::mimeType( currentMimeFilter() );
00599 }
00600 
00601 void KFileWidget::setPreviewWidget(KPreviewWidgetBase *w) {
00602     d->ops->setPreviewWidget(w);
00603     d->ops->clearHistory();
00604     d->hasView = true;
00605 }
00606 
00607 KUrl KFileWidgetPrivate::getCompleteUrl(const QString &_url) const
00608 {
00609     QString url = KShell::tildeExpand(_url);
00610     KUrl u;
00611 
00612     if ( KUrl::isRelativeUrl(url) ) // only a full URL isn't relative. Even /path is.
00613     {
00614         if (!url.isEmpty() && !QDir::isRelativePath(url) ) // absolute path
00615             u.setPath( url );
00616         else
00617         {
00618             u = ops->url();
00619             u.addPath( url ); // works for filenames and relative paths
00620             u.cleanPath(); // fix "dir/.."
00621         }
00622     }
00623     else // complete URL
00624         u = url;
00625 
00626     return u;
00627 }
00628 
00629 // Called by KFileDialog
00630 void KFileWidget::slotOk()
00631 {
00632     kDebug(kfile_area) << "slotOk\n";
00633 
00634     // a list of all selected files/directories (if any)
00635     // can only be used if the user didn't type any filenames/urls himself
00636     const KFileItemList items = d->ops->selectedItems();
00637 
00638     const QString locationEditCurrentText( d->locationEditCurrentText() );
00639 
00640     if ( (mode() & KFile::Directory) != KFile::Directory ) {
00641         if ( locationEditCurrentText.isEmpty() ) {
00642             // allow directory navigation by entering a path and pressing
00643             // enter, by simply returning we will browse the new path
00644             if (items.isEmpty())
00645                 return;
00646 
00647             // weird case: the location edit is empty, but there are
00648             // highlighted files
00649             bool multi = (mode() & KFile::Files) != 0;
00650             QString endQuote = QLatin1String("\" ");
00651             QString name;
00652             KUrl::List urlList;
00653             foreach (const KFileItem &fileItem, items) {
00654                 name = fileItem.name();
00655                 if ( multi ) {
00656                     name.prepend( QLatin1Char( '"' ) );
00657                     name.append( endQuote );
00658                 }
00659 
00660                 urlList << fileItem.url();
00661             }
00662             d->setLocationText( urlList );
00663             return;
00664         }
00665     }
00666 
00667     bool dirOnly = d->ops->dirOnlyMode();
00668 
00669     // we can use our kfileitems, no need to parse anything
00670     if ( !d->locationEdit->lineEdit()->isModified() &&
00671          !(items.isEmpty() && !dirOnly) ) {
00672 
00673         d->urlList.clear();
00674         d->filenames.clear();
00675 
00676         if ( dirOnly ) {
00677             d->url = d->ops->url();
00678         }
00679         else {
00680             if ( !(mode() & KFile::Files) ) {// single selection
00681                 d->url = items.first().url();
00682             }
00683 
00684             else { // multi (dirs and/or files)
00685                 d->url = d->ops->url();
00686                 KUrl::List urlList;
00687                 foreach (const KFileItem &item, items) {
00688                     urlList.append(item.url());
00689                 }
00690                 d->urlList = urlList;
00691             }
00692         }
00693 
00694         KUrl url = KIO::NetAccess::mostLocalUrl(d->url,topLevelWidget());
00695         if ( ( (mode() & KFile::LocalOnly) == KFile::LocalOnly ) &&
00696              !url.isLocalFile() )
00697         {
00698 // ### after message freeze, add message for directories!
00699             KMessageBox::sorry( this,
00700                                 i18n("You can only select local files."),
00701                                 i18n("Remote Files Not Accepted") );
00702             return;
00703         }
00704 
00705         d->url = url;
00706         emit accepted();
00707         return;
00708     }
00709 
00710     KUrl selectedUrl;
00711 
00712     if ( (mode() & KFile::Files) == KFile::Files ) {// multiselection mode
00713         if ( locationEditCurrentText.contains( '/' ) ) {
00714             // relative path? -> prepend the current directory
00715             KUrl u( d->ops->url(), KShell::tildeExpand( locationEditCurrentText ));
00716             if ( u.isValid() )
00717                 selectedUrl = u;
00718             else
00719                 selectedUrl = d->ops->url();
00720         }
00721         else // simple filename -> just use the current URL
00722             selectedUrl = d->ops->url();
00723     }
00724 
00725     else {
00726         selectedUrl = d->getCompleteUrl( locationEditCurrentText );
00727 
00728         // appendExtension() may change selectedUrl
00729         d->appendExtension (selectedUrl);
00730     }
00731 
00732     if ( !selectedUrl.isValid() ) {
00733        KMessageBox::sorry( this, i18n("%1\ndoes not appear to be a valid URL.\n", d->url.url()), i18n("Invalid URL") );
00734        return;
00735     }
00736 
00737     KUrl url = KIO::NetAccess::mostLocalUrl(selectedUrl,topLevelWidget());
00738     if ( ( (mode() & KFile::LocalOnly) == KFile::LocalOnly ) &&
00739          !url.isLocalFile() )
00740     {
00741         KMessageBox::sorry( this,
00742                             i18n("You can only select local files."),
00743                             i18n("Remote Files Not Accepted") );
00744         return;
00745     }
00746 
00747     d->url = url;
00748 
00749     // d->url is a correct URL now
00750 
00751     if ( (mode() & KFile::Directory) == KFile::Directory ) {
00752         kDebug(kfile_area) << "Directory";
00753         bool done = true;
00754         if ( d->url.isLocalFile() ) {
00755             if ( locationEditCurrentText.isEmpty() ) {
00756                 QFileInfo info( d->url.toLocalFile() );
00757                 if ( info.isDir() ) {
00758                     d->filenames.clear();
00759                     d->urlList.clear();
00760                     d->urlList.append( d->url );
00761                     emit accepted();
00762                 }
00763                 else if (!info.exists() && (mode() & KFile::File) != KFile::File) {
00764                     // directory doesn't exist, create and enter it
00765                     if ( d->ops->mkdir( d->url.url(), true ))
00766                         return;
00767                     else
00768                         emit accepted();
00769                 }
00770                 else { // d->url is not a directory,
00771                     // maybe we are in File(s) | Directory mode
00772                     if ( (mode() & KFile::File) == KFile::File ||
00773                         (mode() & KFile::Files) == KFile::Files )
00774                         done = false;
00775                 }
00776             }
00777             else  // Directory mode, with file[s]/dir[s] selected
00778             {
00779                 if ( mode() & KFile::ExistingOnly )
00780                 {
00781                     if ( d->ops->dirOnlyMode() )
00782                     {
00783                         KUrl fullURL(d->url, locationEditCurrentText);
00784                         if ( QFile::exists( fullURL.toLocalFile() ) )
00785                         {
00786                             d->url = fullURL;
00787                             d->filenames.clear();
00788                             d->urlList.clear();
00789                             emit accepted();
00790                             return;
00791                         }
00792                         else // doesn't exist -> reject
00793                             return;
00794                     }
00795                 }
00796 
00797                 d->filenames = locationEditCurrentText;
00798                 emit accepted(); // what can we do?
00799             }
00800 
00801         }
00802         else { // FIXME: remote directory, should we allow that?
00803 //             qDebug( "**** Selected remote directory: %s", d->url.url().toLatin1().constData());
00804             d->filenames.clear();
00805             d->urlList.clear();
00806             d->urlList.append( d->url );
00807 
00808             if ( mode() & KFile::ExistingOnly )
00809                 done = false;
00810             else
00811                 emit accepted();
00812         }
00813 
00814         if ( done )
00815             return;
00816     }
00817     else { // we don't want dir
00818         KUrl::List urls = d->tokenize( locationEditCurrentText );
00819         if ( urls.count()==1 && urls.first().isLocalFile() ) {
00820             QFileInfo info( urls.first().toLocalFile() );
00821             if ( info.isDir() && this->selectedUrl().isValid() && !this->selectedUrl().equals( urls.first(), KUrl::CompareWithoutTrailingSlash ) ) {
00822                 setSelection( info.absolutePath() );
00823                 slotOk();
00824                 return;
00825             }
00826         }
00827     }
00828 
00829     if (!KAuthorized::authorizeUrlAction("open", KUrl(), d->url))
00830     {
00831         QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, d->url.prettyUrl());
00832         KMessageBox::error( this, msg);
00833         return;
00834     }
00835 
00836     KIO::StatJob *job = 0L;
00837     d->statJobs.clear();
00838     d->filenames = KShell::tildeExpand( locationEditCurrentText );
00839 
00840     if ( (mode() & KFile::Files) == KFile::Files &&
00841          !locationEditCurrentText.contains( '/' ) ) {
00842         kDebug(kfile_area) << "Files\n";
00843         KUrl::List list = d->parseSelectedUrls();
00844         for ( KUrl::List::ConstIterator it = list.begin();
00845               it != list.end(); ++it )
00846         {
00847             if (!KAuthorized::authorizeUrlAction("open", KUrl(), *it))
00848             {
00849                 QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, (*it).prettyUrl());
00850                 KMessageBox::error( this, msg);
00851                 return;
00852             }
00853         }
00854         for ( KUrl::List::ConstIterator it = list.begin();
00855               it != list.end(); ++it )
00856         {
00857             KIO::JobFlags flags = !(*it).isLocalFile() ? KIO::DefaultFlags : KIO::HideProgressInfo;
00858             job = KIO::stat( *it, flags );
00859             job->ui()->setWindow (topLevelWidget());
00860             KIO::Scheduler::scheduleJob( job );
00861             d->statJobs.append( job );
00862             connect( job, SIGNAL( result(KJob *) ),
00863                      SLOT( _k_slotStatResult( KJob *) ));
00864         }
00865         return;
00866     }
00867 
00868     KIO::JobFlags flags = !d->url.isLocalFile() ? KIO::DefaultFlags : KIO::HideProgressInfo;
00869     job = KIO::stat(d->url,flags);
00870     job->ui()->setWindow (topLevelWidget());
00871     d->statJobs.append( job );
00872     connect(job, SIGNAL(result(KJob*)), SLOT(_k_slotStatResult(KJob*)));
00873 }
00874 
00875 // FIXME : count all errors and show messagebox when d->statJobs.count() == 0
00876 // in case of an error, we cancel the whole operation (clear d->statJobs and
00877 // don't call accept)
00878 void KFileWidgetPrivate::_k_slotStatResult(KJob* job)
00879 {
00880     kDebug(kfile_area) << "slotStatResult";
00881     KIO::StatJob *sJob = static_cast<KIO::StatJob *>( job );
00882 
00883     if ( !statJobs.removeAll( sJob ) ) {
00884         return;
00885     }
00886 
00887     int count = statJobs.count();
00888 
00889     // errors mean in general, the location is no directory ;/
00890     // Can we be sure that it is exististant at all? (pfeiffer)
00891     if (sJob->error() && count == 0 && !ops->dirOnlyMode())
00892     {
00893         emit q->accepted();
00894         return;
00895     }
00896 
00897     KIO::UDSEntry t = sJob->statResult();
00898     if (t.isDir())
00899     {
00900         if ( ops->dirOnlyMode() )
00901         {
00902             filenames.clear();
00903             urlList.clear();
00904             emit q->accepted();
00905         }
00906         else // in File[s] mode, directory means error -> cd into it
00907         {
00908             if ( count == 0 ) {
00909                 locationEdit->clearEditText();
00910                 locationEdit->lineEdit()->setModified( false );
00911                 q->setUrl( sJob->url() );
00912             }
00913         }
00914         statJobs.clear();
00915         return;
00916     }
00917     else if ( ops->dirOnlyMode() )
00918     {
00919         return; // ### error message?
00920     }
00921 
00922     kDebug(kfile_area) << "filename " << sJob->url().url();
00923 
00924     if ( count == 0 )
00925         emit q->accepted();
00926 }
00927 
00928 void KFileWidget::accept()
00929 {
00930     d->inAccept = true; // parseSelectedUrls() checks that
00931 
00932     *lastDirectory = d->ops->url();
00933     if (!d->fileClass.isEmpty())
00934        KRecentDirs::add(d->fileClass, d->ops->url().url());
00935 
00936     // clear the topmost item, we insert it as full path later on as item 1
00937     d->locationEdit->setItemText( 0, QString() );
00938 
00939     KUrl::List list = selectedUrls();
00940     QList<KUrl>::const_iterator it = list.begin();
00941     int atmost = d->locationEdit->maxItems(); //don't add more items than necessary
00942     for ( ; it != list.end() && atmost > 0; ++it ) {
00943         const KUrl& url = *it;
00944         // we strip the last slash (-1) because KUrlComboBox does that as well
00945         // when operating in file-mode. If we wouldn't , dupe-finding wouldn't
00946         // work.
00947         QString file = url.isLocalFile() ? url.path(KUrl::RemoveTrailingSlash) : url.prettyUrl(KUrl::RemoveTrailingSlash);
00948 
00949         // remove dupes
00950         for ( int i = 1; i < d->locationEdit->count(); i++ ) {
00951             if ( d->locationEdit->itemText( i ) == file ) {
00952                 d->locationEdit->removeItem( i-- );
00953                 break;
00954             }
00955         }
00956         //FIXME I don't think this works correctly when the KUrlComboBox has some default urls.
00957         //KUrlComboBox should provide a function to add an url and rotate the existing ones, keeping
00958         //track of maxItems, and we shouldn't be able to insert items as we please.
00959         d->locationEdit->insertItem( 1,file);
00960         atmost--;
00961     }
00962 
00963     KSharedConfig::Ptr config = KGlobal::config();
00964     config->setForceGlobal( true );
00965     KConfigGroup grp(config,ConfigGroup);
00966     d->writeConfig(grp);
00967     config->setForceGlobal( false );
00968 
00969     d->saveRecentFiles(config.data());
00970     config->sync();
00971 
00972     d->addToRecentDocuments();
00973 
00974     if ( (mode() & KFile::Files) != KFile::Files ) // single selection
00975         emit fileSelected(d->url.url());
00976 
00977     d->ops->close();
00978 }
00979 
00980 
00981 void KFileWidgetPrivate::_k_fileHighlighted(const KFileItem &i)
00982 {
00983     const bool modified = locationEdit->lineEdit()->isModified();
00984     locationEdit->lineEdit()->setModified( false );
00985 
00986     if ( ( !i.isNull() && i.isDir() ) ||
00987          ( locationEdit->hasFocus() && !locationEdit->currentText().isEmpty() ) ) // don't disturb
00988         return;
00989 
00990     if ( (ops->mode() & KFile::Files) != KFile::Files ) {
00991         if ( i.isNull() ) {
00992             if ( !modified ) {
00993                 setLocationText( KUrl() );
00994             }
00995             return;
00996         }
00997 
00998         url = i.url();
00999 
01000         if ( !locationEdit->hasFocus() ) { // don't disturb while editing
01001             setLocationText( url );
01002         }
01003         emit q->fileHighlighted(url.url());
01004     }
01005 
01006     else {
01007         multiSelectionChanged();
01008         emit q->selectionChanged();
01009     }
01010 
01011     locationEdit->lineEdit()->selectAll();
01012 }
01013 
01014 void KFileWidgetPrivate::_k_fileSelected(const KFileItem &i)
01015 {
01016     if (!i.isNull() && i.isDir())
01017         return;
01018 
01019     if ( (ops->mode() & KFile::Files) != KFile::Files ) {
01020         if ( i.isNull() ) {
01021             setLocationText( KUrl() );
01022             return;
01023         }
01024 
01025         setLocationText( i.url() );
01026     }
01027     else {
01028         multiSelectionChanged();
01029         emit q->selectionChanged();
01030     }
01031     q->slotOk();
01032 }
01033 
01034 
01035 // I know it's slow to always iterate thru the whole filelist
01036 // (d->ops->selectedItems()), but what can we do?
01037 void KFileWidgetPrivate::multiSelectionChanged()
01038 {
01039     if ( locationEdit->hasFocus() && !locationEdit->currentText().isEmpty() ) // don't disturb
01040         return;
01041 
01042     const KFileItemList list = ops->selectedItems();
01043 
01044     if ( list.isEmpty() ) {
01045         setLocationText( KUrl() );
01046         return;
01047     }
01048 
01049     static const QString &begin = KGlobal::staticQString(" \"");
01050     KUrl::List urlList;
01051     foreach (const KFileItem &fileItem, list) {
01052         urlList << fileItem.url();
01053     }
01054 
01055     setLocationText( urlList );
01056 }
01057 
01058 void KFileWidgetPrivate::setDummyHistoryEntry( const QString& text, const QPixmap& icon,
01059                                                bool usePreviousPixmapIfNull )
01060 {
01061     // setCurrentItem() will cause textChanged() being emitted,
01062     // so slotLocationChanged() will be called. Make sure we don't clear
01063     // the KDirOperator's view-selection in there
01064     QObject::disconnect( locationEdit, SIGNAL( editTextChanged( const QString& ) ),
01065                         q, SLOT( _k_slotLocationChanged( const QString& ) ) );
01066 
01067     bool dummyExists = dummyAdded;
01068 
01069     int cursorPosition = locationEdit->lineEdit()->cursorPosition();
01070 
01071     if ( dummyAdded ) {
01072         if ( !icon.isNull() ) {
01073             locationEdit->setItemIcon( 0, icon );
01074             locationEdit->setItemText( 0, text );
01075         } else {
01076             if ( !usePreviousPixmapIfNull ) {
01077                 locationEdit->setItemIcon( 0, QPixmap() );
01078             }
01079             locationEdit->setItemText( 0, text );
01080         }
01081     } else {
01082         if ( !text.isEmpty() ) {
01083             if ( !icon.isNull() ) {
01084                 locationEdit->insertItem( 0, icon, text );
01085             } else {
01086                 if ( !usePreviousPixmapIfNull ) {
01087                     locationEdit->insertItem( 0, QPixmap(), text );
01088                 } else {
01089                     locationEdit->insertItem( 0, text );
01090                 }
01091             }
01092             dummyAdded = true;
01093             dummyExists = true;
01094         }
01095     }
01096 
01097     if ( dummyExists && !text.isEmpty() ) {
01098         locationEdit->setCurrentIndex( 0 );
01099     }
01100 
01101     locationEdit->lineEdit()->setCursorPosition( cursorPosition );
01102 
01103     QObject::connect( locationEdit, SIGNAL( editTextChanged ( const QString& ) ),
01104                     q, SLOT( _k_slotLocationChanged( const QString& )) );
01105 }
01106 
01107 void KFileWidgetPrivate::removeDummyHistoryEntry()
01108 {
01109     if ( !dummyAdded || locationEdit->lineEdit()->isModified() ) {
01110         return;
01111     }
01112 
01113     // setCurrentItem() will cause textChanged() being emitted,
01114     // so slotLocationChanged() will be called. Make sure we don't clear
01115     // the KDirOperator's view-selection in there
01116     QObject::disconnect( locationEdit, SIGNAL( editTextChanged( const QString& ) ),
01117                         q, SLOT( _k_slotLocationChanged( const QString& ) ) );
01118 
01119     locationEdit->removeItem( 0 );
01120     locationEdit->setCurrentIndex( -1 );
01121     dummyAdded = false;
01122 
01123     QObject::connect( locationEdit, SIGNAL( editTextChanged ( const QString& ) ),
01124                     q, SLOT( _k_slotLocationChanged( const QString& )) );
01125 }
01126 
01127 void KFileWidgetPrivate::setLocationText( const KUrl& url )
01128 {
01129     if ( !url.isEmpty() ) {
01130         QPixmap mimeTypeIcon = KIconLoader::global()->loadMimeTypeIcon( KMimeType::iconNameForUrl( url ), KIconLoader::Small );
01131         setDummyHistoryEntry( url.fileName(), mimeTypeIcon );
01132     } else {
01133         removeDummyHistoryEntry();
01134     }
01135 
01136     // don't change selection when user has clicked on an item
01137     if ( operationMode == KFileWidget::Saving && !locationEdit->isVisible())
01138        setNonExtSelection();
01139 }
01140 
01141 void KFileWidgetPrivate::setLocationText( const KUrl::List& urlList )
01142 {
01143     if ( urlList.count() > 1 ) {
01144         QString urls;
01145         foreach (const KUrl &url, urlList) {
01146             urls += QString( "\"%1\"" ).arg( url.fileName() ) + ' ';
01147         }
01148         urls = urls.left( urls.size() - 1 );
01149 
01150         setDummyHistoryEntry( urls, QPixmap(), false );
01151     } else if ( urlList.count() ) {
01152         const QPixmap mimeTypeIcon = KIconLoader::global()->loadMimeTypeIcon( KMimeType::iconNameForUrl( urlList[0] ),  KIconLoader::Small );
01153         setDummyHistoryEntry( urlList[0].fileName(), mimeTypeIcon );
01154     } else {
01155         removeDummyHistoryEntry();
01156     }
01157 
01158     // don't change selection when user has clicked on an item
01159     if ( operationMode == KFileWidget::Saving && !locationEdit->isVisible())
01160        setNonExtSelection();
01161 }
01162 
01163 void KFileWidgetPrivate::updateLocationWhatsThis()
01164 {
01165     QString whatsThisText;
01166     if (operationMode == KFileWidget::Saving)
01167     {
01168         whatsThisText = "<qt>" + i18n("This is the name to save the file as.") +
01169                              i18n (autocompletionWhatsThisText);
01170     }
01171     else if (ops->mode() & KFile::Files)
01172     {
01173         whatsThisText = "<qt>" + i18n("This is the list of files to open. More than "
01174                              "one file can be specified by listing several "
01175                              "files, separated by spaces.") +
01176                               i18n (autocompletionWhatsThisText);
01177     }
01178     else
01179     {
01180         whatsThisText = "<qt>" + i18n("This is the name of the file to open.") +
01181                              i18n (autocompletionWhatsThisText);
01182     }
01183 
01184     locationLabel->setWhatsThis(whatsThisText);
01185     locationEdit->setWhatsThis(whatsThisText);
01186 }
01187 
01188 void KFileWidgetPrivate::initSpeedbar()
01189 {
01190     placesView = new KFilePlacesView( q );
01191     placesView->setModel(model);
01192     placesView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
01193 
01194     placesView->setObjectName( QLatin1String( "url bar" ) );
01195     QObject::connect( placesView, SIGNAL( urlChanged( const KUrl& )),
01196                       q, SLOT( _k_enterUrl( const KUrl& )) );
01197 
01198     // need to set the current url of the urlbar manually (not via urlEntered()
01199     // here, because the initial url of KDirOperator might be the same as the
01200     // one that will be set later (and then urlEntered() won't be emitted).
01201     // ### REMOVE THIS when KDirOperator's initial URL (in the c'tor) is gone.
01202     placesView->setUrl( url );
01203 
01204     placesViewSplitter->insertWidget( 0, placesView );
01205 }
01206 
01207 void KFileWidgetPrivate::initGUI()
01208 {
01209     delete boxLayout; // deletes all sub layouts
01210 
01211     boxLayout = new QVBoxLayout( q);
01212     boxLayout->setMargin(0); // no additional margin to the already existing
01213     boxLayout->setSpacing(0);
01214     boxLayout->addWidget(toolbar, 0, Qt::AlignTop);
01215 
01216     placesViewSplitter = new QSplitter(q);
01217     placesViewSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
01218     placesViewSplitter->setChildrenCollapsible(false);
01219     boxLayout->addWidget(placesViewSplitter);
01220 
01221     QObject::connect(placesViewSplitter, SIGNAL(splitterMoved(int,int)), q, SLOT(_k_placesViewSplitterMoved()));
01222 
01223     vbox = new QVBoxLayout();
01224     vbox->setMargin(0);
01225     QWidget *vboxWidget = new QWidget();
01226     vboxWidget->setLayout(vbox);
01227     placesViewSplitter->insertWidget(0, vboxWidget);
01228 
01229     vbox->addWidget(ops, 4);
01230     vbox->addSpacing(KDialog::spacingHint());
01231 
01232     lafBox = new QGridLayout();
01233 
01234     lafBox->setSpacing(KDialog::spacingHint());
01235     lafBox->addWidget(locationLabel, 0, 0, Qt::AlignVCenter);
01236     lafBox->addWidget(locationEdit, 0, 1, Qt::AlignVCenter);
01237     lafBox->addWidget(okButton, 0, 2, Qt::AlignVCenter);
01238 
01239     lafBox->addWidget(filterLabel, 1, 0, Qt::AlignVCenter);
01240     lafBox->addWidget(filterWidget, 1, 1, Qt::AlignVCenter);
01241     lafBox->addWidget(cancelButton, 1, 2, Qt::AlignVCenter);
01242 
01243     lafBox->setColumnStretch(1, 4);
01244 
01245     vbox->addLayout(lafBox);
01246 
01247     // add the Automatically Select Extension checkbox
01248     vbox->addWidget(autoSelectExtCheckBox);
01249 
01250     q->setTabOrder(ops, autoSelectExtCheckBox);
01251     q->setTabOrder(autoSelectExtCheckBox, locationEdit);
01252     q->setTabOrder(locationEdit, filterWidget);
01253     q->setTabOrder(filterWidget, okButton);
01254     q->setTabOrder(okButton, cancelButton);
01255     q->setTabOrder(cancelButton, urlNavigator);
01256     q->setTabOrder(urlNavigator, ops);
01257     q->setTabOrder(cancelButton, urlNavigator);
01258     q->setTabOrder(urlNavigator, ops);
01259 }
01260 
01261 void KFileWidgetPrivate::_k_slotFilterChanged()
01262 {
01263     QString filter = filterWidget->currentFilter();
01264     ops->clearFilter();
01265 
01266     if ( filter.indexOf( '/' ) > -1 ) {
01267         QStringList types = filter.split(" ",QString::SkipEmptyParts); //QStringList::split( " ", filter );
01268         types.prepend( "inode/directory" );
01269         ops->setMimeFilter( types );
01270     }
01271     else
01272         ops->setNameFilter( filter );
01273 
01274     ops->updateDir();
01275 
01276     updateAutoSelectExtension();
01277 
01278     emit q->filterChanged( filter );
01279 }
01280 
01281 
01282 void KFileWidget::setUrl(const KUrl& url, bool clearforward)
01283 {
01284     d->selection.clear();
01285     d->ops->setUrl( url, clearforward);
01286 }
01287 
01288 // Protected
01289 void KFileWidgetPrivate::_k_urlEntered(const KUrl& url)
01290 {
01291     QString filename = locationEditCurrentText();
01292     selection.clear();
01293 
01294     KUrlComboBox* pathCombo = urlNavigator->editor();
01295     if ( pathCombo->count() != 0 ) { // little hack
01296         pathCombo->setUrl( url );
01297     }
01298 
01299     bool blocked = locationEdit->blockSignals( true );
01300     if ( keepLocation ) {
01301         locationEdit->changeUrl( 0, KIcon( KMimeType::iconNameForUrl( filename ) ), filename );
01302         locationEdit->lineEdit()->setModified( true );
01303     }
01304 
01305     locationEdit->blockSignals( blocked );
01306 
01307     urlNavigator->setUrl(  url );
01308 
01309     QString dir = url.url(KUrl::AddTrailingSlash);
01310     // is trigged in ctor before completion object is set
01311     KUrlCompletion *completion = dynamic_cast<KUrlCompletion*>( locationEdit->completionObject() );
01312     if( completion )
01313         completion->setDir( dir );
01314 
01315     if ( placesView )
01316         placesView->setUrl( url );
01317 }
01318 
01319 void KFileWidgetPrivate::_k_locationAccepted( const QString& url )
01320 {
01321     ops->setCurrentItem( url );
01322     q->slotOk();
01323 }
01324 
01325 void KFileWidgetPrivate::_k_locationActivated( const QString& url )
01326 {
01327     // the location has been activated by selecting an entry
01328     // from the combo box of the location bar
01329     q->setUrl( url );
01330 }
01331 
01332 void KFileWidgetPrivate::_k_enterUrl( const KUrl& url )
01333 {
01334     KUrl fixedUrl( url );
01335     // append '/' if needed: url combo does not add it
01336     // tokenize() expects it because uses KUrl::setFileName()
01337     fixedUrl.adjustPath( KUrl::AddTrailingSlash );
01338     q->setUrl( fixedUrl );
01339 }
01340 
01341 void KFileWidgetPrivate::_k_enterUrl( const QString& url )
01342 {
01343     _k_enterUrl( KUrl( KUrlCompletion::replacedPath( url, true, true )) );
01344 }
01345 
01346 
01347 void KFileWidget::setSelection(const QString& url)
01348 {
01349     kDebug(kfile_area) << "setSelection " << url;
01350 
01351     if (url.isEmpty()) {
01352         d->selection.clear();
01353         return;
01354     }
01355 
01356     KUrl u = d->getCompleteUrl(url);
01357     if (!u.isValid()) { // if it still is
01358         kWarning() << url << " is not a correct argument for setSelection!";
01359         return;
01360     }
01361 
01362     // Honor protocols that do not support directory listing
01363     if (!KProtocolManager::supportsListing(u))
01364         return;
01365 
01366     /* we strip the first / from the path to avoid file://usr which means
01367      *  / on host usr
01368      */
01369     KIO::UDSEntry entry;
01370     bool res = KIO::NetAccess::stat(u, entry, this);
01371     KFileItem i(entry, u);
01372     //    KFileItem i(u.path());
01373     kDebug(kfile_area) << "KFileItem " << u.path() << " " << i.isDir() << " " << u.isLocalFile() << " " << QFile::exists( u.path() );
01374     if ( res && i.isDir() && u.isLocalFile() && QFile::exists( u.path() ) ) {
01375         // trust isDir() only if the file is
01376         // local (we cannot stat non-local urls) and if it exists!
01377         // (as KFileItem does not check if the file exists or not
01378         // -> the statbuffer is undefined -> isDir() is unreliable) (Simon)
01379         setUrl(u, true);
01380     }
01381     else if ( res ) {
01382         QString filename = u.url();
01383         int sep = filename.lastIndexOf('/');
01384         if (sep >= 0) { // there is a / in it
01385             KUrl dir(u);
01386             dir.setQuery( QString() );
01387             dir.setFileName( QString() );
01388             setUrl(dir, true );
01389 
01390             // filename must be decoded, or "name with space" would become
01391             // "name%20with%20space", so we use KUrl::fileName()
01392             filename = u.fileName();
01393             kDebug(kfile_area) << "filename " << filename;
01394             d->selection = filename;
01395             d->setLocationText( u );
01396 
01397             // tell the line edit that it has been edited
01398             // otherwise we won't know this was set by the user
01399             // and it will be ignored if there has been an
01400             // auto completion. this caused bugs where automcompletion
01401             // would start, the user would pick something from the
01402             // history and then hit Ok only to get the autocompleted
01403             // selection. OOD->OPS.
01404             d->locationEdit->lineEdit()->setModified( true );
01405         }
01406 
01407         d->url = d->ops->url();
01408         d->url.addPath(filename);
01409     }
01410     else {
01411         d->setLocationText(url);
01412         d->locationEdit->lineEdit()->setModified( true );
01413     }
01414 }
01415 
01416 void KFileWidgetPrivate::_k_slotLoadingFinished()
01417 {
01418     if ( !selection.isEmpty() )
01419         ops->setCurrentItem( selection );
01420 }
01421 
01422 void KFileWidgetPrivate::_k_fileCompletion( const QString& match )
01423 {
01424     if ( match.isEmpty() && ops->view() ) {
01425         ops->view()->clearSelection();
01426     } else {
01427         ops->setCurrentItem( match );
01428         setDummyHistoryEntry( locationEdit->currentText(), KIconLoader::global()->loadMimeTypeIcon( KMimeType::iconNameForUrl( match ), KIconLoader::Small ), !locationEdit->currentText().isEmpty() );
01429         locationEdit->setCompletedText( match );
01430     }
01431 }
01432 
01433 void KFileWidgetPrivate::_k_slotLocationChanged( const QString& text )
01434 {
01435     locationEdit->lineEdit()->setModified( true );
01436 
01437     if ( text.isEmpty() && ops->view() )
01438         ops->view()->clearSelection();
01439 
01440     if ( text.isEmpty() ) {
01441         removeDummyHistoryEntry();
01442     } else {
01443         setDummyHistoryEntry( text );
01444     }
01445 
01446     ops->setCurrentItem( text );
01447 
01448     updateFilter();
01449 }
01450 
01451 KUrl KFileWidget::selectedUrl() const
01452 {
01453     if ( d->inAccept )
01454         return d->url;
01455     else
01456         return KUrl();
01457 }
01458 
01459 KUrl::List KFileWidget::selectedUrls() const
01460 {
01461     KUrl::List list;
01462     if ( d->inAccept ) {
01463         if ( (d->ops->mode() & KFile::Files) == KFile::Files )
01464             list = d->parseSelectedUrls();
01465         else
01466             list.append( d->url );
01467     }
01468     return list;
01469 }
01470 
01471 
01472 KUrl::List& KFileWidgetPrivate::parseSelectedUrls()
01473 {
01474     if ( filenames.isEmpty() ) {
01475         return urlList;
01476     }
01477 
01478     urlList.clear();
01479     if ( filenames.contains( '/' )) { // assume _one_ absolute filename
01480         KUrl u;
01481         if ( containsProtocolSection( filenames ) )
01482             u = filenames;
01483         else
01484             u.setPath( filenames );
01485 
01486         if ( u.isValid() )
01487             urlList.append( u );
01488         else
01489             KMessageBox::error( q,
01490                                 i18n("The chosen filenames do not\n"
01491                                      "appear to be valid."),
01492                                 i18n("Invalid Filenames") );
01493     }
01494 
01495     else
01496         urlList = tokenize( filenames );
01497 
01498     filenames.clear(); // indicate that we parsed that one
01499 
01500     return urlList;
01501 }
01502 
01503 
01504 // FIXME: current implementation drawback: a filename can't contain quotes
01505 KUrl::List KFileWidgetPrivate::tokenize( const QString& line ) const
01506 {
01507     KUrl::List urls;
01508     KUrl u( ops->url() );
01509     QString name;
01510 
01511     const int count = line.count( QLatin1Char( '"' ) );
01512     if ( count == 0 ) { // no " " -> assume one single file
01513         u.setFileName( line );
01514         if ( u.isValid() )
01515             urls.append( u );
01516 
01517         return urls;
01518     }
01519 
01520     if ( (count % 2) == 1 ) { // odd number of " -> error
01521         KMessageBox::sorry(q, i18n("The requested filenames\n"
01522                                    "%1\n"
01523                                    "do not appear to be valid;\n"
01524                                    "make sure every filename is enclosed in double quotes.", line),
01525                            i18n("Filename Error"));
01526         return urls;
01527     }
01528 
01529     int start = 0;
01530     int index1 = -1, index2 = -1;
01531     while ( true ) {
01532         index1 = line.indexOf( '"', start );
01533         index2 = line.indexOf( '"', index1 + 1 );
01534 
01535         if ( index1 < 0 )
01536             break;
01537 
01538         // get everything between the " "
01539         name = line.mid( index1 + 1, index2 - index1 - 1 );
01540         u.setFileName( name );
01541         if ( u.isValid() )
01542             urls.append( u );
01543 
01544         start = index2 + 1;
01545     }
01546     return urls;
01547 }
01548 
01549 
01550 QString KFileWidget::selectedFile() const
01551 {
01552     if ( d->inAccept ) {
01553         const KUrl url = KIO::NetAccess::mostLocalUrl(d->url,topLevelWidget());
01554         if (url.isLocalFile())
01555             return url.path();
01556         else {
01557             KMessageBox::sorry( const_cast<KFileWidget*>(this),
01558                                 i18n("You can only select local files."),
01559                                 i18n("Remote Files Not Accepted") );
01560         }
01561     }
01562     return QString();
01563 }
01564 
01565 QStringList KFileWidget::selectedFiles() const
01566 {
01567     QStringList list;
01568 
01569     if ( d->inAccept ) {
01570         if ( (d->ops->mode() & KFile::Files) == KFile::Files ) {
01571             KUrl::List urls = d->parseSelectedUrls();
01572             QList<KUrl>::const_iterator it = urls.begin();
01573             while ( it != urls.end() ) {
01574                 KUrl url = KIO::NetAccess::mostLocalUrl(*it,topLevelWidget());
01575                 if ( url.isLocalFile() )
01576                     list.append( url.path() );
01577                 ++it;
01578             }
01579         }
01580 
01581         else { // single-selection mode
01582             if ( d->url.isLocalFile() )
01583                 list.append( d->url.path() );
01584         }
01585     }
01586 
01587     return list;
01588 }
01589 
01590 KUrl KFileWidget::baseUrl() const
01591 {
01592     return d->ops->url();
01593 }
01594 
01595 void KFileWidget::resizeEvent(QResizeEvent* event)
01596 {
01597     d->updateSplitterSize();
01598 
01599     QWidget::resizeEvent(event);
01600 }
01601 
01602 void KFileWidget::showEvent(QShowEvent* event)
01603 {
01604     if ( !d->hasView ) { // delayed view-creation
01605         d->ops->setView(KFile::Default);
01606         d->ops->view()->setSizePolicy( QSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ) );
01607         d->hasView = true;
01608     }
01609     d->ops->clearHistory();
01610 
01611     QWidget::showEvent(event);
01612 }
01613 
01614 void KFileWidget::setMode( KFile::Modes m )
01615 {
01616     d->ops->setMode(m);
01617     if ( d->ops->dirOnlyMode() ) {
01618         d->filterWidget->setDefaultFilter( i18n("*|All Folders") );
01619     }
01620     else {
01621         d->filterWidget->setDefaultFilter( i18n("*|All Files") );
01622     }
01623 
01624     d->updateAutoSelectExtension();
01625 }
01626 
01627 KFile::Modes KFileWidget::mode() const
01628 {
01629     return d->ops->mode();
01630 }
01631 
01632 
01633 void KFileWidgetPrivate::readConfig( const KConfigGroup &configGroup)
01634 {
01635     ops->readConfig(configGroup);
01636 
01637     KUrlComboBox *combo = urlNavigator->editor();
01638     combo->setUrls( configGroup.readPathEntry( RecentURLs, QStringList() ), KUrlComboBox::RemoveTop );
01639     combo->setMaxItems( configGroup.readEntry( RecentURLsNumber,
01640                                        DefaultRecentURLsNumber ) );
01641     combo->setUrl( ops->url() );
01642     autoDirectoryFollowing = configGroup.readEntry( AutoDirectoryFollowing,
01643                                             DefaultDirectoryFollowing );
01644 
01645     KGlobalSettings::Completion cm = (KGlobalSettings::Completion)
01646                                       configGroup.readEntry( PathComboCompletionMode,
01647                                       static_cast<int>( KGlobalSettings::completionMode() ) );
01648     if ( cm != KGlobalSettings::completionMode() )
01649         combo->setCompletionMode( cm );
01650 
01651     cm = (KGlobalSettings::Completion)
01652          configGroup.readEntry( LocationComboCompletionMode,
01653                         static_cast<int>( KGlobalSettings::completionMode() ) );
01654     if ( cm != KGlobalSettings::completionMode() )
01655         locationEdit->setCompletionMode( cm );
01656 
01657     // show or don't show the speedbar
01658     _k_toggleSpeedbar( configGroup.readEntry( ShowSpeedbar, true ) );
01659 
01660     // show or don't show the bookmarks
01661     _k_toggleBookmarks( configGroup.readEntry(ShowBookmarks, false) );
01662 
01663     // does the user want Automatically Select Extension?
01664     autoSelectExtChecked = configGroup.readEntry (AutoSelectExtChecked, DefaultAutoSelectExtChecked);
01665     updateAutoSelectExtension();
01666 
01667     // should the URL navigator use the breadcrumb navigation?
01668     urlNavigator->setUrlEditable( !configGroup.readEntry(BreadcrumbNavigation, true) );
01669 
01670     int w1 = q->minimumSize().width();
01671     int w2 = toolbar->sizeHint().width();
01672     if (w1 < w2)
01673         q->setMinimumWidth(w2);
01674 }
01675 
01676 void KFileWidgetPrivate::writeConfig(KConfigGroup &configGroup)
01677 {
01678     KUrlComboBox *pathCombo = urlNavigator->editor();
01679     configGroup.writePathEntry( RecentURLs, pathCombo->urls() );
01680     //saveDialogSize( configGroup, KConfigGroup::Persistent | KConfigGroup::Global );
01681     configGroup.writeEntry( PathComboCompletionMode, static_cast<int>(pathCombo->completionMode()) );
01682     configGroup.writeEntry( LocationComboCompletionMode, static_cast<int>(locationEdit->completionMode()) );
01683 
01684     const bool showSpeedbar = placesView && !placesView->isHidden();
01685     configGroup.writeEntry( ShowSpeedbar, showSpeedbar );
01686     if (showSpeedbar) {
01687         const QList<int> sizes = placesViewSplitter->sizes();
01688         Q_ASSERT( sizes.count() > 0 );
01689         configGroup.writeEntry( SpeedbarWidth, sizes[0] );
01690     }
01691 
01692     configGroup.writeEntry( ShowBookmarks, bookmarkHandler != 0 );
01693     configGroup.writeEntry( AutoSelectExtChecked, autoSelectExtChecked );
01694     configGroup.writeEntry( BreadcrumbNavigation, !urlNavigator->isUrlEditable() );
01695 
01696     ops->writeConfig(configGroup);
01697 }
01698 
01699 
01700 void KFileWidgetPrivate::readRecentFiles( KConfig *kc )
01701 {
01702     KConfigGroup cg( kc, ConfigGroup );
01703 
01704     locationEdit->setMaxItems( cg.readEntry( RecentFilesNumber,
01705                                              DefaultRecentURLsNumber ) );
01706     locationEdit->setUrls( cg.readPathEntry( RecentFiles, QStringList() ),
01707                            KUrlComboBox::RemoveBottom );
01708     locationEdit->setCurrentIndex( -1 );
01709 }
01710 
01711 void KFileWidgetPrivate::saveRecentFiles( KConfig *kc )
01712 {
01713     KConfigGroup cg(kc, ConfigGroup );
01714     cg.writePathEntry( RecentFiles, locationEdit->urls() );
01715 }
01716 
01717 KPushButton * KFileWidget::okButton() const
01718 {
01719     return d->okButton;
01720 }
01721 
01722 KPushButton * KFileWidget::cancelButton() const
01723 {
01724     return d->cancelButton;
01725 }
01726 
01727 // Called by KFileDialog
01728 void KFileWidget::slotCancel()
01729 {
01730     d->ops->close();
01731 
01732     KSharedConfig::Ptr config = KGlobal::config();
01733     config->setForceGlobal( true );
01734     KConfigGroup grp(config,ConfigGroup);
01735     d->writeConfig(grp);
01736     config->setForceGlobal( false );
01737 }
01738 
01739 void KFileWidget::setKeepLocation( bool keep )
01740 {
01741     d->keepLocation = keep;
01742 }
01743 
01744 bool KFileWidget::keepsLocation() const
01745 {
01746     return d->keepLocation;
01747 }
01748 
01749 void KFileWidget::setOperationMode( OperationMode mode )
01750 {
01751     d->operationMode = mode;
01752     d->keepLocation = (mode == Saving);
01753     d->filterWidget->setEditable( !d->hasDefaultFilter || mode != Saving );
01754     if ( mode == Opening )
01755        // don't use KStandardGuiItem::open() here which has trailing ellipsis!
01756        d->okButton->setGuiItem( KGuiItem( i18n( "&Open" ), "document-open") );
01757     else if ( mode == Saving ) {
01758        d->okButton->setGuiItem( KStandardGuiItem::save() );
01759        d->setNonExtSelection();
01760     }
01761     else
01762        d->okButton->setGuiItem( KStandardGuiItem::ok() );
01763     d->updateLocationWhatsThis();
01764     d->updateAutoSelectExtension();
01765 }
01766 
01767 KFileWidget::OperationMode KFileWidget::operationMode() const
01768 {
01769     return d->operationMode;
01770 }
01771 
01772 void KFileWidgetPrivate::_k_slotAutoSelectExtClicked()
01773 {
01774     kDebug (kfile_area) << "slotAutoSelectExtClicked(): "
01775                          << autoSelectExtCheckBox->isChecked() << endl;
01776 
01777     // whether the _user_ wants it on/off
01778     autoSelectExtChecked = autoSelectExtCheckBox->isChecked();
01779 
01780     // update the current filename's extension
01781     updateLocationEditExtension (extension /* extension hasn't changed */);
01782 }
01783 
01784 void KFileWidgetPrivate::_k_placesViewSplitterMoved()
01785 {
01786     const QList<int> sizes = placesViewSplitter->sizes();
01787     speedBarWidth = sizes[0];
01788 }
01789 
01790 static QString getExtensionFromPatternList(const QStringList &patternList)
01791 {
01792     QString ret;
01793     kDebug (kfile_area) << "\tgetExtension " << patternList;
01794 
01795     QStringList::ConstIterator patternListEnd = patternList.end();
01796     for (QStringList::ConstIterator it = patternList.begin();
01797          it != patternListEnd;
01798          ++it)
01799     {
01800         kDebug (kfile_area) << "\t\ttry: \'" << (*it) << "\'";
01801 
01802         // is this pattern like "*.BMP" rather than useless things like:
01803         //
01804         // README
01805         // *.
01806         // *.*
01807         // *.JP*G
01808         // *.JP?
01809         if ((*it).startsWith ("*.") &&
01810             (*it).length() > 2 &&
01811             (*it).indexOf('*', 2) < 0 && (*it).indexOf ('?', 2) < 0)
01812         {
01813             ret = (*it).mid (1);
01814             break;
01815         }
01816     }
01817 
01818     return ret;
01819 }
01820 
01821 static QString stripUndisplayable (const QString &string)
01822 {
01823     QString ret = string;
01824 
01825     ret.remove (':');
01826     ret.remove ('&');
01827 
01828     return ret;
01829 }
01830 
01831 
01832 //QString KFileWidget::currentFilterExtension()
01833 //{
01834 //    return d->extension;
01835 //}
01836 
01837 void KFileWidgetPrivate::updateAutoSelectExtension()
01838 {
01839     if (!autoSelectExtCheckBox) return;
01840 
01841     //
01842     // Figure out an extension for the Automatically Select Extension thing
01843     // (some Windows users apparently don't know what to do when confronted
01844     // with a text file called "COPYING" but do know what to do with
01845     // COPYING.txt ...)
01846     //
01847 
01848     kDebug (kfile_area) << "Figure out an extension: ";
01849     QString lastExtension = extension;
01850     extension.clear();
01851 
01852     // Automatically Select Extension is only valid if the user is _saving_ a _file_
01853     if ((operationMode == KFileWidget::Saving) && (ops->mode() & KFile::File))
01854     {
01855         //
01856         // Get an extension from the filter
01857         //
01858 
01859         QString filter = filterWidget->currentFilter();
01860         if (!filter.isEmpty())
01861         {
01862             // e.g. "*.cpp"
01863             if (filter.indexOf ('/') < 0)
01864             {
01865                 extension = getExtensionFromPatternList (filter.split(" ",QString::SkipEmptyParts)/*QStringList::split (" ", filter)*/).toLower();
01866                 kDebug (kfile_area) << "\tsetFilter-style: pattern ext=\'"
01867                                     << extension << "\'" << endl;
01868             }
01869             // e.g. "text/html"
01870             else
01871             {
01872                 KMimeType::Ptr mime = KMimeType::mimeType (filter);
01873 
01874                 if (mime)
01875                 {
01876                     // first try X-KDE-NativeExtension
01877                     QString nativeExtension = mime->property ("X-KDE-NativeExtension").toString();
01878                     if (!nativeExtension.isEmpty() && nativeExtension.at (0) == '.')
01879                     {
01880                         extension = nativeExtension.toLower();
01881                         kDebug (kfile_area) << "\tsetMimeFilter-style: native ext=\'"
01882                                             << extension << "\'" << endl;
01883                     }
01884 
01885                     // no X-KDE-NativeExtension
01886                     if (extension.isEmpty())
01887                     {
01888                         extension = getExtensionFromPatternList (mime->patterns()).toLower();
01889                         kDebug (kfile_area) << "\tsetMimeFilter-style: pattern ext=\'"
01890                                             << extension << "\'" << endl;
01891                     }
01892                 }
01893             }
01894         }
01895 
01896 
01897         //
01898         // GUI: checkbox
01899         //
01900 
01901         QString whatsThisExtension;
01902         if (!extension.isEmpty())
01903         {
01904             // remember: sync any changes to the string with below
01905             autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension (%1)",  extension));
01906             whatsThisExtension = i18n ("the extension <b>%1</b>",  extension);
01907 
01908             autoSelectExtCheckBox->setEnabled (true);
01909             autoSelectExtCheckBox->setChecked (autoSelectExtChecked);
01910         }
01911         else
01912         {
01913             // remember: sync any changes to the string with above
01914             autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension"));
01915             whatsThisExtension = i18n ("a suitable extension");
01916 
01917             autoSelectExtCheckBox->setChecked (false);
01918             autoSelectExtCheckBox->setEnabled (false);
01919         }
01920 
01921         const QString locationLabelText = stripUndisplayable (locationLabel->text());
01922         const QString filterLabelText = stripUndisplayable (filterLabel->text());
01923         autoSelectExtCheckBox->setWhatsThis(            "<qt>" +
01924                 i18n (
01925                   "This option enables some convenient features for "
01926                   "saving files with extensions:<br />"
01927                   "<ol>"
01928                     "<li>Any extension specified in the <b>%1</b> text "
01929                     "area will be updated if you change the file type "
01930                     "to save in.<br />"
01931                     "<br /></li>"
01932                     "<li>If no extension is specified in the <b>%2</b> "
01933                     "text area when you click "
01934                     "<b>Save</b>, %3 will be added to the end of the "
01935                     "filename (if the filename does not already exist). "
01936                     "This extension is based on the file type that you "
01937                     "have chosen to save in.<br />"
01938                     "<br />"
01939                     "If you do not want KDE to supply an extension for the "
01940                     "filename, you can either turn this option off or you "
01941                     "can suppress it by adding a period (.) to the end of "
01942                     "the filename (the period will be automatically "
01943                     "removed)."
01944                     "</li>"
01945                   "</ol>"
01946                   "If unsure, keep this option enabled as it makes your "
01947                   "files more manageable."
01948                     ,
01949                   locationLabelText,
01950                   locationLabelText,
01951                   whatsThisExtension)
01952             + "</qt>"
01953             );
01954 
01955         autoSelectExtCheckBox->show();
01956 
01957 
01958         // update the current filename's extension
01959         updateLocationEditExtension (lastExtension);
01960     }
01961     // Automatically Select Extension not valid
01962     else
01963     {
01964         autoSelectExtCheckBox->setChecked (false);
01965         autoSelectExtCheckBox->hide();
01966     }
01967 }
01968 
01969 // Updates the extension of the filename specified in d->locationEdit if the
01970 // Automatically Select Extension feature is enabled.
01971 // (this prevents you from accidently saving "file.kwd" as RTF, for example)
01972 void KFileWidgetPrivate::updateLocationEditExtension (const QString &lastExtension)
01973 {
01974     if (!autoSelectExtCheckBox->isChecked() || extension.isEmpty())
01975         return;
01976 
01977     QString urlStr = locationEditCurrentText();
01978     if (urlStr.isEmpty())
01979         return;
01980 
01981     KUrl url = getCompleteUrl(urlStr);
01982     kDebug (kfile_area) << "updateLocationEditExtension (" << url << ")";
01983 
01984     const int fileNameOffset = urlStr.lastIndexOf ('/') + 1;
01985     QString fileName = urlStr.mid (fileNameOffset);
01986 
01987     const int dot = fileName.lastIndexOf ('.');
01988     const int len = fileName.length();
01989     if (dot > 0 && // has an extension already and it's not a hidden file
01990                    // like ".hidden" (but we do accept ".hidden.ext")
01991         dot != len - 1 // and not deliberately suppressing extension
01992         )
01993     {
01994         // exists?
01995         KIO::UDSEntry t;
01996         if (KIO::NetAccess::stat (url, t, q->topLevelWidget()))
01997         {
01998             kDebug (kfile_area) << "\tfile exists";
01999 
02000             if (t.isDir())
02001             {
02002                 kDebug (kfile_area) << "\tisDir - won't alter extension";
02003                 return;
02004             }
02005 
02006             // --- fall through ---
02007         }
02008 
02009 
02010         //
02011         // try to get rid of the current extension
02012         //
02013 
02014         // catch "double extensions" like ".tar.gz"
02015         if (lastExtension.length() && fileName.endsWith (lastExtension))
02016             fileName.truncate (len - lastExtension.length());
02017         else if (extension.length() && fileName.endsWith (extension))
02018             fileName.truncate (len - extension.length());
02019         // can only handle "single extensions"
02020         else
02021             fileName.truncate (dot);
02022 
02023         // add extension
02024         const QString newText = urlStr.left (fileNameOffset) + fileName + extension;
02025         if ( newText != locationEditCurrentText() )
02026         {
02027             locationEdit->setItemText(locationEdit->currentIndex(),urlStr.left (fileNameOffset) + fileName + extension);
02028             locationEdit->lineEdit()->setModified (true);
02029         }
02030     }
02031 }
02032 
02033 // Updates the filter if the extension of the filename specified in d->locationEdit is changed
02034 // (this prevents you from accidently saving "file.kwd" as RTF, for example)
02035 void KFileWidgetPrivate::updateFilter()
02036 {
02037     if ((operationMode == KFileWidget::Saving) && (ops->mode() & KFile::File) ) {
02038         const QString urlStr = locationEditCurrentText();
02039         if (urlStr.isEmpty())
02040             return;
02041 
02042         KMimeType::Ptr mime = KMimeType::findByPath(urlStr, 0, true);
02043         if (mime && mime->name() != KMimeType::defaultMimeType()) {
02044             if (filterWidget->currentFilter() != mime->name() &&
02045                 filterWidget->filters().indexOf(mime->name()) != -1)
02046                 filterWidget->setCurrentFilter(mime->name());
02047         }
02048     }
02049 }
02050 
02051 // Updates the splitter size. This is necessary since we call to this method when the widget is
02052 // shown as well as when it is resized. This is also very important, because this will be
02053 // contained in other widget, which can try to resize this one, and make the places view be
02054 // wider than what the user wanted.
02055 void KFileWidgetPrivate::updateSplitterSize()
02056 {
02057     QList<int> sizes = placesViewSplitter->sizes();
02058     if (sizes.count() == 2) {
02059         // restore width of speedbar
02060         KConfigGroup configGroup( KGlobal::config(), ConfigGroup );
02061         const int speedbarWidth = speedBarWidth == -1 ? configGroup.readEntry( SpeedbarWidth, placesView->sizeHintForColumn(0) )
02062                                                       : speedBarWidth;
02063         const int availableWidth = q->width();
02064         sizes[0] = speedbarWidth + 1; // without this pixel, our places view is reduced 1 pixel each time is shown.
02065         sizes[1] = availableWidth - speedbarWidth - 1;
02066         placesViewSplitter->setSizes( sizes );
02067     }
02068 }
02069 
02070 // applies only to a file that doesn't already exist
02071 void KFileWidgetPrivate::appendExtension (KUrl &url)
02072 {
02073     if (!autoSelectExtCheckBox->isChecked() || extension.isEmpty())
02074         return;
02075 
02076     QString fileName = url.fileName();
02077     if (fileName.isEmpty())
02078         return;
02079 
02080     kDebug (kfile_area) << "appendExtension(" << url << ")";
02081 
02082     const int len = fileName.length();
02083     const int dot = fileName.lastIndexOf ('.');
02084 
02085     const bool suppressExtension = (dot == len - 1);
02086     const bool unspecifiedExtension = (dot <= 0);
02087 
02088     // don't KIO::NetAccess::Stat if unnecessary
02089     if (!(suppressExtension || unspecifiedExtension))
02090         return;
02091 
02092     // exists?
02093     KIO::UDSEntry t;
02094     if (KIO::NetAccess::stat (url, t, q->topLevelWidget()))
02095     {
02096         kDebug (kfile_area) << "\tfile exists - won't append extension";
02097         return;
02098     }
02099 
02100     // suppress automatically append extension?
02101     if (suppressExtension)
02102     {
02103         //
02104         // Strip trailing dot
02105         // This allows lazy people to have autoSelectExtCheckBox->isChecked
02106         // but don't want a file extension to be appended
02107         // e.g. "README." will make a file called "README"
02108         //
02109         // If you really want a name like "README.", then type "README.."
02110         // and the trailing dot will be removed (or just stop being lazy and
02111         // turn off this feature so that you can type "README.")
02112         //
02113         kDebug (kfile_area) << "\tstrip trailing dot";
02114         url.setFileName (fileName.left (len - 1));
02115     }
02116     // evilmatically append extension :) if the user hasn't specified one
02117     else if (unspecifiedExtension)
02118     {
02119         kDebug (kfile_area) << "\tappending extension \'" << extension << "\'...";
02120         url.setFileName (fileName + extension);
02121         kDebug (kfile_area) << "\tsaving as \'" << url << "\'";
02122     }
02123 }
02124 
02125 
02126 // adds the selected files/urls to 'recent documents'
02127 void KFileWidgetPrivate::addToRecentDocuments()
02128 {
02129     int m = ops->mode();
02130     int atmost = KRecentDocument::maximumItems();
02131     //don't add more than we need. KRecentDocument::add() is pretty slow
02132 
02133     if ( m & KFile::LocalOnly ) {
02134         const QStringList files = q->selectedFiles();
02135         QStringList::ConstIterator it = files.begin();
02136         for ( ; it != files.end() && atmost > 0; ++it ) {
02137             KRecentDocument::add( *it );
02138             atmost--;
02139         }
02140     }
02141 
02142     else { // urls
02143         KUrl::List urls = q->selectedUrls();
02144         KUrl::List::ConstIterator it = urls.begin();
02145         for ( ; it != urls.end() && atmost > 0; ++it ) {
02146             if ( (*it).isValid() ) {
02147                 KRecentDocument::add( *it );
02148                 atmost--;
02149             }
02150         }
02151     }
02152 }
02153 
02154 KUrlComboBox* KFileWidget::locationEdit() const
02155 {
02156     return d->locationEdit;
02157 }
02158 
02159 KFileFilterCombo* KFileWidget::filterWidget() const
02160 {
02161     return d->filterWidget;
02162 }
02163 
02164 KActionCollection * KFileWidget::actionCollection() const
02165 {
02166     return d->ops->actionCollection();
02167 }
02168 
02169 void KFileWidgetPrivate::_k_toggleSpeedbar( bool show )
02170 {
02171     if ( show )
02172     {
02173         if ( !placesView )
02174             initSpeedbar();
02175 
02176         placesView->show();
02177 
02178         // check to see if they have a home item defined, if not show the home button
02179         KUrl homeURL;
02180         homeURL.setPath( QDir::homePath() );
02181         KFilePlacesModel *model = static_cast<KFilePlacesModel*>(placesView->model());
02182         for ( int rowIndex = 0 ; rowIndex < placesView->model()->rowCount() ; rowIndex++ )
02183         {
02184             QModelIndex index = model->index(rowIndex, 0);
02185             KUrl url = model->url(index);
02186 
02187             if ( homeURL.equals( url, KUrl::CompareWithoutTrailingSlash ) ) {
02188                 toolbar->removeAction( ops->actionCollection()->action( "home" ) );
02189                 break;
02190             }
02191         }
02192     }
02193     else
02194     {
02195         if (placesView)
02196             placesView->hide();
02197 
02198         QAction* homeAction = ops->actionCollection()->action( "home" );
02199         QAction* reloadAction = ops->actionCollection()->action( "reload" );
02200         if ( !toolbar->actions().contains(homeAction) )
02201             toolbar->insertAction( reloadAction, homeAction );
02202     }
02203 
02204     static_cast<KToggleAction *>(q->actionCollection()->action("toggleSpeedbar"))->setChecked( show );
02205 }
02206 
02207 void KFileWidgetPrivate::_k_toggleBookmarks(bool show)
02208 {
02209     if (show)
02210     {
02211         if (bookmarkHandler)
02212         {
02213             return;
02214         }
02215 
02216         bookmarkHandler = new KFileBookmarkHandler( q );
02217         q->connect( bookmarkHandler, SIGNAL( openUrl( const QString& )),
02218                     SLOT( _k_enterUrl( const QString& )));
02219 
02220         bookmarkButton = new KActionMenu(KIcon("bookmarks"),i18n("Bookmarks"), q);
02221         bookmarkButton->setDelayed(false);
02222         q->actionCollection()->addAction("bookmark", bookmarkButton);
02223         bookmarkButton->setMenu(bookmarkHandler->menu());
02224         bookmarkButton->setWhatsThis(i18n("<qt>This button allows you to bookmark specific locations. "
02225                                 "Click on this button to open the bookmark menu where you may add, "
02226                                 "edit or select a bookmark.<br /><br />"
02227                                 "These bookmarks are specific to the file dialog, but otherwise operate "
02228                                 "like bookmarks elsewhere in KDE.</qt>"));
02229         toolbar->addAction(bookmarkButton);
02230     }
02231     else if (bookmarkHandler)
02232     {
02233         delete bookmarkHandler;
02234         bookmarkHandler = 0;
02235         delete bookmarkButton;
02236         bookmarkButton = 0;
02237     }
02238 
02239     static_cast<KToggleAction *>(q->actionCollection()->action("toggleBookmarks"))->setChecked( show );
02240 }
02241 
02242 // static
02243 KUrl KFileWidget::getStartUrl( const KUrl& startDir,
02244                                QString& recentDirClass )
02245 {
02246     recentDirClass.clear();
02247     KUrl ret;
02248 
02249     bool useDefaultStartDir = startDir.isEmpty();
02250     if ( !useDefaultStartDir )
02251     {
02252         if (startDir.protocol() == "kfiledialog")
02253         {
02254             if ( startDir.query() == "?global" )
02255               recentDirClass = QString( "::%1" ).arg( startDir.path().mid( 1 ) );
02256             else
02257               recentDirClass = QString( ":%1" ).arg( startDir.path().mid( 1 ) );
02258 
02259             ret = KUrl( KRecentDirs::dir(recentDirClass) );
02260         }
02261         else
02262         {
02263             ret = startDir;
02264             // If we won't be able to list it (e.g. http), then use default
02265             if ( !KProtocolManager::supportsListing( ret ) )
02266                 useDefaultStartDir = true;
02267         }
02268     }
02269 
02270     if ( useDefaultStartDir )
02271     {
02272         if (lastDirectory->isEmpty()) {
02273             lastDirectory->setPath(KGlobalSettings::documentPath());
02274             KUrl home;
02275             home.setPath( QDir::homePath() );
02276             // if there is no docpath set (== home dir), we prefer the current
02277             // directory over it. We also prefer the homedir when our CWD is
02278             // different from our homedirectory or when the document dir
02279             // does not exist
02280             if ( lastDirectory->path(KUrl::AddTrailingSlash) == home.path(KUrl::AddTrailingSlash) ||
02281                  QDir::currentPath() != QDir::homePath() ||
02282                  !QDir(lastDirectory->path(KUrl::AddTrailingSlash)).exists() )
02283                 lastDirectory->setPath(QDir::currentPath());
02284         }
02285         ret = *lastDirectory;
02286     }
02287 
02288     return ret;
02289 }
02290 
02291 void KFileWidget::setStartDir( const KUrl& directory )
02292 {
02293     if ( directory.isValid() )
02294         *lastDirectory = directory;
02295 }
02296 
02297 void KFileWidgetPrivate::setNonExtSelection()
02298 {
02299     // Enhanced rename: Don't highlight the file extension.
02300     QString filename = locationEditCurrentText();
02301     QString extension = KMimeType::extractKnownExtension( filename );
02302 
02303     if ( !extension.isEmpty() )
02304        locationEdit->lineEdit()->setSelection( 0, filename.length() - extension.length() - 1 );
02305     else
02306     {
02307        int lastDot = filename.lastIndexOf( '.' );
02308        if ( lastDot > 0 )
02309           locationEdit->lineEdit()->setSelection( 0, lastDot );
02310     }
02311 }
02312 
02313 KToolBar * KFileWidget::toolBar() const
02314 {
02315     return d->toolbar;
02316 }
02317 
02318 void KFileWidget::setCustomWidget(QWidget* widget)
02319 {
02320     delete d->bottomCustomWidget;
02321     d->bottomCustomWidget = widget;
02322 
02323     // add it to the dialog, below the filter list box.
02324 
02325     // Change the parent so that this widget is a child of the main widget
02326     d->bottomCustomWidget->setParent( this );
02327 
02328     d->vbox->addWidget( d->bottomCustomWidget );
02329     //d->vbox->addSpacing(3); // can't do this every time...
02330 
02331     // FIXME: This should adjust the tab orders so that the custom widget
02332     // comes after the Cancel button. The code appears to do this, but the result
02333     // somehow screws up the tab order of the file path combo box. Not a major
02334     // problem, but ideally the tab order with a custom widget should be
02335     // the same as the order without one.
02336     setTabOrder(d->cancelButton, d->bottomCustomWidget);
02337     setTabOrder(d->bottomCustomWidget, d->urlNavigator);
02338 }
02339 
02340 void KFileWidget::setCustomWidget(const QString& text, QWidget* widget)
02341 {
02342     delete d->labeledCustomWidget;
02343     d->labeledCustomWidget = widget;
02344 
02345     QLabel* label = new QLabel(text, this);
02346     d->lafBox->addWidget(label, 2, 0, Qt::AlignVCenter);
02347     d->lafBox->addWidget(widget, 2, 1, Qt::AlignVCenter);
02348 }
02349 
02350 void KFileWidget::virtual_hook( int id, void* data )
02351 {
02352     Q_UNUSED(id);
02353     Q_UNUSED(data);
02354 }
02355 
02356 QString KFileWidgetPrivate::locationEditCurrentText() const
02357 {
02358     return QDir::fromNativeSeparators(locationEdit->currentText().trimmed());
02359 }
02360 
02361 #include "kfilewidget.moc"

KFile

Skip menu "KFile"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • 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