• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.10.2 API Reference
  • KDE Home
  • Contact Us
 

KFile

  • kfile
kfilewidget.cpp
Go to the documentation of this file.
1 // -*- c++ -*-
2 /* This file is part of the KDE libraries
3  Copyright (C) 1997, 1998 Richard Moore <rich@kde.org>
4  1998 Stephan Kulow <coolo@kde.org>
5  1998 Daniel Grana <grana@ie.iwi.unibe.ch>
6  1999,2000,2001,2002,2003 Carsten Pfeiffer <pfeiffer@kde.org>
7  2003 Clarence Dang <dang@kde.org>
8  2007 David Faure <faure@kde.org>
9  2008 Rafael Fernández López <ereslibre@kde.org>
10 
11  This library is free software; you can redistribute it and/or
12  modify it under the terms of the GNU Library General Public
13  License as published by the Free Software Foundation; either
14  version 2 of the License, or (at your option) any later version.
15 
16  This library is distributed in the hope that it will be useful,
17  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  Library General Public License for more details.
20 
21  You should have received a copy of the GNU Library General Public License
22  along with this library; see the file COPYING.LIB. If not, write to
23  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24  Boston, MA 02110-1301, USA.
25 */
26 
27 #include "kfilewidget.h"
28 
29 #include "kfileplacesview.h"
30 #include "kfileplacesmodel.h"
31 #include "kfilebookmarkhandler_p.h"
32 #include "kurlcombobox.h"
33 #include "kurlnavigator.h"
34 #include "kfilepreviewgenerator.h"
35 #include <config-kfile.h>
36 
37 #include <kactioncollection.h>
38 #include <kdiroperator.h>
39 #include <kdirselectdialog.h>
40 #include <kfilefiltercombo.h>
41 #include <kimagefilepreview.h>
42 #include <kmenu.h>
43 #include <kmimetype.h>
44 #include <kpushbutton.h>
45 #include <krecentdocument.h>
46 #include <ktoolbar.h>
47 #include <kurlcompletion.h>
48 #include <kuser.h>
49 #include <kprotocolmanager.h>
50 #include <kio/job.h>
51 #include <kio/jobuidelegate.h>
52 #include <kio/netaccess.h>
53 #include <kio/scheduler.h>
54 #include <krecentdirs.h>
55 #include <kdebug.h>
56 #include <kio/kfileitemdelegate.h>
57 #include <kde_file.h>
58 
59 #include <QtGui/QCheckBox>
60 #include <QtGui/QDockWidget>
61 #include <QtGui/QLayout>
62 #include <QtGui/QLabel>
63 #include <QtGui/QLineEdit>
64 #include <QtGui/QSplitter>
65 #include <QtGui/QAbstractProxyModel>
66 #include <QtGui/QHelpEvent>
67 #include <QtGui/QApplication>
68 #include <QtCore/QFSFileEngine>
69 #include <kshell.h>
70 #include <kmessagebox.h>
71 #include <kauthorized.h>
72 
73 class KFileWidgetPrivate
74 {
75 public:
76  KFileWidgetPrivate(KFileWidget *widget)
77  : q(widget),
78  boxLayout(0),
79  placesDock(0),
80  placesView(0),
81  placesViewSplitter(0),
82  placesViewWidth(-1),
83  labeledCustomWidget(0),
84  bottomCustomWidget(0),
85  autoSelectExtCheckBox(0),
86  operationMode(KFileWidget::Opening),
87  bookmarkHandler(0),
88  toolbar(0),
89  locationEdit(0),
90  ops(0),
91  filterWidget(0),
92  autoSelectExtChecked(false),
93  keepLocation(false),
94  hasView(false),
95  hasDefaultFilter(false),
96  inAccept(false),
97  dummyAdded(false),
98  confirmOverwrite(false),
99  differentHierarchyLevelItemsEntered(false),
100  previewGenerator(0),
101  iconSizeSlider(0)
102  {
103  }
104 
105  ~KFileWidgetPrivate()
106  {
107  delete bookmarkHandler; // Should be deleted before ops!
108  delete ops;
109  }
110 
111  void updateLocationWhatsThis();
112  void updateAutoSelectExtension();
113  void initSpeedbar();
114  void initGUI();
115  void readConfig(KConfigGroup &configGroup);
116  void writeConfig(KConfigGroup &configGroup);
117  void setNonExtSelection();
118  void setLocationText(const KUrl&);
119  void setLocationText(const KUrl::List&);
120  void appendExtension(KUrl &url);
121  void updateLocationEditExtension(const QString &);
122  void updateFilter();
123  KUrl::List& parseSelectedUrls();
130  KUrl::List tokenize(const QString& line) const;
134  void readRecentFiles(KConfigGroup &cg);
138  void saveRecentFiles(KConfigGroup &cg);
143  void multiSelectionChanged();
144 
148  KUrl getCompleteUrl(const QString&) const;
149 
154  void setDummyHistoryEntry(const QString& text, const QPixmap& icon = QPixmap(),
155  bool usePreviousPixmapIfNull = true);
156 
160  void removeDummyHistoryEntry();
161 
168  bool toOverwrite(const KUrl&);
169 
170  // private slots
171  void _k_slotLocationChanged( const QString& );
172  void _k_urlEntered( const KUrl& );
173  void _k_enterUrl( const KUrl& );
174  void _k_enterUrl( const QString& );
175  void _k_locationAccepted( const QString& );
176  void _k_slotFilterChanged();
177  void _k_fileHighlighted( const KFileItem& );
178  void _k_fileSelected( const KFileItem& );
179  void _k_slotLoadingFinished();
180  void _k_fileCompletion( const QString& );
181  void _k_toggleSpeedbar( bool );
182  void _k_toggleBookmarks( bool );
183  void _k_slotAutoSelectExtClicked();
184  void _k_placesViewSplitterMoved(int, int);
185  void _k_activateUrlNavigator();
186  void _k_zoomOutIconsSize();
187  void _k_zoomInIconsSize();
188  void _k_slotIconSizeSliderMoved(int);
189  void _k_slotIconSizeChanged(int);
190 
191  void addToRecentDocuments();
192 
193  QString locationEditCurrentText() const;
194 
200  KUrl mostLocalUrl(const KUrl &url);
201 
202  void setInlinePreviewShown(bool show);
203 
204  KFileWidget* q;
205 
206  // the last selected url
207  KUrl url;
208 
209  // the selected filenames in multiselection mode -- FIXME
210  QString filenames;
211 
212  // now following all kind of widgets, that I need to rebuild
213  // the geometry management
214  QBoxLayout *boxLayout;
215  QGridLayout *lafBox;
216  QVBoxLayout *vbox;
217 
218  QLabel *locationLabel;
219  QWidget *opsWidget;
220  QWidget *pathSpacer;
221 
222  QLabel *filterLabel;
223  KUrlNavigator *urlNavigator;
224  KPushButton *okButton, *cancelButton;
225  QDockWidget *placesDock;
226  KFilePlacesView *placesView;
227  QSplitter *placesViewSplitter;
228  // caches the places view width. This value will be updated when the splitter
229  // is moved. This allows us to properly set a value when the dialog itself
230  // is resized
231  int placesViewWidth;
232 
233  QWidget *labeledCustomWidget;
234  QWidget *bottomCustomWidget;
235 
236  // Automatically Select Extension stuff
237  QCheckBox *autoSelectExtCheckBox;
238  QString extension; // current extension for this filter
239 
240  QList<KIO::StatJob*> statJobs;
241 
242  KUrl::List urlList; //the list of selected urls
243 
244  KFileWidget::OperationMode operationMode;
245 
246  // The file class used for KRecentDirs
247  QString fileClass;
248 
249  KFileBookmarkHandler *bookmarkHandler;
250 
251  KActionMenu* bookmarkButton;
252 
253  KToolBar *toolbar;
254  KUrlComboBox *locationEdit;
255  KDirOperator *ops;
256  KFileFilterCombo *filterWidget;
257  QTimer filterDelayTimer;
258 
259  KFilePlacesModel *model;
260 
261  // whether or not the _user_ has checked the above box
262  bool autoSelectExtChecked : 1;
263 
264  // indicates if the location edit should be kept or cleared when changing
265  // directories
266  bool keepLocation : 1;
267 
268  // the KDirOperators view is set in KFileWidget::show(), so to avoid
269  // setting it again and again, we have this nice little boolean :)
270  bool hasView : 1;
271 
272  bool hasDefaultFilter : 1; // necessary for the operationMode
273  bool autoDirectoryFollowing : 1;
274  bool inAccept : 1; // true between beginning and end of accept()
275  bool dummyAdded : 1; // if the dummy item has been added. This prevents the combo from having a
276  // blank item added when loaded
277  bool confirmOverwrite : 1;
278  bool differentHierarchyLevelItemsEntered;
279 
280  KFilePreviewGenerator *previewGenerator;
281  QSlider *iconSizeSlider;
282 };
283 
284 K_GLOBAL_STATIC(KUrl, lastDirectory) // to set the start path
285 
286 static const char autocompletionWhatsThisText[] = I18N_NOOP("<qt>While typing in the text area, you may be presented "
287  "with possible matches. "
288  "This feature can be controlled by clicking with the right mouse button "
289  "and selecting a preferred mode from the <b>Text Completion</b> menu.</qt>");
290 
291 // returns true if the string contains "<a>:/" sequence, where <a> is at least 2 alpha chars
292 static bool containsProtocolSection( const QString& string )
293 {
294  int len = string.length();
295  static const char prot[] = ":/";
296  for (int i=0; i < len;) {
297  i = string.indexOf( QLatin1String(prot), i );
298  if (i == -1)
299  return false;
300  int j=i-1;
301  for (; j >= 0; j--) {
302  const QChar& ch( string[j] );
303  if (ch.toLatin1() == 0 || !ch.isLetter())
304  break;
305  if (ch.isSpace() && (i-j-1) >= 2)
306  return true;
307  }
308  if (j < 0 && i >= 2)
309  return true; // at least two letters before ":/"
310  i += 3; // skip : and / and one char
311  }
312  return false;
313 }
314 
315 KFileWidget::KFileWidget( const KUrl& _startDir, QWidget *parent )
316  : QWidget(parent), KAbstractFileWidget(), d(new KFileWidgetPrivate(this))
317 {
318  KUrl startDir(_startDir);
319  kDebug(kfile_area) << "startDir" << startDir;
320  QString filename;
321 
322  d->okButton = new KPushButton(KStandardGuiItem::ok(), this);
323  d->okButton->setDefault(true);
324  d->cancelButton = new KPushButton(KStandardGuiItem::cancel(), this);
325  // The dialog shows them
326  d->okButton->hide();
327  d->cancelButton->hide();
328 
329  d->opsWidget = new QWidget(this);
330  QVBoxLayout *opsWidgetLayout = new QVBoxLayout(d->opsWidget);
331  opsWidgetLayout->setMargin(0);
332  opsWidgetLayout->setSpacing(0);
333  //d->toolbar = new KToolBar(this, true);
334  d->toolbar = new KToolBar(d->opsWidget, true);
335  d->toolbar->setObjectName("KFileWidget::toolbar");
336  d->toolbar->setMovable(false);
337  opsWidgetLayout->addWidget(d->toolbar);
338 
339  d->model = new KFilePlacesModel(this);
340 
341  // Resolve this now so that a 'kfiledialog:' URL, if specified,
342  // does not get inserted into the urlNavigator history.
343  d->url = getStartUrl( startDir, d->fileClass, filename );
344  startDir = d->url;
345 
346  // Don't pass startDir to the KUrlNavigator at this stage: as well as
347  // the above, it may also contain a file name which should not get
348  // inserted in that form into the old-style navigation bar history.
349  // Wait until the KIO::stat has been done later.
350  //
351  // The stat cannot be done before this point, bug 172678.
352  d->urlNavigator = new KUrlNavigator(d->model, KUrl(), d->opsWidget); //d->toolbar);
353  d->urlNavigator->setPlacesSelectorVisible(false);
354  opsWidgetLayout->addWidget(d->urlNavigator);
355 
356  KUrl u;
357  KUrlComboBox *pathCombo = d->urlNavigator->editor();
358 #ifdef Q_WS_WIN
359  foreach( const QFileInfo &drive,QFSFileEngine::drives() )
360  {
361  u.setPath( drive.filePath() );
362  pathCombo->addDefaultUrl(u,
363  KIO::pixmapForUrl( u, 0, KIconLoader::Small ),
364  i18n("Drive: %1", u.toLocalFile()));
365  }
366 #else
367  u.setPath(QDir::rootPath());
368  pathCombo->addDefaultUrl(u,
369  KIO::pixmapForUrl(u, 0, KIconLoader::Small),
370  u.toLocalFile());
371 #endif
372 
373  u.setPath(QDir::homePath());
374  pathCombo->addDefaultUrl(u, KIO::pixmapForUrl(u, 0, KIconLoader::Small),
375  u.path(KUrl::AddTrailingSlash));
376 
377  KUrl docPath;
378  docPath.setPath( KGlobalSettings::documentPath() );
379  if ( (u.path(KUrl::AddTrailingSlash) != docPath.path(KUrl::AddTrailingSlash)) &&
380  QDir(docPath.path(KUrl::AddTrailingSlash)).exists() )
381  {
382  pathCombo->addDefaultUrl( docPath,
383  KIO::pixmapForUrl( docPath, 0, KIconLoader::Small ),
384  docPath.path(KUrl::AddTrailingSlash));
385  }
386 
387  u.setPath( KGlobalSettings::desktopPath() );
388  pathCombo->addDefaultUrl(u,
389  KIO::pixmapForUrl(u, 0, KIconLoader::Small),
390  u.path(KUrl::AddTrailingSlash));
391 
392  d->ops = new KDirOperator(KUrl(), d->opsWidget);
393  d->ops->setObjectName( "KFileWidget::ops" );
394  d->ops->setIsSaving(d->operationMode == Saving);
395  opsWidgetLayout->addWidget(d->ops);
396  connect(d->ops, SIGNAL(urlEntered(KUrl)),
397  SLOT(_k_urlEntered(KUrl)));
398  connect(d->ops, SIGNAL(fileHighlighted(KFileItem)),
399  SLOT(_k_fileHighlighted(KFileItem)));
400  connect(d->ops, SIGNAL(fileSelected(KFileItem)),
401  SLOT(_k_fileSelected(KFileItem)));
402  connect(d->ops, SIGNAL(finishedLoading()),
403  SLOT(_k_slotLoadingFinished()));
404 
405  d->ops->setupMenu(KDirOperator::SortActions |
406  KDirOperator::FileActions |
407  KDirOperator::ViewActions);
408  KActionCollection *coll = d->ops->actionCollection();
409  coll->addAssociatedWidget(this);
410 
411  // add nav items to the toolbar
412  //
413  // NOTE: The order of the button icons here differs from that
414  // found in the file manager and web browser, but has been discussed
415  // and agreed upon on the kde-core-devel mailing list:
416  //
417  // http://lists.kde.org/?l=kde-core-devel&m=116888382514090&w=2
418 
419  coll->action( "up" )->setWhatsThis(i18n("<qt>Click this button to enter the parent folder.<br /><br />"
420  "For instance, if the current location is file:/home/%1 clicking this "
421  "button will take you to file:/home.</qt>", KUser().loginName() ));
422 
423  coll->action( "back" )->setWhatsThis(i18n("Click this button to move backwards one step in the browsing history."));
424  coll->action( "forward" )->setWhatsThis(i18n("Click this button to move forward one step in the browsing history."));
425 
426  coll->action( "reload" )->setWhatsThis(i18n("Click this button to reload the contents of the current location."));
427  coll->action( "mkdir" )->setShortcut( QKeySequence(Qt::Key_F10) );
428  coll->action( "mkdir" )->setWhatsThis(i18n("Click this button to create a new folder."));
429 
430  KAction *goToNavigatorAction = coll->addAction( "gotonavigator", this, SLOT(_k_activateUrlNavigator()) );
431  goToNavigatorAction->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L) );
432 
433  KToggleAction *showSidebarAction =
434  new KToggleAction(i18n("Show Places Navigation Panel"), this);
435  coll->addAction("toggleSpeedbar", showSidebarAction);
436  showSidebarAction->setShortcut( QKeySequence(Qt::Key_F9) );
437  connect( showSidebarAction, SIGNAL(toggled(bool)),
438  SLOT(_k_toggleSpeedbar(bool)) );
439 
440  KToggleAction *showBookmarksAction =
441  new KToggleAction(i18n("Show Bookmarks"), this);
442  coll->addAction("toggleBookmarks", showBookmarksAction);
443  connect( showBookmarksAction, SIGNAL(toggled(bool)),
444  SLOT(_k_toggleBookmarks(bool)) );
445 
446  KActionMenu *menu = new KActionMenu( KIcon("configure"), i18n("Options"), this);
447  coll->addAction("extra menu", menu);
448  menu->setWhatsThis(i18n("<qt>This is the preferences menu for the file dialog. "
449  "Various options can be accessed from this menu including: <ul>"
450  "<li>how files are sorted in the list</li>"
451  "<li>types of view, including icon and list</li>"
452  "<li>showing of hidden files</li>"
453  "<li>the Places navigation panel</li>"
454  "<li>file previews</li>"
455  "<li>separating folders from files</li></ul></qt>"));
456  menu->addAction(coll->action("sorting menu"));
457  menu->addAction(coll->action("view menu"));
458  menu->addSeparator();
459  menu->addAction(coll->action("decoration menu"));
460  menu->addSeparator();
461  KAction * showHidden = qobject_cast<KAction*>(coll->action( "show hidden" ));
462  if (showHidden) {
463  showHidden->setShortcut(
464  KShortcut( QKeySequence(Qt::ALT + Qt::Key_Period), QKeySequence(Qt::Key_F8) ) );
465  }
466  menu->addAction( showHidden );
467  menu->addAction( showSidebarAction );
468  menu->addAction( showBookmarksAction );
469  coll->action( "inline preview" )->setShortcut( QKeySequence(Qt::Key_F11) );
470  menu->addAction( coll->action( "preview" ));
471 
472  menu->setDelayed( false );
473  connect( menu->menu(), SIGNAL(aboutToShow()),
474  d->ops, SLOT(updateSelectionDependentActions()));
475 
476  d->iconSizeSlider = new QSlider(this);
477  d->iconSizeSlider->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
478  d->iconSizeSlider->setOrientation(Qt::Horizontal);
479  d->iconSizeSlider->setMinimum(0);
480  d->iconSizeSlider->setMaximum(100);
481  d->iconSizeSlider->installEventFilter(this);
482  connect(d->iconSizeSlider, SIGNAL(valueChanged(int)),
483  d->ops, SLOT(setIconsZoom(int)));
484  connect(d->iconSizeSlider, SIGNAL(valueChanged(int)),
485  this, SLOT(_k_slotIconSizeChanged(int)));
486  connect(d->iconSizeSlider, SIGNAL(sliderMoved(int)),
487  this, SLOT(_k_slotIconSizeSliderMoved(int)));
488  connect(d->ops, SIGNAL(currentIconSizeChanged(int)),
489  d->iconSizeSlider, SLOT(setValue(int)));
490 
491  KAction *furtherAction = new KAction(KIcon("file-zoom-out"), i18n("Zoom out"), this);
492  connect(furtherAction, SIGNAL(triggered()), SLOT(_k_zoomOutIconsSize()));
493  KAction *closerAction = new KAction(KIcon("file-zoom-in"), i18n("Zoom in"), this);
494  connect(closerAction, SIGNAL(triggered()), SLOT(_k_zoomInIconsSize()));
495 
496  QWidget *midSpacer = new QWidget(this);
497  midSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
498 
499  QAction *separator = new QAction(this);
500  separator->setSeparator(true);
501 
502  QAction *separator2 = new QAction(this);
503  separator2->setSeparator(true);
504 
505  d->toolbar->addAction(coll->action("back" ));
506  d->toolbar->addAction(coll->action("forward"));
507  d->toolbar->addAction(coll->action("up"));
508  d->toolbar->addAction(coll->action("reload"));
509  d->toolbar->addAction(separator);
510  d->toolbar->addAction(coll->action("inline preview"));
511  d->toolbar->addWidget(midSpacer);
512  d->toolbar->addAction(furtherAction);
513  d->toolbar->addWidget(d->iconSizeSlider);
514  d->toolbar->addAction(closerAction);
515  d->toolbar->addAction(separator2);
516  d->toolbar->addAction(coll->action("mkdir"));
517  d->toolbar->addAction(menu);
518 
519  d->toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
520  d->toolbar->setMovable(false);
521 
522  KUrlCompletion *pathCompletionObj = new KUrlCompletion( KUrlCompletion::DirCompletion );
523  pathCombo->setCompletionObject( pathCompletionObj );
524  pathCombo->setAutoDeleteCompletionObject( true );
525 
526  connect( d->urlNavigator, SIGNAL(urlChanged(KUrl)),
527  this, SLOT(_k_enterUrl(KUrl)));
528  connect( d->urlNavigator, SIGNAL(returnPressed()),
529  d->ops, SLOT(setFocus()));
530 
531  QString whatsThisText;
532 
533  // the Location label/edit
534  d->locationLabel = new QLabel(i18n("&Name:"), this);
535  d->locationEdit = new KUrlComboBox(KUrlComboBox::Files, true, this);
536  d->locationEdit->installEventFilter(this);
537  // Properly let the dialog be resized (to smaller). Otherwise we could have
538  // huge dialogs that can't be resized to smaller (it would be as big as the longest
539  // item in this combo box). (ereslibre)
540  d->locationEdit->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
541  connect( d->locationEdit, SIGNAL(editTextChanged(QString)),
542  SLOT(_k_slotLocationChanged(QString)) );
543 
544  d->updateLocationWhatsThis();
545  d->locationLabel->setBuddy(d->locationEdit);
546 
547  KUrlCompletion *fileCompletionObj = new KUrlCompletion( KUrlCompletion::FileCompletion );
548  d->locationEdit->setCompletionObject( fileCompletionObj );
549  d->locationEdit->setAutoDeleteCompletionObject( true );
550  connect( fileCompletionObj, SIGNAL(match(QString)),
551  SLOT(_k_fileCompletion(QString)) );
552 
553  connect(d->locationEdit, SIGNAL(returnPressed(QString)),
554  this, SLOT(_k_locationAccepted(QString)));
555 
556  // the Filter label/edit
557  whatsThisText = i18n("<qt>This is the filter to apply to the file list. "
558  "File names that do not match the filter will not be shown.<p>"
559  "You may select from one of the preset filters in the "
560  "drop down menu, or you may enter a custom filter "
561  "directly into the text area.</p><p>"
562  "Wildcards such as * and ? are allowed.</p></qt>");
563  d->filterLabel = new QLabel(i18n("&Filter:"), this);
564  d->filterLabel->setWhatsThis(whatsThisText);
565  d->filterWidget = new KFileFilterCombo(this);
566  // Properly let the dialog be resized (to smaller). Otherwise we could have
567  // huge dialogs that can't be resized to smaller (it would be as big as the longest
568  // item in this combo box). (ereslibre)
569  d->filterWidget->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
570  d->filterWidget->setWhatsThis(whatsThisText);
571  d->filterLabel->setBuddy(d->filterWidget);
572  connect(d->filterWidget, SIGNAL(filterChanged()), SLOT(_k_slotFilterChanged()));
573 
574  d->filterDelayTimer.setSingleShot(true);
575  d->filterDelayTimer.setInterval(300);
576  connect(d->filterWidget, SIGNAL(editTextChanged(QString)), &d->filterDelayTimer, SLOT(start()));
577  connect(&d->filterDelayTimer, SIGNAL(timeout()), SLOT(_k_slotFilterChanged()));
578 
579  // the Automatically Select Extension checkbox
580  // (the text, visibility etc. is set in updateAutoSelectExtension(), which is called by readConfig())
581  d->autoSelectExtCheckBox = new QCheckBox (this);
582  d->autoSelectExtCheckBox->setStyleSheet(QString("QCheckBox { padding-top: %1px; }").arg(KDialog::spacingHint()));
583  connect(d->autoSelectExtCheckBox, SIGNAL(clicked()), SLOT(_k_slotAutoSelectExtClicked()));
584 
585  d->initGUI(); // activate GM
586 
587  // read our configuration
588  KSharedConfig::Ptr config = KGlobal::config();
589  KConfigGroup viewConfigGroup(config, ConfigGroup);
590  d->readConfig(viewConfigGroup);
591 
592  coll->action("inline preview")->setChecked(d->ops->isInlinePreviewShown());
593  d->iconSizeSlider->setValue(d->ops->iconsZoom());
594 
595  KFilePreviewGenerator *pg = d->ops->previewGenerator();
596  if (pg) {
597  coll->action("inline preview")->setChecked(pg->isPreviewShown());
598  }
599 
600  // getStartUrl() above will have resolved the startDir parameter into
601  // a directory and file name in the two cases: (a) where it is a
602  // special "kfiledialog:" URL, or (b) where it is a plain file name
603  // only without directory or protocol. For any other startDir
604  // specified, it is not possible to resolve whether there is a file name
605  // present just by looking at the URL; the only way to be sure is
606  // to stat it.
607  bool statRes = false;
608  if ( filename.isEmpty() )
609  {
610  KIO::StatJob *statJob = KIO::stat(startDir, KIO::HideProgressInfo);
611  statRes = KIO::NetAccess::synchronousRun(statJob, this);
612  kDebug(kfile_area) << "stat of" << startDir << "-> statRes" << statRes << "isDir" << statJob->statResult().isDir();
613  if (!statRes || !statJob->statResult().isDir()) {
614  filename = startDir.fileName();
615  startDir.setPath(startDir.directory());
616  kDebug(kfile_area) << "statJob -> startDir" << startDir << "filename" << filename;
617  }
618  }
619 
620  d->ops->setUrl(startDir, true);
621  d->urlNavigator->setLocationUrl(startDir);
622  if (d->placesView) {
623  d->placesView->setUrl(startDir);
624  }
625 
626  // We have a file name either explicitly specified, or have checked that
627  // we could stat it and it is not a directory. Set it.
628  if (!filename.isEmpty()) {
629  QLineEdit* lineEdit = d->locationEdit->lineEdit();
630  kDebug(kfile_area) << "selecting filename" << filename;
631  if (statRes) {
632  d->setLocationText(filename);
633  } else {
634  lineEdit->setText(filename);
635  // Preserve this filename when clicking on the view (cf _k_fileHighlighted)
636  lineEdit->setModified(true);
637  }
638  lineEdit->selectAll();
639  }
640 
641  d->locationEdit->setFocus();
642 }
643 
644 KFileWidget::~KFileWidget()
645 {
646  KSharedConfig::Ptr config = KGlobal::config();
647  config->sync();
648 
649  delete d;
650 }
651 
652 void KFileWidget::setLocationLabel(const QString& text)
653 {
654  d->locationLabel->setText(text);
655 }
656 
657 void KFileWidget::setFilter(const QString& filter)
658 {
659  int pos = filter.indexOf('/');
660 
661  // Check for an un-escaped '/', if found
662  // interpret as a MIME filter.
663 
664  if (pos > 0 && filter[pos - 1] != '\\') {
665  QStringList filters = filter.split(' ', QString::SkipEmptyParts);
666  setMimeFilter( filters );
667  return;
668  }
669 
670  // Strip the escape characters from
671  // escaped '/' characters.
672 
673  QString copy (filter);
674  for (pos = 0; (pos = copy.indexOf("\\/", pos)) != -1; ++pos)
675  copy.remove(pos, 1);
676 
677  d->ops->clearFilter();
678  d->filterWidget->setFilter(copy);
679  d->ops->setNameFilter(d->filterWidget->currentFilter());
680  d->ops->updateDir();
681  d->hasDefaultFilter = false;
682  d->filterWidget->setEditable( true );
683 
684  d->updateAutoSelectExtension ();
685 }
686 
687 QString KFileWidget::currentFilter() const
688 {
689  return d->filterWidget->currentFilter();
690 }
691 
692 void KFileWidget::setMimeFilter( const QStringList& mimeTypes,
693  const QString& defaultType )
694 {
695  d->filterWidget->setMimeFilter( mimeTypes, defaultType );
696 
697  QStringList types = d->filterWidget->currentFilter().split(' ', QString::SkipEmptyParts); //QStringList::split(" ", d->filterWidget->currentFilter());
698  types.append( QLatin1String( "inode/directory" ));
699  d->ops->clearFilter();
700  d->ops->setMimeFilter( types );
701  d->hasDefaultFilter = !defaultType.isEmpty();
702  d->filterWidget->setEditable( !d->hasDefaultFilter ||
703  d->operationMode != Saving );
704 
705  d->updateAutoSelectExtension ();
706 }
707 
708 void KFileWidget::clearFilter()
709 {
710  d->filterWidget->setFilter( QString() );
711  d->ops->clearFilter();
712  d->hasDefaultFilter = false;
713  d->filterWidget->setEditable( true );
714 
715  d->updateAutoSelectExtension ();
716 }
717 
718 QString KFileWidget::currentMimeFilter() const
719 {
720  int i = d->filterWidget->currentIndex();
721  if (d->filterWidget->showsAllTypes() && i == 0)
722  return QString(); // The "all types" item has no mimetype
723 
724  return d->filterWidget->filters()[i];
725 }
726 
727 KMimeType::Ptr KFileWidget::currentFilterMimeType()
728 {
729  return KMimeType::mimeType( currentMimeFilter() );
730 }
731 
732 void KFileWidget::setPreviewWidget(KPreviewWidgetBase *w) {
733  d->ops->setPreviewWidget(w);
734  d->ops->clearHistory();
735  d->hasView = true;
736 }
737 
738 KUrl KFileWidgetPrivate::getCompleteUrl(const QString &_url) const
739 {
740 // kDebug(kfile_area) << "got url " << _url;
741 
742  const QString url = KShell::tildeExpand(_url);
743  KUrl u;
744 
745  if (QDir::isAbsolutePath(url)) {
746  u = url;
747  } else {
748  KUrl relativeUrlTest(ops->url());
749  relativeUrlTest.addPath(url);
750  if (!ops->dirLister()->findByUrl(relativeUrlTest).isNull() ||
751  !KProtocolInfo::isKnownProtocol(relativeUrlTest)) {
752  u = relativeUrlTest;
753  } else {
754  u = url;
755  }
756  }
757 
758  return u;
759 }
760 
761 // Called by KFileDialog
762 void KFileWidget::slotOk()
763 {
764 // kDebug(kfile_area) << "slotOk\n";
765 
766  const KFileItemList items = d->ops->selectedItems();
767  const QString locationEditCurrentText(KShell::tildeExpand(d->locationEditCurrentText()));
768 
769  KUrl::List locationEditCurrentTextList(d->tokenize(locationEditCurrentText));
770  KFile::Modes mode = d->ops->mode();
771 
772  // if there is nothing to do, just return from here
773  if (!locationEditCurrentTextList.count()) {
774  return;
775  }
776 
777  // Make sure that one of the modes was provided
778  if (!((mode & KFile::File) || (mode & KFile::Directory) || (mode & KFile::Files))) {
779  mode |= KFile::File;
780  kDebug(kfile_area) << "No mode() provided";
781  }
782 
783  // if we are on file mode, and the list of provided files/folder is greater than one, inform
784  // the user about it
785  if (locationEditCurrentTextList.count() > 1) {
786  if (mode & KFile::File) {
787  KMessageBox::sorry(this,
788  i18n("You can only select one file"),
789  i18n("More than one file provided"));
790  return;
791  }
792 
813  if (!d->differentHierarchyLevelItemsEntered) { // avoid infinite recursion. running this
814  KUrl::List urlList; // one time is always enough.
815  int start = 0;
816  KUrl topMostUrl;
817  KIO::StatJob *statJob = 0;
818  bool res = false;
819 
820  // we need to check for a valid first url, so in theory we only iterate one time over
821  // this loop. However it can happen that the user did
822  // "home/foo/nonexistantfile" "boot/grub/menu.lst", so we look for a good first
823  // candidate.
824  while (!res && start < locationEditCurrentTextList.count()) {
825  topMostUrl = locationEditCurrentTextList.at(start);
826  statJob = KIO::stat(topMostUrl, KIO::HideProgressInfo);
827  res = KIO::NetAccess::synchronousRun(statJob, this);
828  start++;
829  }
830 
831  Q_ASSERT(statJob);
832 
833  // if this is not a dir, strip the filename. after this we have an existent and valid
834  // dir (if we stated correctly the file, setting a null filename won't make any bad).
835  if (!statJob->statResult().isDir()) {
836  topMostUrl.setFileName(QString());
837  }
838 
839  // now the funny part. for the rest of filenames, go and look for the closest ancestor
840  // of all them.
841  for (int i = start; i < locationEditCurrentTextList.count(); ++i) {
842  KUrl currUrl = locationEditCurrentTextList.at(i);
843  KIO::StatJob *statJob = KIO::stat(currUrl, KIO::HideProgressInfo);
844  bool res = KIO::NetAccess::synchronousRun(statJob, this);
845  if (res) {
846  // again, we don't care about filenames
847  if (!statJob->statResult().isDir()) {
848  currUrl.setFileName(QString());
849  }
850 
851  // iterate while this item is contained on the top most url
852  while (!topMostUrl.isParentOf(currUrl)) {
853  topMostUrl = topMostUrl.upUrl();
854  }
855  }
856  }
857 
858  // now recalculate all paths for them being relative in base of the top most url
859  for (int i = 0; i < locationEditCurrentTextList.count(); ++i) {
860  locationEditCurrentTextList[i] = KUrl::relativeUrl(topMostUrl, locationEditCurrentTextList[i]);
861  }
862 
863  d->ops->setUrl(topMostUrl, true);
864  const bool signalsBlocked = d->locationEdit->lineEdit()->blockSignals(true);
865  QStringList stringList;
866  foreach (const KUrl &url, locationEditCurrentTextList) {
867  stringList << url.prettyUrl();
868  }
869  d->locationEdit->lineEdit()->setText(QString("\"%1\"").arg(stringList.join("\" \"")));
870  d->locationEdit->lineEdit()->blockSignals(signalsBlocked);
871 
872  d->differentHierarchyLevelItemsEntered = true;
873  slotOk();
874  return;
875  }
879  } else if (locationEditCurrentTextList.count()) {
880  // if we are on file or files mode, and we have an absolute url written by
881  // the user, convert it to relative
882  if (!locationEditCurrentText.isEmpty() && !(mode & KFile::Directory) &&
883  (QDir::isAbsolutePath(locationEditCurrentText) ||
884  containsProtocolSection(locationEditCurrentText))) {
885 
886  QString fileName;
887  KUrl url(locationEditCurrentText);
888  if (d->operationMode == Opening) {
889  KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
890  bool res = KIO::NetAccess::synchronousRun(statJob, this);
891  if (res) {
892  if (!statJob->statResult().isDir()) {
893  url.adjustPath(KUrl::RemoveTrailingSlash);
894  fileName = url.fileName();
895  url.setFileName(QString());
896  } else {
897  url.adjustPath(KUrl::AddTrailingSlash);
898  }
899  }
900  } else {
901  KUrl directory = url;
902  directory.setFileName(QString());
903  //Check if the folder exists
904  KIO::StatJob * statJob = KIO::stat(directory, KIO::HideProgressInfo);
905  bool res = KIO::NetAccess::synchronousRun(statJob, this);
906  if (res) {
907  if (statJob->statResult().isDir()) {
908  url.adjustPath(KUrl::RemoveTrailingSlash);
909  fileName = url.fileName();
910  url.setFileName(QString());
911  }
912  }
913  }
914  d->ops->setUrl(url, true);
915  const bool signalsBlocked = d->locationEdit->lineEdit()->blockSignals(true);
916  d->locationEdit->lineEdit()->setText(fileName);
917  d->locationEdit->lineEdit()->blockSignals(signalsBlocked);
918  slotOk();
919  return;
920  }
921  }
922 
923  // restore it
924  d->differentHierarchyLevelItemsEntered = false;
925 
926  // locationEditCurrentTextList contains absolute paths
927  // this is the general loop for the File and Files mode. Obviously we know
928  // that the File mode will iterate only one time here
929  bool directoryMode = (mode & KFile::Directory);
930  bool onlyDirectoryMode = directoryMode && !(mode & KFile::File) && !(mode & KFile::Files);
931  KUrl::List::ConstIterator it = locationEditCurrentTextList.constBegin();
932  bool filesInList = false;
933  while (it != locationEditCurrentTextList.constEnd()) {
934  KUrl url(*it);
935 
936  if (d->operationMode == Saving && !directoryMode) {
937  d->appendExtension(url);
938  }
939 
940  d->url = url;
941  KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
942  bool res = KIO::NetAccess::synchronousRun(statJob, this);
943 
944  if (!KAuthorized::authorizeUrlAction("open", KUrl(), url)) {
945  QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, d->url.prettyUrl());
946  KMessageBox::error(this, msg);
947  return;
948  }
949 
950  // if we are on local mode, make sure we haven't got a remote base url
951  if ((mode & KFile::LocalOnly) && !d->mostLocalUrl(d->url).isLocalFile()) {
952  KMessageBox::sorry(this,
953  i18n("You can only select local files"),
954  i18n("Remote files not accepted"));
955  return;
956  }
957 
958  if ((d->operationMode == Saving) && d->confirmOverwrite && !d->toOverwrite(url)) {
959  return;
960  }
961 
962  // if we are given a folder when not on directory mode, let's get into it
963  if (res && !directoryMode && statJob->statResult().isDir()) {
964  // check if we were given more than one folder, in that case we don't know to which one
965  // cd
966  ++it;
967  while (it != locationEditCurrentTextList.constEnd()) {
968  KUrl checkUrl(*it);
969  KIO::StatJob *checkStatJob = KIO::stat(checkUrl, KIO::HideProgressInfo);
970  bool res = KIO::NetAccess::synchronousRun(checkStatJob, this);
971  if (res && checkStatJob->statResult().isDir()) {
972  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"));
973  return;
974  } else if (res) {
975  filesInList = true;
976  }
977  ++it;
978  }
979  if (filesInList) {
980  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"));
981  }
982  d->ops->setUrl(url, true);
983  const bool signalsBlocked = d->locationEdit->lineEdit()->blockSignals(true);
984  d->locationEdit->lineEdit()->setText(QString());
985  d->locationEdit->lineEdit()->blockSignals(signalsBlocked);
986  return;
987  } else if (!(mode & KFile::ExistingOnly) || res) {
988  // if we don't care about ExistingOnly flag, add the file even if
989  // it doesn't exist. If we care about it, don't add it to the list
990  if (!onlyDirectoryMode || (res && statJob->statResult().isDir())) {
991  d->urlList << url;
992  }
993  filesInList = true;
994  } else {
995  KMessageBox::sorry(this, i18n("The file \"%1\" could not be found", url.pathOrUrl()), i18n("Cannot open file"));
996  return; // do not emit accepted() if we had ExistingOnly flag and stat failed
997  }
998  ++it;
999  }
1000 
1001  // if we have reached this point and we didn't return before, that is because
1002  // we want this dialog to be accepted
1003  emit accepted();
1004 }
1005 
1006 void KFileWidget::accept()
1007 {
1008  d->inAccept = true; // parseSelectedUrls() checks that
1009 
1010  *lastDirectory = d->ops->url();
1011  if (!d->fileClass.isEmpty())
1012  KRecentDirs::add(d->fileClass, d->ops->url().url());
1013 
1014  // clear the topmost item, we insert it as full path later on as item 1
1015  d->locationEdit->setItemText( 0, QString() );
1016 
1017  const KUrl::List list = selectedUrls();
1018  QList<KUrl>::const_iterator it = list.begin();
1019  int atmost = d->locationEdit->maxItems(); //don't add more items than necessary
1020  for ( ; it != list.end() && atmost > 0; ++it ) {
1021  const KUrl& url = *it;
1022  // we strip the last slash (-1) because KUrlComboBox does that as well
1023  // when operating in file-mode. If we wouldn't , dupe-finding wouldn't
1024  // work.
1025  QString file = url.isLocalFile() ? url.toLocalFile(KUrl::RemoveTrailingSlash) : url.prettyUrl(KUrl::RemoveTrailingSlash);
1026 
1027  // remove dupes
1028  for ( int i = 1; i < d->locationEdit->count(); i++ ) {
1029  if ( d->locationEdit->itemText( i ) == file ) {
1030  d->locationEdit->removeItem( i-- );
1031  break;
1032  }
1033  }
1034  //FIXME I don't think this works correctly when the KUrlComboBox has some default urls.
1035  //KUrlComboBox should provide a function to add an url and rotate the existing ones, keeping
1036  //track of maxItems, and we shouldn't be able to insert items as we please.
1037  d->locationEdit->insertItem( 1,file);
1038  atmost--;
1039  }
1040 
1041  KSharedConfig::Ptr config = KGlobal::config();
1042  KConfigGroup grp(config,ConfigGroup);
1043  d->writeConfig(grp);
1044  d->saveRecentFiles(grp);
1045 
1046  d->addToRecentDocuments();
1047 
1048  if (!(mode() & KFile::Files)) { // single selection
1049  emit fileSelected(d->url.url()); // old
1050  emit fileSelected(d->url);
1051  }
1052 
1053  d->ops->close();
1054 }
1055 
1056 
1057 void KFileWidgetPrivate::_k_fileHighlighted(const KFileItem &i)
1058 {
1059  if ((!i.isNull() && i.isDir() ) ||
1060  (locationEdit->hasFocus() && !locationEdit->currentText().isEmpty())) // don't disturb
1061  return;
1062 
1063  const bool modified = locationEdit->lineEdit()->isModified();
1064 
1065  if (!(ops->mode() & KFile::Files)) {
1066  if (i.isNull()) {
1067  if (!modified) {
1068  setLocationText(KUrl());
1069  }
1070  return;
1071  }
1072 
1073  url = i.url();
1074 
1075  if (!locationEdit->hasFocus()) { // don't disturb while editing
1076  setLocationText( url );
1077  }
1078 
1079  emit q->fileHighlighted(url.url()); // old
1080  emit q->fileHighlighted(url);
1081  } else {
1082  multiSelectionChanged();
1083  emit q->selectionChanged();
1084  }
1085 
1086  locationEdit->lineEdit()->setModified( false );
1087  locationEdit->lineEdit()->selectAll();
1088 }
1089 
1090 void KFileWidgetPrivate::_k_fileSelected(const KFileItem &i)
1091 {
1092  if (!i.isNull() && i.isDir()) {
1093  return;
1094  }
1095 
1096  if (!(ops->mode() & KFile::Files)) {
1097  if (i.isNull()) {
1098  setLocationText(KUrl());
1099  return;
1100  }
1101  setLocationText(i.url());
1102  } else {
1103  multiSelectionChanged();
1104  emit q->selectionChanged();
1105  }
1106 
1107  // if we are saving, let another chance to the user before accepting the dialog (or trying to
1108  // accept). This way the user can choose a file and add a "_2" for instance to the filename
1109  if (operationMode == KFileWidget::Saving) {
1110  locationEdit->setFocus();
1111  } else {
1112  q->slotOk();
1113  }
1114 }
1115 
1116 
1117 // I know it's slow to always iterate thru the whole filelist
1118 // (d->ops->selectedItems()), but what can we do?
1119 void KFileWidgetPrivate::multiSelectionChanged()
1120 {
1121  if (locationEdit->hasFocus() && !locationEdit->currentText().isEmpty()) { // don't disturb
1122  return;
1123  }
1124 
1125  const KFileItemList list = ops->selectedItems();
1126 
1127  if (list.isEmpty()) {
1128  setLocationText(KUrl());
1129  return;
1130  }
1131 
1132  KUrl::List urlList;
1133  foreach (const KFileItem &fileItem, list) {
1134  urlList << fileItem.url();
1135  }
1136 
1137  setLocationText(urlList);
1138 }
1139 
1140 void KFileWidgetPrivate::setDummyHistoryEntry( const QString& text, const QPixmap& icon,
1141  bool usePreviousPixmapIfNull )
1142 {
1143  // setCurrentItem() will cause textChanged() being emitted,
1144  // so slotLocationChanged() will be called. Make sure we don't clear
1145  // the KDirOperator's view-selection in there
1146  QObject::disconnect( locationEdit, SIGNAL(editTextChanged(QString)),
1147  q, SLOT(_k_slotLocationChanged(QString)) );
1148 
1149  bool dummyExists = dummyAdded;
1150 
1151  int cursorPosition = locationEdit->lineEdit()->cursorPosition();
1152 
1153  if ( dummyAdded ) {
1154  if ( !icon.isNull() ) {
1155  locationEdit->setItemIcon( 0, icon );
1156  locationEdit->setItemText( 0, text );
1157  } else {
1158  if ( !usePreviousPixmapIfNull ) {
1159  locationEdit->setItemIcon( 0, QPixmap() );
1160  }
1161  locationEdit->setItemText( 0, text );
1162  }
1163  } else {
1164  if ( !text.isEmpty() ) {
1165  if ( !icon.isNull() ) {
1166  locationEdit->insertItem( 0, icon, text );
1167  } else {
1168  if ( !usePreviousPixmapIfNull ) {
1169  locationEdit->insertItem( 0, QPixmap(), text );
1170  } else {
1171  locationEdit->insertItem( 0, text );
1172  }
1173  }
1174  dummyAdded = true;
1175  dummyExists = true;
1176  }
1177  }
1178 
1179  if ( dummyExists && !text.isEmpty() ) {
1180  locationEdit->setCurrentIndex( 0 );
1181  }
1182 
1183  locationEdit->lineEdit()->setCursorPosition( cursorPosition );
1184 
1185  QObject::connect( locationEdit, SIGNAL(editTextChanged(QString)),
1186  q, SLOT(_k_slotLocationChanged(QString)) );
1187 }
1188 
1189 void KFileWidgetPrivate::removeDummyHistoryEntry()
1190 {
1191  if ( !dummyAdded ) {
1192  return;
1193  }
1194 
1195  // setCurrentItem() will cause textChanged() being emitted,
1196  // so slotLocationChanged() will be called. Make sure we don't clear
1197  // the KDirOperator's view-selection in there
1198  QObject::disconnect( locationEdit, SIGNAL(editTextChanged(QString)),
1199  q, SLOT(_k_slotLocationChanged(QString)) );
1200 
1201  if (locationEdit->count()) {
1202  locationEdit->removeItem( 0 );
1203  }
1204  locationEdit->setCurrentIndex( -1 );
1205  dummyAdded = false;
1206 
1207  QObject::connect( locationEdit, SIGNAL(editTextChanged(QString)),
1208  q, SLOT(_k_slotLocationChanged(QString)) );
1209 }
1210 
1211 void KFileWidgetPrivate::setLocationText(const KUrl& url)
1212 {
1213  if (!url.isEmpty()) {
1214  QPixmap mimeTypeIcon = KIconLoader::global()->loadMimeTypeIcon( KMimeType::iconNameForUrl( url ), KIconLoader::Small );
1215  if (url.hasPath()) {
1216  if (!url.directory().isEmpty())
1217  {
1218  KUrl u(url);
1219  u.setPath(u.directory());
1220  q->setUrl(u, false);
1221  }
1222  else {
1223  q->setUrl(url.path(), false);
1224  }
1225  }
1226  setDummyHistoryEntry(url.fileName() , mimeTypeIcon);
1227  } else {
1228  removeDummyHistoryEntry();
1229  }
1230 
1231  // don't change selection when user has clicked on an item
1232  if (operationMode == KFileWidget::Saving && !locationEdit->isVisible()) {
1233  setNonExtSelection();
1234  }
1235 }
1236 
1237 void KFileWidgetPrivate::setLocationText( const KUrl::List& urlList )
1238 {
1239  const KUrl currUrl = ops->url();
1240 
1241  if ( urlList.count() > 1 ) {
1242  QString urls;
1243  foreach (const KUrl &url, urlList) {
1244  urls += QString( "\"%1\"" ).arg( KUrl::relativeUrl(currUrl, url) ) + ' ';
1245  }
1246  urls = urls.left( urls.size() - 1 );
1247 
1248  setDummyHistoryEntry( urls, QPixmap(), false );
1249  } else if ( urlList.count() ) {
1250  const QPixmap mimeTypeIcon = KIconLoader::global()->loadMimeTypeIcon( KMimeType::iconNameForUrl( urlList[0] ), KIconLoader::Small );
1251  setDummyHistoryEntry( KUrl::relativeUrl(currUrl, urlList[0]), mimeTypeIcon );
1252  } else {
1253  removeDummyHistoryEntry();
1254  }
1255 
1256  // don't change selection when user has clicked on an item
1257  if ( operationMode == KFileWidget::Saving && !locationEdit->isVisible())
1258  setNonExtSelection();
1259 }
1260 
1261 void KFileWidgetPrivate::updateLocationWhatsThis()
1262 {
1263  QString whatsThisText;
1264  if (operationMode == KFileWidget::Saving)
1265  {
1266  whatsThisText = "<qt>" + i18n("This is the name to save the file as.") +
1267  i18n (autocompletionWhatsThisText);
1268  }
1269  else if (ops->mode() & KFile::Files)
1270  {
1271  whatsThisText = "<qt>" + i18n("This is the list of files to open. More than "
1272  "one file can be specified by listing several "
1273  "files, separated by spaces.") +
1274  i18n (autocompletionWhatsThisText);
1275  }
1276  else
1277  {
1278  whatsThisText = "<qt>" + i18n("This is the name of the file to open.") +
1279  i18n (autocompletionWhatsThisText);
1280  }
1281 
1282  locationLabel->setWhatsThis(whatsThisText);
1283  locationEdit->setWhatsThis(whatsThisText);
1284 }
1285 
1286 void KFileWidgetPrivate::initSpeedbar()
1287 {
1288  if (placesDock) {
1289  return;
1290  }
1291 
1292  placesDock = new QDockWidget(i18nc("@title:window", "Places"), q);
1293  placesDock->setFeatures(QDockWidget::DockWidgetClosable);
1294 
1295  placesView = new KFilePlacesView(placesDock);
1296  placesView->setModel(model);
1297  placesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1298 
1299  placesView->setObjectName(QLatin1String("url bar"));
1300  QObject::connect(placesView, SIGNAL(urlChanged(KUrl)),
1301  q, SLOT(_k_enterUrl(KUrl)));
1302 
1303  // need to set the current url of the urlbar manually (not via urlEntered()
1304  // here, because the initial url of KDirOperator might be the same as the
1305  // one that will be set later (and then urlEntered() won't be emitted).
1306  // TODO: KDE5 ### REMOVE THIS when KDirOperator's initial URL (in the c'tor) is gone.
1307  placesView->setUrl(url);
1308 
1309  placesDock->setWidget(placesView);
1310  placesViewSplitter->insertWidget(0, placesDock);
1311 
1312  // initialize the size of the splitter
1313  KConfigGroup configGroup(KGlobal::config(), ConfigGroup);
1314  placesViewWidth = configGroup.readEntry(SpeedbarWidth, placesView->sizeHint().width());
1315 
1316  QList<int> sizes = placesViewSplitter->sizes();
1317  if (placesViewWidth > 0) {
1318  sizes[0] = placesViewWidth + 1;
1319  sizes[1] = q->width() - placesViewWidth -1;
1320  placesViewSplitter->setSizes(sizes);
1321  }
1322 
1323  QObject::connect(placesDock, SIGNAL(visibilityChanged(bool)),
1324  q, SLOT(_k_toggleSpeedbar(bool)));
1325 }
1326 
1327 void KFileWidgetPrivate::initGUI()
1328 {
1329  delete boxLayout; // deletes all sub layouts
1330 
1331  boxLayout = new QVBoxLayout( q);
1332  boxLayout->setMargin(0); // no additional margin to the already existing
1333 
1334  placesViewSplitter = new QSplitter(q);
1335  placesViewSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
1336  placesViewSplitter->setChildrenCollapsible(false);
1337  boxLayout->addWidget(placesViewSplitter);
1338 
1339  QObject::connect(placesViewSplitter, SIGNAL(splitterMoved(int,int)),
1340  q, SLOT(_k_placesViewSplitterMoved(int,int)));
1341  placesViewSplitter->insertWidget(0, opsWidget);
1342 
1343  vbox = new QVBoxLayout();
1344  vbox->setMargin(0);
1345  boxLayout->addLayout(vbox);
1346 
1347  lafBox = new QGridLayout();
1348 
1349  lafBox->addWidget(locationLabel, 0, 0, Qt::AlignVCenter | Qt::AlignRight);
1350  lafBox->addWidget(locationEdit, 0, 1, Qt::AlignVCenter);
1351  lafBox->addWidget(okButton, 0, 2, Qt::AlignVCenter);
1352 
1353  lafBox->addWidget(filterLabel, 1, 0, Qt::AlignVCenter | Qt::AlignRight);
1354  lafBox->addWidget(filterWidget, 1, 1, Qt::AlignVCenter);
1355  lafBox->addWidget(cancelButton, 1, 2, Qt::AlignVCenter);
1356 
1357  lafBox->setColumnStretch(1, 4);
1358 
1359  vbox->addLayout(lafBox);
1360 
1361  // add the Automatically Select Extension checkbox
1362  vbox->addWidget(autoSelectExtCheckBox);
1363 
1364  q->setTabOrder(ops, autoSelectExtCheckBox);
1365  q->setTabOrder(autoSelectExtCheckBox, locationEdit);
1366  q->setTabOrder(locationEdit, filterWidget);
1367  q->setTabOrder(filterWidget, okButton);
1368  q->setTabOrder(okButton, cancelButton);
1369  q->setTabOrder(cancelButton, urlNavigator);
1370  q->setTabOrder(urlNavigator, ops);
1371  q->setTabOrder(cancelButton, urlNavigator);
1372  q->setTabOrder(urlNavigator, ops);
1373 
1374 }
1375 
1376 void KFileWidgetPrivate::_k_slotFilterChanged()
1377 {
1378 // kDebug(kfile_area);
1379 
1380  filterDelayTimer.stop();
1381 
1382  QString filter = filterWidget->currentFilter();
1383  ops->clearFilter();
1384 
1385  if ( filter.contains('/') ) {
1386  QStringList types = filter.split(' ', QString::SkipEmptyParts);
1387  types.prepend("inode/directory");
1388  ops->setMimeFilter( types );
1389  }
1390  else if ( filter.contains('*') || filter.contains('?') || filter.contains('[') ) {
1391  ops->setNameFilter( filter );
1392  }
1393  else {
1394  ops->setNameFilter('*' + filter.replace(' ', '*') + '*');
1395  }
1396 
1397  ops->updateDir();
1398 
1399  updateAutoSelectExtension();
1400 
1401  emit q->filterChanged(filter);
1402 }
1403 
1404 
1405 void KFileWidget::setUrl(const KUrl& url, bool clearforward)
1406 {
1407 // kDebug(kfile_area);
1408 
1409  d->ops->setUrl(url, clearforward);
1410 }
1411 
1412 // Protected
1413 void KFileWidgetPrivate::_k_urlEntered(const KUrl& url)
1414 {
1415 // kDebug(kfile_area);
1416 
1417  QString filename = locationEditCurrentText();
1418 
1419  KUrlComboBox* pathCombo = urlNavigator->editor();
1420  if (pathCombo->count() != 0) { // little hack
1421  pathCombo->setUrl(url);
1422  }
1423 
1424  bool blocked = locationEdit->blockSignals(true);
1425  if (keepLocation) {
1426  locationEdit->changeUrl(0, KIcon(KMimeType::iconNameForUrl(filename)), filename);
1427  locationEdit->lineEdit()->setModified(true);
1428  }
1429 
1430  locationEdit->blockSignals( blocked );
1431 
1432  urlNavigator->setLocationUrl(url);
1433 
1434  // is trigged in ctor before completion object is set
1435  KUrlCompletion *completion = dynamic_cast<KUrlCompletion*>(locationEdit->completionObject());
1436  if (completion) {
1437  completion->setDir( url.path() );
1438  }
1439 
1440  if (placesView) {
1441  placesView->setUrl( url );
1442  }
1443 }
1444 
1445 void KFileWidgetPrivate::_k_locationAccepted(const QString &url)
1446 {
1447  Q_UNUSED(url);
1448 // kDebug(kfile_area);
1449  q->slotOk();
1450 }
1451 
1452 void KFileWidgetPrivate::_k_enterUrl( const KUrl& url )
1453 {
1454 // kDebug(kfile_area);
1455 
1456  KUrl fixedUrl( url );
1457  // append '/' if needed: url combo does not add it
1458  // tokenize() expects it because uses KUrl::setFileName()
1459  fixedUrl.adjustPath( KUrl::AddTrailingSlash );
1460  q->setUrl( fixedUrl );
1461  if (!locationEdit->hasFocus())
1462  ops->setFocus();
1463 }
1464 
1465 void KFileWidgetPrivate::_k_enterUrl( const QString& url )
1466 {
1467 // kDebug(kfile_area);
1468 
1469  _k_enterUrl( KUrl( KUrlCompletion::replacedPath( url, true, true )) );
1470 }
1471 
1472 bool KFileWidgetPrivate::toOverwrite(const KUrl &url)
1473 {
1474 // kDebug(kfile_area);
1475 
1476  KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
1477  bool res = KIO::NetAccess::synchronousRun(statJob, q);
1478 
1479  if (res) {
1480  int ret = KMessageBox::warningContinueCancel( q,
1481  i18n( "The file \"%1\" already exists. Do you wish to overwrite it?" ,
1482  url.fileName() ), i18n( "Overwrite File?" ), KStandardGuiItem::overwrite(),
1483  KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous);
1484 
1485  if (ret != KMessageBox::Continue) {
1486  return false;
1487  }
1488  return true;
1489  }
1490 
1491  return true;
1492 }
1493 
1494 void KFileWidget::setSelection(const QString& url)
1495 {
1496 // kDebug(kfile_area) << "setSelection " << url;
1497 
1498  if (url.isEmpty()) {
1499  return;
1500  }
1501 
1502  KUrl u = d->getCompleteUrl(url);
1503  if (!u.isValid()) { // if it still is
1504  kWarning() << url << " is not a correct argument for setSelection!";
1505  return;
1506  }
1507 
1508  // Honor protocols that do not support directory listing
1509  if (!u.isRelative() && !KProtocolManager::supportsListing(u))
1510  return;
1511 
1512  d->setLocationText(url);
1513 }
1514 
1515 void KFileWidgetPrivate::_k_slotLoadingFinished()
1516 {
1517  if (locationEdit->currentText().isEmpty()) {
1518  return;
1519  }
1520 
1521  ops->blockSignals(true);
1522  KUrl url = ops->url();
1523  url.adjustPath(KUrl::AddTrailingSlash);
1524  url.setFileName(locationEdit->currentText());
1525  ops->setCurrentItem(url.url());
1526  ops->blockSignals(false);
1527 }
1528 
1529 void KFileWidgetPrivate::_k_fileCompletion( const QString& match )
1530 {
1531 // kDebug(kfile_area);
1532 
1533  if (match.isEmpty() || locationEdit->currentText().contains('"')) {
1534  return;
1535  }
1536 
1537  setDummyHistoryEntry(locationEdit->currentText(), KIconLoader::global()->loadMimeTypeIcon( KMimeType::iconNameForUrl( match ), KIconLoader::Small), !locationEdit->currentText().isEmpty());
1538 }
1539 
1540 void KFileWidgetPrivate::_k_slotLocationChanged( const QString& text )
1541 {
1542 // kDebug(kfile_area);
1543 
1544  locationEdit->lineEdit()->setModified(true);
1545 
1546  if (text.isEmpty() && ops->view()) {
1547  ops->view()->clearSelection();
1548  }
1549 
1550  if (text.isEmpty()) {
1551  removeDummyHistoryEntry();
1552  } else {
1553  setDummyHistoryEntry( text );
1554  }
1555 
1556  if (!locationEdit->lineEdit()->text().isEmpty()) {
1557  const KUrl::List urlList(tokenize(text));
1558  QStringList stringList;
1559  foreach (const KUrl &url, urlList) {
1560  stringList << url.url();
1561  }
1562  ops->setCurrentItems(stringList);
1563  }
1564 
1565  updateFilter();
1566 }
1567 
1568 KUrl KFileWidget::selectedUrl() const
1569 {
1570 // kDebug(kfile_area);
1571 
1572  if ( d->inAccept )
1573  return d->url;
1574  else
1575  return KUrl();
1576 }
1577 
1578 KUrl::List KFileWidget::selectedUrls() const
1579 {
1580 // kDebug(kfile_area);
1581 
1582  KUrl::List list;
1583  if ( d->inAccept ) {
1584  if (d->ops->mode() & KFile::Files)
1585  list = d->parseSelectedUrls();
1586  else
1587  list.append( d->url );
1588  }
1589  return list;
1590 }
1591 
1592 
1593 KUrl::List& KFileWidgetPrivate::parseSelectedUrls()
1594 {
1595 // kDebug(kfile_area);
1596 
1597  if ( filenames.isEmpty() ) {
1598  return urlList;
1599  }
1600 
1601  urlList.clear();
1602  if ( filenames.contains( '/' )) { // assume _one_ absolute filename
1603  KUrl u;
1604  if ( containsProtocolSection( filenames ) )
1605  u = filenames;
1606  else
1607  u.setPath( filenames );
1608 
1609  if ( u.isValid() )
1610  urlList.append( u );
1611  else
1612  KMessageBox::error( q,
1613  i18n("The chosen filenames do not\n"
1614  "appear to be valid."),
1615  i18n("Invalid Filenames") );
1616  }
1617 
1618  else
1619  urlList = tokenize( filenames );
1620 
1621  filenames.clear(); // indicate that we parsed that one
1622 
1623  return urlList;
1624 }
1625 
1626 
1627 // FIXME: current implementation drawback: a filename can't contain quotes
1628 KUrl::List KFileWidgetPrivate::tokenize( const QString& line ) const
1629 {
1630 // kDebug(kfile_area);
1631 
1632  KUrl::List urls;
1633  KUrl u( ops->url() );
1634  u.adjustPath(KUrl::AddTrailingSlash);
1635  QString name;
1636 
1637  const int count = line.count( QLatin1Char( '"' ) );
1638  if ( count == 0 ) { // no " " -> assume one single file
1639  if (!QDir::isAbsolutePath(line)) {
1640  u.setFileName( line );
1641  if ( u.isValid() )
1642  urls.append( u );
1643  } else {
1644  urls << KUrl(line);
1645  }
1646 
1647  return urls;
1648  }
1649 
1650  int start = 0;
1651  int index1 = -1, index2 = -1;
1652  while ( true ) {
1653  index1 = line.indexOf( '"', start );
1654  index2 = line.indexOf( '"', index1 + 1 );
1655 
1656  if ( index1 < 0 || index2 < 0 )
1657  break;
1658 
1659  // get everything between the " "
1660  name = line.mid( index1 + 1, index2 - index1 - 1 );
1661 
1662  // since we use setFileName we need to do this under a temporary url
1663  KUrl _u( u );
1664  KUrl currUrl( name );
1665 
1666  if ( !QDir::isAbsolutePath(currUrl.url()) ) {
1667  _u.setFileName( name );
1668  } else {
1669  // we allow to insert various absolute paths like:
1670  // "/home/foo/bar.txt" "/boot/grub/menu.lst"
1671  _u = currUrl;
1672  }
1673 
1674  if ( _u.isValid() ) {
1675  urls.append( _u );
1676  }
1677 
1678  start = index2 + 1;
1679  }
1680 
1681  return urls;
1682 }
1683 
1684 
1685 QString KFileWidget::selectedFile() const
1686 {
1687 // kDebug(kfile_area);
1688 
1689  if ( d->inAccept ) {
1690  const KUrl url = d->mostLocalUrl(d->url);
1691  if (url.isLocalFile())
1692  return url.toLocalFile();
1693  else {
1694  KMessageBox::sorry( const_cast<KFileWidget*>(this),
1695  i18n("You can only select local files."),
1696  i18n("Remote Files Not Accepted") );
1697  }
1698  }
1699  return QString();
1700 }
1701 
1702 QStringList KFileWidget::selectedFiles() const
1703 {
1704 // kDebug(kfile_area);
1705 
1706  QStringList list;
1707 
1708  if (d->inAccept) {
1709  if (d->ops->mode() & KFile::Files) {
1710  const KUrl::List urls = d->parseSelectedUrls();
1711  QList<KUrl>::const_iterator it = urls.begin();
1712  while (it != urls.end()) {
1713  KUrl url = d->mostLocalUrl(*it);
1714  if (url.isLocalFile())
1715  list.append(url.toLocalFile());
1716  ++it;
1717  }
1718  }
1719 
1720  else { // single-selection mode
1721  if ( d->url.isLocalFile() )
1722  list.append( d->url.toLocalFile() );
1723  }
1724  }
1725 
1726  return list;
1727 }
1728 
1729 KUrl KFileWidget::baseUrl() const
1730 {
1731  return d->ops->url();
1732 }
1733 
1734 void KFileWidget::resizeEvent(QResizeEvent* event)
1735 {
1736  QWidget::resizeEvent(event);
1737 
1738  if (d->placesDock) {
1739  // we don't want our places dock actually changing size when we resize
1740  // and qt doesn't make it easy to enforce such a thing with QSplitter
1741  QList<int> sizes = d->placesViewSplitter->sizes();
1742  sizes[0] = d->placesViewWidth + 1; // without this pixel, our places view is reduced 1 pixel each time is shown.
1743  sizes[1] = width() - d->placesViewWidth - 1;
1744  d->placesViewSplitter->setSizes( sizes );
1745  }
1746 }
1747 
1748 void KFileWidget::showEvent(QShowEvent* event)
1749 {
1750  if ( !d->hasView ) { // delayed view-creation
1751  Q_ASSERT( d );
1752  Q_ASSERT( d->ops );
1753  d->ops->setView( KFile::Default );
1754  d->ops->view()->setSizePolicy( QSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ) );
1755  d->hasView = true;
1756  }
1757  d->ops->clearHistory();
1758 
1759  QWidget::showEvent(event);
1760 }
1761 
1762 bool KFileWidget::eventFilter(QObject* watched, QEvent* event)
1763 {
1764  const bool res = QWidget::eventFilter(watched, event);
1765 
1766  QKeyEvent *keyEvent = dynamic_cast<QKeyEvent*>(event);
1767  if (watched == d->iconSizeSlider && keyEvent) {
1768  if (keyEvent->key() == Qt::Key_Left || keyEvent->key() == Qt::Key_Up ||
1769  keyEvent->key() == Qt::Key_Right || keyEvent->key() == Qt::Key_Down) {
1770  d->_k_slotIconSizeSliderMoved(d->iconSizeSlider->value());
1771  }
1772  } else if (watched == d->locationEdit && event->type() == QEvent::KeyPress) {
1773  if (keyEvent->modifiers() & Qt::AltModifier) {
1774  switch (keyEvent->key()) {
1775  case Qt::Key_Up:
1776  d->ops->actionCollection()->action("up")->trigger();
1777  break;
1778  case Qt::Key_Left:
1779  d->ops->actionCollection()->action("back")->trigger();
1780  break;
1781  case Qt::Key_Right:
1782  d->ops->actionCollection()->action("forward")->trigger();
1783  break;
1784  default:
1785  break;
1786  }
1787  }
1788  }
1789 
1790  return res;
1791 }
1792 
1793 void KFileWidget::setMode( KFile::Modes m )
1794 {
1795 // kDebug(kfile_area);
1796 
1797  d->ops->setMode(m);
1798  if ( d->ops->dirOnlyMode() ) {
1799  d->filterWidget->setDefaultFilter( i18n("*|All Folders") );
1800  }
1801  else {
1802  d->filterWidget->setDefaultFilter( i18n("*|All Files") );
1803  }
1804 
1805  d->updateAutoSelectExtension();
1806 }
1807 
1808 KFile::Modes KFileWidget::mode() const
1809 {
1810  return d->ops->mode();
1811 }
1812 
1813 
1814 void KFileWidgetPrivate::readConfig(KConfigGroup &configGroup)
1815 {
1816 // kDebug(kfile_area);
1817 
1818  readRecentFiles(configGroup);
1819 
1820  ops->setViewConfig(configGroup);
1821  ops->readConfig(configGroup);
1822 
1823  KUrlComboBox *combo = urlNavigator->editor();
1824  combo->setUrls( configGroup.readPathEntry( RecentURLs, QStringList() ), KUrlComboBox::RemoveTop );
1825  combo->setMaxItems( configGroup.readEntry( RecentURLsNumber,
1826  DefaultRecentURLsNumber ) );
1827  combo->setUrl( ops->url() );
1828  autoDirectoryFollowing = configGroup.readEntry(AutoDirectoryFollowing,
1829  DefaultDirectoryFollowing);
1830 
1831  KGlobalSettings::Completion cm = (KGlobalSettings::Completion)
1832  configGroup.readEntry( PathComboCompletionMode,
1833  static_cast<int>( KGlobalSettings::completionMode() ) );
1834  if ( cm != KGlobalSettings::completionMode() )
1835  combo->setCompletionMode( cm );
1836 
1837  cm = (KGlobalSettings::Completion)
1838  configGroup.readEntry( LocationComboCompletionMode,
1839  static_cast<int>( KGlobalSettings::completionMode() ) );
1840  if ( cm != KGlobalSettings::completionMode() )
1841  locationEdit->setCompletionMode( cm );
1842 
1843  // since we delayed this moment, initialize the directory of the completion object to
1844  // our current directory (that was very probably set on the constructor)
1845  KUrlCompletion *completion = dynamic_cast<KUrlCompletion*>(locationEdit->completionObject());
1846  if (completion) {
1847  completion->setDir(ops->url().url());
1848  }
1849 
1850  // show or don't show the speedbar
1851  _k_toggleSpeedbar( configGroup.readEntry( ShowSpeedbar, true ) );
1852 
1853  // show or don't show the bookmarks
1854  _k_toggleBookmarks( configGroup.readEntry(ShowBookmarks, false) );
1855 
1856  // does the user want Automatically Select Extension?
1857  autoSelectExtChecked = configGroup.readEntry (AutoSelectExtChecked, DefaultAutoSelectExtChecked);
1858  updateAutoSelectExtension();
1859 
1860  // should the URL navigator use the breadcrumb navigation?
1861  urlNavigator->setUrlEditable( !configGroup.readEntry(BreadcrumbNavigation, true) );
1862 
1863  // should the URL navigator show the full path?
1864  urlNavigator->setShowFullPath( configGroup.readEntry(ShowFullPath, false) );
1865 
1866  int w1 = q->minimumSize().width();
1867  int w2 = toolbar->sizeHint().width();
1868  if (w1 < w2)
1869  q->setMinimumWidth(w2);
1870 }
1871 
1872 void KFileWidgetPrivate::writeConfig(KConfigGroup &configGroup)
1873 {
1874 // kDebug(kfile_area);
1875 
1876  // these settings are global settings; ALL instances of the file dialog
1877  // should reflect them
1878  KConfig config("kdeglobals");
1879  KConfigGroup group(&config, configGroup.name());
1880 
1881  KUrlComboBox *pathCombo = urlNavigator->editor();
1882  group.writePathEntry( RecentURLs, pathCombo->urls() );
1883  //saveDialogSize( group, KConfigGroup::Persistent | KConfigGroup::Global );
1884  group.writeEntry( PathComboCompletionMode, static_cast<int>(pathCombo->completionMode()) );
1885  group.writeEntry( LocationComboCompletionMode, static_cast<int>(locationEdit->completionMode()) );
1886 
1887  const bool showSpeedbar = placesDock && !placesDock->isHidden();
1888  group.writeEntry( ShowSpeedbar, showSpeedbar );
1889  if (showSpeedbar) {
1890  const QList<int> sizes = placesViewSplitter->sizes();
1891  Q_ASSERT( sizes.count() > 0 );
1892  group.writeEntry( SpeedbarWidth, sizes[0] );
1893  }
1894 
1895  group.writeEntry( ShowBookmarks, bookmarkHandler != 0 );
1896  group.writeEntry( AutoSelectExtChecked, autoSelectExtChecked );
1897  group.writeEntry( BreadcrumbNavigation, !urlNavigator->isUrlEditable() );
1898  group.writeEntry( ShowFullPath, urlNavigator->showFullPath() );
1899 
1900  ops->writeConfig(group);
1901 }
1902 
1903 
1904 void KFileWidgetPrivate::readRecentFiles(KConfigGroup &cg)
1905 {
1906 // kDebug(kfile_area);
1907 
1908  QObject::disconnect(locationEdit, SIGNAL(editTextChanged(QString)),
1909  q, SLOT(_k_slotLocationChanged(QString)));
1910 
1911  locationEdit->setMaxItems(cg.readEntry(RecentFilesNumber, DefaultRecentURLsNumber));
1912  locationEdit->setUrls(cg.readPathEntry(RecentFiles, QStringList()),
1913  KUrlComboBox::RemoveBottom);
1914  locationEdit->setCurrentIndex(-1);
1915 
1916  QObject::connect(locationEdit, SIGNAL(editTextChanged(QString)),
1917  q, SLOT(_k_slotLocationChanged(QString)));
1918 }
1919 
1920 void KFileWidgetPrivate::saveRecentFiles(KConfigGroup &cg)
1921 {
1922 // kDebug(kfile_area);
1923  cg.writePathEntry(RecentFiles, locationEdit->urls());
1924 }
1925 
1926 KPushButton * KFileWidget::okButton() const
1927 {
1928  return d->okButton;
1929 }
1930 
1931 KPushButton * KFileWidget::cancelButton() const
1932 {
1933  return d->cancelButton;
1934 }
1935 
1936 // Called by KFileDialog
1937 void KFileWidget::slotCancel()
1938 {
1939 // kDebug(kfile_area);
1940 
1941  d->ops->close();
1942 
1943  KConfigGroup grp(KGlobal::config(), ConfigGroup);
1944  d->writeConfig(grp);
1945 }
1946 
1947 void KFileWidget::setKeepLocation( bool keep )
1948 {
1949  d->keepLocation = keep;
1950 }
1951 
1952 bool KFileWidget::keepsLocation() const
1953 {
1954  return d->keepLocation;
1955 }
1956 
1957 void KFileWidget::setOperationMode( OperationMode mode )
1958 {
1959 // kDebug(kfile_area);
1960 
1961  d->operationMode = mode;
1962  d->keepLocation = (mode == Saving);
1963  d->filterWidget->setEditable( !d->hasDefaultFilter || mode != Saving );
1964  if ( mode == Opening ) {
1965  // don't use KStandardGuiItem::open() here which has trailing ellipsis!
1966  d->okButton->setGuiItem( KGuiItem( i18n( "&Open" ), "document-open") );
1967  // hide the new folder actions...usability team says they shouldn't be in open file dialog
1968  actionCollection()->removeAction( actionCollection()->action("mkdir" ) );
1969  } else if ( mode == Saving ) {
1970  d->okButton->setGuiItem( KStandardGuiItem::save() );
1971  d->setNonExtSelection();
1972  } else {
1973  d->okButton->setGuiItem( KStandardGuiItem::ok() );
1974  }
1975  d->updateLocationWhatsThis();
1976  d->updateAutoSelectExtension();
1977 
1978  if (d->ops) {
1979  d->ops->setIsSaving(mode == Saving);
1980  }
1981 }
1982 
1983 KFileWidget::OperationMode KFileWidget::operationMode() const
1984 {
1985  return d->operationMode;
1986 }
1987 
1988 void KFileWidgetPrivate::_k_slotAutoSelectExtClicked()
1989 {
1990 // kDebug (kfile_area) << "slotAutoSelectExtClicked(): "
1991 // << autoSelectExtCheckBox->isChecked() << endl;
1992 
1993  // whether the _user_ wants it on/off
1994  autoSelectExtChecked = autoSelectExtCheckBox->isChecked();
1995 
1996  // update the current filename's extension
1997  updateLocationEditExtension (extension /* extension hasn't changed */);
1998 }
1999 
2000 void KFileWidgetPrivate::_k_placesViewSplitterMoved(int pos, int index)
2001 {
2002 // kDebug(kfile_area);
2003 
2004  // we need to record the size of the splitter when the splitter changes size
2005  // so we can keep the places box the right size!
2006  if (placesDock && index == 1) {
2007  placesViewWidth = pos;
2008 // kDebug() << "setting lafBox minwidth to" << placesViewWidth;
2009  lafBox->setColumnMinimumWidth(0, placesViewWidth);
2010  }
2011 }
2012 
2013 void KFileWidgetPrivate::_k_activateUrlNavigator()
2014 {
2015 // kDebug(kfile_area);
2016 
2017  urlNavigator->setUrlEditable(!urlNavigator->isUrlEditable());
2018  if(urlNavigator->isUrlEditable()) {
2019  urlNavigator->setFocus();
2020  urlNavigator->editor()->lineEdit()->selectAll();
2021  }
2022 }
2023 
2024 void KFileWidgetPrivate::_k_zoomOutIconsSize()
2025 {
2026  const int currValue = ops->iconsZoom();
2027  const int futValue = qMax(0, currValue - 10);
2028  iconSizeSlider->setValue(futValue);
2029  _k_slotIconSizeSliderMoved(futValue);
2030 }
2031 
2032 void KFileWidgetPrivate::_k_zoomInIconsSize()
2033 {
2034  const int currValue = ops->iconsZoom();
2035  const int futValue = qMin(100, currValue + 10);
2036  iconSizeSlider->setValue(futValue);
2037  _k_slotIconSizeSliderMoved(futValue);
2038 }
2039 
2040 void KFileWidgetPrivate::_k_slotIconSizeChanged(int _value)
2041 {
2042  int maxSize = KIconLoader::SizeEnormous - KIconLoader::SizeSmall;
2043  int value = (maxSize * _value / 100) + KIconLoader::SizeSmall;
2044  switch (value) {
2045  case KIconLoader::SizeSmall:
2046  case KIconLoader::SizeSmallMedium:
2047  case KIconLoader::SizeMedium:
2048  case KIconLoader::SizeLarge:
2049  case KIconLoader::SizeHuge:
2050  case KIconLoader::SizeEnormous:
2051  iconSizeSlider->setToolTip(i18n("Icon size: %1 pixels (standard size)", value));
2052  break;
2053  default:
2054  iconSizeSlider->setToolTip(i18n("Icon size: %1 pixels", value));
2055  break;
2056  }
2057 }
2058 
2059 void KFileWidgetPrivate::_k_slotIconSizeSliderMoved(int _value)
2060 {
2061  // Force this to be called in case this slot is called first on the
2062  // slider move.
2063  _k_slotIconSizeChanged(_value);
2064 
2065  QPoint global(iconSizeSlider->rect().topLeft());
2066  global.ry() += iconSizeSlider->height() / 2;
2067  QHelpEvent toolTipEvent(QEvent::ToolTip, QPoint(0, 0), iconSizeSlider->mapToGlobal(global));
2068  QApplication::sendEvent(iconSizeSlider, &toolTipEvent);
2069 }
2070 
2071 static QString getExtensionFromPatternList(const QStringList &patternList)
2072 {
2073 // kDebug(kfile_area);
2074 
2075  QString ret;
2076 // kDebug (kfile_area) << "\tgetExtension " << patternList;
2077 
2078  QStringList::ConstIterator patternListEnd = patternList.end();
2079  for (QStringList::ConstIterator it = patternList.begin();
2080  it != patternListEnd;
2081  ++it)
2082  {
2083 // kDebug (kfile_area) << "\t\ttry: \'" << (*it) << "\'";
2084 
2085  // is this pattern like "*.BMP" rather than useless things like:
2086  //
2087  // README
2088  // *.
2089  // *.*
2090  // *.JP*G
2091  // *.JP?
2092  if ((*it).startsWith (QLatin1String("*.")) &&
2093  (*it).length() > 2 &&
2094  (*it).indexOf('*', 2) < 0 && (*it).indexOf ('?', 2) < 0)
2095  {
2096  ret = (*it).mid (1);
2097  break;
2098  }
2099  }
2100 
2101  return ret;
2102 }
2103 
2104 static QString stripUndisplayable (const QString &string)
2105 {
2106  QString ret = string;
2107 
2108  ret.remove (':');
2109  ret = KGlobal::locale()->removeAcceleratorMarker (ret);
2110 
2111  return ret;
2112 }
2113 
2114 
2115 //QString KFileWidget::currentFilterExtension()
2116 //{
2117 // return d->extension;
2118 //}
2119 
2120 void KFileWidgetPrivate::updateAutoSelectExtension()
2121 {
2122  if (!autoSelectExtCheckBox) return;
2123 
2124  //
2125  // Figure out an extension for the Automatically Select Extension thing
2126  // (some Windows users apparently don't know what to do when confronted
2127  // with a text file called "COPYING" but do know what to do with
2128  // COPYING.txt ...)
2129  //
2130 
2131 // kDebug (kfile_area) << "Figure out an extension: ";
2132  QString lastExtension = extension;
2133  extension.clear();
2134 
2135  // Automatically Select Extension is only valid if the user is _saving_ a _file_
2136  if ((operationMode == KFileWidget::Saving) && (ops->mode() & KFile::File))
2137  {
2138  //
2139  // Get an extension from the filter
2140  //
2141 
2142  QString filter = filterWidget->currentFilter();
2143  if (!filter.isEmpty())
2144  {
2145  // if the currently selected filename already has an extension which
2146  // is also included in the currently allowed extensions, keep it
2147  // otherwise use the default extension
2148  QString currentExtension = KMimeType::extractKnownExtension(locationEditCurrentText());
2149  if ( currentExtension.isEmpty() )
2150  currentExtension = locationEditCurrentText().section(QLatin1Char('.'), -1, -1);
2151  kDebug (kfile_area) << "filter:" << filter << "locationEdit:" << locationEditCurrentText()
2152  << "currentExtension:" << currentExtension;
2153 
2154  QString defaultExtension;
2155  QStringList extensionList;
2156 
2157  // e.g. "*.cpp"
2158  if (filter.indexOf ('/') < 0)
2159  {
2160  extensionList = filter.split(' ', QString::SkipEmptyParts);
2161  defaultExtension = getExtensionFromPatternList(extensionList);
2162  }
2163  // e.g. "text/html"
2164  else
2165  {
2166  KMimeType::Ptr mime = KMimeType::mimeType (filter);
2167  if (mime)
2168  {
2169  extensionList = mime->patterns();
2170  defaultExtension = mime->mainExtension();
2171  }
2172  }
2173 
2174  if ( !currentExtension.isEmpty() && extensionList.contains(QLatin1String("*.") + currentExtension) )
2175  extension = QLatin1Char('.') + currentExtension;
2176  else
2177  extension = defaultExtension;
2178 
2179  kDebug (kfile_area) << "List:" << extensionList << "auto-selected extension:" << extension;
2180  }
2181 
2182 
2183  //
2184  // GUI: checkbox
2185  //
2186 
2187  QString whatsThisExtension;
2188  if (!extension.isEmpty())
2189  {
2190  // remember: sync any changes to the string with below
2191  autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension (%1)", extension));
2192  whatsThisExtension = i18n ("the extension <b>%1</b>", extension);
2193 
2194  autoSelectExtCheckBox->setEnabled (true);
2195  autoSelectExtCheckBox->setChecked (autoSelectExtChecked);
2196  }
2197  else
2198  {
2199  // remember: sync any changes to the string with above
2200  autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension"));
2201  whatsThisExtension = i18n ("a suitable extension");
2202 
2203  autoSelectExtCheckBox->setChecked (false);
2204  autoSelectExtCheckBox->setEnabled (false);
2205  }
2206 
2207  const QString locationLabelText = stripUndisplayable (locationLabel->text());
2208  const QString filterLabelText = stripUndisplayable (filterLabel->text());
2209  autoSelectExtCheckBox->setWhatsThis( "<qt>" +
2210  i18n (
2211  "This option enables some convenient features for "
2212  "saving files with extensions:<br />"
2213  "<ol>"
2214  "<li>Any extension specified in the <b>%1</b> text "
2215  "area will be updated if you change the file type "
2216  "to save in.<br />"
2217  "<br /></li>"
2218  "<li>If no extension is specified in the <b>%2</b> "
2219  "text area when you click "
2220  "<b>Save</b>, %3 will be added to the end of the "
2221  "filename (if the filename does not already exist). "
2222  "This extension is based on the file type that you "
2223  "have chosen to save in.<br />"
2224  "<br />"
2225  "If you do not want KDE to supply an extension for the "
2226  "filename, you can either turn this option off or you "
2227  "can suppress it by adding a period (.) to the end of "
2228  "the filename (the period will be automatically "
2229  "removed)."
2230  "</li>"
2231  "</ol>"
2232  "If unsure, keep this option enabled as it makes your "
2233  "files more manageable."
2234  ,
2235  locationLabelText,
2236  locationLabelText,
2237  whatsThisExtension)
2238  + "</qt>"
2239  );
2240 
2241  autoSelectExtCheckBox->show();
2242 
2243 
2244  // update the current filename's extension
2245  updateLocationEditExtension (lastExtension);
2246  }
2247  // Automatically Select Extension not valid
2248  else
2249  {
2250  autoSelectExtCheckBox->setChecked (false);
2251  autoSelectExtCheckBox->hide();
2252  }
2253 }
2254 
2255 // Updates the extension of the filename specified in d->locationEdit if the
2256 // Automatically Select Extension feature is enabled.
2257 // (this prevents you from accidently saving "file.kwd" as RTF, for example)
2258 void KFileWidgetPrivate::updateLocationEditExtension (const QString &lastExtension)
2259 {
2260  if (!autoSelectExtCheckBox->isChecked() || extension.isEmpty())
2261  return;
2262 
2263  QString urlStr = locationEditCurrentText();
2264  if (urlStr.isEmpty())
2265  return;
2266 
2267  KUrl url = getCompleteUrl(urlStr);
2268 // kDebug (kfile_area) << "updateLocationEditExtension (" << url << ")";
2269 
2270  const int fileNameOffset = urlStr.lastIndexOf ('/') + 1;
2271  QString fileName = urlStr.mid (fileNameOffset);
2272 
2273  const int dot = fileName.lastIndexOf ('.');
2274  const int len = fileName.length();
2275  if (dot > 0 && // has an extension already and it's not a hidden file
2276  // like ".hidden" (but we do accept ".hidden.ext")
2277  dot != len - 1 // and not deliberately suppressing extension
2278  )
2279  {
2280  // exists?
2281  KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
2282  bool result = KIO::NetAccess::synchronousRun(statJob, q);
2283  if (result)
2284  {
2285 // kDebug (kfile_area) << "\tfile exists";
2286 
2287  if (statJob->statResult().isDir())
2288  {
2289 // kDebug (kfile_area) << "\tisDir - won't alter extension";
2290  return;
2291  }
2292 
2293  // --- fall through ---
2294  }
2295 
2296 
2297  //
2298  // try to get rid of the current extension
2299  //
2300 
2301  // catch "double extensions" like ".tar.gz"
2302  if (lastExtension.length() && fileName.endsWith (lastExtension))
2303  fileName.truncate (len - lastExtension.length());
2304  else if (extension.length() && fileName.endsWith (extension))
2305  fileName.truncate (len - extension.length());
2306  // can only handle "single extensions"
2307  else
2308  fileName.truncate (dot);
2309 
2310  // add extension
2311  const QString newText = urlStr.left (fileNameOffset) + fileName + extension;
2312  if ( newText != locationEditCurrentText() )
2313  {
2314  locationEdit->setItemText(locationEdit->currentIndex(),urlStr.left (fileNameOffset) + fileName + extension);
2315  locationEdit->lineEdit()->setModified (true);
2316  }
2317  }
2318 }
2319 
2320 // Updates the filter if the extension of the filename specified in d->locationEdit is changed
2321 // (this prevents you from accidently saving "file.kwd" as RTF, for example)
2322 void KFileWidgetPrivate::updateFilter()
2323 {
2324 // kDebug(kfile_area);
2325 
2326  if ((operationMode == KFileWidget::Saving) && (ops->mode() & KFile::File) ) {
2327  QString urlStr = locationEditCurrentText();
2328  if (urlStr.isEmpty())
2329  return;
2330 
2331  if( filterWidget->isMimeFilter()) {
2332  KMimeType::Ptr mime = KMimeType::findByPath(urlStr, 0, true);
2333  if (mime && mime->name() != KMimeType::defaultMimeType()) {
2334  if (filterWidget->currentFilter() != mime->name() &&
2335  filterWidget->filters().indexOf(mime->name()) != -1)
2336  filterWidget->setCurrentFilter(mime->name());
2337  }
2338  } else {
2339  QString filename = urlStr.mid( urlStr.lastIndexOf( KDIR_SEPARATOR ) + 1 ); // only filename
2340  foreach( const QString& filter, filterWidget->filters()) {
2341  QStringList patterns = filter.left( filter.indexOf( '|' )).split ( ' ', QString::SkipEmptyParts ); // '*.foo *.bar|Foo type' -> '*.foo', '*.bar'
2342  foreach ( const QString& p, patterns ) {
2343  if( KMimeType::matchFileName( filename, p )) {
2344  if ( p != "*" ) { // never match the catch-all filter
2345  filterWidget->setCurrentFilter( filter );
2346  }
2347  return; // do not repeat, could match a later filter
2348  }
2349  }
2350  }
2351  }
2352  }
2353 }
2354 
2355 // applies only to a file that doesn't already exist
2356 void KFileWidgetPrivate::appendExtension (KUrl &url)
2357 {
2358 // kDebug(kfile_area);
2359 
2360  if (!autoSelectExtCheckBox->isChecked() || extension.isEmpty())
2361  return;
2362 
2363  QString fileName = url.fileName();
2364  if (fileName.isEmpty())
2365  return;
2366 
2367 // kDebug (kfile_area) << "appendExtension(" << url << ")";
2368 
2369  const int len = fileName.length();
2370  const int dot = fileName.lastIndexOf ('.');
2371 
2372  const bool suppressExtension = (dot == len - 1);
2373  const bool unspecifiedExtension = (dot <= 0);
2374 
2375  // don't KIO::Stat if unnecessary
2376  if (!(suppressExtension || unspecifiedExtension))
2377  return;
2378 
2379  // exists?
2380  KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
2381  bool res = KIO::NetAccess::synchronousRun(statJob, q);
2382  if (res)
2383  {
2384 // kDebug (kfile_area) << "\tfile exists - won't append extension";
2385  return;
2386  }
2387 
2388  // suppress automatically append extension?
2389  if (suppressExtension)
2390  {
2391  //
2392  // Strip trailing dot
2393  // This allows lazy people to have autoSelectExtCheckBox->isChecked
2394  // but don't want a file extension to be appended
2395  // e.g. "README." will make a file called "README"
2396  //
2397  // If you really want a name like "README.", then type "README.."
2398  // and the trailing dot will be removed (or just stop being lazy and
2399  // turn off this feature so that you can type "README.")
2400  //
2401 // kDebug (kfile_area) << "\tstrip trailing dot";
2402  url.setFileName (fileName.left (len - 1));
2403  }
2404  // evilmatically append extension :) if the user hasn't specified one
2405  else if (unspecifiedExtension)
2406  {
2407 // kDebug (kfile_area) << "\tappending extension \'" << extension << "\'...";
2408  url.setFileName (fileName + extension);
2409 // kDebug (kfile_area) << "\tsaving as \'" << url << "\'";
2410  }
2411 }
2412 
2413 
2414 // adds the selected files/urls to 'recent documents'
2415 void KFileWidgetPrivate::addToRecentDocuments()
2416 {
2417  int m = ops->mode();
2418  int atmost = KRecentDocument::maximumItems();
2419  //don't add more than we need. KRecentDocument::add() is pretty slow
2420 
2421  if (m & KFile::LocalOnly) {
2422  const QStringList files = q->selectedFiles();
2423  QStringList::ConstIterator it = files.begin();
2424  for ( ; it != files.end() && atmost > 0; ++it ) {
2425  KRecentDocument::add( *it );
2426  atmost--;
2427  }
2428  }
2429 
2430  else { // urls
2431  const KUrl::List urls = q->selectedUrls();
2432  KUrl::List::ConstIterator it = urls.begin();
2433  for ( ; it != urls.end() && atmost > 0; ++it ) {
2434  if ( (*it).isValid() ) {
2435  KRecentDocument::add( *it );
2436  atmost--;
2437  }
2438  }
2439  }
2440 }
2441 
2442 KUrlComboBox* KFileWidget::locationEdit() const
2443 {
2444  return d->locationEdit;
2445 }
2446 
2447 KFileFilterCombo* KFileWidget::filterWidget() const
2448 {
2449  return d->filterWidget;
2450 }
2451 
2452 KActionCollection * KFileWidget::actionCollection() const
2453 {
2454  return d->ops->actionCollection();
2455 }
2456 
2457 void KFileWidgetPrivate::_k_toggleSpeedbar(bool show)
2458 {
2459  if (show) {
2460  initSpeedbar();
2461  placesDock->show();
2462  lafBox->setColumnMinimumWidth(0, placesViewWidth);
2463 
2464  // check to see if they have a home item defined, if not show the home button
2465  KUrl homeURL;
2466  homeURL.setPath( QDir::homePath() );
2467  KFilePlacesModel *model = static_cast<KFilePlacesModel*>(placesView->model());
2468  for (int rowIndex = 0 ; rowIndex < model->rowCount() ; rowIndex++) {
2469  QModelIndex index = model->index(rowIndex, 0);
2470  KUrl url = model->url(index);
2471 
2472  if ( homeURL.equals( url, KUrl::CompareWithoutTrailingSlash ) ) {
2473  toolbar->removeAction( ops->actionCollection()->action( "home" ) );
2474  break;
2475  }
2476  }
2477  } else {
2478  if (q->sender() == placesDock && placesDock && placesDock->isVisibleTo(q)) {
2479  // we didn't *really* go away! the dialog was simply hidden or
2480  // we changed virtual desktops or ...
2481  return;
2482  }
2483 
2484  if (placesDock) {
2485  placesDock->hide();
2486  }
2487 
2488  QAction* homeAction = ops->actionCollection()->action("home");
2489  QAction* reloadAction = ops->actionCollection()->action("reload");
2490  if (!toolbar->actions().contains(homeAction)) {
2491  toolbar->insertAction(reloadAction, homeAction);
2492  }
2493 
2494  // reset the lafbox to not follow the width of the splitter
2495  lafBox->setColumnMinimumWidth(0, 0);
2496  }
2497 
2498  static_cast<KToggleAction *>(q->actionCollection()->action("toggleSpeedbar"))->setChecked(show);
2499 
2500  // if we don't show the places panel, at least show the places menu
2501  urlNavigator->setPlacesSelectorVisible(!show);
2502 }
2503 
2504 void KFileWidgetPrivate::_k_toggleBookmarks(bool show)
2505 {
2506  if (show)
2507  {
2508  if (bookmarkHandler)
2509  {
2510  return;
2511  }
2512 
2513  bookmarkHandler = new KFileBookmarkHandler( q );
2514  q->connect( bookmarkHandler, SIGNAL(openUrl(QString)),
2515  SLOT(_k_enterUrl(QString)));
2516 
2517  bookmarkButton = new KActionMenu(KIcon("bookmarks"),i18n("Bookmarks"), q);
2518  bookmarkButton->setDelayed(false);
2519  q->actionCollection()->addAction("bookmark", bookmarkButton);
2520  bookmarkButton->setMenu(bookmarkHandler->menu());
2521  bookmarkButton->setWhatsThis(i18n("<qt>This button allows you to bookmark specific locations. "
2522  "Click on this button to open the bookmark menu where you may add, "
2523  "edit or select a bookmark.<br /><br />"
2524  "These bookmarks are specific to the file dialog, but otherwise operate "
2525  "like bookmarks elsewhere in KDE.</qt>"));
2526  toolbar->addAction(bookmarkButton);
2527  }
2528  else if (bookmarkHandler)
2529  {
2530  delete bookmarkHandler;
2531  bookmarkHandler = 0;
2532  delete bookmarkButton;
2533  bookmarkButton = 0;
2534  }
2535 
2536  static_cast<KToggleAction *>(q->actionCollection()->action("toggleBookmarks"))->setChecked( show );
2537 }
2538 
2539 
2540 // static, overloaded
2541 KUrl KFileWidget::getStartUrl( const KUrl& startDir,
2542  QString& recentDirClass )
2543 {
2544  QString fileName; // result discarded
2545  return getStartUrl( startDir, recentDirClass, fileName );
2546 }
2547 
2548 
2549 // static, overloaded
2550 KUrl KFileWidget::getStartUrl( const KUrl& startDir,
2551  QString& recentDirClass,
2552  QString& fileName )
2553 {
2554  recentDirClass.clear();
2555  fileName.clear();
2556  KUrl ret;
2557 
2558  bool useDefaultStartDir = startDir.isEmpty();
2559  if ( !useDefaultStartDir )
2560  {
2561  if ( startDir.protocol() == "kfiledialog" )
2562  {
2563 
2564 // The startDir URL with this protocol may be in the format:
2565 // directory() fileName()
2566 // 1. kfiledialog:///keyword "/" keyword
2567 // 2. kfiledialog:///keyword?global "/" keyword
2568 // 3. kfiledialog:///keyword/ "/" keyword
2569 // 4. kfiledialog:///keyword/?global "/" keyword
2570 // 5. kfiledialog:///keyword/filename /keyword filename
2571 // 6. kfiledialog:///keyword/filename?global /keyword filename
2572 
2573  QString keyword;
2574  QString urlDir = startDir.directory();
2575  QString urlFile = startDir.fileName();
2576  if ( urlDir == "/" ) // '1'..'4' above
2577  {
2578  keyword = urlFile;
2579  fileName.clear();
2580  }
2581  else // '5' or '6' above
2582  {
2583  keyword = urlDir.mid( 1 );
2584  fileName = urlFile;
2585  }
2586 
2587  if ( startDir.query() == "?global" )
2588  recentDirClass = QString( "::%1" ).arg( keyword );
2589  else
2590  recentDirClass = QString( ":%1" ).arg( keyword );
2591 
2592  ret = KUrl( KRecentDirs::dir(recentDirClass) );
2593  }
2594  else // not special "kfiledialog" URL
2595  {
2596  // We can use startDir as the starting directory if either:
2597  // (a) it has a directory part, or
2598  // (b) there is a scheme (protocol), and it is not just "file".
2599  if (!startDir.directory().isEmpty() ||
2600  (!startDir.scheme().isEmpty() && !startDir.isLocalFile()))
2601  { // can use start directory
2602  ret = startDir; // will be checked by stat later
2603  // If we won't be able to list it (e.g. http), then use default
2604  if ( !KProtocolManager::supportsListing( ret ) ) {
2605  useDefaultStartDir = true;
2606  fileName = startDir.fileName();
2607  }
2608  }
2609  else // file name only
2610  {
2611  fileName = startDir.fileName();
2612  useDefaultStartDir = true;
2613  }
2614  }
2615  }
2616 
2617  if ( useDefaultStartDir )
2618  {
2619  if (lastDirectory->isEmpty()) {
2620  lastDirectory->setPath(KGlobalSettings::documentPath());
2621  KUrl home;
2622  home.setPath( QDir::homePath() );
2623  // if there is no docpath set (== home dir), we prefer the current
2624  // directory over it. We also prefer the homedir when our CWD is
2625  // different from our homedirectory or when the document dir
2626  // does not exist
2627  if ( lastDirectory->path(KUrl::AddTrailingSlash) == home.path(KUrl::AddTrailingSlash) ||
2628  QDir::currentPath() != QDir::homePath() ||
2629  !QDir(lastDirectory->path(KUrl::AddTrailingSlash)).exists() )
2630  lastDirectory->setPath(QDir::currentPath());
2631  }
2632  ret = *lastDirectory;
2633  }
2634 
2635  kDebug(kfile_area) << "for" << startDir << "->" << ret << "recentDirClass" << recentDirClass << "fileName" << fileName;
2636  return ret;
2637 }
2638 
2639 void KFileWidget::setStartDir( const KUrl& directory )
2640 {
2641  if ( directory.isValid() )
2642  *lastDirectory = directory;
2643 }
2644 
2645 void KFileWidgetPrivate::setNonExtSelection()
2646 {
2647  // Enhanced rename: Don't highlight the file extension.
2648  QString filename = locationEditCurrentText();
2649  QString extension = KMimeType::extractKnownExtension( filename );
2650 
2651  if ( !extension.isEmpty() )
2652  locationEdit->lineEdit()->setSelection( 0, filename.length() - extension.length() - 1 );
2653  else
2654  {
2655  int lastDot = filename.lastIndexOf( '.' );
2656  if ( lastDot > 0 )
2657  locationEdit->lineEdit()->setSelection( 0, lastDot );
2658  }
2659 }
2660 
2661 KToolBar * KFileWidget::toolBar() const
2662 {
2663  return d->toolbar;
2664 }
2665 
2666 void KFileWidget::setCustomWidget(QWidget* widget)
2667 {
2668  delete d->bottomCustomWidget;
2669  d->bottomCustomWidget = widget;
2670 
2671  // add it to the dialog, below the filter list box.
2672 
2673  // Change the parent so that this widget is a child of the main widget
2674  d->bottomCustomWidget->setParent( this );
2675 
2676  d->vbox->addWidget( d->bottomCustomWidget );
2677  //d->vbox->addSpacing(3); // can't do this every time...
2678 
2679  // FIXME: This should adjust the tab orders so that the custom widget
2680  // comes after the Cancel button. The code appears to do this, but the result
2681  // somehow screws up the tab order of the file path combo box. Not a major
2682  // problem, but ideally the tab order with a custom widget should be
2683  // the same as the order without one.
2684  setTabOrder(d->cancelButton, d->bottomCustomWidget);
2685  setTabOrder(d->bottomCustomWidget, d->urlNavigator);
2686 }
2687 
2688 void KFileWidget::setCustomWidget(const QString& text, QWidget* widget)
2689 {
2690  delete d->labeledCustomWidget;
2691  d->labeledCustomWidget = widget;
2692 
2693  QLabel* label = new QLabel(text, this);
2694  label->setAlignment(Qt::AlignRight);
2695  d->lafBox->addWidget(label, 2, 0, Qt::AlignVCenter);
2696  d->lafBox->addWidget(widget, 2, 1, Qt::AlignVCenter);
2697 }
2698 
2699 void KFileWidget::virtual_hook( int id, void* data )
2700 {
2701  // this is a workaround to avoid binary compatibility breakage
2702  // since setConfirmOverwrite in kabstractfilewidget.h is a new function
2703  // introduced for 4.2. As stated in kabstractfilewidget.h this workaround
2704  // is going to become a virtual function for KDE5
2705 
2706  switch (id) {
2707  case 0: { // setConfirmOverwrite(bool)
2708  bool *enable = static_cast<bool*>(data);
2709  d->confirmOverwrite = *enable;
2710  }
2711  break;
2712  case 1: { // setInlinePreviewShown(bool)
2713  bool *show = static_cast<bool*>(data);
2714  d->setInlinePreviewShown(*show);
2715  }
2716  break;
2717  default:
2718  break;
2719  }
2720 }
2721 
2722 KDirOperator* KFileWidget::dirOperator()
2723 {
2724  return d->ops;
2725 }
2726 
2727 void KFileWidget::readConfig( KConfigGroup& group )
2728 {
2729  d->readConfig(group);
2730 }
2731 
2732 QString KFileWidgetPrivate::locationEditCurrentText() const
2733 {
2734  return QDir::fromNativeSeparators(locationEdit->currentText());
2735 }
2736 
2737 KUrl KFileWidgetPrivate::mostLocalUrl(const KUrl &url)
2738 {
2739  if (url.isLocalFile()) {
2740  return url;
2741  }
2742 
2743  KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
2744  bool res = KIO::NetAccess::synchronousRun(statJob, q);
2745 
2746  if (!res) {
2747  return url;
2748  }
2749 
2750  const QString path = statJob->statResult().stringValue(KIO::UDSEntry::UDS_LOCAL_PATH);
2751  if (!path.isEmpty()) {
2752  KUrl newUrl;
2753  newUrl.setPath(path);
2754  return newUrl;
2755  }
2756 
2757  return url;
2758 }
2759 
2760 void KFileWidgetPrivate::setInlinePreviewShown(bool show)
2761 {
2762  ops->setInlinePreviewShown(show);
2763 }
2764 
2765 
2766 #include "kfilewidget.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Tue Apr 16 2013 19:18:12 by doxygen 1.8.3.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KFile

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

kdelibs-4.10.2 API Reference

Skip menu "kdelibs-4.10.2 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal