fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / kfile / kdirselectdialog.cpp
blobc73857ef40821a8bfbb89c1c1fc7461fa1eee213
1 /*
2 Copyright (C) 2001,2002 Carsten Pfeiffer <pfeiffer@kde.org>
3 Copyright (C) 2001 Michael Jarrett <michaelj@corel.com>
4 Copyright (C) 2009 Shaun Reich <shaun.reich@kdemail.net>
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License version 2 as published by the Free Software Foundation.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 #include "kdirselectdialog.h"
23 #include <QtCore/QDir>
24 #include <QtCore/QStringList>
25 #include <QtGui/QLayout>
27 #include <kactioncollection.h>
28 #include <kapplication.h>
29 #include <kauthorized.h>
30 #include <kconfig.h>
31 #include <kconfiggroup.h>
32 #include <khistorycombobox.h>
33 #include <kfiledialog.h>
34 #include <kfiletreeview.h>
35 #include <kfileitemdelegate.h>
36 #include <kglobalsettings.h>
37 #include <kicon.h>
38 #include <kinputdialog.h>
39 #include <kio/job.h>
40 #include <kio/deletejob.h>
41 #include <kio/copyjob.h>
42 #include <kio/netaccess.h>
43 #include <kio/renamedialog.h>
44 #include <jobuidelegate.h>
45 #include <klocale.h>
46 #include <kmessagebox.h>
47 #include <krecentdirs.h>
48 #include <ktoggleaction.h>
49 #include <kurlcompletion.h>
50 #include <kurlpixmapprovider.h>
51 #include <kdebug.h>
52 #include <kpropertiesdialog.h>
53 #include <kpushbutton.h>
54 #include <kmenu.h>
56 #include "kfileplacesview.h"
57 #include "kfileplacesmodel.h"
58 // ### add mutator for treeview!
62 class KDirSelectDialog::Private
64 public:
65 Private( bool localOnly, KDirSelectDialog *parent )
66 : m_parent( parent ),
67 m_localOnly( localOnly ),
68 m_comboLocked( false ),
69 m_urlCombo(0)
73 void readConfig(const KSharedConfigPtr &config, const QString& group);
74 void saveConfig(KSharedConfigPtr config, const QString& group);
75 void slotMkdir();
77 void slotCurrentChanged();
78 void slotExpand(const QModelIndex&);
79 void slotUrlActivated(const QString&);
80 void slotComboTextChanged(const QString&);
81 void slotContextMenuRequested(const QPoint&);
82 void slotNewFolder();
83 void slotMoveToTrash();
84 void slotDelete();
85 void slotProperties();
87 KDirSelectDialog *m_parent;
88 bool m_localOnly : 1;
89 bool m_comboLocked : 1;
90 KUrl m_rootUrl;
91 KUrl m_startDir;
92 KFileTreeView *m_treeView;
93 KMenu *m_contextMenu;
94 KActionCollection *m_actions;
95 KFilePlacesView *m_placesView;
96 KHistoryComboBox *m_urlCombo;
97 QString m_recentDirClass;
98 KUrl m_startURL;
99 KAction* moveToTrash;
100 KAction* deleteAction;
103 void KDirSelectDialog::Private::readConfig(const KSharedConfig::Ptr &config, const QString& group)
105 m_urlCombo->clear();
107 KConfigGroup conf( config, group );
108 m_urlCombo->setHistoryItems( conf.readPathEntry( "History Items", QStringList() ));
110 m_parent->resize( conf.readEntry( "DirSelectDialog Size", QSize( 400, 450 ) ) );
113 void KDirSelectDialog::Private::saveConfig(KSharedConfig::Ptr config, const QString& group)
115 KConfigGroup conf( config, group );
116 KConfigGroup::WriteConfigFlags flags(KConfigGroup::Persistent|KConfigGroup::Global);
117 conf.writePathEntry( "History Items", m_urlCombo->historyItems(), flags );
118 conf.writeEntry( "DirSelectDialog Size", m_parent->size(), flags );
120 config->sync();
123 void KDirSelectDialog::Private::slotMkdir()
125 bool ok;
126 QString where = m_parent->url().pathOrUrl();
127 QString name = i18nc("folder name", "New Folder" );
128 if ( m_parent->url().isLocalFile() && QFileInfo( m_parent->url().path(KUrl::AddTrailingSlash) + name ).exists() )
129 name = KIO::RenameDialog::suggestName( m_parent->url(), name );
131 QString directory = KIO::encodeFileName( KInputDialog::getText( i18nc("@title:window", "New Folder" ),
132 i18nc("@label:textbox", "Create new folder in:\n%1" , where ),
133 name, &ok, m_parent));
134 if (!ok)
135 return;
137 bool selectDirectory = true;
138 bool writeOk = false;
139 bool exists = false;
140 KUrl folderurl( m_parent->url() );
142 const QStringList dirs = directory.split( QDir::separator(), QString::SkipEmptyParts );
143 QStringList::ConstIterator it = dirs.begin();
145 for ( ; it != dirs.end(); ++it )
147 folderurl.addPath( *it );
148 exists = KIO::NetAccess::exists( folderurl, KIO::NetAccess::DestinationSide, 0 );
149 writeOk = !exists && KIO::NetAccess::mkdir( folderurl, m_parent->topLevelWidget() );
152 if ( exists ) // url was already existent
154 QString which = folderurl.isLocalFile() ? folderurl.path() : folderurl.prettyUrl();
155 KMessageBox::sorry(m_parent, i18n("A file or folder named %1 already exists.", which));
156 selectDirectory = false;
158 else if ( !writeOk ) {
159 KMessageBox::sorry(m_parent, i18n("You do not have permission to create that folder." ));
161 else if ( selectDirectory ) {
162 m_parent->setCurrentUrl( folderurl );
166 void KDirSelectDialog::Private::slotCurrentChanged()
168 if ( m_comboLocked )
169 return;
171 const KUrl u = m_treeView->currentUrl();
173 if ( u.isValid() )
175 if ( u.isLocalFile() )
176 m_urlCombo->setEditText( u.toLocalFile() );
178 else // remote url
179 m_urlCombo->setEditText( u.prettyUrl() );
181 else
182 m_urlCombo->setEditText( QString() );
185 void KDirSelectDialog::Private::slotUrlActivated( const QString& text )
187 if ( text.isEmpty() )
188 return;
190 KUrl url( text );
191 m_urlCombo->addToHistory( url.prettyUrl() );
193 if ( m_parent->localOnly() && !url.isLocalFile() )
194 return; //FIXME: messagebox for the user
196 KUrl oldUrl = m_treeView->currentUrl();
197 if ( oldUrl.isEmpty() )
198 oldUrl = m_startDir;
200 m_parent->setCurrentUrl( oldUrl );
203 void KDirSelectDialog::Private::slotComboTextChanged( const QString& text )
205 m_treeView->blockSignals(true);
206 m_treeView->setCurrentUrl( KUrl( text ) );
207 m_treeView->blockSignals(false);
210 void KDirSelectDialog::Private::slotContextMenuRequested( const QPoint& pos )
212 m_contextMenu->popup( m_treeView->viewport()->mapToGlobal(pos) );
215 void KDirSelectDialog::Private::slotExpand(const QModelIndex &index)
217 m_treeView->setExpanded(index, !m_treeView->isExpanded(index));
220 void KDirSelectDialog::Private::slotNewFolder()
222 slotMkdir();
225 void KDirSelectDialog::Private::slotMoveToTrash()
227 const KUrl url = m_treeView->selectedUrl();
228 KIO::JobUiDelegate job;
229 if (job.askDeleteConfirmation(KUrl::List() << url, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) {
230 KIO::CopyJob* copyJob = KIO::trash(url);
231 copyJob->ui()->setWindow(this->m_parent);
232 copyJob->ui()->setAutoErrorHandlingEnabled(true);
236 void KDirSelectDialog::Private::slotDelete()
238 const KUrl url = m_treeView->selectedUrl();
239 KIO::JobUiDelegate job;
240 if (job.askDeleteConfirmation(KUrl::List() << url, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) {
241 KIO::DeleteJob* deleteJob = KIO::del(url);
242 deleteJob->ui()->setWindow(this->m_parent);
243 deleteJob->ui()->setAutoErrorHandlingEnabled(true);
247 void KDirSelectDialog::Private::slotProperties()
249 KPropertiesDialog* dialog = 0;
250 dialog = new KPropertiesDialog(m_treeView->selectedUrl(), this->m_parent);
251 dialog->setAttribute(Qt::WA_DeleteOnClose);
252 dialog->show();
256 KDirSelectDialog::KDirSelectDialog(const KUrl &startDir, bool localOnly,
257 QWidget *parent)
258 #ifdef Q_WS_WIN
259 : KDialog( parent , Qt::WindowMinMaxButtonsHint),
260 #else
261 : KDialog( parent ),
262 #endif
263 d( new Private( localOnly, this ) )
265 setCaption( i18nc("@title:window","Select Folder") );
266 setButtons( Ok | Cancel | User1 );
267 setButtonGuiItem( User1, KGuiItem( i18nc("@action:button","New Folder..."), "folder-new" ) );
268 showButtonSeparator(false);
269 setDefaultButton(Ok);
270 button(Ok)->setFocus();
272 QFrame *page = new QFrame(this);
273 setMainWidget(page);
274 QHBoxLayout *hlay = new QHBoxLayout( page);
275 hlay->setMargin(0);
276 hlay->setSpacing(spacingHint());
277 QVBoxLayout *mainLayout = new QVBoxLayout();
278 d->m_actions=new KActionCollection(this);
279 d->m_placesView = new KFilePlacesView( page );
280 d->m_placesView->setModel(new KFilePlacesModel(d->m_placesView));
281 d->m_placesView->setObjectName( QLatin1String( "speedbar" ) );
282 d->m_placesView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
283 d->m_placesView->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
284 connect( d->m_placesView, SIGNAL( urlChanged( const KUrl& )),
285 SLOT( setCurrentUrl( const KUrl& )) );
286 hlay->addWidget( d->m_placesView );
287 hlay->addLayout( mainLayout );
289 d->m_treeView = new KFileTreeView(page);
290 d->m_treeView->setDirOnlyMode(true);
291 d->m_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
293 for (int i = 1; i < d->m_treeView->model()->columnCount(); ++i)
294 d->m_treeView->hideColumn(i);
296 d->m_urlCombo = new KHistoryComboBox( page);
297 d->m_urlCombo->setLayoutDirection( Qt::LeftToRight );
298 d->m_urlCombo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
299 d->m_urlCombo->setTrapReturnKey( true );
300 d->m_urlCombo->setPixmapProvider( new KUrlPixmapProvider() );
301 KUrlCompletion *comp = new KUrlCompletion();
302 comp->setMode( KUrlCompletion::DirCompletion );
303 d->m_urlCombo->setCompletionObject( comp, true );
304 d->m_urlCombo->setAutoDeleteCompletionObject( true );
305 d->m_urlCombo->setDuplicatesEnabled( false );
307 d->m_contextMenu = new KMenu( this );
309 KAction* newFolder = new KAction( i18nc("@action:inmenu","New Folder..."), this);
310 d->m_actions->addAction( newFolder->objectName(), newFolder );
311 newFolder->setIcon( KIcon( "folder-new" ) );
312 newFolder->setShortcut( Qt::Key_F10);
313 connect( newFolder, SIGNAL( triggered( bool ) ), this, SLOT( slotNewFolder() ) );
314 d->m_contextMenu->addAction( newFolder );
316 d->moveToTrash = new KAction( i18nc( "@action:inmenu","Move to trash" ), this );
317 d->m_actions->addAction( d->moveToTrash->objectName(), d->moveToTrash );
318 d->moveToTrash->setIcon( KIcon( "user-trash" ) );
319 d->moveToTrash->setShortcut(KShortcut(Qt::Key_Delete));
320 connect( d->moveToTrash, SIGNAL( triggered( bool ) ), this, SLOT( slotMoveToTrash() ) );
321 d->m_contextMenu->addAction( d->moveToTrash );
323 d->deleteAction = new KAction( i18nc("@action:inmenu","Delete"), this );
324 d->m_actions->addAction( d->deleteAction->objectName(), d->deleteAction );
325 d->deleteAction->setIcon( KIcon( "edit-delete" ) );
326 d->deleteAction->setShortcut( KShortcut( Qt::SHIFT + Qt::Key_Delete ) );
327 connect( d->deleteAction, SIGNAL( triggered( bool ) ), this, SLOT( slotDelete() ) );
328 d->m_contextMenu->addAction( d->deleteAction );
330 d->m_contextMenu->addSeparator();
332 KToggleAction *showHiddenFolders = new KToggleAction( i18nc("@option:check", "Show Hidden Folders"), this );
333 d->m_actions->addAction( showHiddenFolders->objectName(), showHiddenFolders );
334 connect( showHiddenFolders, SIGNAL( triggered( bool ) ), d->m_treeView, SLOT( setShowHiddenFiles( bool ) ) );
335 d->m_contextMenu->addAction( showHiddenFolders );
336 d->m_contextMenu->addSeparator();
338 KAction* propertiesAction = new KAction( i18nc("@action:inmenu","Properties"), this);
339 d->m_actions->addAction(propertiesAction->objectName(), propertiesAction);
340 propertiesAction->setIcon(KIcon("document-properties"));
341 propertiesAction->setShortcut(KShortcut(Qt::ALT + Qt::Key_Return));
342 connect( propertiesAction, SIGNAL( triggered( bool ) ), this, SLOT( slotProperties() ) );
343 d->m_contextMenu->addAction( propertiesAction );
345 d->m_startURL = KFileDialog::getStartUrl( startDir, d->m_recentDirClass );
346 if ( localOnly && !d->m_startURL.isLocalFile() )
348 d->m_startURL = KUrl();
349 QString docPath = KGlobalSettings::documentPath();
350 if (QDir(docPath).exists())
351 d->m_startURL.setPath( docPath );
352 else
353 d->m_startURL.setPath( QDir::homePath() );
356 d->m_startDir = d->m_startURL;
357 d->m_rootUrl = d->m_treeView->rootUrl();
359 d->readConfig( KGlobal::config(), "DirSelect Dialog" );
361 mainLayout->addWidget( d->m_treeView, 1 );
362 mainLayout->addWidget( d->m_urlCombo, 0 );
364 connect( d->m_treeView, SIGNAL( currentChanged(const KUrl&)),
365 SLOT( slotCurrentChanged() ));
366 connect( d->m_treeView, SIGNAL( activated(const QModelIndex&)),
367 SLOT( slotExpand(const QModelIndex&) ));
368 connect( d->m_treeView, SIGNAL( customContextMenuRequested( const QPoint & )),
369 SLOT( slotContextMenuRequested( const QPoint & )));
371 connect( d->m_urlCombo, SIGNAL( editTextChanged( const QString& ) ),
372 SLOT( slotComboTextChanged( const QString& ) ));
373 connect( d->m_urlCombo, SIGNAL( activated( const QString& )),
374 SLOT( slotUrlActivated( const QString& )));
375 connect( d->m_urlCombo, SIGNAL( returnPressed( const QString& )),
376 SLOT( slotUrlActivated( const QString& )));
378 connect(this, SIGNAL(user1Clicked()), this, SLOT(slotNewFolder()));
380 setCurrentUrl(d->m_startURL);
384 KDirSelectDialog::~KDirSelectDialog()
386 delete d;
389 KUrl KDirSelectDialog::url() const
391 KUrl comboUrl(d->m_urlCombo->currentText());
393 if ( comboUrl.isValid() ) {
394 KIO::StatJob *statJob = KIO::stat(comboUrl, KIO::HideProgressInfo);
395 const bool ok = KIO::NetAccess::synchronousRun(statJob, 0);
396 if (ok && statJob->statResult().isDir()) {
397 return comboUrl;
401 kDebug() << comboUrl.path() << " is not an accessible directory";
402 return d->m_treeView->currentUrl();
405 QAbstractItemView* KDirSelectDialog::view() const
407 return d->m_treeView;
410 bool KDirSelectDialog::localOnly() const
412 return d->m_localOnly;
415 KUrl KDirSelectDialog::startDir() const
417 return d->m_startDir;
420 void KDirSelectDialog::setCurrentUrl( const KUrl& url )
422 if ( !url.isValid() )
423 return;
425 if (url.protocol() != d->m_rootUrl.protocol()) {
426 KUrl u( url );
427 u.cd("/");//NOTE portability?
428 d->m_treeView->setRootUrl( u );
429 d->m_rootUrl = u;
432 d->m_treeView->setCurrentUrl( url );
435 void KDirSelectDialog::accept()
437 KUrl selectedUrl = url();
438 if (!selectedUrl.isValid()) {
439 return;
442 if (!d->m_recentDirClass.isEmpty()) {
443 KRecentDirs::add(d->m_recentDirClass, selectedUrl.url());
446 d->m_urlCombo->addToHistory( selectedUrl.prettyUrl() );
447 KFileDialog::setStartDir( url() );
449 KDialog::accept();
452 void KDirSelectDialog::hideEvent( QHideEvent *event )
454 d->saveConfig( KGlobal::config(), "DirSelect Dialog" );
456 KDialog::hideEvent(event);
459 // static
460 KUrl KDirSelectDialog::selectDirectory( const KUrl& startDir,
461 bool localOnly,
462 QWidget *parent,
463 const QString& caption)
465 KDirSelectDialog myDialog( startDir, localOnly, parent);
467 if ( !caption.isNull() )
468 myDialog.setCaption( caption );
470 if ( myDialog.exec() == QDialog::Accepted )
471 return KIO::NetAccess::mostLocalUrl(myDialog.url(),parent);
472 else
473 return KUrl();
476 #include "kdirselectdialog.moc"