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 "kfilepreviewgenerator.h" 00035 #include <config-kfile.h> 00036 00037 #include <kactioncollection.h> 00038 #include <kdiroperator.h> 00039 #include <kdirselectdialog.h> 00040 #include <kfilefiltercombo.h> 00041 #include <kimagefilepreview.h> 00042 #include <kmenu.h> 00043 #include <kmimetype.h> 00044 #include <kpushbutton.h> 00045 #include <krecentdocument.h> 00046 #include <ktoolbar.h> 00047 #include <kurlcompletion.h> 00048 #include <kuser.h> 00049 #include <kprotocolmanager.h> 00050 #include <kio/job.h> 00051 #include <kio/jobuidelegate.h> 00052 #include <kio/netaccess.h> 00053 #include <kio/scheduler.h> 00054 #include <krecentdirs.h> 00055 #include <kdebug.h> 00056 #include <kio/kfileitemdelegate.h> 00057 00058 #include <QtGui/QCheckBox> 00059 #include <QtGui/QDockWidget> 00060 #include <QtGui/QLayout> 00061 #include <QtGui/QLabel> 00062 #include <QtGui/QLineEdit> 00063 #include <QtGui/QSplitter> 00064 #include <QtGui/QAbstractProxyModel> 00065 #include <QtGui/QHelpEvent> 00066 #include <QtGui/QApplication> 00067 #include <QtCore/QFSFileEngine> 00068 #include <kshell.h> 00069 #include <kmessagebox.h> 00070 #include <kauthorized.h> 00071 00072 class KFileWidgetPrivate 00073 { 00074 public: 00075 KFileWidgetPrivate(KFileWidget *widget) 00076 : q(widget), 00077 boxLayout(0), 00078 placesDock(0), 00079 placesView(0), 00080 placesViewSplitter(0), 00081 placesViewWidth(-1), 00082 labeledCustomWidget(0), 00083 bottomCustomWidget(0), 00084 autoSelectExtCheckBox(0), 00085 operationMode(KFileWidget::Opening), 00086 bookmarkHandler(0), 00087 toolbar(0), 00088 locationEdit(0), 00089 ops(0), 00090 filterWidget(0), 00091 autoSelectExtChecked(false), 00092 keepLocation(false), 00093 hasView(false), 00094 hasDefaultFilter(false), 00095 inAccept(false), 00096 dummyAdded(false), 00097 confirmOverwrite(false), 00098 differentHierarchyLevelItemsEntered(false), 00099 previewGenerator(0), 00100 iconSizeSlider(0) 00101 { 00102 } 00103 00104 ~KFileWidgetPrivate() 00105 { 00106 delete bookmarkHandler; // Should be deleted before ops! 00107 delete ops; 00108 } 00109 00110 void updateLocationWhatsThis(); 00111 void updateAutoSelectExtension(); 00112 void initSpeedbar(); 00113 void initGUI(); 00114 void readConfig(KConfigGroup &configGroup); 00115 void writeConfig(KConfigGroup &configGroup); 00116 void setNonExtSelection(); 00117 void setLocationText(const KUrl&); 00118 void setLocationText(const KUrl::List&); 00119 void appendExtension(KUrl &url); 00120 void updateLocationEditExtension(const QString &); 00121 void updateFilter(); 00122 KUrl::List& parseSelectedUrls(); 00129 KUrl::List tokenize(const QString& line) const; 00133 void readRecentFiles(KConfigGroup &cg); 00137 void saveRecentFiles(KConfigGroup &cg); 00142 void multiSelectionChanged(); 00143 00147 KUrl getCompleteUrl(const QString&) const; 00148 00153 void setDummyHistoryEntry(const QString& text, const QPixmap& icon = QPixmap(), 00154 bool usePreviousPixmapIfNull = true); 00155 00159 void removeDummyHistoryEntry(); 00160 00167 bool toOverwrite(const KUrl&); 00168 00169 // private slots 00170 void _k_slotLocationChanged( const QString& ); 00171 void _k_urlEntered( const KUrl& ); 00172 void _k_enterUrl( const KUrl& ); 00173 void _k_enterUrl( const QString& ); 00174 void _k_locationAccepted( const QString& ); 00175 void _k_slotFilterChanged(); 00176 void _k_fileHighlighted( const KFileItem& ); 00177 void _k_fileSelected( const KFileItem& ); 00178 void _k_slotLoadingFinished(); 00179 void _k_fileCompletion( const QString& ); 00180 void _k_toggleSpeedbar( bool ); 00181 void _k_toggleBookmarks( bool ); 00182 void _k_slotAutoSelectExtClicked(); 00183 void _k_placesViewSplitterMoved(int, int); 00184 void _k_activateUrlNavigator(); 00185 void _k_zoomOutIconsSize(); 00186 void _k_zoomInIconsSize(); 00187 void _k_slotIconSizeSliderMoved(int); 00188 void _k_slotIconSizeChanged(int); 00189 00190 void addToRecentDocuments(); 00191 00192 QString locationEditCurrentText() const; 00193 00199 static KUrl mostLocalUrl(const KUrl &url); 00200 00201 void setInlinePreviewShown(bool show); 00202 00203 KFileWidget* q; 00204 00205 // the last selected url 00206 KUrl url; 00207 00208 // the selected filenames in multiselection mode -- FIXME 00209 QString filenames; 00210 00211 // now following all kind of widgets, that I need to rebuild 00212 // the geometry management 00213 QBoxLayout *boxLayout; 00214 QGridLayout *lafBox; 00215 QVBoxLayout *vbox; 00216 00217 QLabel *locationLabel; 00218 QWidget *opsWidget; 00219 QWidget *pathSpacer; 00220 00221 QLabel *filterLabel; 00222 KUrlNavigator *urlNavigator; 00223 KPushButton *okButton, *cancelButton; 00224 QDockWidget *placesDock; 00225 KFilePlacesView *placesView; 00226 QSplitter *placesViewSplitter; 00227 // caches the places view width. This value will be updated when the splitter 00228 // is moved. This allows us to properly set a value when the dialog itself 00229 // is resized 00230 int placesViewWidth; 00231 00232 QWidget *labeledCustomWidget; 00233 QWidget *bottomCustomWidget; 00234 00235 // Automatically Select Extension stuff 00236 QCheckBox *autoSelectExtCheckBox; 00237 QString extension; // current extension for this filter 00238 00239 QList<KIO::StatJob*> statJobs; 00240 00241 KUrl::List urlList; //the list of selected urls 00242 00243 KFileWidget::OperationMode operationMode; 00244 00245 // The file class used for KRecentDirs 00246 QString fileClass; 00247 00248 KFileBookmarkHandler *bookmarkHandler; 00249 00250 KActionMenu* bookmarkButton; 00251 00252 KToolBar *toolbar; 00253 KUrlComboBox *locationEdit; 00254 KDirOperator *ops; 00255 KFileFilterCombo *filterWidget; 00256 00257 KFilePlacesModel *model; 00258 00259 // whether or not the _user_ has checked the above box 00260 bool autoSelectExtChecked : 1; 00261 00262 // indicates if the location edit should be kept or cleared when changing 00263 // directories 00264 bool keepLocation : 1; 00265 00266 // the KDirOperators view is set in KFileWidget::show(), so to avoid 00267 // setting it again and again, we have this nice little boolean :) 00268 bool hasView : 1; 00269 00270 bool hasDefaultFilter : 1; // necessary for the operationMode 00271 bool autoDirectoryFollowing : 1; 00272 bool inAccept : 1; // true between beginning and end of accept() 00273 bool dummyAdded : 1; // if the dummy item has been added. This prevents the combo from having a 00274 // blank item added when loaded 00275 bool confirmOverwrite : 1; 00276 bool differentHierarchyLevelItemsEntered; 00277 00278 KFilePreviewGenerator *previewGenerator; 00279 QSlider *iconSizeSlider; 00280 }; 00281 00282 K_GLOBAL_STATIC(KUrl, lastDirectory) // to set the start path 00283 00284 static const char autocompletionWhatsThisText[] = I18N_NOOP("<qt>While typing in the text area, you may be presented " 00285 "with possible matches. " 00286 "This feature can be controlled by clicking with the right mouse button " 00287 "and selecting a preferred mode from the <b>Text Completion</b> menu.</qt>"); 00288 00289 // returns true if the string contains "<a>:/" sequence, where <a> is at least 2 alpha chars 00290 static bool containsProtocolSection( const QString& string ) 00291 { 00292 int len = string.length(); 00293 static const char prot[] = ":/"; 00294 for (int i=0; i < len;) { 00295 i = string.indexOf( QLatin1String(prot), i ); 00296 if (i == -1) 00297 return false; 00298 int j=i-1; 00299 for (; j >= 0; j--) { 00300 const QChar& ch( string[j] ); 00301 if (ch.toAscii() == 0 || !ch.isLetter()) 00302 break; 00303 if (ch.isSpace() && (i-j-1) >= 2) 00304 return true; 00305 } 00306 if (j < 0 && i >= 2) 00307 return true; // at least two letters before ":/" 00308 i += 3; // skip : and / and one char 00309 } 00310 return false; 00311 } 00312 00313 KFileWidget::KFileWidget( const KUrl& _startDir, QWidget *parent ) 00314 : QWidget(parent), KAbstractFileWidget(), d(new KFileWidgetPrivate(this)) 00315 { 00316 KUrl startDir(_startDir); 00317 kDebug(kfile_area) << "startDir" << startDir; 00318 QString filename; 00319 00320 d->okButton = new KPushButton(KStandardGuiItem::ok(), this); 00321 d->okButton->setDefault(true); 00322 d->cancelButton = new KPushButton(KStandardGuiItem::cancel(), this); 00323 // The dialog shows them 00324 d->okButton->hide(); 00325 d->cancelButton->hide(); 00326 00327 d->opsWidget = new QWidget(this); 00328 QVBoxLayout *opsWidgetLayout = new QVBoxLayout(d->opsWidget); 00329 opsWidgetLayout->setMargin(0); 00330 opsWidgetLayout->setSpacing(0); 00331 //d->toolbar = new KToolBar(this, true); 00332 d->toolbar = new KToolBar(d->opsWidget, true); 00333 d->toolbar->setObjectName("KFileWidget::toolbar"); 00334 d->toolbar->setMovable(false); 00335 opsWidgetLayout->addWidget(d->toolbar); 00336 00337 d->model = new KFilePlacesModel(this); 00338 00339 // Resolve this now so that a 'kfiledialog:' URL, if specified, 00340 // does not get inserted into the urlNavigator history. 00341 d->url = getStartUrl( startDir, d->fileClass, filename ); 00342 startDir = d->url; 00343 00344 // Don't pass startDir to the KUrlNavigator at this stage: as well as 00345 // the above, it may also contain a file name which should not get 00346 // inserted in that form into the old-style navigation bar history. 00347 // Wait until the KIO::stat has been done later. 00348 // 00349 // The stat cannot be done before this point, bug 172678. 00350 d->urlNavigator = new KUrlNavigator(d->model, KUrl(), d->opsWidget); //d->toolbar); 00351 d->urlNavigator->setPlacesSelectorVisible(false); 00352 opsWidgetLayout->addWidget(d->urlNavigator); 00353 00354 KUrl u; 00355 KUrlComboBox *pathCombo = d->urlNavigator->editor(); 00356 #ifdef Q_WS_WIN 00357 foreach( const QFileInfo &drive,QFSFileEngine::drives() ) 00358 { 00359 u.setPath( drive.filePath() ); 00360 pathCombo->addDefaultUrl(u, 00361 KIO::pixmapForUrl( u, 0, KIconLoader::Small ), 00362 i18n("Drive: %1", u.toLocalFile())); 00363 } 00364 #else 00365 u.setPath(QDir::rootPath()); 00366 pathCombo->addDefaultUrl(u, 00367 KIO::pixmapForUrl(u, 0, KIconLoader::Small), 00368 u.toLocalFile()); 00369 #endif 00370 00371 u.setPath(QDir::homePath()); 00372 pathCombo->addDefaultUrl(u, KIO::pixmapForUrl(u, 0, KIconLoader::Small), 00373 u.path(KUrl::AddTrailingSlash)); 00374 00375 KUrl docPath; 00376 docPath.setPath( KGlobalSettings::documentPath() ); 00377 if ( (u.path(KUrl::AddTrailingSlash) != docPath.path(KUrl::AddTrailingSlash)) && 00378 QDir(docPath.path(KUrl::AddTrailingSlash)).exists() ) 00379 { 00380 pathCombo->addDefaultUrl( docPath, 00381 KIO::pixmapForUrl( docPath, 0, KIconLoader::Small ), 00382 docPath.path(KUrl::AddTrailingSlash)); 00383 } 00384 00385 u.setPath( KGlobalSettings::desktopPath() ); 00386 pathCombo->addDefaultUrl(u, 00387 KIO::pixmapForUrl(u, 0, KIconLoader::Small), 00388 u.path(KUrl::AddTrailingSlash)); 00389 00390 d->ops = new KDirOperator(KUrl(), d->opsWidget); 00391 d->ops->setObjectName( "KFileWidget::ops" ); 00392 d->ops->setIsSaving(d->operationMode == Saving); 00393 opsWidgetLayout->addWidget(d->ops); 00394 connect(d->ops, SIGNAL(urlEntered(const KUrl&)), 00395 SLOT(_k_urlEntered(const KUrl&))); 00396 connect(d->ops, SIGNAL(fileHighlighted(const KFileItem &)), 00397 SLOT(_k_fileHighlighted(const KFileItem &))); 00398 connect(d->ops, SIGNAL(fileSelected(const KFileItem &)), 00399 SLOT(_k_fileSelected(const KFileItem &))); 00400 connect(d->ops, SIGNAL(finishedLoading()), 00401 SLOT(_k_slotLoadingFinished())); 00402 00403 d->ops->setupMenu(KDirOperator::SortActions | 00404 KDirOperator::FileActions | 00405 KDirOperator::ViewActions); 00406 KActionCollection *coll = d->ops->actionCollection(); 00407 coll->addAssociatedWidget(this); 00408 00409 // add nav items to the toolbar 00410 // 00411 // NOTE: The order of the button icons here differs from that 00412 // found in the file manager and web browser, but has been discussed 00413 // and agreed upon on the kde-core-devel mailing list: 00414 // 00415 // http://lists.kde.org/?l=kde-core-devel&m=116888382514090&w=2 00416 00417 coll->action( "up" )->setWhatsThis(i18n("<qt>Click this button to enter the parent folder.<br /><br />" 00418 "For instance, if the current location is file:/home/%1 clicking this " 00419 "button will take you to file:/home.</qt>", KUser().loginName() )); 00420 00421 coll->action( "back" )->setWhatsThis(i18n("Click this button to move backwards one step in the browsing history.")); 00422 coll->action( "forward" )->setWhatsThis(i18n("Click this button to move forward one step in the browsing history.")); 00423 00424 coll->action( "reload" )->setWhatsThis(i18n("Click this button to reload the contents of the current location.")); 00425 coll->action( "mkdir" )->setShortcut( QKeySequence(Qt::Key_F10) ); 00426 coll->action( "mkdir" )->setWhatsThis(i18n("Click this button to create a new folder.")); 00427 00428 KAction *goToNavigatorAction = coll->addAction( "gotonavigator", this, SLOT( _k_activateUrlNavigator() ) ); 00429 goToNavigatorAction->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L) ); 00430 00431 KToggleAction *showSidebarAction = 00432 new KToggleAction(i18n("Show Places Navigation Panel"), this); 00433 coll->addAction("toggleSpeedbar", showSidebarAction); 00434 showSidebarAction->setShortcut( QKeySequence(Qt::Key_F9) ); 00435 connect( showSidebarAction, SIGNAL( toggled( bool ) ), 00436 SLOT( _k_toggleSpeedbar( bool )) ); 00437 00438 KToggleAction *showBookmarksAction = 00439 new KToggleAction(i18n("Show Bookmarks"), this); 00440 coll->addAction("toggleBookmarks", showBookmarksAction); 00441 connect( showBookmarksAction, SIGNAL( toggled( bool ) ), 00442 SLOT( _k_toggleBookmarks( bool )) ); 00443 00444 KActionMenu *menu = new KActionMenu( KIcon("configure"), i18n("Options"), this); 00445 coll->addAction("extra menu", menu); 00446 menu->setWhatsThis(i18n("<qt>This is the preferences menu for the file dialog. " 00447 "Various options can be accessed from this menu including: <ul>" 00448 "<li>how files are sorted in the list</li>" 00449 "<li>types of view, including icon and list</li>" 00450 "<li>showing of hidden files</li>" 00451 "<li>the Places navigation panel</li>" 00452 "<li>file previews</li>" 00453 "<li>separating folders from files</li></ul></qt>")); 00454 menu->addAction(coll->action("sorting menu")); 00455 menu->addAction(coll->action("view menu")); 00456 menu->addSeparator(); 00457 menu->addAction(coll->action("decoration menu")); 00458 menu->addSeparator(); 00459 KAction * showHidden = qobject_cast<KAction*>(coll->action( "show hidden" )); 00460 if (showHidden) { 00461 showHidden->setShortcut( 00462 KShortcut( QKeySequence(Qt::ALT + Qt::Key_Period), QKeySequence(Qt::Key_F8) ) ); 00463 } 00464 menu->addAction( showHidden ); 00465 menu->addAction( showSidebarAction ); 00466 menu->addAction( showBookmarksAction ); 00467 coll->action( "inline preview" )->setShortcut( QKeySequence(Qt::Key_F11) ); 00468 menu->addAction( coll->action( "preview" )); 00469 00470 menu->setDelayed( false ); 00471 connect( menu->menu(), SIGNAL( aboutToShow() ), 00472 d->ops, SLOT( updateSelectionDependentActions() )); 00473 00474 d->iconSizeSlider = new QSlider(this); 00475 d->iconSizeSlider->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); 00476 d->iconSizeSlider->setOrientation(Qt::Horizontal); 00477 d->iconSizeSlider->setMinimum(0); 00478 d->iconSizeSlider->setMaximum(100); 00479 d->iconSizeSlider->installEventFilter(this); 00480 connect(d->iconSizeSlider, SIGNAL(valueChanged(int)), 00481 d->ops, SLOT(setIconsZoom(int))); 00482 connect(d->iconSizeSlider, SIGNAL(valueChanged(int)), 00483 this, SLOT(_k_slotIconSizeChanged(int))); 00484 connect(d->iconSizeSlider, SIGNAL(sliderMoved(int)), 00485 this, SLOT(_k_slotIconSizeSliderMoved(int))); 00486 connect(d->ops, SIGNAL(currentIconSizeChanged(int)), 00487 d->iconSizeSlider, SLOT(setValue(int))); 00488 00489 KAction *furtherAction = new KAction(KIcon("zoom-out"), i18n("Zoom out"), this); 00490 connect(furtherAction, SIGNAL(triggered()), SLOT(_k_zoomOutIconsSize())); 00491 KAction *closerAction = new KAction(KIcon("zoom-in"), i18n("Zoom in"), this); 00492 connect(closerAction, SIGNAL(triggered()), SLOT(_k_zoomInIconsSize())); 00493 00494 QWidget *midSpacer = new QWidget(this); 00495 midSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 00496 00497 QAction *separator = new QAction(this); 00498 separator->setSeparator(true); 00499 00500 QAction *separator2 = new QAction(this); 00501 separator2->setSeparator(true); 00502 00503 d->toolbar->addAction(coll->action("back" )); 00504 d->toolbar->addAction(coll->action("forward")); 00505 d->toolbar->addAction(coll->action("up")); 00506 d->toolbar->addAction(coll->action("reload")); 00507 d->toolbar->addAction(separator); 00508 d->toolbar->addAction(coll->action("inline preview")); 00509 d->toolbar->addWidget(midSpacer); 00510 d->toolbar->addAction(furtherAction); 00511 d->toolbar->addWidget(d->iconSizeSlider); 00512 d->toolbar->addAction(closerAction); 00513 d->toolbar->addAction(separator2); 00514 d->toolbar->addAction(coll->action("mkdir")); 00515 d->toolbar->addAction(menu); 00516 00517 d->toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); 00518 d->toolbar->setMovable(false); 00519 00520 KUrlCompletion *pathCompletionObj = new KUrlCompletion( KUrlCompletion::DirCompletion ); 00521 pathCombo->setCompletionObject( pathCompletionObj ); 00522 pathCombo->setAutoDeleteCompletionObject( true ); 00523 00524 connect( d->urlNavigator, SIGNAL( urlChanged( const KUrl& )), 00525 this, SLOT( _k_enterUrl( const KUrl& ) )); 00526 connect( d->urlNavigator, SIGNAL( returnPressed() ), 00527 d->ops, SLOT( setFocus() )); 00528 00529 QString whatsThisText; 00530 00531 // the Location label/edit 00532 d->locationLabel = new QLabel(i18n("&Name:"), this); 00533 d->locationEdit = new KUrlComboBox(KUrlComboBox::Files, true, this); 00534 d->locationEdit->installEventFilter(this); 00535 // Properly let the dialog be resized (to smaller). Otherwise we could have 00536 // huge dialogs that can't be resized to smaller (it would be as big as the longest 00537 // item in this combo box). (ereslibre) 00538 d->locationEdit->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); 00539 connect( d->locationEdit, SIGNAL( editTextChanged( const QString& ) ), 00540 SLOT( _k_slotLocationChanged( const QString& )) ); 00541 00542 d->updateLocationWhatsThis(); 00543 d->locationLabel->setBuddy(d->locationEdit); 00544 00545 KUrlCompletion *fileCompletionObj = new KUrlCompletion( KUrlCompletion::FileCompletion ); 00546 d->locationEdit->setCompletionObject( fileCompletionObj ); 00547 d->locationEdit->setAutoDeleteCompletionObject( true ); 00548 connect( fileCompletionObj, SIGNAL( match( const QString& ) ), 00549 SLOT( _k_fileCompletion( const QString& )) ); 00550 00551 connect(d->locationEdit, SIGNAL( returnPressed( const QString& )), 00552 this, SLOT( _k_locationAccepted( const QString& ) )); 00553 00554 // the Filter label/edit 00555 whatsThisText = i18n("<qt>This is the filter to apply to the file list. " 00556 "File names that do not match the filter will not be shown.<p>" 00557 "You may select from one of the preset filters in the " 00558 "drop down menu, or you may enter a custom filter " 00559 "directly into the text area.</p><p>" 00560 "Wildcards such as * and ? are allowed.</p></qt>"); 00561 d->filterLabel = new QLabel(i18n("&Filter:"), this); 00562 d->filterLabel->setWhatsThis(whatsThisText); 00563 d->filterWidget = new KFileFilterCombo(this); 00564 // Properly let the dialog be resized (to smaller). Otherwise we could have 00565 // huge dialogs that can't be resized to smaller (it would be as big as the longest 00566 // item in this combo box). (ereslibre) 00567 d->filterWidget->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); 00568 d->filterWidget->setWhatsThis(whatsThisText); 00569 d->filterLabel->setBuddy(d->filterWidget); 00570 connect(d->filterWidget, SIGNAL(filterChanged()), SLOT(_k_slotFilterChanged())); 00571 00572 // the Automatically Select Extension checkbox 00573 // (the text, visibility etc. is set in updateAutoSelectExtension(), which is called by readConfig()) 00574 d->autoSelectExtCheckBox = new QCheckBox (this); 00575 d->autoSelectExtCheckBox->setStyleSheet(QString("QCheckBox { padding-top: %1px; }").arg(KDialog::spacingHint())); 00576 connect(d->autoSelectExtCheckBox, SIGNAL(clicked()), SLOT(_k_slotAutoSelectExtClicked())); 00577 00578 d->initGUI(); // activate GM 00579 00580 // read our configuration 00581 KSharedConfig::Ptr config = KGlobal::config(); 00582 KConfigGroup viewConfigGroup(config, ConfigGroup); 00583 d->readConfig(viewConfigGroup); 00584 00585 coll->action("inline preview")->setChecked(d->ops->isInlinePreviewShown()); 00586 d->iconSizeSlider->setValue(d->ops->iconsZoom()); 00587 00588 KFilePreviewGenerator *pg = d->ops->previewGenerator(); 00589 if (pg) { 00590 coll->action("inline preview")->setChecked(pg->isPreviewShown()); 00591 } 00592 00593 // getStartUrl() above will have resolved the startDir parameter into 00594 // a directory and file name in the two cases: (a) where it is a 00595 // special "kfiledialog:" URL, or (b) where it is a plain file name 00596 // only without directory or protocol. For any other startDir 00597 // specified, it is not possible to resolve whether there is a file name 00598 // present just by looking at the URL; the only way to be sure is 00599 // to stat it. 00600 bool statRes = false; 00601 if ( filename.isEmpty() ) 00602 { 00603 KIO::StatJob *statJob = KIO::stat(startDir, KIO::HideProgressInfo); 00604 statRes = KIO::NetAccess::synchronousRun(statJob, 0); 00605 kDebug(kfile_area) << "stat of" << startDir << "-> statRes" << statRes << "isDir" << statJob->statResult().isDir(); 00606 if (!statRes || !statJob->statResult().isDir()) { 00607 filename = startDir.fileName(); 00608 startDir.setPath(startDir.directory()); 00609 kDebug(kfile_area) << "statJob -> startDir" << startDir << "filename" << filename; 00610 } 00611 } 00612 00613 d->ops->setUrl(startDir, true); 00614 d->urlNavigator->setLocationUrl(startDir); 00615 if (d->placesView) { 00616 d->placesView->setUrl(startDir); 00617 } 00618 00619 // We have a file name either explicitly specified, or have checked that 00620 // we could stat it and it is not a directory. Set it. 00621 if (!filename.isEmpty()) { 00622 QLineEdit* lineEdit = d->locationEdit->lineEdit(); 00623 kDebug(kfile_area) << "selecting filename" << filename; 00624 if (statRes) { 00625 d->setLocationText(filename); 00626 } else { 00627 lineEdit->setText(filename); 00628 // Preserve this filename when clicking on the view (cf _k_fileHighlighted) 00629 lineEdit->setModified(true); 00630 } 00631 lineEdit->selectAll(); 00632 } 00633 00634 d->locationEdit->setFocus(); 00635 } 00636 00637 KFileWidget::~KFileWidget() 00638 { 00639 KSharedConfig::Ptr config = KGlobal::config(); 00640 config->sync(); 00641 00642 delete d; 00643 } 00644 00645 void KFileWidget::setLocationLabel(const QString& text) 00646 { 00647 d->locationLabel->setText(text); 00648 } 00649 00650 void KFileWidget::setFilter(const QString& filter) 00651 { 00652 int pos = filter.indexOf('/'); 00653 00654 // Check for an un-escaped '/', if found 00655 // interpret as a MIME filter. 00656 00657 if (pos > 0 && filter[pos - 1] != '\\') { 00658 QStringList filters = filter.split(' ', QString::SkipEmptyParts); 00659 setMimeFilter( filters ); 00660 return; 00661 } 00662 00663 // Strip the escape characters from 00664 // escaped '/' characters. 00665 00666 QString copy (filter); 00667 for (pos = 0; (pos = copy.indexOf("\\/", pos)) != -1; ++pos) 00668 copy.remove(pos, 1); 00669 00670 d->ops->clearFilter(); 00671 d->filterWidget->setFilter(copy); 00672 d->ops->setNameFilter(d->filterWidget->currentFilter()); 00673 d->ops->updateDir(); 00674 d->hasDefaultFilter = false; 00675 d->filterWidget->setEditable( true ); 00676 00677 d->updateAutoSelectExtension (); 00678 } 00679 00680 QString KFileWidget::currentFilter() const 00681 { 00682 return d->filterWidget->currentFilter(); 00683 } 00684 00685 void KFileWidget::setMimeFilter( const QStringList& mimeTypes, 00686 const QString& defaultType ) 00687 { 00688 d->filterWidget->setMimeFilter( mimeTypes, defaultType ); 00689 00690 QStringList types = d->filterWidget->currentFilter().split(' ', QString::SkipEmptyParts); //QStringList::split(" ", d->filterWidget->currentFilter()); 00691 types.append( QLatin1String( "inode/directory" )); 00692 d->ops->clearFilter(); 00693 d->ops->setMimeFilter( types ); 00694 d->hasDefaultFilter = !defaultType.isEmpty(); 00695 d->filterWidget->setEditable( !d->hasDefaultFilter || 00696 d->operationMode != Saving ); 00697 00698 d->updateAutoSelectExtension (); 00699 } 00700 00701 void KFileWidget::clearFilter() 00702 { 00703 d->filterWidget->setFilter( QString() ); 00704 d->ops->clearFilter(); 00705 d->hasDefaultFilter = false; 00706 d->filterWidget->setEditable( true ); 00707 00708 d->updateAutoSelectExtension (); 00709 } 00710 00711 QString KFileWidget::currentMimeFilter() const 00712 { 00713 int i = d->filterWidget->currentIndex(); 00714 if (d->filterWidget->showsAllTypes() && i == 0) 00715 return QString(); // The "all types" item has no mimetype 00716 00717 return d->filterWidget->filters()[i]; 00718 } 00719 00720 KMimeType::Ptr KFileWidget::currentFilterMimeType() 00721 { 00722 return KMimeType::mimeType( currentMimeFilter() ); 00723 } 00724 00725 void KFileWidget::setPreviewWidget(KPreviewWidgetBase *w) { 00726 d->ops->setPreviewWidget(w); 00727 d->ops->clearHistory(); 00728 d->hasView = true; 00729 } 00730 00731 KUrl KFileWidgetPrivate::getCompleteUrl(const QString &_url) const 00732 { 00733 // kDebug(kfile_area) << "got url " << _url; 00734 00735 const QString url = KShell::tildeExpand(_url); 00736 KUrl u; 00737 00738 if (QDir::isAbsolutePath(url)) { 00739 u = url; 00740 } else { 00741 KUrl relativeUrlTest(ops->url()); 00742 relativeUrlTest.addPath(url); 00743 if (!ops->dirLister()->findByUrl(relativeUrlTest).isNull() || 00744 !KProtocolInfo::isKnownProtocol(relativeUrlTest)) { 00745 u = relativeUrlTest; 00746 } else { 00747 u = url; 00748 } 00749 } 00750 00751 return u; 00752 } 00753 00754 // Called by KFileDialog 00755 void KFileWidget::slotOk() 00756 { 00757 // kDebug(kfile_area) << "slotOk\n"; 00758 00759 const KFileItemList items = d->ops->selectedItems(); 00760 const QString locationEditCurrentText(KShell::tildeExpand(d->locationEditCurrentText())); 00761 00762 KUrl::List locationEditCurrentTextList(d->tokenize(locationEditCurrentText)); 00763 KFile::Modes mode = d->ops->mode(); 00764 00765 // if there is nothing to do, just return from here 00766 if (!locationEditCurrentTextList.count()) { 00767 return; 00768 } 00769 00770 // Make sure that one of the modes was provided 00771 if (!((mode & KFile::File) || (mode & KFile::Directory) || (mode & KFile::Files))) { 00772 mode |= KFile::File; 00773 kDebug(kfile_area) << "No mode() provided"; 00774 } 00775 00776 // if we are on file mode, and the list of provided files/folder is greater than one, inform 00777 // the user about it 00778 if (locationEditCurrentTextList.count() > 1) { 00779 if (mode & KFile::File) { 00780 KMessageBox::sorry(this, 00781 i18n("You can only select one file"), 00782 i18n("More than one file provided")); 00783 return; 00784 } 00785 00806 if (!d->differentHierarchyLevelItemsEntered) { // avoid infinite recursion. running this 00807 KUrl::List urlList; // one time is always enough. 00808 int start = 0; 00809 KUrl topMostUrl; 00810 KIO::StatJob *statJob = 0; 00811 bool res = false; 00812 00813 // we need to check for a valid first url, so in theory we only iterate one time over 00814 // this loop. However it can happen that the user did 00815 // "home/foo/nonexistantfile" "boot/grub/menu.lst", so we look for a good first 00816 // candidate. 00817 while (!res && start < locationEditCurrentTextList.count()) { 00818 topMostUrl = locationEditCurrentTextList.at(start); 00819 statJob = KIO::stat(topMostUrl, KIO::HideProgressInfo); 00820 res = KIO::NetAccess::synchronousRun(statJob, 0); 00821 start++; 00822 } 00823 00824 Q_ASSERT(statJob); 00825 00826 // if this is not a dir, strip the filename. after this we have an existent and valid 00827 // dir (if we stated correctly the file, setting a null filename won't make any bad). 00828 if (!statJob->statResult().isDir()) { 00829 topMostUrl.setFileName(QString()); 00830 } 00831 00832 // now the funny part. for the rest of filenames, go and look for the closest ancestor 00833 // of all them. 00834 for (int i = start; i < locationEditCurrentTextList.count(); ++i) { 00835 KUrl currUrl = locationEditCurrentTextList.at(i); 00836 KIO::StatJob *statJob = KIO::stat(currUrl, KIO::HideProgressInfo); 00837 bool res = KIO::NetAccess::synchronousRun(statJob, 0); 00838 if (res) { 00839 // again, we don't care about filenames 00840 if (!statJob->statResult().isDir()) { 00841 currUrl.setFileName(QString()); 00842 } 00843 00844 // iterate while this item is contained on the top most url 00845 while (!topMostUrl.isParentOf(currUrl)) { 00846 topMostUrl = topMostUrl.upUrl(); 00847 } 00848 } 00849 } 00850 00851 // now recalculate all paths for them being relative in base of the top most url 00852 for (int i = 0; i < locationEditCurrentTextList.count(); ++i) { 00853 locationEditCurrentTextList[i] = KUrl::relativeUrl(topMostUrl, locationEditCurrentTextList[i]); 00854 } 00855 00856 d->ops->setUrl(topMostUrl, true); 00857 const bool signalsBlocked = d->locationEdit->lineEdit()->blockSignals(true); 00858 QStringList stringList; 00859 foreach (const KUrl &url, locationEditCurrentTextList) { 00860 stringList << url.prettyUrl(); 00861 } 00862 d->locationEdit->lineEdit()->setText(QString("\"%1\"").arg(stringList.join("\" \""))); 00863 d->locationEdit->lineEdit()->blockSignals(signalsBlocked); 00864 00865 d->differentHierarchyLevelItemsEntered = true; 00866 slotOk(); 00867 return; 00868 } 00872 } else if (locationEditCurrentTextList.count()) { 00873 // if we are on file or files mode, and we have an absolute url written by 00874 // the user, convert it to relative 00875 if (!locationEditCurrentText.isEmpty() && !(mode & KFile::Directory) && 00876 (QDir::isAbsolutePath(locationEditCurrentText) || 00877 containsProtocolSection(locationEditCurrentText))) { 00878 00879 QString fileName; 00880 KUrl url(locationEditCurrentText); 00881 if (d->operationMode == Opening) { 00882 KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); 00883 bool res = KIO::NetAccess::synchronousRun(statJob, 0); 00884 if (res) { 00885 if (!statJob->statResult().isDir()) { 00886 url.adjustPath(KUrl::RemoveTrailingSlash); 00887 fileName = url.fileName(); 00888 url.setFileName(QString()); 00889 } else { 00890 url.adjustPath(KUrl::AddTrailingSlash); 00891 } 00892 } 00893 } else { 00894 KUrl directory = url; 00895 directory.setFileName(QString()); 00896 //Check if the folder exists 00897 KIO::StatJob * statJob = KIO::stat(directory, KIO::HideProgressInfo); 00898 bool res = KIO::NetAccess::synchronousRun(statJob, 0); 00899 if (res) { 00900 if (statJob->statResult().isDir()) { 00901 url.adjustPath(KUrl::RemoveTrailingSlash); 00902 fileName = url.fileName(); 00903 url.setFileName(QString()); 00904 } 00905 } 00906 } 00907 d->ops->setUrl(url, true); 00908 const bool signalsBlocked = d->locationEdit->lineEdit()->blockSignals(true); 00909 d->locationEdit->lineEdit()->setText(fileName); 00910 d->locationEdit->lineEdit()->blockSignals(signalsBlocked); 00911 slotOk(); 00912 return; 00913 } 00914 } 00915 00916 // restore it 00917 d->differentHierarchyLevelItemsEntered = false; 00918 00919 // locationEditCurrentTextList contains absolute paths 00920 // this is the general loop for the File and Files mode. Obviously we know 00921 // that the File mode will iterate only one time here 00922 bool directoryMode = (mode & KFile::Directory); 00923 bool onlyDirectoryMode = directoryMode && !(mode & KFile::File) && !(mode & KFile::Files); 00924 KUrl::List::ConstIterator it = locationEditCurrentTextList.constBegin(); 00925 bool filesInList = false; 00926 while (it != locationEditCurrentTextList.constEnd()) { 00927 KUrl url(*it); 00928 00929 if (d->operationMode == Saving && !directoryMode) { 00930 d->appendExtension(url); 00931 } 00932 00933 d->url = url; 00934 KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); 00935 bool res = KIO::NetAccess::synchronousRun(statJob, 0); 00936 00937 if (!KAuthorized::authorizeUrlAction("open", KUrl(), url)) { 00938 QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, d->url.prettyUrl()); 00939 KMessageBox::error(this, msg); 00940 return; 00941 } 00942 00943 // if we are on local mode, make sure we haven't got a remote base url 00944 if ((mode & KFile::LocalOnly) && !d->mostLocalUrl(d->url).isLocalFile()) { 00945 KMessageBox::sorry(this, 00946 i18n("You can only select local files"), 00947 i18n("Remote files not accepted")); 00948 return; 00949 } 00950 00951 if ((d->operationMode == Saving) && d->confirmOverwrite && !d->toOverwrite(url)) { 00952 return; 00953 } 00954 00955 // if we are given a folder when not on directory mode, let's get into it 00956 if (res && !directoryMode && statJob->statResult().isDir()) { 00957 // check if we were given more than one folder, in that case we don't know to which one 00958 // cd 00959 ++it; 00960 while (it != locationEditCurrentTextList.constEnd()) { 00961 KUrl checkUrl(*it); 00962 KIO::StatJob *checkStatJob = KIO::stat(checkUrl, KIO::HideProgressInfo); 00963 bool res = KIO::NetAccess::synchronousRun(checkStatJob, 0); 00964 if (res && checkStatJob->statResult().isDir()) { 00965 KMessageBox::sorry(this, i18n("More than one folder has been selected and this dialog does not accept folders, so it is not possible to decide which one to enter. Please select only one folder to list it."), i18n("More than one folder provided")); 00966 return; 00967 } else if (res) { 00968 filesInList = true; 00969 } 00970 ++it; 00971 } 00972 if (filesInList) { 00973 KMessageBox::information(this, i18n("At least one folder and one file has been selected. Selected files will be ignored and the selected folder will be listed"), i18n("Files and folders selected")); 00974 } 00975 d->ops->setUrl(url, true); 00976 const bool signalsBlocked = d->locationEdit->lineEdit()->blockSignals(true); 00977 d->locationEdit->lineEdit()->setText(QString()); 00978 d->locationEdit->lineEdit()->blockSignals(signalsBlocked); 00979 return; 00980 } else if (!(mode & KFile::ExistingOnly) || res) { 00981 // if we don't care about ExistingOnly flag, add the file even if 00982 // it doesn't exist. If we care about it, don't add it to the list 00983 if (!onlyDirectoryMode || (res && statJob->statResult().isDir())) { 00984 d->urlList << url; 00985 } 00986 filesInList = true; 00987 } else { 00988 KMessageBox::sorry(this, i18n("The file \"%1\" could not be found", url.pathOrUrl()), i18n("Cannot open file")); 00989 return; // do not emit accepted() if we had ExistingOnly flag and stat failed 00990 } 00991 ++it; 00992 } 00993 00994 // if we have reached this point and we didn't return before, that is because 00995 // we want this dialog to be accepted 00996 emit accepted(); 00997 } 00998 00999 void KFileWidget::accept() 01000 { 01001 d->inAccept = true; // parseSelectedUrls() checks that 01002 01003 *lastDirectory = d->ops->url(); 01004 if (!d->fileClass.isEmpty()) 01005 KRecentDirs::add(d->fileClass, d->ops->url().url()); 01006 01007 // clear the topmost item, we insert it as full path later on as item 1 01008 d->locationEdit->setItemText( 0, QString() ); 01009 01010 const KUrl::List list = selectedUrls(); 01011 QList<KUrl>::const_iterator it = list.begin(); 01012 int atmost = d->locationEdit->maxItems(); //don't add more items than necessary 01013 for ( ; it != list.end() && atmost > 0; ++it ) { 01014 const KUrl& url = *it; 01015 // we strip the last slash (-1) because KUrlComboBox does that as well 01016 // when operating in file-mode. If we wouldn't , dupe-finding wouldn't 01017 // work. 01018 QString file = url.isLocalFile() ? url.toLocalFile(KUrl::RemoveTrailingSlash) : url.prettyUrl(KUrl::RemoveTrailingSlash); 01019 01020 // remove dupes 01021 for ( int i = 1; i < d->locationEdit->count(); i++ ) { 01022 if ( d->locationEdit->itemText( i ) == file ) { 01023 d->locationEdit->removeItem( i-- ); 01024 break; 01025 } 01026 } 01027 //FIXME I don't think this works correctly when the KUrlComboBox has some default urls. 01028 //KUrlComboBox should provide a function to add an url and rotate the existing ones, keeping 01029 //track of maxItems, and we shouldn't be able to insert items as we please. 01030 d->locationEdit->insertItem( 1,file); 01031 atmost--; 01032 } 01033 01034 KSharedConfig::Ptr config = KGlobal::config(); 01035 KConfigGroup grp(config,ConfigGroup); 01036 d->writeConfig(grp); 01037 d->saveRecentFiles(grp); 01038 01039 d->addToRecentDocuments(); 01040 01041 if (!(mode() & KFile::Files)) { // single selection 01042 emit fileSelected(d->url.url()); // old 01043 emit fileSelected(d->url); 01044 } 01045 01046 d->ops->close(); 01047 } 01048 01049 01050 void KFileWidgetPrivate::_k_fileHighlighted(const KFileItem &i) 01051 { 01052 if ((!i.isNull() && i.isDir() ) || 01053 (locationEdit->hasFocus() && !locationEdit->currentText().isEmpty())) // don't disturb 01054 return; 01055 01056 const bool modified = locationEdit->lineEdit()->isModified(); 01057 01058 if (!(ops->mode() & KFile::Files)) { 01059 if (i.isNull()) { 01060 if (!modified) { 01061 setLocationText(KUrl()); 01062 } 01063 return; 01064 } 01065 01066 url = i.url(); 01067 01068 if (!locationEdit->hasFocus()) { // don't disturb while editing 01069 setLocationText( url ); 01070 } 01071 01072 emit q->fileHighlighted(url.url()); // old 01073 emit q->fileHighlighted(url); 01074 } else { 01075 multiSelectionChanged(); 01076 emit q->selectionChanged(); 01077 } 01078 01079 locationEdit->lineEdit()->setModified( false ); 01080 locationEdit->lineEdit()->selectAll(); 01081 } 01082 01083 void KFileWidgetPrivate::_k_fileSelected(const KFileItem &i) 01084 { 01085 if (!i.isNull() && i.isDir()) { 01086 return; 01087 } 01088 01089 if (!(ops->mode() & KFile::Files)) { 01090 if (i.isNull()) { 01091 setLocationText(KUrl()); 01092 return; 01093 } 01094 setLocationText(i.url()); 01095 } else { 01096 multiSelectionChanged(); 01097 emit q->selectionChanged(); 01098 } 01099 01100 // if we are saving, let another chance to the user before accepting the dialog (or trying to 01101 // accept). This way the user can choose a file and add a "_2" for instance to the filename 01102 if (operationMode == KFileWidget::Saving) { 01103 locationEdit->setFocus(); 01104 } else { 01105 q->slotOk(); 01106 } 01107 } 01108 01109 01110 // I know it's slow to always iterate thru the whole filelist 01111 // (d->ops->selectedItems()), but what can we do? 01112 void KFileWidgetPrivate::multiSelectionChanged() 01113 { 01114 if (locationEdit->hasFocus() && !locationEdit->currentText().isEmpty()) { // don't disturb 01115 return; 01116 } 01117 01118 const KFileItemList list = ops->selectedItems(); 01119 01120 if (list.isEmpty()) { 01121 setLocationText(KUrl()); 01122 return; 01123 } 01124 01125 KUrl::List urlList; 01126 foreach (const KFileItem &fileItem, list) { 01127 urlList << fileItem.url(); 01128 } 01129 01130 setLocationText(urlList); 01131 } 01132 01133 void KFileWidgetPrivate::setDummyHistoryEntry( const QString& text, const QPixmap& icon, 01134 bool usePreviousPixmapIfNull ) 01135 { 01136 // setCurrentItem() will cause textChanged() being emitted, 01137 // so slotLocationChanged() will be called. Make sure we don't clear 01138 // the KDirOperator's view-selection in there 01139 QObject::disconnect( locationEdit, SIGNAL( editTextChanged( const QString& ) ), 01140 q, SLOT( _k_slotLocationChanged( const QString& ) ) ); 01141 01142 bool dummyExists = dummyAdded; 01143 01144 int cursorPosition = locationEdit->lineEdit()->cursorPosition(); 01145 01146 if ( dummyAdded ) { 01147 if ( !icon.isNull() ) { 01148 locationEdit->setItemIcon( 0, icon ); 01149 locationEdit->setItemText( 0, text ); 01150 } else { 01151 if ( !usePreviousPixmapIfNull ) { 01152 locationEdit->setItemIcon( 0, QPixmap() ); 01153 } 01154 locationEdit->setItemText( 0, text ); 01155 } 01156 } else { 01157 if ( !text.isEmpty() ) { 01158 if ( !icon.isNull() ) { 01159 locationEdit->insertItem( 0, icon, text ); 01160 } else { 01161 if ( !usePreviousPixmapIfNull ) { 01162 locationEdit->insertItem( 0, QPixmap(), text ); 01163 } else { 01164 locationEdit->insertItem( 0, text ); 01165 } 01166 } 01167 dummyAdded = true; 01168 dummyExists = true; 01169 } 01170 } 01171 01172 if ( dummyExists && !text.isEmpty() ) { 01173 locationEdit->setCurrentIndex( 0 ); 01174 } 01175 01176 locationEdit->lineEdit()->setCursorPosition( cursorPosition ); 01177 01178 QObject::connect( locationEdit, SIGNAL( editTextChanged ( const QString& ) ), 01179 q, SLOT( _k_slotLocationChanged( const QString& )) ); 01180 } 01181 01182 void KFileWidgetPrivate::removeDummyHistoryEntry() 01183 { 01184 if ( !dummyAdded ) { 01185 return; 01186 } 01187 01188 // setCurrentItem() will cause textChanged() being emitted, 01189 // so slotLocationChanged() will be called. Make sure we don't clear 01190 // the KDirOperator's view-selection in there 01191 QObject::disconnect( locationEdit, SIGNAL( editTextChanged( const QString& ) ), 01192 q, SLOT( _k_slotLocationChanged( const QString& ) ) ); 01193 01194 if (locationEdit->count()) { 01195 locationEdit->removeItem( 0 ); 01196 } 01197 locationEdit->setCurrentIndex( -1 ); 01198 dummyAdded = false; 01199 01200 QObject::connect( locationEdit, SIGNAL( editTextChanged ( const QString& ) ), 01201 q, SLOT( _k_slotLocationChanged( const QString& )) ); 01202 } 01203 01204 void KFileWidgetPrivate::setLocationText(const KUrl& url) 01205 { 01206 if (!url.isEmpty()) { 01207 QPixmap mimeTypeIcon = KIconLoader::global()->loadMimeTypeIcon( KMimeType::iconNameForUrl( url ), KIconLoader::Small ); 01208 if (url.hasPath()) { 01209 if (!url.directory().isEmpty()) 01210 { 01211 KUrl u(url); 01212 u.setPath(u.directory()); 01213 q->setUrl(u, false); 01214 } 01215 else { 01216 q->setUrl(url.path(), false); 01217 } 01218 } 01219 setDummyHistoryEntry(url.fileName() , mimeTypeIcon); 01220 } else { 01221 removeDummyHistoryEntry(); 01222 } 01223 01224 // don't change selection when user has clicked on an item 01225 if (operationMode == KFileWidget::Saving && !locationEdit->isVisible()) { 01226 setNonExtSelection(); 01227 } 01228 } 01229 01230 void KFileWidgetPrivate::setLocationText( const KUrl::List& urlList ) 01231 { 01232 const KUrl currUrl = ops->url(); 01233 01234 if ( urlList.count() > 1 ) { 01235 QString urls; 01236 foreach (const KUrl &url, urlList) { 01237 urls += QString( "\"%1\"" ).arg( KUrl::relativeUrl(currUrl, url) ) + ' '; 01238 } 01239 urls = urls.left( urls.size() - 1 ); 01240 01241 setDummyHistoryEntry( urls, QPixmap(), false ); 01242 } else if ( urlList.count() ) { 01243 const QPixmap mimeTypeIcon = KIconLoader::global()->loadMimeTypeIcon( KMimeType::iconNameForUrl( urlList[0] ), KIconLoader::Small ); 01244 setDummyHistoryEntry( KUrl::relativeUrl(currUrl, urlList[0]), mimeTypeIcon ); 01245 } else { 01246 removeDummyHistoryEntry(); 01247 } 01248 01249 // don't change selection when user has clicked on an item 01250 if ( operationMode == KFileWidget::Saving && !locationEdit->isVisible()) 01251 setNonExtSelection(); 01252 } 01253 01254 void KFileWidgetPrivate::updateLocationWhatsThis() 01255 { 01256 QString whatsThisText; 01257 if (operationMode == KFileWidget::Saving) 01258 { 01259 whatsThisText = "<qt>" + i18n("This is the name to save the file as.") + 01260 i18n (autocompletionWhatsThisText); 01261 } 01262 else if (ops->mode() & KFile::Files) 01263 { 01264 whatsThisText = "<qt>" + i18n("This is the list of files to open. More than " 01265 "one file can be specified by listing several " 01266 "files, separated by spaces.") + 01267 i18n (autocompletionWhatsThisText); 01268 } 01269 else 01270 { 01271 whatsThisText = "<qt>" + i18n("This is the name of the file to open.") + 01272 i18n (autocompletionWhatsThisText); 01273 } 01274 01275 locationLabel->setWhatsThis(whatsThisText); 01276 locationEdit->setWhatsThis(whatsThisText); 01277 } 01278 01279 void KFileWidgetPrivate::initSpeedbar() 01280 { 01281 if (placesDock) { 01282 return; 01283 } 01284 01285 placesDock = new QDockWidget(i18nc("@title:window", "Places"), q); 01286 placesDock->setFeatures(QDockWidget::DockWidgetClosable); 01287 01288 placesView = new KFilePlacesView(placesDock); 01289 placesView->setModel(model); 01290 placesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 01291 01292 placesView->setObjectName(QLatin1String("url bar")); 01293 QObject::connect(placesView, SIGNAL(urlChanged(KUrl)), 01294 q, SLOT(_k_enterUrl(KUrl))); 01295 01296 // need to set the current url of the urlbar manually (not via urlEntered() 01297 // here, because the initial url of KDirOperator might be the same as the 01298 // one that will be set later (and then urlEntered() won't be emitted). 01299 // TODO: KDE5 ### REMOVE THIS when KDirOperator's initial URL (in the c'tor) is gone. 01300 placesView->setUrl(url); 01301 01302 placesDock->setWidget(placesView); 01303 placesViewSplitter->insertWidget(0, placesDock); 01304 01305 // initialize the size of the splitter 01306 KConfigGroup configGroup(KGlobal::config(), ConfigGroup); 01307 placesViewWidth = configGroup.readEntry(SpeedbarWidth, placesView->sizeHint().width()); 01308 01309 QList<int> sizes = placesViewSplitter->sizes(); 01310 if (placesViewWidth > 0) { 01311 sizes[0] = placesViewWidth + 1; 01312 sizes[1] = q->width() - placesViewWidth -1; 01313 placesViewSplitter->setSizes(sizes); 01314 } 01315 01316 QObject::connect(placesDock, SIGNAL(visibilityChanged(bool)), 01317 q, SLOT(_k_toggleSpeedbar(bool))); 01318 } 01319 01320 void KFileWidgetPrivate::initGUI() 01321 { 01322 delete boxLayout; // deletes all sub layouts 01323 01324 boxLayout = new QVBoxLayout( q); 01325 boxLayout->setMargin(0); // no additional margin to the already existing 01326 01327 placesViewSplitter = new QSplitter(q); 01328 placesViewSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 01329 placesViewSplitter->setChildrenCollapsible(false); 01330 boxLayout->addWidget(placesViewSplitter); 01331 01332 QObject::connect(placesViewSplitter, SIGNAL(splitterMoved(int,int)), 01333 q, SLOT(_k_placesViewSplitterMoved(int,int))); 01334 placesViewSplitter->insertWidget(0, opsWidget); 01335 01336 vbox = new QVBoxLayout(); 01337 vbox->setMargin(0); 01338 boxLayout->addLayout(vbox); 01339 01340 lafBox = new QGridLayout(); 01341 01342 lafBox->addWidget(locationLabel, 0, 0, Qt::AlignVCenter | Qt::AlignRight); 01343 lafBox->addWidget(locationEdit, 0, 1, Qt::AlignVCenter); 01344 lafBox->addWidget(okButton, 0, 2, Qt::AlignVCenter); 01345 01346 lafBox->addWidget(filterLabel, 1, 0, Qt::AlignVCenter | Qt::AlignRight); 01347 lafBox->addWidget(filterWidget, 1, 1, Qt::AlignVCenter); 01348 lafBox->addWidget(cancelButton, 1, 2, Qt::AlignVCenter); 01349 01350 lafBox->setColumnStretch(1, 4); 01351 01352 vbox->addLayout(lafBox); 01353 01354 // add the Automatically Select Extension checkbox 01355 vbox->addWidget(autoSelectExtCheckBox); 01356 01357 q->setTabOrder(ops, autoSelectExtCheckBox); 01358 q->setTabOrder(autoSelectExtCheckBox, locationEdit); 01359 q->setTabOrder(locationEdit, filterWidget); 01360 q->setTabOrder(filterWidget, okButton); 01361 q->setTabOrder(okButton, cancelButton); 01362 q->setTabOrder(cancelButton, urlNavigator); 01363 q->setTabOrder(urlNavigator, ops); 01364 q->setTabOrder(cancelButton, urlNavigator); 01365 q->setTabOrder(urlNavigator, ops); 01366 01367 } 01368 01369 void KFileWidgetPrivate::_k_slotFilterChanged() 01370 { 01371 // kDebug(kfile_area); 01372 01373 QString filter = filterWidget->currentFilter(); 01374 ops->clearFilter(); 01375 01376 if ( filter.indexOf( '/' ) > -1 ) { 01377 QStringList types = filter.split(' ', QString::SkipEmptyParts); 01378 types.prepend("inode/directory"); 01379 ops->setMimeFilter( types ); 01380 } 01381 else 01382 ops->setNameFilter( filter ); 01383 01384 ops->updateDir(); 01385 01386 updateAutoSelectExtension(); 01387 01388 emit q->filterChanged(filter); 01389 } 01390 01391 01392 void KFileWidget::setUrl(const KUrl& url, bool clearforward) 01393 { 01394 // kDebug(kfile_area); 01395 01396 d->ops->setUrl(url, clearforward); 01397 } 01398 01399 // Protected 01400 void KFileWidgetPrivate::_k_urlEntered(const KUrl& url) 01401 { 01402 // kDebug(kfile_area); 01403 01404 QString filename = locationEditCurrentText(); 01405 01406 KUrlComboBox* pathCombo = urlNavigator->editor(); 01407 if (pathCombo->count() != 0) { // little hack 01408 pathCombo->setUrl(url); 01409 } 01410 01411 bool blocked = locationEdit->blockSignals(true); 01412 if (keepLocation) { 01413 locationEdit->changeUrl(0, KIcon(KMimeType::iconNameForUrl(filename)), filename); 01414 locationEdit->lineEdit()->setModified(true); 01415 } 01416 01417 locationEdit->blockSignals( blocked ); 01418 01419 urlNavigator->setLocationUrl(url); 01420 01421 // is trigged in ctor before completion object is set 01422 KUrlCompletion *completion = dynamic_cast<KUrlCompletion*>(locationEdit->completionObject()); 01423 if (completion) { 01424 completion->setDir( url.path() ); 01425 } 01426 01427 if (placesView) { 01428 placesView->setUrl( url ); 01429 } 01430 } 01431 01432 void KFileWidgetPrivate::_k_locationAccepted(const QString &url) 01433 { 01434 Q_UNUSED(url); 01435 // kDebug(kfile_area); 01436 q->slotOk(); 01437 } 01438 01439 void KFileWidgetPrivate::_k_enterUrl( const KUrl& url ) 01440 { 01441 // kDebug(kfile_area); 01442 01443 KUrl fixedUrl( url ); 01444 // append '/' if needed: url combo does not add it 01445 // tokenize() expects it because uses KUrl::setFileName() 01446 fixedUrl.adjustPath( KUrl::AddTrailingSlash ); 01447 q->setUrl( fixedUrl ); 01448 if (!locationEdit->hasFocus()) 01449 ops->setFocus(); 01450 } 01451 01452 void KFileWidgetPrivate::_k_enterUrl( const QString& url ) 01453 { 01454 // kDebug(kfile_area); 01455 01456 _k_enterUrl( KUrl( KUrlCompletion::replacedPath( url, true, true )) ); 01457 } 01458 01459 bool KFileWidgetPrivate::toOverwrite(const KUrl &url) 01460 { 01461 // kDebug(kfile_area); 01462 01463 KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); 01464 bool res = KIO::NetAccess::synchronousRun(statJob, 0); 01465 01466 if (res) { 01467 int ret = KMessageBox::warningContinueCancel( q, 01468 i18n( "The file \"%1\" already exists. Do you wish to overwrite it?" , 01469 url.fileName() ), i18n( "Overwrite File?" ), KStandardGuiItem::overwrite(), 01470 KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous); 01471 01472 if (ret != KMessageBox::Continue) { 01473 return false; 01474 } 01475 return true; 01476 } 01477 01478 return true; 01479 } 01480 01481 void KFileWidget::setSelection(const QString& url) 01482 { 01483 // kDebug(kfile_area) << "setSelection " << url; 01484 01485 if (url.isEmpty()) { 01486 return; 01487 } 01488 01489 KUrl u = d->getCompleteUrl(url); 01490 if (!u.isValid()) { // if it still is 01491 kWarning() << url << " is not a correct argument for setSelection!"; 01492 return; 01493 } 01494 01495 // Honor protocols that do not support directory listing 01496 if (!u.isRelative() && !KProtocolManager::supportsListing(u)) 01497 return; 01498 01499 d->setLocationText(url); 01500 } 01501 01502 void KFileWidgetPrivate::_k_slotLoadingFinished() 01503 { 01504 if (locationEdit->currentText().isEmpty()) { 01505 return; 01506 } 01507 01508 ops->blockSignals(true); 01509 KUrl url = ops->url(); 01510 url.adjustPath(KUrl::AddTrailingSlash); 01511 url.setFileName(locationEdit->currentText()); 01512 ops->setCurrentItem(url.url()); 01513 ops->blockSignals(false); 01514 } 01515 01516 void KFileWidgetPrivate::_k_fileCompletion( const QString& match ) 01517 { 01518 // kDebug(kfile_area); 01519 01520 if (match.isEmpty() || locationEdit->currentText().contains('"')) { 01521 return; 01522 } 01523 01524 setDummyHistoryEntry(locationEdit->currentText(), KIconLoader::global()->loadMimeTypeIcon( KMimeType::iconNameForUrl( match ), KIconLoader::Small), !locationEdit->currentText().isEmpty()); 01525 } 01526 01527 void KFileWidgetPrivate::_k_slotLocationChanged( const QString& text ) 01528 { 01529 // kDebug(kfile_area); 01530 01531 locationEdit->lineEdit()->setModified(true); 01532 01533 if (text.isEmpty() && ops->view()) { 01534 ops->view()->clearSelection(); 01535 } 01536 01537 if (text.isEmpty()) { 01538 removeDummyHistoryEntry(); 01539 } else { 01540 setDummyHistoryEntry( text ); 01541 } 01542 01543 if (!locationEdit->lineEdit()->text().isEmpty()) { 01544 const KUrl::List urlList(tokenize(text)); 01545 QStringList stringList; 01546 foreach (const KUrl &url, urlList) { 01547 stringList << url.url(); 01548 } 01549 ops->setCurrentItems(stringList); 01550 } 01551 01552 updateFilter(); 01553 } 01554 01555 KUrl KFileWidget::selectedUrl() const 01556 { 01557 // kDebug(kfile_area); 01558 01559 if ( d->inAccept ) 01560 return d->url; 01561 else 01562 return KUrl(); 01563 } 01564 01565 KUrl::List KFileWidget::selectedUrls() const 01566 { 01567 // kDebug(kfile_area); 01568 01569 KUrl::List list; 01570 if ( d->inAccept ) { 01571 if (d->ops->mode() & KFile::Files) 01572 list = d->parseSelectedUrls(); 01573 else 01574 list.append( d->url ); 01575 } 01576 return list; 01577 } 01578 01579 01580 KUrl::List& KFileWidgetPrivate::parseSelectedUrls() 01581 { 01582 // kDebug(kfile_area); 01583 01584 if ( filenames.isEmpty() ) { 01585 return urlList; 01586 } 01587 01588 urlList.clear(); 01589 if ( filenames.contains( '/' )) { // assume _one_ absolute filename 01590 KUrl u; 01591 if ( containsProtocolSection( filenames ) ) 01592 u = filenames; 01593 else 01594 u.setPath( filenames ); 01595 01596 if ( u.isValid() ) 01597 urlList.append( u ); 01598 else 01599 KMessageBox::error( q, 01600 i18n("The chosen filenames do not\n" 01601 "appear to be valid."), 01602 i18n("Invalid Filenames") ); 01603 } 01604 01605 else 01606 urlList = tokenize( filenames ); 01607 01608 filenames.clear(); // indicate that we parsed that one 01609 01610 return urlList; 01611 } 01612 01613 01614 // FIXME: current implementation drawback: a filename can't contain quotes 01615 KUrl::List KFileWidgetPrivate::tokenize( const QString& line ) const 01616 { 01617 // kDebug(kfile_area); 01618 01619 KUrl::List urls; 01620 KUrl u( ops->url() ); 01621 u.adjustPath(KUrl::AddTrailingSlash); 01622 QString name; 01623 01624 const int count = line.count( QLatin1Char( '"' ) ); 01625 if ( count == 0 ) { // no " " -> assume one single file 01626 if (!QDir::isAbsolutePath(line)) { 01627 u.setFileName( line ); 01628 if ( u.isValid() ) 01629 urls.append( u ); 01630 } else { 01631 urls << KUrl(line); 01632 } 01633 01634 return urls; 01635 } 01636 01637 int start = 0; 01638 int index1 = -1, index2 = -1; 01639 while ( true ) { 01640 index1 = line.indexOf( '"', start ); 01641 index2 = line.indexOf( '"', index1 + 1 ); 01642 01643 if ( index1 < 0 || index2 < 0 ) 01644 break; 01645 01646 // get everything between the " " 01647 name = line.mid( index1 + 1, index2 - index1 - 1 ); 01648 01649 // since we use setFileName we need to do this under a temporary url 01650 KUrl _u( u ); 01651 KUrl currUrl( name ); 01652 01653 if ( !QDir::isAbsolutePath(currUrl.url()) ) { 01654 _u.setFileName( name ); 01655 } else { 01656 // we allow to insert various absolute paths like: 01657 // "/home/foo/bar.txt" "/boot/grub/menu.lst" 01658 _u = currUrl; 01659 } 01660 01661 if ( _u.isValid() ) { 01662 urls.append( _u ); 01663 } 01664 01665 start = index2 + 1; 01666 } 01667 01668 return urls; 01669 } 01670 01671 01672 QString KFileWidget::selectedFile() const 01673 { 01674 // kDebug(kfile_area); 01675 01676 if ( d->inAccept ) { 01677 const KUrl url = d->mostLocalUrl(d->url); 01678 if (url.isLocalFile()) 01679 return url.toLocalFile(); 01680 else { 01681 KMessageBox::sorry( const_cast<KFileWidget*>(this), 01682 i18n("You can only select local files."), 01683 i18n("Remote Files Not Accepted") ); 01684 } 01685 } 01686 return QString(); 01687 } 01688 01689 QStringList KFileWidget::selectedFiles() const 01690 { 01691 // kDebug(kfile_area); 01692 01693 QStringList list; 01694 01695 if (d->inAccept) { 01696 if (d->ops->mode() & KFile::Files) { 01697 const KUrl::List urls = d->parseSelectedUrls(); 01698 QList<KUrl>::const_iterator it = urls.begin(); 01699 while (it != urls.end()) { 01700 KUrl url = d->mostLocalUrl(*it); 01701 if (url.isLocalFile()) 01702 list.append(url.toLocalFile()); 01703 ++it; 01704 } 01705 } 01706 01707 else { // single-selection mode 01708 if ( d->url.isLocalFile() ) 01709 list.append( d->url.toLocalFile() ); 01710 } 01711 } 01712 01713 return list; 01714 } 01715 01716 KUrl KFileWidget::baseUrl() const 01717 { 01718 return d->ops->url(); 01719 } 01720 01721 void KFileWidget::resizeEvent(QResizeEvent* event) 01722 { 01723 QWidget::resizeEvent(event); 01724 01725 if (d->placesDock) { 01726 // we don't want our places dock actually changing size when we resize 01727 // and qt doesn't make it easy to enforce such a thing with QSplitter 01728 QList<int> sizes = d->placesViewSplitter->sizes(); 01729 sizes[0] = d->placesViewWidth + 1; // without this pixel, our places view is reduced 1 pixel each time is shown. 01730 sizes[1] = width() - d->placesViewWidth - 1; 01731 d->placesViewSplitter->setSizes( sizes ); 01732 } 01733 } 01734 01735 void KFileWidget::showEvent(QShowEvent* event) 01736 { 01737 if ( !d->hasView ) { // delayed view-creation 01738 Q_ASSERT( d ); 01739 Q_ASSERT( d->ops ); 01740 d->ops->setView( KFile::Default ); 01741 d->ops->view()->setSizePolicy( QSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ) ); 01742 d->hasView = true; 01743 } 01744 d->ops->clearHistory(); 01745 01746 QWidget::showEvent(event); 01747 } 01748 01749 bool KFileWidget::eventFilter(QObject* watched, QEvent* event) 01750 { 01751 const bool res = QWidget::eventFilter(watched, event); 01752 01753 QKeyEvent *keyEvent = dynamic_cast<QKeyEvent*>(event); 01754 if (watched == d->iconSizeSlider && keyEvent) { 01755 if (keyEvent->key() == Qt::Key_Left || keyEvent->key() == Qt::Key_Up || 01756 keyEvent->key() == Qt::Key_Right || keyEvent->key() == Qt::Key_Down) { 01757 d->_k_slotIconSizeSliderMoved(d->iconSizeSlider->value()); 01758 } 01759 } else if (watched == d->locationEdit && event->type() == QEvent::KeyPress) { 01760 if (keyEvent->modifiers() & Qt::AltModifier) { 01761 switch (keyEvent->key()) { 01762 case Qt::Key_Up: 01763 d->ops->actionCollection()->action("up")->trigger(); 01764 break; 01765 case Qt::Key_Left: 01766 d->ops->actionCollection()->action("back")->trigger(); 01767 break; 01768 case Qt::Key_Right: 01769 d->ops->actionCollection()->action("forward")->trigger(); 01770 break; 01771 default: 01772 break; 01773 } 01774 } 01775 } 01776 01777 return res; 01778 } 01779 01780 void KFileWidget::setMode( KFile::Modes m ) 01781 { 01782 // kDebug(kfile_area); 01783 01784 d->ops->setMode(m); 01785 if ( d->ops->dirOnlyMode() ) { 01786 d->filterWidget->setDefaultFilter( i18n("*|All Folders") ); 01787 } 01788 else { 01789 d->filterWidget->setDefaultFilter( i18n("*|All Files") ); 01790 } 01791 01792 d->updateAutoSelectExtension(); 01793 } 01794 01795 KFile::Modes KFileWidget::mode() const 01796 { 01797 return d->ops->mode(); 01798 } 01799 01800 01801 void KFileWidgetPrivate::readConfig(KConfigGroup &configGroup) 01802 { 01803 // kDebug(kfile_area); 01804 01805 readRecentFiles(configGroup); 01806 01807 ops->setViewConfig(configGroup); 01808 ops->readConfig(configGroup); 01809 01810 KUrlComboBox *combo = urlNavigator->editor(); 01811 combo->setUrls( configGroup.readPathEntry( RecentURLs, QStringList() ), KUrlComboBox::RemoveTop ); 01812 combo->setMaxItems( configGroup.readEntry( RecentURLsNumber, 01813 DefaultRecentURLsNumber ) ); 01814 combo->setUrl( ops->url() ); 01815 autoDirectoryFollowing = configGroup.readEntry(AutoDirectoryFollowing, 01816 DefaultDirectoryFollowing); 01817 01818 KGlobalSettings::Completion cm = (KGlobalSettings::Completion) 01819 configGroup.readEntry( PathComboCompletionMode, 01820 static_cast<int>( KGlobalSettings::completionMode() ) ); 01821 if ( cm != KGlobalSettings::completionMode() ) 01822 combo->setCompletionMode( cm ); 01823 01824 cm = (KGlobalSettings::Completion) 01825 configGroup.readEntry( LocationComboCompletionMode, 01826 static_cast<int>( KGlobalSettings::completionMode() ) ); 01827 if ( cm != KGlobalSettings::completionMode() ) 01828 locationEdit->setCompletionMode( cm ); 01829 01830 // since we delayed this moment, initialize the directory of the completion object to 01831 // our current directory (that was very probably set on the constructor) 01832 KUrlCompletion *completion = dynamic_cast<KUrlCompletion*>(locationEdit->completionObject()); 01833 if (completion) { 01834 completion->setDir(ops->url().url()); 01835 } 01836 01837 // show or don't show the speedbar 01838 _k_toggleSpeedbar( configGroup.readEntry( ShowSpeedbar, true ) ); 01839 01840 // show or don't show the bookmarks 01841 _k_toggleBookmarks( configGroup.readEntry(ShowBookmarks, false) ); 01842 01843 // does the user want Automatically Select Extension? 01844 autoSelectExtChecked = configGroup.readEntry (AutoSelectExtChecked, DefaultAutoSelectExtChecked); 01845 updateAutoSelectExtension(); 01846 01847 // should the URL navigator use the breadcrumb navigation? 01848 urlNavigator->setUrlEditable( !configGroup.readEntry(BreadcrumbNavigation, true) ); 01849 01850 // should the URL navigator show the full path? 01851 urlNavigator->setShowFullPath( configGroup.readEntry(ShowFullPath, false) ); 01852 01853 int w1 = q->minimumSize().width(); 01854 int w2 = toolbar->sizeHint().width(); 01855 if (w1 < w2) 01856 q->setMinimumWidth(w2); 01857 } 01858 01859 void KFileWidgetPrivate::writeConfig(KConfigGroup &configGroup) 01860 { 01861 // kDebug(kfile_area); 01862 01863 // these settings are global settings; ALL instances of the file dialog 01864 // should reflect them 01865 KConfig config("kdeglobals"); 01866 KConfigGroup group(&config, configGroup.name()); 01867 01868 KUrlComboBox *pathCombo = urlNavigator->editor(); 01869 group.writePathEntry( RecentURLs, pathCombo->urls() ); 01870 //saveDialogSize( group, KConfigGroup::Persistent | KConfigGroup::Global ); 01871 group.writeEntry( PathComboCompletionMode, static_cast<int>(pathCombo->completionMode()) ); 01872 group.writeEntry( LocationComboCompletionMode, static_cast<int>(locationEdit->completionMode()) ); 01873 01874 const bool showSpeedbar = placesDock && !placesDock->isHidden(); 01875 group.writeEntry( ShowSpeedbar, showSpeedbar ); 01876 if (showSpeedbar) { 01877 const QList<int> sizes = placesViewSplitter->sizes(); 01878 Q_ASSERT( sizes.count() > 0 ); 01879 group.writeEntry( SpeedbarWidth, sizes[0] ); 01880 } 01881 01882 group.writeEntry( ShowBookmarks, bookmarkHandler != 0 ); 01883 group.writeEntry( AutoSelectExtChecked, autoSelectExtChecked ); 01884 group.writeEntry( BreadcrumbNavigation, !urlNavigator->isUrlEditable() ); 01885 group.writeEntry( ShowFullPath, urlNavigator->showFullPath() ); 01886 01887 ops->writeConfig(group); 01888 } 01889 01890 01891 void KFileWidgetPrivate::readRecentFiles(KConfigGroup &cg) 01892 { 01893 // kDebug(kfile_area); 01894 01895 QObject::disconnect(locationEdit, SIGNAL(editTextChanged(QString)), 01896 q, SLOT(_k_slotLocationChanged(QString))); 01897 01898 locationEdit->setMaxItems(cg.readEntry(RecentFilesNumber, DefaultRecentURLsNumber)); 01899 locationEdit->setUrls(cg.readPathEntry(RecentFiles, QStringList()), 01900 KUrlComboBox::RemoveBottom); 01901 locationEdit->setCurrentIndex(-1); 01902 01903 QObject::connect(locationEdit, SIGNAL(editTextChanged(QString)), 01904 q, SLOT(_k_slotLocationChanged(QString))); 01905 } 01906 01907 void KFileWidgetPrivate::saveRecentFiles(KConfigGroup &cg) 01908 { 01909 // kDebug(kfile_area); 01910 cg.writePathEntry(RecentFiles, locationEdit->urls()); 01911 } 01912 01913 KPushButton * KFileWidget::okButton() const 01914 { 01915 return d->okButton; 01916 } 01917 01918 KPushButton * KFileWidget::cancelButton() const 01919 { 01920 return d->cancelButton; 01921 } 01922 01923 // Called by KFileDialog 01924 void KFileWidget::slotCancel() 01925 { 01926 // kDebug(kfile_area); 01927 01928 d->ops->close(); 01929 01930 KConfigGroup grp(KGlobal::config(), ConfigGroup); 01931 d->writeConfig(grp); 01932 } 01933 01934 void KFileWidget::setKeepLocation( bool keep ) 01935 { 01936 d->keepLocation = keep; 01937 } 01938 01939 bool KFileWidget::keepsLocation() const 01940 { 01941 return d->keepLocation; 01942 } 01943 01944 void KFileWidget::setOperationMode( OperationMode mode ) 01945 { 01946 // kDebug(kfile_area); 01947 01948 d->operationMode = mode; 01949 d->keepLocation = (mode == Saving); 01950 d->filterWidget->setEditable( !d->hasDefaultFilter || mode != Saving ); 01951 if ( mode == Opening ) { 01952 // don't use KStandardGuiItem::open() here which has trailing ellipsis! 01953 d->okButton->setGuiItem( KGuiItem( i18n( "&Open" ), "document-open") ); 01954 // hide the new folder actions...usability team says they shouldn't be in open file dialog 01955 actionCollection()->removeAction( actionCollection()->action("mkdir" ) ); 01956 } else if ( mode == Saving ) { 01957 d->okButton->setGuiItem( KStandardGuiItem::save() ); 01958 d->setNonExtSelection(); 01959 } else { 01960 d->okButton->setGuiItem( KStandardGuiItem::ok() ); 01961 } 01962 d->updateLocationWhatsThis(); 01963 d->updateAutoSelectExtension(); 01964 01965 if (d->ops) { 01966 d->ops->setIsSaving(mode == Saving); 01967 } 01968 } 01969 01970 KFileWidget::OperationMode KFileWidget::operationMode() const 01971 { 01972 return d->operationMode; 01973 } 01974 01975 void KFileWidgetPrivate::_k_slotAutoSelectExtClicked() 01976 { 01977 // kDebug (kfile_area) << "slotAutoSelectExtClicked(): " 01978 // << autoSelectExtCheckBox->isChecked() << endl; 01979 01980 // whether the _user_ wants it on/off 01981 autoSelectExtChecked = autoSelectExtCheckBox->isChecked(); 01982 01983 // update the current filename's extension 01984 updateLocationEditExtension (extension /* extension hasn't changed */); 01985 } 01986 01987 void KFileWidgetPrivate::_k_placesViewSplitterMoved(int pos, int index) 01988 { 01989 // kDebug(kfile_area); 01990 01991 // we need to record the size of the splitter when the splitter changes size 01992 // so we can keep the places box the right size! 01993 if (placesDock && index == 1) { 01994 placesViewWidth = pos; 01995 // kDebug() << "setting lafBox minwidth to" << placesViewWidth; 01996 lafBox->setColumnMinimumWidth(0, placesViewWidth); 01997 } 01998 } 01999 02000 void KFileWidgetPrivate::_k_activateUrlNavigator() 02001 { 02002 // kDebug(kfile_area); 02003 02004 urlNavigator->setUrlEditable(!urlNavigator->isUrlEditable()); 02005 if(urlNavigator->isUrlEditable()) { 02006 urlNavigator->setFocus(); 02007 urlNavigator->editor()->lineEdit()->selectAll(); 02008 } 02009 } 02010 02011 void KFileWidgetPrivate::_k_zoomOutIconsSize() 02012 { 02013 const int currValue = ops->iconsZoom(); 02014 const int futValue = qMax(0, currValue - 10); 02015 iconSizeSlider->setValue(futValue); 02016 _k_slotIconSizeSliderMoved(futValue); 02017 } 02018 02019 void KFileWidgetPrivate::_k_zoomInIconsSize() 02020 { 02021 const int currValue = ops->iconsZoom(); 02022 const int futValue = qMin(100, currValue + 10); 02023 iconSizeSlider->setValue(futValue); 02024 _k_slotIconSizeSliderMoved(futValue); 02025 } 02026 02027 void KFileWidgetPrivate::_k_slotIconSizeChanged(int _value) 02028 { 02029 int maxSize = KIconLoader::SizeEnormous - KIconLoader::SizeSmall; 02030 int value = (maxSize * _value / 100) + KIconLoader::SizeSmall; 02031 switch (value) { 02032 case KIconLoader::SizeSmall: 02033 case KIconLoader::SizeSmallMedium: 02034 case KIconLoader::SizeMedium: 02035 case KIconLoader::SizeLarge: 02036 case KIconLoader::SizeHuge: 02037 case KIconLoader::SizeEnormous: 02038 iconSizeSlider->setToolTip(i18n("Icon size: %1 pixels (standard size)", value)); 02039 break; 02040 default: 02041 iconSizeSlider->setToolTip(i18n("Icon size: %1 pixels", value)); 02042 break; 02043 } 02044 } 02045 02046 void KFileWidgetPrivate::_k_slotIconSizeSliderMoved(int _value) 02047 { 02048 // Force this to be called in case this slot is called first on the 02049 // slider move. 02050 _k_slotIconSizeChanged(_value); 02051 02052 QPoint global(iconSizeSlider->rect().topLeft()); 02053 global.ry() += iconSizeSlider->height() / 2; 02054 QHelpEvent toolTipEvent(QEvent::ToolTip, QPoint(0, 0), iconSizeSlider->mapToGlobal(global)); 02055 QApplication::sendEvent(iconSizeSlider, &toolTipEvent); 02056 } 02057 02058 static QString getExtensionFromPatternList(const QStringList &patternList) 02059 { 02060 // kDebug(kfile_area); 02061 02062 QString ret; 02063 // kDebug (kfile_area) << "\tgetExtension " << patternList; 02064 02065 QStringList::ConstIterator patternListEnd = patternList.end(); 02066 for (QStringList::ConstIterator it = patternList.begin(); 02067 it != patternListEnd; 02068 ++it) 02069 { 02070 // kDebug (kfile_area) << "\t\ttry: \'" << (*it) << "\'"; 02071 02072 // is this pattern like "*.BMP" rather than useless things like: 02073 // 02074 // README 02075 // *. 02076 // *.* 02077 // *.JP*G 02078 // *.JP? 02079 if ((*it).startsWith (QLatin1String("*.")) && 02080 (*it).length() > 2 && 02081 (*it).indexOf('*', 2) < 0 && (*it).indexOf ('?', 2) < 0) 02082 { 02083 ret = (*it).mid (1); 02084 break; 02085 } 02086 } 02087 02088 return ret; 02089 } 02090 02091 static QString stripUndisplayable (const QString &string) 02092 { 02093 QString ret = string; 02094 02095 ret.remove (':'); 02096 ret = KGlobal::locale()->removeAcceleratorMarker (ret); 02097 02098 return ret; 02099 } 02100 02101 02102 //QString KFileWidget::currentFilterExtension() 02103 //{ 02104 // return d->extension; 02105 //} 02106 02107 void KFileWidgetPrivate::updateAutoSelectExtension() 02108 { 02109 if (!autoSelectExtCheckBox) return; 02110 02111 // 02112 // Figure out an extension for the Automatically Select Extension thing 02113 // (some Windows users apparently don't know what to do when confronted 02114 // with a text file called "COPYING" but do know what to do with 02115 // COPYING.txt ...) 02116 // 02117 02118 // kDebug (kfile_area) << "Figure out an extension: "; 02119 QString lastExtension = extension; 02120 extension.clear(); 02121 02122 // Automatically Select Extension is only valid if the user is _saving_ a _file_ 02123 if ((operationMode == KFileWidget::Saving) && (ops->mode() & KFile::File)) 02124 { 02125 // 02126 // Get an extension from the filter 02127 // 02128 02129 QString filter = filterWidget->currentFilter(); 02130 if (!filter.isEmpty()) 02131 { 02132 // e.g. "*.cpp" 02133 if (filter.indexOf ('/') < 0) 02134 { 02135 extension = getExtensionFromPatternList (filter.split(' ', QString::SkipEmptyParts)/*QStringList::split (" ", filter)*/).toLower(); 02136 // kDebug (kfile_area) << "\tsetFilter-style: pattern ext=\'" 02137 // << extension << "\'" << endl; 02138 } 02139 // e.g. "text/html" 02140 else 02141 { 02142 KMimeType::Ptr mime = KMimeType::mimeType (filter); 02143 if (mime) 02144 { 02145 extension = mime->mainExtension().toLower(); 02146 // kDebug (kfile_area) << "\tsetMimeFilter-style: pattern ext=\'" 02147 // << extension << "\'" << endl; 02148 } 02149 } 02150 } 02151 02152 02153 // 02154 // GUI: checkbox 02155 // 02156 02157 QString whatsThisExtension; 02158 if (!extension.isEmpty()) 02159 { 02160 // remember: sync any changes to the string with below 02161 autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension (%1)", extension)); 02162 whatsThisExtension = i18n ("the extension <b>%1</b>", extension); 02163 02164 autoSelectExtCheckBox->setEnabled (true); 02165 autoSelectExtCheckBox->setChecked (autoSelectExtChecked); 02166 } 02167 else 02168 { 02169 // remember: sync any changes to the string with above 02170 autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension")); 02171 whatsThisExtension = i18n ("a suitable extension"); 02172 02173 autoSelectExtCheckBox->setChecked (false); 02174 autoSelectExtCheckBox->setEnabled (false); 02175 } 02176 02177 const QString locationLabelText = stripUndisplayable (locationLabel->text()); 02178 const QString filterLabelText = stripUndisplayable (filterLabel->text()); 02179 autoSelectExtCheckBox->setWhatsThis( "<qt>" + 02180 i18n ( 02181 "This option enables some convenient features for " 02182 "saving files with extensions:<br />" 02183 "<ol>" 02184 "<li>Any extension specified in the <b>%1</b> text " 02185 "area will be updated if you change the file type " 02186 "to save in.<br />" 02187 "<br /></li>" 02188 "<li>If no extension is specified in the <b>%2</b> " 02189 "text area when you click " 02190 "<b>Save</b>, %3 will be added to the end of the " 02191 "filename (if the filename does not already exist). " 02192 "This extension is based on the file type that you " 02193 "have chosen to save in.<br />" 02194 "<br />" 02195 "If you do not want KDE to supply an extension for the " 02196 "filename, you can either turn this option off or you " 02197 "can suppress it by adding a period (.) to the end of " 02198 "the filename (the period will be automatically " 02199 "removed)." 02200 "</li>" 02201 "</ol>" 02202 "If unsure, keep this option enabled as it makes your " 02203 "files more manageable." 02204 , 02205 locationLabelText, 02206 locationLabelText, 02207 whatsThisExtension) 02208 + "</qt>" 02209 ); 02210 02211 autoSelectExtCheckBox->show(); 02212 02213 02214 // update the current filename's extension 02215 updateLocationEditExtension (lastExtension); 02216 } 02217 // Automatically Select Extension not valid 02218 else 02219 { 02220 autoSelectExtCheckBox->setChecked (false); 02221 autoSelectExtCheckBox->hide(); 02222 } 02223 } 02224 02225 // Updates the extension of the filename specified in d->locationEdit if the 02226 // Automatically Select Extension feature is enabled. 02227 // (this prevents you from accidently saving "file.kwd" as RTF, for example) 02228 void KFileWidgetPrivate::updateLocationEditExtension (const QString &lastExtension) 02229 { 02230 if (!autoSelectExtCheckBox->isChecked() || extension.isEmpty()) 02231 return; 02232 02233 QString urlStr = locationEditCurrentText(); 02234 if (urlStr.isEmpty()) 02235 return; 02236 02237 KUrl url = getCompleteUrl(urlStr); 02238 // kDebug (kfile_area) << "updateLocationEditExtension (" << url << ")"; 02239 02240 const int fileNameOffset = urlStr.lastIndexOf ('/') + 1; 02241 QString fileName = urlStr.mid (fileNameOffset); 02242 02243 const int dot = fileName.lastIndexOf ('.'); 02244 const int len = fileName.length(); 02245 if (dot > 0 && // has an extension already and it's not a hidden file 02246 // like ".hidden" (but we do accept ".hidden.ext") 02247 dot != len - 1 // and not deliberately suppressing extension 02248 ) 02249 { 02250 // exists? 02251 KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); 02252 bool result = KIO::NetAccess::synchronousRun(statJob, 0); 02253 if (result) 02254 { 02255 // kDebug (kfile_area) << "\tfile exists"; 02256 02257 if (statJob->statResult().isDir()) 02258 { 02259 // kDebug (kfile_area) << "\tisDir - won't alter extension"; 02260 return; 02261 } 02262 02263 // --- fall through --- 02264 } 02265 02266 02267 // 02268 // try to get rid of the current extension 02269 // 02270 02271 // catch "double extensions" like ".tar.gz" 02272 if (lastExtension.length() && fileName.endsWith (lastExtension)) 02273 fileName.truncate (len - lastExtension.length()); 02274 else if (extension.length() && fileName.endsWith (extension)) 02275 fileName.truncate (len - extension.length()); 02276 // can only handle "single extensions" 02277 else 02278 fileName.truncate (dot); 02279 02280 // add extension 02281 const QString newText = urlStr.left (fileNameOffset) + fileName + extension; 02282 if ( newText != locationEditCurrentText() ) 02283 { 02284 locationEdit->setItemText(locationEdit->currentIndex(),urlStr.left (fileNameOffset) + fileName + extension); 02285 locationEdit->lineEdit()->setModified (true); 02286 } 02287 } 02288 } 02289 02290 // Updates the filter if the extension of the filename specified in d->locationEdit is changed 02291 // (this prevents you from accidently saving "file.kwd" as RTF, for example) 02292 void KFileWidgetPrivate::updateFilter() 02293 { 02294 // kDebug(kfile_area); 02295 02296 if ((operationMode == KFileWidget::Saving) && (ops->mode() & KFile::File) ) { 02297 const QString urlStr = locationEditCurrentText(); 02298 if (urlStr.isEmpty()) 02299 return; 02300 02301 KMimeType::Ptr mime = KMimeType::findByPath(urlStr, 0, true); 02302 if (mime && mime->name() != KMimeType::defaultMimeType()) { 02303 if (filterWidget->currentFilter() != mime->name() && 02304 filterWidget->filters().indexOf(mime->name()) != -1) 02305 filterWidget->setCurrentFilter(mime->name()); 02306 } 02307 } 02308 } 02309 02310 // applies only to a file that doesn't already exist 02311 void KFileWidgetPrivate::appendExtension (KUrl &url) 02312 { 02313 // kDebug(kfile_area); 02314 02315 if (!autoSelectExtCheckBox->isChecked() || extension.isEmpty()) 02316 return; 02317 02318 QString fileName = url.fileName(); 02319 if (fileName.isEmpty()) 02320 return; 02321 02322 // kDebug (kfile_area) << "appendExtension(" << url << ")"; 02323 02324 const int len = fileName.length(); 02325 const int dot = fileName.lastIndexOf ('.'); 02326 02327 const bool suppressExtension = (dot == len - 1); 02328 const bool unspecifiedExtension = (dot <= 0); 02329 02330 // don't KIO::Stat if unnecessary 02331 if (!(suppressExtension || unspecifiedExtension)) 02332 return; 02333 02334 // exists? 02335 KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); 02336 bool res = KIO::NetAccess::synchronousRun(statJob, 0); 02337 if (res) 02338 { 02339 // kDebug (kfile_area) << "\tfile exists - won't append extension"; 02340 return; 02341 } 02342 02343 // suppress automatically append extension? 02344 if (suppressExtension) 02345 { 02346 // 02347 // Strip trailing dot 02348 // This allows lazy people to have autoSelectExtCheckBox->isChecked 02349 // but don't want a file extension to be appended 02350 // e.g. "README." will make a file called "README" 02351 // 02352 // If you really want a name like "README.", then type "README.." 02353 // and the trailing dot will be removed (or just stop being lazy and 02354 // turn off this feature so that you can type "README.") 02355 // 02356 // kDebug (kfile_area) << "\tstrip trailing dot"; 02357 url.setFileName (fileName.left (len - 1)); 02358 } 02359 // evilmatically append extension :) if the user hasn't specified one 02360 else if (unspecifiedExtension) 02361 { 02362 // kDebug (kfile_area) << "\tappending extension \'" << extension << "\'..."; 02363 url.setFileName (fileName + extension); 02364 // kDebug (kfile_area) << "\tsaving as \'" << url << "\'"; 02365 } 02366 } 02367 02368 02369 // adds the selected files/urls to 'recent documents' 02370 void KFileWidgetPrivate::addToRecentDocuments() 02371 { 02372 int m = ops->mode(); 02373 int atmost = KRecentDocument::maximumItems(); 02374 //don't add more than we need. KRecentDocument::add() is pretty slow 02375 02376 if (m & KFile::LocalOnly) { 02377 const QStringList files = q->selectedFiles(); 02378 QStringList::ConstIterator it = files.begin(); 02379 for ( ; it != files.end() && atmost > 0; ++it ) { 02380 KRecentDocument::add( *it ); 02381 atmost--; 02382 } 02383 } 02384 02385 else { // urls 02386 const KUrl::List urls = q->selectedUrls(); 02387 KUrl::List::ConstIterator it = urls.begin(); 02388 for ( ; it != urls.end() && atmost > 0; ++it ) { 02389 if ( (*it).isValid() ) { 02390 KRecentDocument::add( *it ); 02391 atmost--; 02392 } 02393 } 02394 } 02395 } 02396 02397 KUrlComboBox* KFileWidget::locationEdit() const 02398 { 02399 return d->locationEdit; 02400 } 02401 02402 KFileFilterCombo* KFileWidget::filterWidget() const 02403 { 02404 return d->filterWidget; 02405 } 02406 02407 KActionCollection * KFileWidget::actionCollection() const 02408 { 02409 return d->ops->actionCollection(); 02410 } 02411 02412 void KFileWidgetPrivate::_k_toggleSpeedbar(bool show) 02413 { 02414 if (show) { 02415 initSpeedbar(); 02416 placesDock->show(); 02417 lafBox->setColumnMinimumWidth(0, placesViewWidth); 02418 02419 // check to see if they have a home item defined, if not show the home button 02420 KUrl homeURL; 02421 homeURL.setPath( QDir::homePath() ); 02422 KFilePlacesModel *model = static_cast<KFilePlacesModel*>(placesView->model()); 02423 for (int rowIndex = 0 ; rowIndex < model->rowCount() ; rowIndex++) { 02424 QModelIndex index = model->index(rowIndex, 0); 02425 KUrl url = model->url(index); 02426 02427 if ( homeURL.equals( url, KUrl::CompareWithoutTrailingSlash ) ) { 02428 toolbar->removeAction( ops->actionCollection()->action( "home" ) ); 02429 break; 02430 } 02431 } 02432 } else { 02433 if (q->sender() == placesDock && placesDock && placesDock->isVisibleTo(q)) { 02434 // we didn't *really* go away! the dialog was simply hidden or 02435 // we changed virtual desktops or ... 02436 return; 02437 } 02438 02439 if (placesDock) { 02440 placesDock->hide(); 02441 } 02442 02443 QAction* homeAction = ops->actionCollection()->action("home"); 02444 QAction* reloadAction = ops->actionCollection()->action("reload"); 02445 if (!toolbar->actions().contains(homeAction)) { 02446 toolbar->insertAction(reloadAction, homeAction); 02447 } 02448 02449 // reset the lafbox to not follow the width of the splitter 02450 lafBox->setColumnMinimumWidth(0, 0); 02451 } 02452 02453 static_cast<KToggleAction *>(q->actionCollection()->action("toggleSpeedbar"))->setChecked(show); 02454 } 02455 02456 void KFileWidgetPrivate::_k_toggleBookmarks(bool show) 02457 { 02458 if (show) 02459 { 02460 if (bookmarkHandler) 02461 { 02462 return; 02463 } 02464 02465 bookmarkHandler = new KFileBookmarkHandler( q ); 02466 q->connect( bookmarkHandler, SIGNAL( openUrl( const QString& )), 02467 SLOT( _k_enterUrl( const QString& ))); 02468 02469 bookmarkButton = new KActionMenu(KIcon("bookmarks"),i18n("Bookmarks"), q); 02470 bookmarkButton->setDelayed(false); 02471 q->actionCollection()->addAction("bookmark", bookmarkButton); 02472 bookmarkButton->setMenu(bookmarkHandler->menu()); 02473 bookmarkButton->setWhatsThis(i18n("<qt>This button allows you to bookmark specific locations. " 02474 "Click on this button to open the bookmark menu where you may add, " 02475 "edit or select a bookmark.<br /><br />" 02476 "These bookmarks are specific to the file dialog, but otherwise operate " 02477 "like bookmarks elsewhere in KDE.</qt>")); 02478 toolbar->addAction(bookmarkButton); 02479 } 02480 else if (bookmarkHandler) 02481 { 02482 delete bookmarkHandler; 02483 bookmarkHandler = 0; 02484 delete bookmarkButton; 02485 bookmarkButton = 0; 02486 } 02487 02488 static_cast<KToggleAction *>(q->actionCollection()->action("toggleBookmarks"))->setChecked( show ); 02489 } 02490 02491 02492 // static, overloaded 02493 KUrl KFileWidget::getStartUrl( const KUrl& startDir, 02494 QString& recentDirClass ) 02495 { 02496 QString fileName; // result discarded 02497 return getStartUrl( startDir, recentDirClass, fileName ); 02498 } 02499 02500 02501 // static, overloaded 02502 KUrl KFileWidget::getStartUrl( const KUrl& startDir, 02503 QString& recentDirClass, 02504 QString& fileName ) 02505 { 02506 recentDirClass.clear(); 02507 fileName.clear(); 02508 KUrl ret; 02509 02510 bool useDefaultStartDir = startDir.isEmpty(); 02511 if ( !useDefaultStartDir ) 02512 { 02513 if ( startDir.protocol() == "kfiledialog" ) 02514 { 02515 02516 // The startDir URL with this protocol may be in the format: 02517 // directory() fileName() 02518 // 1. kfiledialog:///keyword "/" keyword 02519 // 2. kfiledialog:///keyword?global "/" keyword 02520 // 3. kfiledialog:///keyword/ "/" keyword 02521 // 4. kfiledialog:///keyword/?global "/" keyword 02522 // 5. kfiledialog:///keyword/filename /keyword filename 02523 // 6. kfiledialog:///keyword/filename?global /keyword filename 02524 02525 QString keyword; 02526 QString urlDir = startDir.directory(); 02527 QString urlFile = startDir.fileName(); 02528 if ( urlDir == "/" ) // '1'..'4' above 02529 { 02530 keyword = urlFile; 02531 fileName.clear(); 02532 } 02533 else // '5' or '6' above 02534 { 02535 keyword = urlDir.mid( 1 ); 02536 fileName = urlFile; 02537 } 02538 02539 if ( startDir.query() == "?global" ) 02540 recentDirClass = QString( "::%1" ).arg( keyword ); 02541 else 02542 recentDirClass = QString( ":%1" ).arg( keyword ); 02543 02544 ret = KUrl( KRecentDirs::dir(recentDirClass) ); 02545 } 02546 else // not special "kfiledialog" URL 02547 { 02548 if (!startDir.directory().isEmpty()) // has directory, maybe with filename 02549 { 02550 ret = startDir; // will be checked by stat later 02551 // If we won't be able to list it (e.g. http), then use default 02552 if ( !KProtocolManager::supportsListing( ret ) ) 02553 useDefaultStartDir = true; 02554 } 02555 else // file name only 02556 { 02557 fileName = startDir.fileName(); 02558 useDefaultStartDir = true; 02559 } 02560 } 02561 } 02562 02563 if ( useDefaultStartDir ) 02564 { 02565 if (lastDirectory->isEmpty()) { 02566 lastDirectory->setPath(KGlobalSettings::documentPath()); 02567 KUrl home; 02568 home.setPath( QDir::homePath() ); 02569 // if there is no docpath set (== home dir), we prefer the current 02570 // directory over it. We also prefer the homedir when our CWD is 02571 // different from our homedirectory or when the document dir 02572 // does not exist 02573 if ( lastDirectory->path(KUrl::AddTrailingSlash) == home.path(KUrl::AddTrailingSlash) || 02574 QDir::currentPath() != QDir::homePath() || 02575 !QDir(lastDirectory->path(KUrl::AddTrailingSlash)).exists() ) 02576 lastDirectory->setPath(QDir::currentPath()); 02577 } 02578 ret = *lastDirectory; 02579 } 02580 02581 kDebug(kfile_area) << "for" << startDir << "->" << ret << "recentDirClass" << recentDirClass << "fileName" << fileName; 02582 return ret; 02583 } 02584 02585 void KFileWidget::setStartDir( const KUrl& directory ) 02586 { 02587 if ( directory.isValid() ) 02588 *lastDirectory = directory; 02589 } 02590 02591 void KFileWidgetPrivate::setNonExtSelection() 02592 { 02593 // Enhanced rename: Don't highlight the file extension. 02594 QString filename = locationEditCurrentText(); 02595 QString extension = KMimeType::extractKnownExtension( filename ); 02596 02597 if ( !extension.isEmpty() ) 02598 locationEdit->lineEdit()->setSelection( 0, filename.length() - extension.length() - 1 ); 02599 else 02600 { 02601 int lastDot = filename.lastIndexOf( '.' ); 02602 if ( lastDot > 0 ) 02603 locationEdit->lineEdit()->setSelection( 0, lastDot ); 02604 } 02605 } 02606 02607 KToolBar * KFileWidget::toolBar() const 02608 { 02609 return d->toolbar; 02610 } 02611 02612 void KFileWidget::setCustomWidget(QWidget* widget) 02613 { 02614 delete d->bottomCustomWidget; 02615 d->bottomCustomWidget = widget; 02616 02617 // add it to the dialog, below the filter list box. 02618 02619 // Change the parent so that this widget is a child of the main widget 02620 d->bottomCustomWidget->setParent( this ); 02621 02622 d->vbox->addWidget( d->bottomCustomWidget ); 02623 //d->vbox->addSpacing(3); // can't do this every time... 02624 02625 // FIXME: This should adjust the tab orders so that the custom widget 02626 // comes after the Cancel button. The code appears to do this, but the result 02627 // somehow screws up the tab order of the file path combo box. Not a major 02628 // problem, but ideally the tab order with a custom widget should be 02629 // the same as the order without one. 02630 setTabOrder(d->cancelButton, d->bottomCustomWidget); 02631 setTabOrder(d->bottomCustomWidget, d->urlNavigator); 02632 } 02633 02634 void KFileWidget::setCustomWidget(const QString& text, QWidget* widget) 02635 { 02636 delete d->labeledCustomWidget; 02637 d->labeledCustomWidget = widget; 02638 02639 QLabel* label = new QLabel(text, this); 02640 label->setAlignment(Qt::AlignRight); 02641 d->lafBox->addWidget(label, 2, 0, Qt::AlignVCenter); 02642 d->lafBox->addWidget(widget, 2, 1, Qt::AlignVCenter); 02643 } 02644 02645 void KFileWidget::virtual_hook( int id, void* data ) 02646 { 02647 // this is a workaround to avoid binary compatibility breakage 02648 // since setConfirmOverwrite in kabstractfilewidget.h is a new function 02649 // introduced for 4.2. As stated in kabstractfilewidget.h this workaround 02650 // is going to become a virtual function for KDE5 02651 02652 switch (id) { 02653 case 0: { // setConfirmOverwrite(bool) 02654 bool *enable = static_cast<bool*>(data); 02655 d->confirmOverwrite = *enable; 02656 } 02657 break; 02658 case 1: { // setInlinePreviewShown(bool) 02659 bool *show = static_cast<bool*>(data); 02660 d->setInlinePreviewShown(*show); 02661 } 02662 break; 02663 default: 02664 break; 02665 } 02666 } 02667 02668 KDirOperator* KFileWidget::dirOperator() 02669 { 02670 return d->ops; 02671 } 02672 02673 void KFileWidget::readConfig( KConfigGroup& group ) 02674 { 02675 d->readConfig(group); 02676 } 02677 02678 QString KFileWidgetPrivate::locationEditCurrentText() const 02679 { 02680 return QDir::fromNativeSeparators(locationEdit->currentText()); 02681 } 02682 02683 KUrl KFileWidgetPrivate::mostLocalUrl(const KUrl &url) 02684 { 02685 if (url.isLocalFile()) { 02686 return url; 02687 } 02688 02689 KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); 02690 bool res = KIO::NetAccess::synchronousRun(statJob, 0); 02691 02692 if (!res) { 02693 return url; 02694 } 02695 02696 const QString path = statJob->statResult().stringValue(KIO::UDSEntry::UDS_LOCAL_PATH); 02697 if (!path.isEmpty()) { 02698 KUrl newUrl; 02699 newUrl.setPath(path); 02700 return newUrl; 02701 } 02702 02703 return url; 02704 } 02705 02706 void KFileWidgetPrivate::setInlinePreviewShown(bool show) 02707 { 02708 ops->setInlinePreviewShown(show); 02709 } 02710 02711 02712 #include "kfilewidget.moc"
KDE 4.6 API Reference