1/* This file is part of the KDE libraries
2 Copyright (C) 1999,2000 Stephan Kulow <coolo@kde.org>
3 1999,2000,2001,2002,2003 Carsten Pfeiffer <pfeiffer@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
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.
14
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.
19*/
20
21#include "kdiroperator.h"
22#include <kprotocolmanager.h>
23#include "kdirmodel.h"
24#include "kdiroperatordetailview_p.h"
25#include "kdirsortfilterproxymodel.h"
26#include "kfileitem.h"
27#include "kfilemetapreview.h"
28#include "kpreviewwidgetbase.h"
29#include "knewfilemenu.h"
30
31#include <config-kfile.h>
32
33#include <unistd.h>
34
35#include <QtCore/QDir>
36#include <QtCore/QRegExp>
37#include <QtCore/QTimer>
38#include <QtCore/QAbstractItemModel>
39#include <QtGui/QApplication>
40#include <QtGui/QDialog>
41#include <QtGui/QHeaderView>
42#include <QtGui/QLabel>
43#include <QtGui/QLayout>
44#include <QtGui/QListView>
45#include <QtGui/QMouseEvent>
46#include <QtGui/QTreeView>
47#include <QtGui/QPushButton>
48#include <QtGui/QProgressBar>
49#include <QtGui/QScrollBar>
50#include <QtGui/QSplitter>
51#include <QtGui/QWheelEvent>
52
53#include <kaction.h>
54#include <kapplication.h>
55#include <kdebug.h>
56#include <kdialog.h>
57#include <kdirlister.h>
58#include <kfileitemdelegate.h>
59#include <kicon.h>
60#include <kinputdialog.h>
61#include <klocale.h>
62#include <kmessagebox.h>
63#include <kmenu.h>
64#include <kstandardaction.h>
65#include <kio/job.h>
66#include <kio/deletejob.h>
67#include <kio/copyjob.h>
68#include <kio/jobuidelegate.h>
69#include <kio/jobclasses.h>
70#include <kio/netaccess.h>
71#include <kio/previewjob.h>
72#include <kio/renamedialog.h>
73#include <kfilepreviewgenerator.h>
74#include <krun.h>
75#include <kpropertiesdialog.h>
76#include <kstandardshortcut.h>
77#include <kde_file.h>
78#include <kactioncollection.h>
79#include <ktoggleaction.h>
80#include <kactionmenu.h>
81#include <kconfiggroup.h>
82#include <kdeversion.h>
83
84
85template class QHash<QString, KFileItem>;
86
87// QDir::SortByMask is not only undocumented, it also omits QDir::Type which is another
88// sorting mode.
89static const int QDirSortMask = QDir::SortByMask | QDir::Type;
90
91/**
92 * Default icon view for KDirOperator using
93 * custom view options.
94 */
95class KDirOperatorIconView : public QListView
96{
97public:
98 KDirOperatorIconView(KDirOperator *dirOperator, QWidget *parent = 0);
99 virtual ~KDirOperatorIconView();
100
101protected:
102 virtual QStyleOptionViewItem viewOptions() const;
103 virtual void dragEnterEvent(QDragEnterEvent* event);
104 virtual void mousePressEvent(QMouseEvent *event);
105 virtual void wheelEvent(QWheelEvent *event);
106
107private:
108 KDirOperator *ops;
109};
110
111KDirOperatorIconView::KDirOperatorIconView(KDirOperator *dirOperator, QWidget *parent) :
112 QListView(parent),
113 ops(dirOperator)
114{
115 setViewMode(QListView::IconMode);
116 setFlow(QListView::TopToBottom);
117 setResizeMode(QListView::Adjust);
118 setSpacing(0);
119 setMovement(QListView::Static);
120 setDragDropMode(QListView::DragOnly);
121 setVerticalScrollMode(QListView::ScrollPerPixel);
122 setHorizontalScrollMode(QListView::ScrollPerPixel);
123 setEditTriggers(QAbstractItemView::NoEditTriggers);
124 setWordWrap(true);
125 setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall));
126}
127
128KDirOperatorIconView::~KDirOperatorIconView()
129{
130}
131
132QStyleOptionViewItem KDirOperatorIconView::viewOptions() const
133{
134 QStyleOptionViewItem viewOptions = QListView::viewOptions();
135 viewOptions.showDecorationSelected = true;
136 viewOptions.decorationPosition = ops->decorationPosition();
137 if (viewOptions.decorationPosition == QStyleOptionViewItem::Left) {
138 viewOptions.displayAlignment = Qt::AlignLeft | Qt::AlignVCenter;
139 } else {
140 viewOptions.displayAlignment = Qt::AlignCenter;
141 }
142
143 return viewOptions;
144}
145
146void KDirOperatorIconView::dragEnterEvent(QDragEnterEvent* event)
147{
148 if (event->mimeData()->hasUrls()) {
149 event->acceptProposedAction();
150 }
151}
152
153void KDirOperatorIconView::mousePressEvent(QMouseEvent *event)
154{
155 if (!indexAt(event->pos()).isValid()) {
156 const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
157 if (!(modifiers & Qt::ShiftModifier) && !(modifiers & Qt::ControlModifier)) {
158 clearSelection();
159 }
160 }
161
162 QListView::mousePressEvent(event);
163}
164
165void KDirOperatorIconView::wheelEvent(QWheelEvent *event)
166{
167 QListView::wheelEvent(event);
168
169 // apply the vertical wheel event to the horizontal scrollbar, as
170 // the items are aligned from left to right
171 if (event->orientation() == Qt::Vertical) {
172 QWheelEvent horizEvent(event->pos(),
173 event->delta(),
174 event->buttons(),
175 event->modifiers(),
176 Qt::Horizontal);
177 QApplication::sendEvent(horizontalScrollBar(), &horizEvent);
178 }
179}
180
181void KDirOperator::keyPressEvent(QKeyEvent *e)
182{
183 if (!(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter )) {
184 QWidget::keyPressEvent(e);
185 }
186}
187
188class KDirOperator::Private
189{
190public:
191 Private( KDirOperator *parent );
192 ~Private();
193
194 enum InlinePreviewState {
195 ForcedToFalse = 0,
196 ForcedToTrue,
197 NotForced
198 };
199
200 // private methods
201 bool checkPreviewInternal() const;
202 void checkPath(const QString &txt, bool takeFiles = false);
203 bool openUrl(const KUrl &url, KDirLister::OpenUrlFlags flags = KDirLister::NoFlags);
204 int sortColumn() const;
205 Qt::SortOrder sortOrder() const;
206 void updateSorting(QDir::SortFlags sort);
207
208 static bool isReadable(const KUrl &url);
209
210 KFile::FileView allViews();
211
212 // private slots
213 void _k_slotDetailedView();
214 void _k_slotSimpleView();
215 void _k_slotTreeView();
216 void _k_slotDetailedTreeView();
217 void _k_slotToggleHidden(bool);
218 void _k_togglePreview(bool);
219 void _k_toggleInlinePreviews(bool);
220 void _k_slotOpenFileManager();
221 void _k_slotSortByName();
222 void _k_slotSortBySize();
223 void _k_slotSortByDate();
224 void _k_slotSortByType();
225 void _k_slotSortReversed(bool doReverse);
226 void _k_slotToggleDirsFirst();
227 void _k_slotToggleIgnoreCase();
228 void _k_slotStarted();
229 void _k_slotProgress(int);
230 void _k_slotShowProgress();
231 void _k_slotIOFinished();
232 void _k_slotCanceled();
233 void _k_slotRedirected(const KUrl&);
234 void _k_slotProperties();
235 void _k_slotActivated(const QModelIndex&);
236 void _k_slotSelectionChanged();
237 void _k_openContextMenu(const QPoint&);
238 void _k_triggerPreview(const QModelIndex&);
239 void _k_showPreview();
240 void _k_slotSplitterMoved(int, int);
241 void _k_assureVisibleSelection();
242 void _k_synchronizeSortingState(int, Qt::SortOrder);
243 void _k_slotChangeDecorationPosition();
244 void _k_slotExpandToUrl(const QModelIndex&);
245 void _k_slotItemsChanged();
246 void _k_slotDirectoryCreated(const KUrl&);
247
248 void updateListViewGrid();
249 int iconSizeForViewType(QAbstractItemView *itemView) const;
250
251 // private members
252 KDirOperator *parent;
253 QStack<KUrl*> backStack; ///< Contains all URLs you can reach with the back button.
254 QStack<KUrl*> forwardStack; ///< Contains all URLs you can reach with the forward button.
255
256 QModelIndex lastHoveredIndex;
257
258 KDirLister *dirLister;
259 KUrl currUrl;
260
261 KCompletion completion;
262 KCompletion dirCompletion;
263 bool completeListDirty;
264 QDir::SortFlags sorting;
265 QStyleOptionViewItem::Position decorationPosition;
266
267 QSplitter *splitter;
268
269 QAbstractItemView *itemView;
270 KDirModel *dirModel;
271 KDirSortFilterProxyModel *proxyModel;
272
273 KFileItemList pendingMimeTypes;
274
275 // the enum KFile::FileView as an int
276 int viewKind;
277 int defaultView;
278
279 KFile::Modes mode;
280 QProgressBar *progressBar;
281
282 KPreviewWidgetBase *preview;
283 KUrl previewUrl;
284 int previewWidth;
285
286 bool dirHighlighting;
287 bool onlyDoubleClickSelectsFiles;
288 QString lastURL; // used for highlighting a directory on cdUp
289 QTimer *progressDelayTimer;
290 int dropOptions;
291
292 KActionMenu *actionMenu;
293 KActionCollection *actionCollection;
294
295 KNewFileMenu *newFileMenu;
296
297 KConfigGroup *configGroup;
298
299 KFilePreviewGenerator *previewGenerator;
300
301 bool showPreviews;
302 int iconsZoom;
303
304 bool isSaving;
305
306 KActionMenu *decorationMenu;
307 KToggleAction *leftAction;
308 KUrl::List itemsToBeSetAsCurrent;
309 bool shouldFetchForItems;
310 InlinePreviewState inlinePreviewState;
311};
312
313KDirOperator::Private::Private(KDirOperator *_parent) :
314 parent(_parent),
315 dirLister(0),
316 decorationPosition(QStyleOptionViewItem::Left),
317 splitter(0),
318 itemView(0),
319 dirModel(0),
320 proxyModel(0),
321 progressBar(0),
322 preview(0),
323 previewUrl(),
324 previewWidth(0),
325 dirHighlighting(false),
326 onlyDoubleClickSelectsFiles(!KGlobalSettings::singleClick()),
327 progressDelayTimer(0),
328 dropOptions(0),
329 actionMenu(0),
330 actionCollection(0),
331 newFileMenu(0),
332 configGroup(0),
333 previewGenerator(0),
334 showPreviews(false),
335 iconsZoom(0),
336 isSaving(false),
337 decorationMenu(0),
338 leftAction(0),
339 shouldFetchForItems(false),
340 inlinePreviewState(NotForced)
341{
342}
343
344KDirOperator::Private::~Private()
345{
346 delete itemView;
347 itemView = 0;
348
349 // TODO:
350 // if (configGroup) {
351 // itemView->writeConfig(configGroup);
352 // }
353
354 qDeleteAll(backStack);
355 qDeleteAll(forwardStack);
356 delete preview;
357 preview = 0;
358
359 delete proxyModel;
360 proxyModel = 0;
361 delete dirModel;
362 dirModel = 0;
363 dirLister = 0; // deleted by KDirModel
364 delete configGroup;
365 configGroup = 0;
366
367 delete progressDelayTimer;
368 progressDelayTimer = 0;
369}
370
371KDirOperator::KDirOperator(const KUrl& _url, QWidget *parent) :
372 QWidget(parent),
373 d(new Private(this))
374{
375 d->splitter = new QSplitter(this);
376 d->splitter->setChildrenCollapsible(false);
377 connect(d->splitter, SIGNAL(splitterMoved(int,int)),
378 this, SLOT(_k_slotSplitterMoved(int,int)));
379
380 d->preview = 0;
381
382 d->mode = KFile::File;
383 d->viewKind = KFile::Simple;
384
385 if (_url.isEmpty()) { // no dir specified -> current dir
386 QString strPath = QDir::currentPath();
387 strPath.append(QChar('/'));
388 d->currUrl = QUrl::fromLocalFile(strPath);
389 } else {
390 d->currUrl = _url;
391 if (d->currUrl.protocol().isEmpty())
392 d->currUrl.setProtocol(QLatin1String("file"));
393
394 d->currUrl.addPath("/"); // make sure we have a trailing slash!
395 }
396
397 // We set the direction of this widget to LTR, since even on RTL desktops
398 // viewing directory listings in RTL mode makes people's head explode.
399 // Is this the correct place? Maybe it should be in some lower level widgets...?
400 setLayoutDirection(Qt::LeftToRight);
401 setDirLister(new KDirLister());
402
403 connect(&d->completion, SIGNAL(match(QString)),
404 SLOT(slotCompletionMatch(QString)));
405
406 d->progressBar = new QProgressBar(this);
407 d->progressBar->setObjectName("d->progressBar");
408 d->progressBar->adjustSize();
409 d->progressBar->move(2, height() - d->progressBar->height() - 2);
410
411 d->progressDelayTimer = new QTimer(this);
412 d->progressDelayTimer->setObjectName(QLatin1String("d->progressBar delay timer"));
413 connect(d->progressDelayTimer, SIGNAL(timeout()),
414 SLOT(_k_slotShowProgress()));
415
416 d->completeListDirty = false;
417
418 // action stuff
419 setupActions();
420 setupMenu();
421
422 d->sorting = QDir::NoSort; //so updateSorting() doesn't think nothing has changed
423 d->updateSorting(QDir::Name | QDir::DirsFirst);
424
425 setFocusPolicy(Qt::WheelFocus);
426}
427
428KDirOperator::~KDirOperator()
429{
430 resetCursor();
431 disconnect(d->dirLister, 0, this, 0);
432 delete d;
433}
434
435
436void KDirOperator::setSorting(QDir::SortFlags spec)
437{
438 d->updateSorting(spec);
439}
440
441QDir::SortFlags KDirOperator::sorting() const
442{
443 return d->sorting;
444}
445
446bool KDirOperator::isRoot() const
447{
448#ifdef Q_WS_WIN
449 if (url().isLocalFile()) {
450 const QString path = url().toLocalFile();
451 if (path.length() == 3)
452 return (path[0].isLetter() && path[1] == ':' && path[2] == '/');
453 return false;
454 } else
455#endif
456 return url().path() == QString(QLatin1Char('/'));
457}
458
459KDirLister *KDirOperator::dirLister() const
460{
461 return d->dirLister;
462}
463
464void KDirOperator::resetCursor()
465{
466 if (qApp)
467 QApplication::restoreOverrideCursor();
468 d->progressBar->hide();
469}
470
471void KDirOperator::sortByName()
472{
473 d->updateSorting((d->sorting & ~QDirSortMask) | QDir::Name);
474}
475
476void KDirOperator::sortBySize()
477{
478 d->updateSorting((d->sorting & ~QDirSortMask) | QDir::Size);
479}
480
481void KDirOperator::sortByDate()
482{
483 d->updateSorting((d->sorting & ~QDirSortMask) | QDir::Time);
484}
485
486void KDirOperator::sortByType()
487{
488 d->updateSorting((d->sorting & ~QDirSortMask) | QDir::Type);
489}
490
491void KDirOperator::sortReversed()
492{
493 // toggle it, hence the inversion of current state
494 d->_k_slotSortReversed(!(d->sorting & QDir::Reversed));
495}
496
497void KDirOperator::toggleDirsFirst()
498{
499 d->_k_slotToggleDirsFirst();
500}
501
502void KDirOperator::toggleIgnoreCase()
503{
504 if (d->proxyModel != 0) {
505 Qt::CaseSensitivity cs = d->proxyModel->sortCaseSensitivity();
506 cs = (cs == Qt::CaseSensitive) ? Qt::CaseInsensitive : Qt::CaseSensitive;
507 d->proxyModel->setSortCaseSensitivity(cs);
508 }
509}
510
511void KDirOperator::updateSelectionDependentActions()
512{
513 const bool hasSelection = (d->itemView != 0) &&
514 d->itemView->selectionModel()->hasSelection();
515 d->actionCollection->action("trash")->setEnabled(hasSelection);
516 d->actionCollection->action("delete")->setEnabled(hasSelection);
517 d->actionCollection->action("properties")->setEnabled(hasSelection);
518}
519
520void KDirOperator::setPreviewWidget(KPreviewWidgetBase *w)
521{
522 const bool showPreview = (w != 0);
523 if (showPreview) {
524 d->viewKind = (d->viewKind | KFile::PreviewContents);
525 } else {
526 d->viewKind = (d->viewKind & ~KFile::PreviewContents);
527 }
528
529 delete d->preview;
530 d->preview = w;
531
532 if (w) {
533 d->splitter->addWidget(w);
534 }
535
536 KToggleAction *previewAction = static_cast<KToggleAction*>(d->actionCollection->action("preview"));
537 previewAction->setEnabled(showPreview);
538 previewAction->setChecked(showPreview);
539 setView(static_cast<KFile::FileView>(d->viewKind));
540}
541
542KFileItemList KDirOperator::selectedItems() const
543{
544 KFileItemList itemList;
545 if (d->itemView == 0) {
546 return itemList;
547 }
548
549 const QItemSelection selection = d->proxyModel->mapSelectionToSource(d->itemView->selectionModel()->selection());
550
551 const QModelIndexList indexList = selection.indexes();
552 foreach(const QModelIndex &index, indexList) {
553 KFileItem item = d->dirModel->itemForIndex(index);
554 if (!item.isNull()) {
555 itemList.append(item);
556 }
557 }
558
559 return itemList;
560}
561
562bool KDirOperator::isSelected(const KFileItem &item) const
563{
564 if ((item.isNull()) || (d->itemView == 0)) {
565 return false;
566 }
567
568 const QModelIndex dirIndex = d->dirModel->indexForItem(item);
569 const QModelIndex proxyIndex = d->proxyModel->mapFromSource(dirIndex);
570 return d->itemView->selectionModel()->isSelected(proxyIndex);
571}
572
573int KDirOperator::numDirs() const
574{
575 return (d->dirLister == 0) ? 0 : d->dirLister->directories().count();
576}
577
578int KDirOperator::numFiles() const
579{
580 return (d->dirLister == 0) ? 0 : d->dirLister->items().count() - numDirs();
581}
582
583KCompletion * KDirOperator::completionObject() const
584{
585 return const_cast<KCompletion *>(&d->completion);
586}
587
588KCompletion *KDirOperator::dirCompletionObject() const
589{
590 return const_cast<KCompletion *>(&d->dirCompletion);
591}
592
593KActionCollection * KDirOperator::actionCollection() const
594{
595 return d->actionCollection;
596}
597
598KFile::FileView KDirOperator::Private::allViews() {
599 return static_cast<KFile::FileView>(KFile::Simple | KFile::Detail | KFile::Tree | KFile::DetailTree);
600}
601
602void KDirOperator::Private::_k_slotDetailedView()
603{
604 KFile::FileView view = static_cast<KFile::FileView>((viewKind & ~allViews()) | KFile::Detail);
605 parent->setView(view);
606}
607
608void KDirOperator::Private::_k_slotSimpleView()
609{
610 KFile::FileView view = static_cast<KFile::FileView>((viewKind & ~allViews()) | KFile::Simple);
611 parent->setView(view);
612}
613
614void KDirOperator::Private::_k_slotTreeView()
615{
616 KFile::FileView view = static_cast<KFile::FileView>((viewKind & ~allViews()) | KFile::Tree);
617 parent->setView(view);
618}
619
620void KDirOperator::Private::_k_slotDetailedTreeView()
621{
622 KFile::FileView view = static_cast<KFile::FileView>((viewKind & ~allViews()) | KFile::DetailTree);
623 parent->setView(view);
624}
625
626void KDirOperator::Private::_k_slotToggleHidden(bool show)
627{
628 dirLister->setShowingDotFiles(show);
629 parent->updateDir();
630 _k_assureVisibleSelection();
631}
632
633void KDirOperator::Private::_k_togglePreview(bool on)
634{
635 if (on) {
636 viewKind = viewKind | KFile::PreviewContents;
637 if (preview == 0) {
638 preview = new KFileMetaPreview(parent);
639 actionCollection->action("preview")->setChecked(true);
640 splitter->addWidget(preview);
641 }
642
643 preview->show();
644
645 QMetaObject::invokeMethod(parent, "_k_assureVisibleSelection", Qt::QueuedConnection);
646 if (itemView != 0) {
647 const QModelIndex index = itemView->selectionModel()->currentIndex();
648 if (index.isValid()) {
649 _k_triggerPreview(index);
650 }
651 }
652 } else if (preview != 0) {
653 viewKind = viewKind & ~KFile::PreviewContents;
654 preview->hide();
655 }
656}
657
658void KDirOperator::Private::_k_toggleInlinePreviews(bool show)
659{
660 if (showPreviews == show) {
661 return;
662 }
663
664 showPreviews = show;
665
666 if (!previewGenerator) {
667 return;
668 }
669
670 previewGenerator->setPreviewShown(show);
671
672 if (!show) {
673 // remove all generated previews
674 QAbstractItemModel *model = dirModel;
675 for (int i = 0; i < model->rowCount(); ++i) {
676 QModelIndex index = model->index(i, 0);
677 const KFileItem item = dirModel->itemForIndex(index);
678 const_cast<QAbstractItemModel*>(index.model())->setData(index, KIcon(item.iconName()), Qt::DecorationRole);
679 }
680 }
681}
682
683void KDirOperator::Private::_k_slotOpenFileManager()
684{
685 new KRun(currUrl, parent);
686}
687
688void KDirOperator::Private::_k_slotSortByName()
689{
690 parent->sortByName();
691}
692
693void KDirOperator::Private::_k_slotSortBySize()
694{
695 parent->sortBySize();
696}
697
698void KDirOperator::Private::_k_slotSortByDate()
699{
700 parent->sortByDate();
701}
702
703void KDirOperator::Private::_k_slotSortByType()
704{
705 parent->sortByType();
706}
707
708void KDirOperator::Private::_k_slotSortReversed(bool doReverse)
709{
710 QDir::SortFlags s = sorting & ~QDir::Reversed;
711 if (doReverse) {
712 s |= QDir::Reversed;
713 }
714 updateSorting(s);
715}
716
717void KDirOperator::Private::_k_slotToggleDirsFirst()
718{
719 QDir::SortFlags s = (sorting ^ QDir::DirsFirst);
720 updateSorting(s);
721}
722
723void KDirOperator::Private::_k_slotToggleIgnoreCase()
724{
725 // TODO: port to Qt4's QAbstractItemView
726 /*if ( !d->fileView )
727 return;
728
729 QDir::SortFlags sorting = d->fileView->sorting();
730 if ( !KFile::isSortCaseInsensitive( sorting ) )
731 d->fileView->setSorting( sorting | QDir::IgnoreCase );
732 else
733 d->fileView->setSorting( sorting & ~QDir::IgnoreCase );
734 d->sorting = d->fileView->sorting();*/
735}
736
737void KDirOperator::mkdir()
738{
739 d->newFileMenu->setPopupFiles(url());
740 d->newFileMenu->setViewShowsHiddenFiles(showHiddenFiles());
741 d->newFileMenu->createDirectory();
742}
743
744bool KDirOperator::mkdir(const QString& directory, bool enterDirectory)
745{
746 // Creates "directory", relative to the current directory (d->currUrl).
747 // The given path may contain any number directories, existent or not.
748 // They will all be created, if possible.
749
750 bool writeOk = false;
751 bool exists = false;
752 KUrl url(d->currUrl);
753
754 const QStringList dirs = directory.split('/', QString::SkipEmptyParts);
755 QStringList::ConstIterator it = dirs.begin();
756
757 for (; it != dirs.end(); ++it) {
758 url.addPath(*it);
759 exists = KIO::NetAccess::exists(url, KIO::NetAccess::DestinationSide, this);
760 writeOk = !exists && KIO::NetAccess::mkdir(url, this);
761 }
762
763 if (exists) { // url was already existent
764 KMessageBox::sorry(d->itemView, i18n("A file or folder named %1 already exists.", url.pathOrUrl()));
765 } else if (!writeOk) {
766 KMessageBox::sorry(d->itemView, i18n("You do not have permission to "
767 "create that folder."));
768 } else if (enterDirectory) {
769 setUrl(url, true);
770 }
771
772 return writeOk;
773}
774
775KIO::DeleteJob * KDirOperator::del(const KFileItemList& items,
776 QWidget *parent,
777 bool ask, bool showProgress)
778{
779 if (items.isEmpty()) {
780 KMessageBox::information(parent,
781 i18n("You did not select a file to delete."),
782 i18n("Nothing to Delete"));
783 return 0L;
784 }
785
786 if (parent == 0) {
787 parent = this;
788 }
789
790 KUrl::List urls;
791 QStringList files;
792 foreach (const KFileItem &item, items) {
793 const KUrl url = item.url();
794 urls.append(url);
795 files.append(url.pathOrUrl());
796 }
797
798 bool doIt = !ask;
799 if (ask) {
800 int ret;
801 if (items.count() == 1) {
802 ret = KMessageBox::warningContinueCancel(parent,
803 i18n("<qt>Do you really want to delete\n <b>'%1'</b>?</qt>" ,
804 files.first()),
805 i18n("Delete File"),
806 KStandardGuiItem::del(),
807 KStandardGuiItem::cancel(), "AskForDelete");
808 } else
809 ret = KMessageBox::warningContinueCancelList(parent,
810 i18np("Do you really want to delete this item?", "Do you really want to delete these %1 items?", items.count()),
811 files,
812 i18n("Delete Files"),
813 KStandardGuiItem::del(),
814 KStandardGuiItem::cancel(), "AskForDelete");
815 doIt = (ret == KMessageBox::Continue);
816 }
817
818 if (doIt) {
819 KIO::JobFlags flags = showProgress ? KIO::DefaultFlags : KIO::HideProgressInfo;
820 KIO::DeleteJob *job = KIO::del(urls, flags);
821 job->ui()->setWindow(this);
822 job->ui()->setAutoErrorHandlingEnabled(true);
823 return job;
824 }
825
826 return 0L;
827}
828
829void KDirOperator::deleteSelected()
830{
831 const KFileItemList list = selectedItems();
832 if (!list.isEmpty()) {
833 del(list, this);
834 }
835}
836
837KIO::CopyJob * KDirOperator::trash(const KFileItemList& items,
838 QWidget *parent,
839 bool ask, bool showProgress)
840{
841 if (items.isEmpty()) {
842 KMessageBox::information(parent,
843 i18n("You did not select a file to trash."),
844 i18n("Nothing to Trash"));
845 return 0L;
846 }
847
848 KUrl::List urls;
849 QStringList files;
850 foreach (const KFileItem &item, items) {
851 const KUrl url = item.url();
852 urls.append(url);
853 files.append(url.pathOrUrl());
854 }
855
856 bool doIt = !ask;
857 if (ask) {
858 int ret;
859 if (items.count() == 1) {
860 ret = KMessageBox::warningContinueCancel(parent,
861 i18n("<qt>Do you really want to trash\n <b>'%1'</b>?</qt>" ,
862 files.first()),
863 i18n("Trash File"),
864 KGuiItem(i18nc("to trash", "&Trash"), "user-trash"),
865 KStandardGuiItem::cancel(), "AskForTrash");
866 } else
867 ret = KMessageBox::warningContinueCancelList(parent,
868 i18np("translators: not called for n == 1", "Do you really want to trash these %1 items?", items.count()),
869 files,
870 i18n("Trash Files"),
871 KGuiItem(i18nc("to trash", "&Trash"), "user-trash"),
872 KStandardGuiItem::cancel(), "AskForTrash");
873 doIt = (ret == KMessageBox::Continue);
874 }
875
876 if (doIt) {
877 KIO::JobFlags flags = showProgress ? KIO::DefaultFlags : KIO::HideProgressInfo;
878 KIO::CopyJob *job = KIO::trash(urls, flags);
879 job->ui()->setWindow(this);
880 job->ui()->setAutoErrorHandlingEnabled(true);
881 return job;
882 }
883
884 return 0L;
885}
886
887KFilePreviewGenerator *KDirOperator::previewGenerator() const
888{
889 return d->previewGenerator;
890}
891
892void KDirOperator::setInlinePreviewShown(bool show)
893{
894 d->inlinePreviewState = show ? Private::ForcedToTrue : Private::ForcedToFalse;
895}
896
897bool KDirOperator::isInlinePreviewShown() const
898{
899 return d->showPreviews;
900}
901
902int KDirOperator::iconsZoom() const
903{
904 return d->iconsZoom;
905}
906
907void KDirOperator::setIsSaving(bool isSaving)
908{
909 d->isSaving = isSaving;
910}
911
912bool KDirOperator::isSaving() const
913{
914 return d->isSaving;
915}
916
917void KDirOperator::trashSelected()
918{
919 if (d->itemView == 0) {
920 return;
921 }
922
923 if (QApplication::keyboardModifiers() & Qt::ShiftModifier) {
924 deleteSelected();
925 return;
926 }
927
928 const KFileItemList list = selectedItems();
929 if (!list.isEmpty()) {
930 trash(list, this);
931 }
932}
933
934void KDirOperator::setIconsZoom(int _value)
935{
936 if (d->iconsZoom == _value) {
937 return;
938 }
939
940 int value = _value;
941 value = qMin(100, value);
942 value = qMax(0, value);
943
944 d->iconsZoom = value;
945
946 if (!d->previewGenerator) {
947 return;
948 }
949
950 const int maxSize = KIconLoader::SizeEnormous - KIconLoader::SizeSmall;
951 const int val = (maxSize * value / 100) + KIconLoader::SizeSmall;
952 d->itemView->setIconSize(QSize(val, val));
953 d->updateListViewGrid();
954 d->previewGenerator->updatePreviews();
955
956 emit currentIconSizeChanged(value);
957}
958
959void KDirOperator::close()
960{
961 resetCursor();
962 d->pendingMimeTypes.clear();
963 d->completion.clear();
964 d->dirCompletion.clear();
965 d->completeListDirty = true;
966 d->dirLister->stop();
967}
968
969void KDirOperator::Private::checkPath(const QString &, bool /*takeFiles*/) // SLOT
970{
971#if 0
972 // copy the argument in a temporary string
973 QString text = _txt;
974 // it's unlikely to happen, that at the beginning are spaces, but
975 // for the end, it happens quite often, I guess.
976 text = text.trimmed();
977 // if the argument is no URL (the check is quite fragil) and it's
978 // no absolute path, we add the current directory to get a correct url
979 if (text.find(':') < 0 && text[0] != '/')
980 text.insert(0, d->currUrl);
981
982 // in case we have a selection defined and someone patched the file-
983 // name, we check, if the end of the new name is changed.
984 if (!selection.isNull()) {
985 int position = text.lastIndexOf('/');
986 ASSERT(position >= 0); // we already inserted the current d->dirLister in case
987 QString filename = text.mid(position + 1, text.length());
988 if (filename != selection)
989 selection.clear();
990 }
991
992 KUrl u(text); // I have to take care of entered URLs
993 bool filenameEntered = false;
994
995 if (u.isLocalFile()) {
996 // the empty path is kind of a hack
997 KFileItem i("", u.toLocalFile());
998 if (i.isDir())
999 setUrl(text, true);
1000 else {
1001 if (takeFiles)
1002 if (acceptOnlyExisting && !i.isFile())
1003 warning("you entered an invalid URL");
1004 else
1005 filenameEntered = true;
1006 }
1007 } else
1008 setUrl(text, true);
1009
1010 if (filenameEntered) {
1011 filename_ = u.url();
1012 emit fileSelected(filename_);
1013
1014 QApplication::restoreOverrideCursor();
1015
1016 accept();
1017 }
1018#endif
1019 kDebug(kfile_area) << "TODO KDirOperator::checkPath()";
1020}
1021
1022void KDirOperator::setUrl(const KUrl& _newurl, bool clearforward)
1023{
1024 KUrl newurl;
1025
1026 if (!_newurl.isValid())
1027 newurl = QUrl::fromLocalFile(QDir::homePath());
1028 else
1029 newurl = _newurl;
1030
1031 newurl.adjustPath( KUrl::AddTrailingSlash );
1032
1033 // already set
1034 if (newurl.equals(d->currUrl, KUrl::CompareWithoutTrailingSlash))
1035 return;
1036
1037 if (!Private::isReadable(newurl)) {
1038 // maybe newurl is a file? check its parent directory
1039 newurl.setPath(newurl.directory(KUrl::ObeyTrailingSlash));
1040 if (newurl.equals(d->currUrl, KUrl::CompareWithoutTrailingSlash))
1041 return; // parent is current dir, nothing to do (fixes #173454, too)
1042 KIO::UDSEntry entry;
1043 bool res = KIO::NetAccess::stat(newurl, entry, this);
1044 KFileItem i(entry, newurl);
1045 if ((!res || !Private::isReadable(newurl)) && i.isDir()) {
1046 resetCursor();
1047 KMessageBox::error(d->itemView,
1048 i18n("The specified folder does not exist "
1049 "or was not readable."));
1050 return;
1051 } else if (!i.isDir()) {
1052 return;
1053 }
1054 }
1055
1056 if (clearforward) {
1057 // autodelete should remove this one
1058 d->backStack.push(new KUrl(d->currUrl));
1059 qDeleteAll(d->forwardStack);
1060 d->forwardStack.clear();
1061 }
1062
1063 d->lastURL = d->currUrl.url(KUrl::RemoveTrailingSlash);
1064 d->currUrl = newurl;
1065
1066 pathChanged();
1067 emit urlEntered(newurl);
1068
1069 // enable/disable actions
1070 QAction* forwardAction = d->actionCollection->action("forward");
1071 forwardAction->setEnabled(!d->forwardStack.isEmpty());
1072
1073 QAction* backAction = d->actionCollection->action("back");
1074 backAction->setEnabled(!d->backStack.isEmpty());
1075
1076 QAction* upAction = d->actionCollection->action("up");
1077 upAction->setEnabled(!isRoot());
1078
1079 d->openUrl(newurl);
1080}
1081
1082void KDirOperator::updateDir()
1083{
1084 QApplication::setOverrideCursor(Qt::WaitCursor);
1085 d->dirLister->emitChanges();
1086 QApplication::restoreOverrideCursor();
1087}
1088
1089void KDirOperator::rereadDir()
1090{
1091 pathChanged();
1092 d->openUrl(d->currUrl, KDirLister::Reload);
1093}
1094
1095
1096bool KDirOperator::Private::openUrl(const KUrl& url, KDirLister::OpenUrlFlags flags)
1097{
1098 const bool result = KProtocolManager::supportsListing(url) && dirLister->openUrl(url, flags);
1099 if (!result) // in that case, neither completed() nor canceled() will be emitted by KDL
1100 _k_slotCanceled();
1101
1102 return result;
1103}
1104
1105int KDirOperator::Private::sortColumn() const
1106{
1107 int column = KDirModel::Name;
1108 if (KFile::isSortByDate(sorting)) {
1109 column = KDirModel::ModifiedTime;
1110 } else if (KFile::isSortBySize(sorting)) {
1111 column = KDirModel::Size;
1112 } else if (KFile::isSortByType(sorting)) {
1113 column = KDirModel::Type;
1114 } else {
1115 Q_ASSERT(KFile::isSortByName(sorting));
1116 }
1117
1118 return column;
1119}
1120
1121Qt::SortOrder KDirOperator::Private::sortOrder() const
1122{
1123 return (sorting & QDir::Reversed) ? Qt::DescendingOrder :
1124 Qt::AscendingOrder;
1125}
1126
1127void KDirOperator::Private::updateSorting(QDir::SortFlags sort)
1128{
1129 kDebug(kfile_area) << "changing sort flags from" << sorting << "to" << sort;
1130 if (sort == sorting) {
1131 return;
1132 }
1133
1134 if ((sorting ^ sort) & QDir::DirsFirst) {
1135 // The "Folders First" setting has been changed.
1136 // We need to make sure that the files and folders are really re-sorted.
1137 // Without the following intermediate "fake resorting",
1138 // QSortFilterProxyModel::sort(int column, Qt::SortOrder order)
1139 // would do nothing because neither the column nor the sort order have been changed.
1140 Qt::SortOrder tmpSortOrder = (sortOrder() == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder);
1141 proxyModel->sort(sortOrder(), tmpSortOrder);
1142 proxyModel->setSortFoldersFirst(sort & QDir::DirsFirst);
1143 }
1144
1145 sorting = sort;
1146 parent->updateSortActions();
1147 proxyModel->sort(sortColumn(), sortOrder());
1148
1149 // TODO: The headers from QTreeView don't take care about a sorting
1150 // change of the proxy model hence they must be updated the manually.
1151 // This is done here by a qobject_cast, but it would be nicer to:
1152 // - provide a signal 'sortingChanged()'
1153 // - connect KDirOperatorDetailView() with this signal and update the
1154 // header internally
1155 QTreeView* treeView = qobject_cast<QTreeView*>(itemView);
1156 if (treeView != 0) {
1157 QHeaderView* headerView = treeView->header();
1158 headerView->blockSignals(true);
1159 headerView->setSortIndicator(sortColumn(), sortOrder());
1160 headerView->blockSignals(false);
1161 }
1162
1163 _k_assureVisibleSelection();
1164}
1165
1166// Protected
1167void KDirOperator::pathChanged()
1168{
1169 if (d->itemView == 0)
1170 return;
1171
1172 d->pendingMimeTypes.clear();
1173 //d->fileView->clear(); TODO
1174 d->completion.clear();
1175 d->dirCompletion.clear();
1176
1177 // it may be, that we weren't ready at this time
1178 QApplication::restoreOverrideCursor();
1179
1180 // when KIO::Job emits finished, the slot will restore the cursor
1181 QApplication::setOverrideCursor(Qt::WaitCursor);
1182
1183 if (!Private::isReadable(d->currUrl)) {
1184 KMessageBox::error(d->itemView,
1185 i18n("The specified folder does not exist "
1186 "or was not readable."));
1187 if (d->backStack.isEmpty())
1188 home();
1189 else
1190 back();
1191 }
1192}
1193
1194void KDirOperator::Private::_k_slotRedirected(const KUrl& newURL)
1195{
1196 currUrl = newURL;
1197 pendingMimeTypes.clear();
1198 completion.clear();
1199 dirCompletion.clear();
1200 completeListDirty = true;
1201 emit parent->urlEntered(newURL);
1202}
1203
1204// Code pinched from kfm then hacked
1205void KDirOperator::back()
1206{
1207 if (d->backStack.isEmpty())
1208 return;
1209
1210 d->forwardStack.push(new KUrl(d->currUrl));
1211
1212 KUrl *s = d->backStack.pop();
1213
1214 setUrl(*s, false);
1215 delete s;
1216}
1217
1218// Code pinched from kfm then hacked
1219void KDirOperator::forward()
1220{
1221 if (d->forwardStack.isEmpty())
1222 return;
1223
1224 d->backStack.push(new KUrl(d->currUrl));
1225
1226 KUrl *s = d->forwardStack.pop();
1227 setUrl(*s, false);
1228 delete s;
1229}
1230
1231KUrl KDirOperator::url() const
1232{
1233 return d->currUrl;
1234}
1235
1236void KDirOperator::cdUp()
1237{
1238 KUrl tmp(d->currUrl);
1239 tmp.cd(QLatin1String(".."));
1240 setUrl(tmp, true);
1241}
1242
1243void KDirOperator::home()
1244{
1245 KUrl u = QUrl::fromLocalFile(QDir::homePath());
1246 setUrl(u, true);
1247}
1248
1249void KDirOperator::clearFilter()
1250{
1251 d->dirLister->setNameFilter(QString());
1252 d->dirLister->clearMimeFilter();
1253 checkPreviewSupport();
1254}
1255
1256void KDirOperator::setNameFilter(const QString& filter)
1257{
1258 d->dirLister->setNameFilter(filter);
1259 checkPreviewSupport();
1260}
1261
1262QString KDirOperator::nameFilter() const
1263{
1264 return d->dirLister->nameFilter();
1265}
1266
1267void KDirOperator::setMimeFilter(const QStringList& mimetypes)
1268{
1269 d->dirLister->setMimeFilter(mimetypes);
1270 checkPreviewSupport();
1271}
1272
1273QStringList KDirOperator::mimeFilter() const
1274{
1275 return d->dirLister->mimeFilters();
1276}
1277
1278void KDirOperator::setNewFileMenuSupportedMimeTypes(const QStringList& mimeTypes)
1279{
1280 d->newFileMenu->setSupportedMimeTypes(mimeTypes);
1281}
1282
1283QStringList KDirOperator::newFileMenuSupportedMimeTypes() const
1284{
1285 return d->newFileMenu->supportedMimeTypes();
1286}
1287
1288bool KDirOperator::checkPreviewSupport()
1289{
1290 KToggleAction *previewAction = static_cast<KToggleAction*>(d->actionCollection->action("preview"));
1291
1292 bool hasPreviewSupport = false;
1293 KConfigGroup cg(KGlobal::config(), ConfigGroup);
1294 if (cg.readEntry("Show Default Preview", true))
1295 hasPreviewSupport = d->checkPreviewInternal();
1296
1297 previewAction->setEnabled(hasPreviewSupport);
1298 return hasPreviewSupport;
1299}
1300
1301void KDirOperator::activatedMenu(const KFileItem &item, const QPoint &pos)
1302{
1303 Q_UNUSED(item);
1304 updateSelectionDependentActions();
1305
1306 d->newFileMenu->setPopupFiles(url());
1307 d->newFileMenu->setViewShowsHiddenFiles(showHiddenFiles());
1308 d->newFileMenu->checkUpToDate();
1309
1310 emit contextMenuAboutToShow( item, d->actionMenu->menu() );
1311
1312 d->actionMenu->menu()->exec(pos);
1313}
1314
1315void KDirOperator::changeEvent(QEvent *event)
1316{
1317 QWidget::changeEvent(event);
1318}
1319
1320bool KDirOperator::eventFilter(QObject *watched, QEvent *event)
1321{
1322 Q_UNUSED(watched);
1323
1324 // If we are not hovering any items, check if there is a current index
1325 // set. In that case, we show the preview of that item.
1326 switch(event->type()) {
1327 case QEvent::MouseMove: {
1328 if (d->preview && !d->preview->isHidden()) {
1329 const QModelIndex hoveredIndex = d->itemView->indexAt(d->itemView->viewport()->mapFromGlobal(QCursor::pos()));
1330
1331 if (d->lastHoveredIndex == hoveredIndex)
1332 return QWidget::eventFilter(watched, event);
1333
1334 d->lastHoveredIndex = hoveredIndex;
1335
1336 const QModelIndex focusedIndex = d->itemView->selectionModel() ? d->itemView->selectionModel()->currentIndex()
1337 : QModelIndex();
1338
1339 if (!hoveredIndex.isValid() && focusedIndex.isValid() &&
1340 d->itemView->selectionModel()->isSelected(focusedIndex) &&
1341 (d->lastHoveredIndex != focusedIndex)) {
1342 const QModelIndex sourceFocusedIndex = d->proxyModel->mapToSource(focusedIndex);
1343 const KFileItem item = d->dirModel->itemForIndex(sourceFocusedIndex);
1344 if (!item.isNull()) {
1345 d->preview->showPreview(item.url());
1346 }
1347 }
1348 }
1349 }
1350 break;
1351 case QEvent::MouseButtonRelease: {
1352 if (d->preview != 0 && !d->preview->isHidden()) {
1353 const QModelIndex hoveredIndex = d->itemView->indexAt(d->itemView->viewport()->mapFromGlobal(QCursor::pos()));
1354 const QModelIndex focusedIndex = d->itemView->selectionModel() ? d->itemView->selectionModel()->currentIndex()
1355 : QModelIndex();
1356
1357 if (((!focusedIndex.isValid()) ||
1358 !d->itemView->selectionModel()->isSelected(focusedIndex)) &&
1359 (!hoveredIndex.isValid())) {
1360 d->preview->clearPreview();
1361 }
1362 }
1363 }
1364 break;
1365 case QEvent::Wheel: {
1366 QWheelEvent *evt = static_cast<QWheelEvent*>(event);
1367 if (evt->modifiers() & Qt::ControlModifier) {
1368 if (evt->delta() > 0) {
1369 setIconsZoom(d->iconsZoom + 10);
1370 } else {
1371 setIconsZoom(d->iconsZoom - 10);
1372 }
1373 return true;
1374 }
1375 }
1376 break;
1377 default:
1378 break;
1379 }
1380
1381 return QWidget::eventFilter(watched, event);
1382}
1383
1384bool KDirOperator::Private::checkPreviewInternal() const
1385{
1386 const QStringList supported = KIO::PreviewJob::supportedMimeTypes();
1387 // no preview support for directories?
1388 if (parent->dirOnlyMode() && supported.indexOf("inode/directory") == -1)
1389 return false;
1390
1391 QStringList mimeTypes = dirLister->mimeFilters();
1392 const QStringList nameFilter = dirLister->nameFilter().split(' ', QString::SkipEmptyParts);
1393
1394 if (mimeTypes.isEmpty() && nameFilter.isEmpty() && !supported.isEmpty())
1395 return true;
1396 else {
1397 QRegExp r;
1398 r.setPatternSyntax(QRegExp::Wildcard); // the "mimetype" can be "image/*"
1399
1400 if (!mimeTypes.isEmpty()) {
1401 QStringList::ConstIterator it = supported.begin();
1402
1403 for (; it != supported.end(); ++it) {
1404 r.setPattern(*it);
1405
1406 QStringList result = mimeTypes.filter(r);
1407 if (!result.isEmpty()) { // matches! -> we want previews
1408 return true;
1409 }
1410 }
1411 }
1412
1413 if (!nameFilter.isEmpty()) {
1414 // find the mimetypes of all the filter-patterns
1415 QStringList::const_iterator it1 = nameFilter.begin();
1416 for (; it1 != nameFilter.end(); ++it1) {
1417 if ((*it1) == "*") {
1418 return true;
1419 }
1420
1421 KMimeType::Ptr mt = KMimeType::findByPath(*it1, 0, true /*fast mode, no file contents exist*/);
1422 if (!mt)
1423 continue;
1424 QString mime = mt->name();
1425
1426 // the "mimetypes" we get from the PreviewJob can be "image/*"
1427 // so we need to check in wildcard mode
1428 QStringList::ConstIterator it2 = supported.begin();
1429 for (; it2 != supported.end(); ++it2) {
1430 r.setPattern(*it2);
1431 if (r.indexIn(mime) != -1) {
1432 return true;
1433 }
1434 }
1435 }
1436 }
1437 }
1438
1439 return false;
1440}
1441
1442QAbstractItemView* KDirOperator::createView(QWidget* parent, KFile::FileView viewKind)
1443{
1444 QAbstractItemView *itemView = 0;
1445 if (KFile::isDetailView(viewKind) || KFile::isTreeView(viewKind) || KFile::isDetailTreeView(viewKind)) {
1446 KDirOperatorDetailView *detailView = new KDirOperatorDetailView(parent);
1447 detailView->setViewMode(viewKind);
1448 itemView = detailView;
1449 } else {
1450 itemView = new KDirOperatorIconView(this, parent);
1451 }
1452
1453 return itemView;
1454}
1455
1456void KDirOperator::setAcceptDrops(bool b)
1457{
1458 // TODO:
1459 //if (d->fileView)
1460 // d->fileView->widget()->setAcceptDrops(b);
1461 QWidget::setAcceptDrops(b);
1462}
1463
1464void KDirOperator::setDropOptions(int options)
1465{
1466 d->dropOptions = options;
1467 // TODO:
1468 //if (d->fileView)
1469 // d->fileView->setDropOptions(options);
1470}
1471
1472void KDirOperator::setView(KFile::FileView viewKind)
1473{
1474 bool preview = (KFile::isPreviewInfo(viewKind) || KFile::isPreviewContents(viewKind));
1475
1476 if (viewKind == KFile::Default) {
1477 if (KFile::isDetailView((KFile::FileView)d->defaultView)) {
1478 viewKind = KFile::Detail;
1479 } else if (KFile::isTreeView((KFile::FileView)d->defaultView)) {
1480 viewKind = KFile::Tree;
1481 } else if (KFile::isDetailTreeView((KFile::FileView)d->defaultView)) {
1482 viewKind = KFile::DetailTree;
1483 } else {
1484 viewKind = KFile::Simple;
1485 }
1486
1487 const KFile::FileView defaultViewKind = static_cast<KFile::FileView>(d->defaultView);
1488 preview = (KFile::isPreviewInfo(defaultViewKind) || KFile::isPreviewContents(defaultViewKind))
1489 && d->actionCollection->action("preview")->isEnabled();
1490 }
1491
1492 d->viewKind = static_cast<int>(viewKind);
1493 viewKind = static_cast<KFile::FileView>(d->viewKind);
1494
1495 QAbstractItemView *newView = createView(this, viewKind);
1496 setView(newView);
1497
1498 d->_k_togglePreview(preview);
1499}
1500
1501QAbstractItemView * KDirOperator::view() const
1502{
1503 return d->itemView;
1504}
1505
1506KFile::Modes KDirOperator::mode() const
1507{
1508 return d->mode;
1509}
1510
1511void KDirOperator::setMode(KFile::Modes mode)
1512{
1513 if (d->mode == mode)
1514 return;
1515
1516 d->mode = mode;
1517
1518 d->dirLister->setDirOnlyMode(dirOnlyMode());
1519
1520 // reset the view with the different mode
1521 if (d->itemView != 0)
1522 setView(static_cast<KFile::FileView>(d->viewKind));
1523}
1524
1525void KDirOperator::setView(QAbstractItemView *view)
1526{
1527 if (view == d->itemView) {
1528 return;
1529 }
1530
1531 // TODO: do a real timer and restart it after that
1532 d->pendingMimeTypes.clear();
1533 const bool listDir = (d->itemView == 0);
1534
1535 if (d->mode & KFile::Files) {
1536 view->setSelectionMode(QAbstractItemView::ExtendedSelection);
1537 } else {
1538 view->setSelectionMode(QAbstractItemView::SingleSelection);
1539 }
1540
1541 QItemSelectionModel *selectionModel = 0;
1542 if ((d->itemView != 0) && d->itemView->selectionModel()->hasSelection()) {
1543 // remember the selection of the current item view and apply this selection
1544 // to the new view later
1545 const QItemSelection selection = d->itemView->selectionModel()->selection();
1546 selectionModel = new QItemSelectionModel(d->proxyModel, this);
1547 selectionModel->select(selection, QItemSelectionModel::Select);
1548 }
1549
1550 setFocusProxy(0);
1551 delete d->itemView;
1552 d->itemView = view;
1553 d->itemView->setModel(d->proxyModel);
1554 setFocusProxy(d->itemView);
1555
1556 view->viewport()->installEventFilter(this);
1557
1558 KFileItemDelegate *delegate = new KFileItemDelegate(d->itemView);
1559 d->itemView->setItemDelegate(delegate);
1560 d->itemView->viewport()->setAttribute(Qt::WA_Hover);
1561 d->itemView->setContextMenuPolicy(Qt::CustomContextMenu);
1562 d->itemView->setMouseTracking(true);
1563 //d->itemView->setDropOptions(d->dropOptions);
1564
1565 // first push our settings to the view, then listen for changes from the view
1566 QTreeView* treeView = qobject_cast<QTreeView*>(d->itemView);
1567 if (treeView) {
1568 QHeaderView* headerView = treeView->header();
1569 headerView->setSortIndicator(d->sortColumn(), d->sortOrder());
1570 connect(headerView, SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
1571 this, SLOT(_k_synchronizeSortingState(int,Qt::SortOrder)));
1572 }
1573
1574 connect(d->itemView, SIGNAL(activated(QModelIndex)),
1575 this, SLOT(_k_slotActivated(QModelIndex)));
1576 connect(d->itemView, SIGNAL(customContextMenuRequested(QPoint)),
1577 this, SLOT(_k_openContextMenu(QPoint)));
1578 connect(d->itemView, SIGNAL(entered(QModelIndex)),
1579 this, SLOT(_k_triggerPreview(QModelIndex)));
1580
1581 updateViewActions();
1582 d->splitter->insertWidget(0, d->itemView);
1583
1584 d->splitter->resize(size());
1585 d->itemView->show();
1586
1587 if (listDir) {
1588 QApplication::setOverrideCursor(Qt::WaitCursor);
1589 d->openUrl(d->currUrl);
1590 }
1591
1592 if (selectionModel != 0) {
1593 d->itemView->setSelectionModel(selectionModel);
1594 QMetaObject::invokeMethod(this, "_k_assureVisibleSelection", Qt::QueuedConnection);
1595 }
1596
1597 connect(d->itemView->selectionModel(),
1598 SIGNAL(currentChanged(QModelIndex,QModelIndex)),
1599 this, SLOT(_k_triggerPreview(QModelIndex)));
1600 connect(d->itemView->selectionModel(),
1601 SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
1602 this, SLOT(_k_slotSelectionChanged()));
1603
1604 // if we cannot cast it to a QListView, disable the "Icon Position" menu. Note that this check
1605 // needs to be done here, and not in createView, since we can be set an external view
1606 d->decorationMenu->setEnabled(qobject_cast<QListView*>(d->itemView));
1607
1608 d->shouldFetchForItems = qobject_cast<QTreeView*>(view);
1609 if (d->shouldFetchForItems) {
1610 connect(d->dirModel, SIGNAL(expand(QModelIndex)), this, SLOT(_k_slotExpandToUrl(QModelIndex)));
1611 } else {
1612 d->itemsToBeSetAsCurrent.clear();
1613 }
1614
1615 const bool previewForcedToTrue = d->inlinePreviewState == Private::ForcedToTrue;
1616 const bool previewShown = d->inlinePreviewState == Private::NotForced ? d->showPreviews : previewForcedToTrue;
1617 d->previewGenerator = new KFilePreviewGenerator(d->itemView);
1618 const int maxSize = KIconLoader::SizeEnormous - KIconLoader::SizeSmall;
1619 const int val = (maxSize * d->iconsZoom / 100) + KIconLoader::SizeSmall;
1620 d->itemView->setIconSize(previewForcedToTrue ? QSize(KIconLoader::SizeHuge, KIconLoader::SizeHuge) : QSize(val, val));
1621 d->previewGenerator->setPreviewShown(previewShown);
1622 d->actionCollection->action("inline preview")->setChecked(previewShown);
1623
1624 // ensure we change everything needed
1625 d->_k_slotChangeDecorationPosition();
1626
1627 emit viewChanged(view);
1628
1629 const int zoom = previewForcedToTrue ? (KIconLoader::SizeHuge - KIconLoader::SizeSmall + 1) * 100 / maxSize : d->iconSizeForViewType(view);
1630
1631 // this will make d->iconsZoom be updated, since setIconsZoom slot will be called
1632 emit currentIconSizeChanged(zoom);
1633}
1634
1635void KDirOperator::setDirLister(KDirLister *lister)
1636{
1637 if (lister == d->dirLister) // sanity check
1638 return;
1639
1640 delete d->dirModel;
1641 d->dirModel = 0;
1642
1643 delete d->proxyModel;
1644 d->proxyModel = 0;
1645
1646 //delete d->dirLister; // deleted by KDirModel already, which took ownership
1647 d->dirLister = lister;
1648
1649 d->dirModel = new KDirModel();
1650 d->dirModel->setDirLister(d->dirLister);
1651 d->dirModel->setDropsAllowed(KDirModel::DropOnDirectory);
1652
1653 d->shouldFetchForItems = qobject_cast<QTreeView*>(d->itemView);
1654 if (d->shouldFetchForItems) {
1655 connect(d->dirModel, SIGNAL(expand(QModelIndex)), this, SLOT(_k_slotExpandToUrl(QModelIndex)));
1656 } else {
1657 d->itemsToBeSetAsCurrent.clear();
1658 }
1659
1660 d->proxyModel = new KDirSortFilterProxyModel(this);
1661 d->proxyModel->setSourceModel(d->dirModel);
1662
1663 d->dirLister->setAutoUpdate(true);
1664 d->dirLister->setDelayedMimeTypes(true);
1665
1666 QWidget* mainWidget = topLevelWidget();
1667 d->dirLister->setMainWindow(mainWidget);
1668 kDebug(kfile_area) << "mainWidget=" << mainWidget;
1669
1670 connect(d->dirLister, SIGNAL(percent(int)),
1671 SLOT(_k_slotProgress(int)));
1672 connect(d->dirLister, SIGNAL(started(KUrl)), SLOT(_k_slotStarted()));
1673 connect(d->dirLister, SIGNAL(completed()), SLOT(_k_slotIOFinished()));
1674 connect(d->dirLister, SIGNAL(canceled()), SLOT(_k_slotCanceled()));
1675 connect(d->dirLister, SIGNAL(redirection(KUrl)),
1676 SLOT(_k_slotRedirected(KUrl)));
1677 connect(d->dirLister, SIGNAL(newItems(KFileItemList)), SLOT(_k_slotItemsChanged()));
1678 connect(d->dirLister, SIGNAL(itemsDeleted(KFileItemList)), SLOT(_k_slotItemsChanged()));
1679 connect(d->dirLister, SIGNAL(itemsFilteredByMime(KFileItemList)), SLOT(_k_slotItemsChanged()));
1680 connect(d->dirLister, SIGNAL(clear()), SLOT(_k_slotItemsChanged()));
1681}
1682
1683void KDirOperator::selectDir(const KFileItem &item)
1684{
1685 setUrl(item.targetUrl(), true);
1686}
1687
1688void KDirOperator::selectFile(const KFileItem &item)
1689{
1690 QApplication::restoreOverrideCursor();
1691
1692 emit fileSelected(item);
1693}
1694
1695void KDirOperator::highlightFile(const KFileItem &item)
1696{
1697 if ((d->preview != 0 && !d->preview->isHidden()) && !item.isNull()) {
1698 d->preview->showPreview(item.url());
1699 }
1700
1701 emit fileHighlighted(item);
1702}
1703
1704void KDirOperator::setCurrentItem(const QString& url)
1705{
1706 kDebug(kfile_area);
1707
1708 KFileItem item = d->dirLister->findByUrl(url);
1709 if (d->shouldFetchForItems && item.isNull()) {
1710 d->itemsToBeSetAsCurrent << url;
1711 d->dirModel->expandToUrl(url);
1712 return;
1713 }
1714
1715 setCurrentItem(item);
1716}
1717
1718void KDirOperator::setCurrentItem(const KFileItem& item)
1719{
1720 kDebug(kfile_area);
1721
1722 if (!d->itemView) {
1723 return;
1724 }
1725
1726 QItemSelectionModel *selModel = d->itemView->selectionModel();
1727 if (selModel) {
1728 selModel->clear();
1729 if (!item.isNull()) {
1730 const QModelIndex dirIndex = d->dirModel->indexForItem(item);
1731 const QModelIndex proxyIndex = d->proxyModel->mapFromSource(dirIndex);
1732 selModel->setCurrentIndex(proxyIndex, QItemSelectionModel::Select);
1733 }
1734 }
1735}
1736
1737void KDirOperator::setCurrentItems(const QStringList& urls)
1738{
1739 kDebug(kfile_area);
1740
1741 if (!d->itemView) {
1742 return;
1743 }
1744
1745 KFileItemList itemList;
1746 foreach (const QString &url, urls) {
1747 KFileItem item = d->dirLister->findByUrl(url);
1748 if (d->shouldFetchForItems && item.isNull()) {
1749 d->itemsToBeSetAsCurrent << url;
1750 d->dirModel->expandToUrl(url);
1751 continue;
1752 }
1753 itemList << item;
1754 }
1755
1756 setCurrentItems(itemList);
1757}
1758
1759void KDirOperator::setCurrentItems(const KFileItemList& items)
1760{
1761 kDebug(kfile_area);
1762
1763 if (d->itemView == 0) {
1764 return;
1765 }
1766
1767 QItemSelectionModel *selModel = d->itemView->selectionModel();
1768 if (selModel) {
1769 selModel->clear();
1770 QModelIndex proxyIndex;
1771 foreach (const KFileItem &item, items) {
1772 if (!item.isNull()) {
1773 const QModelIndex dirIndex = d->dirModel->indexForItem(item);
1774 proxyIndex = d->proxyModel->mapFromSource(dirIndex);
1775 selModel->select(proxyIndex, QItemSelectionModel::Select);
1776 }
1777 }
1778 if (proxyIndex.isValid()) {
1779 selModel->setCurrentIndex(proxyIndex, QItemSelectionModel::NoUpdate);
1780 }
1781 }
1782}
1783
1784QString KDirOperator::makeCompletion(const QString& string)
1785{
1786 if (string.isEmpty()) {
1787 d->itemView->selectionModel()->clear();
1788 return QString();
1789 }
1790
1791 prepareCompletionObjects();
1792 return d->completion.makeCompletion(string);
1793}
1794
1795QString KDirOperator::makeDirCompletion(const QString& string)
1796{
1797 if (string.isEmpty()) {
1798 d->itemView->selectionModel()->clear();
1799 return QString();
1800 }
1801
1802 prepareCompletionObjects();
1803 return d->dirCompletion.makeCompletion(string);
1804}
1805
1806void KDirOperator::prepareCompletionObjects()
1807{
1808 if (d->itemView == 0) {
1809 return;
1810 }
1811
1812 if (d->completeListDirty) { // create the list of all possible completions
1813 const KFileItemList itemList = d->dirLister->items();
1814 foreach (const KFileItem &item, itemList) {
1815 d->completion.addItem(item.name());
1816 if (item.isDir()) {
1817 d->dirCompletion.addItem(item.name());
1818 }
1819 }
1820 d->completeListDirty = false;
1821 }
1822}
1823
1824void KDirOperator::slotCompletionMatch(const QString& match)
1825{
1826 setCurrentItem(match);
1827 emit completion(match);
1828}
1829
1830void KDirOperator::setupActions()
1831{
1832 d->actionCollection = new KActionCollection(this);
1833 d->actionCollection->setObjectName("KDirOperator::actionCollection");
1834
1835 d->actionMenu = new KActionMenu(i18n("Menu"), this);
1836 d->actionCollection->addAction("popupMenu", d->actionMenu);
1837
1838 QAction* upAction = d->actionCollection->addAction(KStandardAction::Up, "up", this, SLOT(cdUp()));
1839 upAction->setText(i18n("Parent Folder"));
1840
1841 d->actionCollection->addAction(KStandardAction::Back, "back", this, SLOT(back()));
1842
1843 d->actionCollection->addAction(KStandardAction::Forward, "forward", this, SLOT(forward()));
1844
1845 QAction* homeAction = d->actionCollection->addAction(KStandardAction::Home, "home", this, SLOT(home()));
1846 homeAction->setText(i18n("Home Folder"));
1847
1848 KAction* reloadAction = d->actionCollection->addAction(KStandardAction::Redisplay, "reload", this, SLOT(rereadDir()));
1849 reloadAction->setText(i18n("Reload"));
1850 reloadAction->setShortcuts(KStandardShortcut::shortcut(KStandardShortcut::Reload));
1851
1852 KAction* mkdirAction = new KAction(i18n("New Folder..."), this);
1853 d->actionCollection->addAction("mkdir", mkdirAction);
1854 mkdirAction->setIcon(KIcon(QLatin1String("folder-new")));
1855 connect(mkdirAction, SIGNAL(triggered(bool)), this, SLOT(mkdir()));
1856
1857 KAction* trash = new KAction(i18n("Move to Trash"), this);
1858 d->actionCollection->addAction("trash", trash);
1859 trash->setIcon(KIcon("user-trash"));
1860 trash->setShortcuts(KShortcut(Qt::Key_Delete));
1861 connect(trash, SIGNAL(triggered(bool)), SLOT(trashSelected()));
1862
1863 KAction* action = new KAction(i18n("Delete"), this);
1864 d->actionCollection->addAction("delete", action);
1865 action->setIcon(KIcon("edit-delete"));
1866 action->setShortcuts(KShortcut(Qt::SHIFT + Qt::Key_Delete));
1867 connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteSelected()));
1868
1869 // the sort menu actions
1870 KActionMenu *sortMenu = new KActionMenu(i18n("Sorting"), this);
1871 d->actionCollection->addAction("sorting menu", sortMenu);
1872
1873 KToggleAction *byNameAction = new KToggleAction(i18n("By Name"), this);
1874 d->actionCollection->addAction("by name", byNameAction);
1875 connect(byNameAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortByName()));
1876
1877 KToggleAction *bySizeAction = new KToggleAction(i18n("By Size"), this);
1878 d->actionCollection->addAction("by size", bySizeAction);
1879 connect(bySizeAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortBySize()));
1880
1881 KToggleAction *byDateAction = new KToggleAction(i18n("By Date"), this);
1882 d->actionCollection->addAction("by date", byDateAction);
1883 connect(byDateAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortByDate()));
1884
1885 KToggleAction *byTypeAction = new KToggleAction(i18n("By Type"), this);
1886 d->actionCollection->addAction("by type", byTypeAction);
1887 connect(byTypeAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortByType()));
1888
1889 KToggleAction *descendingAction = new KToggleAction(i18n("Descending"), this);
1890 d->actionCollection->addAction("descending", descendingAction);
1891 connect(descendingAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortReversed(bool)));
1892
1893 KToggleAction *dirsFirstAction = new KToggleAction(i18n("Folders First"), this);
1894 d->actionCollection->addAction("dirs first", dirsFirstAction);
1895 connect(dirsFirstAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotToggleDirsFirst()));
1896
1897 QActionGroup* sortGroup = new QActionGroup(this);
1898 byNameAction->setActionGroup(sortGroup);
1899 bySizeAction->setActionGroup(sortGroup);
1900 byDateAction->setActionGroup(sortGroup);
1901 byTypeAction->setActionGroup(sortGroup);
1902
1903 d->decorationMenu = new KActionMenu(i18n("Icon Position"), this);
1904 d->actionCollection->addAction("decoration menu", d->decorationMenu);
1905
1906 d->leftAction = new KToggleAction(i18n("Next to File Name"), this);
1907 d->actionCollection->addAction("decorationAtLeft", d->leftAction);
1908 connect(d->leftAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotChangeDecorationPosition()));
1909
1910 KToggleAction *topAction = new KToggleAction(i18n("Above File Name"), this);
1911 d->actionCollection->addAction("decorationAtTop", topAction);
1912 connect(topAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotChangeDecorationPosition()));
1913
1914 d->decorationMenu->addAction(d->leftAction);
1915 d->decorationMenu->addAction(topAction);
1916
1917 QActionGroup* decorationGroup = new QActionGroup(this);
1918 d->leftAction->setActionGroup(decorationGroup);
1919 topAction->setActionGroup(decorationGroup);
1920
1921 KToggleAction *shortAction = new KToggleAction(i18n("Short View"), this);
1922 d->actionCollection->addAction("short view", shortAction);
1923 shortAction->setIcon(KIcon(QLatin1String("view-list-icons")));
1924 connect(shortAction, SIGNAL(triggered()), SLOT(_k_slotSimpleView()));
1925
1926 KToggleAction *detailedAction = new KToggleAction(i18n("Detailed View"), this);
1927 d->actionCollection->addAction("detailed view", detailedAction);
1928 detailedAction->setIcon(KIcon(QLatin1String("view-list-details")));
1929 connect(detailedAction, SIGNAL(triggered()), SLOT(_k_slotDetailedView()));
1930
1931 KToggleAction *treeAction = new KToggleAction(i18n("Tree View"), this);
1932 d->actionCollection->addAction("tree view", treeAction);
1933 treeAction->setIcon(KIcon(QLatin1String("view-list-tree")));
1934 connect(treeAction, SIGNAL(triggered()), SLOT(_k_slotTreeView()));
1935
1936 KToggleAction *detailedTreeAction = new KToggleAction(i18n("Detailed Tree View"), this);
1937 d->actionCollection->addAction("detailed tree view", detailedTreeAction);
1938 detailedTreeAction->setIcon(KIcon(QLatin1String("view-list-tree")));
1939 connect(detailedTreeAction, SIGNAL(triggered()), SLOT(_k_slotDetailedTreeView()));
1940
1941 QActionGroup* viewGroup = new QActionGroup(this);
1942 shortAction->setActionGroup(viewGroup);
1943 detailedAction->setActionGroup(viewGroup);
1944 treeAction->setActionGroup(viewGroup);
1945 detailedTreeAction->setActionGroup(viewGroup);
1946
1947 KToggleAction *showHiddenAction = new KToggleAction(i18n("Show Hidden Files"), this);
1948 d->actionCollection->addAction("show hidden", showHiddenAction);
1949 connect(showHiddenAction, SIGNAL(toggled(bool)), SLOT(_k_slotToggleHidden(bool)));
1950
1951 KToggleAction *previewAction = new KToggleAction(i18n("Show Aside Preview"), this);
1952 d->actionCollection->addAction("preview", previewAction);
1953 connect(previewAction, SIGNAL(toggled(bool)),
1954 SLOT(_k_togglePreview(bool)));
1955
1956 KToggleAction *inlinePreview = new KToggleAction(KIcon("view-preview"),
1957 i18n("Show Preview"), this);
1958 d->actionCollection->addAction("inline preview", inlinePreview);
1959 connect(inlinePreview, SIGNAL(toggled(bool)), SLOT(_k_toggleInlinePreviews(bool)));
1960
1961 KAction *fileManager = new KAction(i18n("Open File Manager"), this);
1962 d->actionCollection->addAction("file manager", fileManager);
1963 fileManager->setIcon(KIcon(QLatin1String("system-file-manager")));
1964 connect(fileManager, SIGNAL(triggered()), SLOT(_k_slotOpenFileManager()));
1965
1966 action = new KAction(i18n("Properties"), this);
1967 d->actionCollection->addAction("properties", action);
1968 action->setIcon(KIcon("document-properties"));
1969 action->setShortcut(KShortcut(Qt::ALT + Qt::Key_Return));
1970 connect(action, SIGNAL(triggered(bool)), this, SLOT(_k_slotProperties()));
1971
1972 // the view menu actions
1973 KActionMenu* viewMenu = new KActionMenu(i18n("&View"), this);
1974 d->actionCollection->addAction("view menu", viewMenu);
1975 viewMenu->addAction(shortAction);
1976 viewMenu->addAction(detailedAction);
1977 // Comment following lines to hide the extra two modes
1978 viewMenu->addAction(treeAction);
1979 viewMenu->addAction(detailedTreeAction);
1980 // TODO: QAbstractItemView does not offer an action collection. Provide
1981 // an interface to add a custom action collection.
1982
1983 d->newFileMenu = new KNewFileMenu(d->actionCollection, "new", this);
1984 connect(d->newFileMenu, SIGNAL(directoryCreated(KUrl)), this, SLOT(_k_slotDirectoryCreated(KUrl)));
1985
1986 d->actionCollection->addAssociatedWidget(this);
1987 foreach (QAction* action, d->actionCollection->actions())
1988 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
1989}
1990
1991void KDirOperator::setupMenu()
1992{
1993 setupMenu(SortActions | ViewActions | FileActions);
1994}
1995
1996void KDirOperator::setupMenu(int whichActions)
1997{
1998 // first fill the submenus (sort and view)
1999 KActionMenu *sortMenu = static_cast<KActionMenu*>(d->actionCollection->action("sorting menu"));
2000 sortMenu->menu()->clear();
2001 sortMenu->addAction(d->actionCollection->action("by name"));
2002 sortMenu->addAction(d->actionCollection->action("by size"));
2003 sortMenu->addAction(d->actionCollection->action("by date"));
2004 sortMenu->addAction(d->actionCollection->action("by type"));
2005 sortMenu->addSeparator();
2006 sortMenu->addAction(d->actionCollection->action("descending"));
2007 sortMenu->addAction(d->actionCollection->action("dirs first"));
2008
2009 // now plug everything into the popupmenu
2010 d->actionMenu->menu()->clear();
2011 if (whichActions & NavActions) {
2012 d->actionMenu->addAction(d->actionCollection->action("up"));
2013 d->actionMenu->addAction(d->actionCollection->action("back"));
2014 d->actionMenu->addAction(d->actionCollection->action("forward"));
2015 d->actionMenu->addAction(d->actionCollection->action("home"));
2016 d->actionMenu->addSeparator();
2017 }
2018
2019 if (whichActions & FileActions) {
2020 d->actionMenu->addAction(d->actionCollection->action("new"));
2021 if (d->currUrl.isLocalFile() && !(QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
2022 d->actionMenu->addAction(d->actionCollection->action("trash"));
2023 }
2024 KConfigGroup cg(KGlobal::config(), QLatin1String("KDE"));
2025 const bool del = !d->currUrl.isLocalFile() ||
2026 (QApplication::keyboardModifiers() & Qt::ShiftModifier) ||
2027 cg.readEntry("ShowDeleteCommand", false);
2028 if (del) {
2029 d->actionMenu->addAction(d->actionCollection->action("delete"));
2030 }
2031 d->actionMenu->addSeparator();
2032 }
2033
2034 if (whichActions & SortActions) {
2035 d->actionMenu->addAction(sortMenu);
2036 if (!(whichActions & ViewActions)) {
2037 d->actionMenu->addSeparator();
2038 }
2039 }
2040
2041 if (whichActions & ViewActions) {
2042 d->actionMenu->addAction(d->actionCollection->action("view menu"));
2043 d->actionMenu->addSeparator();
2044 }
2045
2046 if (whichActions & FileActions) {
2047 d->actionMenu->addAction(d->actionCollection->action("file manager"));
2048 d->actionMenu->addAction(d->actionCollection->action("properties"));
2049 }
2050}
2051
2052void KDirOperator::updateSortActions()
2053{
2054 if (KFile::isSortByName(d->sorting)) {
2055 d->actionCollection->action("by name")->setChecked(true);
2056 } else if (KFile::isSortByDate(d->sorting)) {
2057 d->actionCollection->action("by date")->setChecked(true);
2058 } else if (KFile::isSortBySize(d->sorting)) {
2059 d->actionCollection->action("by size")->setChecked(true);
2060 } else if (KFile::isSortByType(d->sorting)) {
2061 d->actionCollection->action("by type")->setChecked(true);
2062 }
2063 d->actionCollection->action("descending")->setChecked(d->sorting & QDir::Reversed);
2064 d->actionCollection->action("dirs first")->setChecked(d->sorting & QDir::DirsFirst);
2065}
2066
2067void KDirOperator::updateViewActions()
2068{
2069 KFile::FileView fv = static_cast<KFile::FileView>(d->viewKind);
2070
2071 //QAction *separateDirs = d->actionCollection->action("separate dirs");
2072 //separateDirs->setChecked(KFile::isSeparateDirs(fv) &&
2073 // separateDirs->isEnabled());
2074
2075 d->actionCollection->action("short view")->setChecked(KFile::isSimpleView(fv));
2076 d->actionCollection->action("detailed view")->setChecked(KFile::isDetailView(fv));
2077 d->actionCollection->action("tree view")->setChecked(KFile::isTreeView(fv));
2078 d->actionCollection->action("detailed tree view")->setChecked(KFile::isDetailTreeView(fv));
2079}
2080
2081void KDirOperator::readConfig(const KConfigGroup& configGroup)
2082{
2083 d->defaultView = 0;
2084 QString viewStyle = configGroup.readEntry("View Style", "Simple");
2085 if (viewStyle == QLatin1String("Detail")) {
2086 d->defaultView |= KFile::Detail;
2087 } else if (viewStyle == QLatin1String("Tree")) {
2088 d->defaultView |= KFile::Tree;
2089 } else if (viewStyle == QLatin1String("DetailTree")) {
2090 d->defaultView |= KFile::DetailTree;
2091 } else {
2092 d->defaultView |= KFile::Simple;
2093 }
2094 //if (configGroup.readEntry(QLatin1String("Separate Directories"),
2095 // DefaultMixDirsAndFiles)) {
2096 // d->defaultView |= KFile::SeparateDirs;
2097 //}
2098 if (configGroup.readEntry(QLatin1String("Show Preview"), false)) {
2099 d->defaultView |= KFile::PreviewContents;
2100 }
2101
2102 d->previewWidth = configGroup.readEntry(QLatin1String("Preview Width"), 100);
2103
2104 if (configGroup.readEntry(QLatin1String("Show hidden files"),
2105 DefaultShowHidden)) {
2106 d->actionCollection->action("show hidden")->setChecked(true);
2107 d->dirLister->setShowingDotFiles(true);
2108 }
2109
2110 QDir::SortFlags sorting = QDir::Name;
2111 if (configGroup.readEntry(QLatin1String("Sort directories first"),
2112 DefaultDirsFirst)) {
2113 sorting |= QDir::DirsFirst;
2114 }
2115 QString name = QLatin1String("Name");
2116 QString sortBy = configGroup.readEntry(QLatin1String("Sort by"), name);
2117 if (sortBy == name) {
2118 sorting |= QDir::Name;
2119 } else if (sortBy == QLatin1String("Size")) {
2120 sorting |= QDir::Size;
2121 } else if (sortBy == QLatin1String("Date")) {
2122 sorting |= QDir::Time;
2123 } else if (sortBy == QLatin1String("Type")) {
2124 sorting |= QDir::Type;
2125 }
2126 if (configGroup.readEntry(QLatin1String("Sort reversed"), DefaultSortReversed)) {
2127 sorting |= QDir::Reversed;
2128 }
2129 d->updateSorting(sorting);
2130
2131 if (d->inlinePreviewState == Private::NotForced) {
2132 d->showPreviews = configGroup.readEntry(QLatin1String("Previews"), false);
2133 }
2134 QStyleOptionViewItem::Position pos = (QStyleOptionViewItem::Position) configGroup.readEntry(QLatin1String("Decoration position"), (int) QStyleOptionViewItem::Left);
2135 setDecorationPosition(pos);
2136}
2137
2138void KDirOperator::writeConfig(KConfigGroup& configGroup)
2139{
2140 QString sortBy = QLatin1String("Name");
2141 if (KFile::isSortBySize(d->sorting)) {
2142 sortBy = QLatin1String("Size");
2143 } else if (KFile::isSortByDate(d->sorting)) {
2144 sortBy = QLatin1String("Date");
2145 } else if (KFile::isSortByType(d->sorting)) {
2146 sortBy = QLatin1String("Type");
2147 }
2148
2149 configGroup.writeEntry(QLatin1String("Sort by"), sortBy);
2150
2151 configGroup.writeEntry(QLatin1String("Sort reversed"),
2152 d->actionCollection->action("descending")->isChecked());
2153
2154 configGroup.writeEntry(QLatin1String("Sort directories first"),
2155 d->actionCollection->action("dirs first")->isChecked());
2156
2157 // don't save the preview when an application specific preview is in use.
2158 bool appSpecificPreview = false;
2159 if (d->preview) {
2160 KFileMetaPreview *tmp = dynamic_cast<KFileMetaPreview*>(d->preview);
2161 appSpecificPreview = (tmp == 0);
2162 }
2163
2164 if (!appSpecificPreview) {
2165 KToggleAction *previewAction = static_cast<KToggleAction*>(d->actionCollection->action("preview"));
2166 if (previewAction->isEnabled()) {
2167 bool hasPreview = previewAction->isChecked();
2168 configGroup.writeEntry(QLatin1String("Show Preview"), hasPreview);
2169
2170 if (hasPreview) {
2171 // remember the width of the preview widget
2172 QList<int> sizes = d->splitter->sizes();
2173 Q_ASSERT(sizes.count() == 2);
2174 configGroup.writeEntry(QLatin1String("Preview Width"), sizes[1]);
2175 }
2176 }
2177 }
2178
2179 configGroup.writeEntry(QLatin1String("Show hidden files"),
2180 d->actionCollection->action("show hidden")->isChecked());
2181
2182 KFile::FileView fv = static_cast<KFile::FileView>(d->viewKind);
2183 QString style;
2184 if (KFile::isDetailView(fv))
2185 style = QLatin1String("Detail");
2186 else if (KFile::isSimpleView(fv))
2187 style = QLatin1String("Simple");
2188 else if (KFile::isTreeView(fv))
2189 style = QLatin1String("Tree");
2190 else if (KFile::isDetailTreeView(fv))
2191 style = QLatin1String("DetailTree");
2192 configGroup.writeEntry(QLatin1String("View Style"), style);
2193
2194 if (d->inlinePreviewState == Private::NotForced) {
2195 configGroup.writeEntry(QLatin1String("Previews"), d->showPreviews);
2196 if (qobject_cast<QListView*>(d->itemView)) {
2197 configGroup.writeEntry(QLatin1String("listViewIconSize"), d->iconsZoom);
2198 } else {
2199 configGroup.writeEntry(QLatin1String("detailedViewIconSize"), d->iconsZoom);
2200 }
2201 }
2202
2203 configGroup.writeEntry(QLatin1String("Decoration position"), (int) d->decorationPosition);
2204}
2205
2206void KDirOperator::resizeEvent(QResizeEvent *)
2207{
2208 // resize the splitter and assure that the width of
2209 // the preview widget is restored
2210 QList<int> sizes = d->splitter->sizes();
2211 const bool hasPreview = (sizes.count() == 2);
2212
2213 d->splitter->resize(size());
2214 sizes = d->splitter->sizes();
2215
2216 const bool restorePreviewWidth = hasPreview && (d->previewWidth != sizes[1]);
2217 if (restorePreviewWidth) {
2218 const int availableWidth = sizes[0] + sizes[1];
2219 sizes[0] = availableWidth - d->previewWidth;
2220 sizes[1] = d->previewWidth;
2221 d->splitter->setSizes(sizes);
2222 }
2223 if (hasPreview) {
2224 d->previewWidth = sizes[1];
2225 }
2226
2227 if (d->progressBar->parent() == this) {
2228 // might be reparented into a statusbar
2229 d->progressBar->move(2, height() - d->progressBar->height() - 2);
2230 }
2231}
2232
2233void KDirOperator::setOnlyDoubleClickSelectsFiles(bool enable)
2234{
2235 d->onlyDoubleClickSelectsFiles = enable;
2236 // TODO: port to Qt4's QAbstractItemModel
2237 //if (d->itemView != 0) {
2238 // d->itemView->setOnlyDoubleClickSelectsFiles(enable);
2239 //}
2240}
2241
2242bool KDirOperator::onlyDoubleClickSelectsFiles() const
2243{
2244 return d->onlyDoubleClickSelectsFiles;
2245}
2246
2247void KDirOperator::Private::_k_slotStarted()
2248{
2249 progressBar->setValue(0);
2250 // delay showing the progressbar for one second
2251 progressDelayTimer->setSingleShot(true);
2252 progressDelayTimer->start(1000);
2253}
2254
2255void KDirOperator::Private::_k_slotShowProgress()
2256{
2257 progressBar->raise();
2258 progressBar->show();
2259 QApplication::flush();
2260}
2261
2262void KDirOperator::Private::_k_slotProgress(int percent)
2263{
2264 progressBar->setValue(percent);
2265 // we have to redraw this as fast as possible
2266 if (progressBar->isVisible())
2267 QApplication::flush();
2268}
2269
2270
2271void KDirOperator::Private::_k_slotIOFinished()
2272{
2273 progressDelayTimer->stop();
2274 _k_slotProgress(100);
2275 progressBar->hide();
2276 emit parent->finishedLoading();
2277 parent->resetCursor();
2278
2279 if (preview) {
2280 preview->clearPreview();
2281 }
2282}
2283
2284void KDirOperator::Private::_k_slotCanceled()
2285{
2286 emit parent->finishedLoading();
2287 parent->resetCursor();
2288}
2289
2290QProgressBar * KDirOperator::progressBar() const
2291{
2292 return d->progressBar;
2293}
2294
2295void KDirOperator::clearHistory()
2296{
2297 qDeleteAll(d->backStack);
2298 d->backStack.clear();
2299 d->actionCollection->action("back")->setEnabled(false);
2300
2301 qDeleteAll(d->forwardStack);
2302 d->forwardStack.clear();
2303 d->actionCollection->action("forward")->setEnabled(false);
2304}
2305
2306void KDirOperator::setEnableDirHighlighting(bool enable)
2307{
2308 d->dirHighlighting = enable;
2309}
2310
2311bool KDirOperator::dirHighlighting() const
2312{
2313 return d->dirHighlighting;
2314}
2315
2316bool KDirOperator::dirOnlyMode() const
2317{
2318 return dirOnlyMode(d->mode);
2319}
2320
2321bool KDirOperator::dirOnlyMode(uint mode)
2322{
2323 return ((mode & KFile::Directory) &&
2324 (mode & (KFile::File | KFile::Files)) == 0);
2325}
2326
2327void KDirOperator::Private::_k_slotProperties()
2328{
2329 if (itemView == 0) {
2330 return;
2331 }
2332
2333 const KFileItemList list = parent->selectedItems();
2334 if (!list.isEmpty()) {
2335 KPropertiesDialog dialog(list, parent);
2336 dialog.exec();
2337 }
2338}
2339
2340void KDirOperator::Private::_k_slotActivated(const QModelIndex& index)
2341{
2342 const QModelIndex dirIndex = proxyModel->mapToSource(index);
2343 KFileItem item = dirModel->itemForIndex(dirIndex);
2344
2345 const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
2346 if (item.isNull() || (modifiers & Qt::ShiftModifier) || (modifiers & Qt::ControlModifier))
2347 return;
2348
2349 if (item.isDir()) {
2350 parent->selectDir(item);
2351 } else {
2352 parent->selectFile(item);
2353 }
2354}
2355
2356void KDirOperator::Private::_k_slotSelectionChanged()
2357{
2358 if (itemView == 0) {
2359 return;
2360 }
2361
2362 // In the multiselection mode each selection change is indicated by
2363 // emitting a null item. Also when the selection has been cleared, a
2364 // null item must be emitted.
2365 const bool multiSelectionMode = (itemView->selectionMode() == QAbstractItemView::ExtendedSelection);
2366 const bool hasSelection = itemView->selectionModel()->hasSelection();
2367 if (multiSelectionMode || !hasSelection) {
2368 KFileItem nullItem;
2369 parent->highlightFile(nullItem);
2370 }
2371 else {
2372 KFileItem selectedItem = parent->selectedItems().first();
2373 parent->highlightFile(selectedItem);
2374 }
2375}
2376
2377void KDirOperator::Private::_k_openContextMenu(const QPoint& pos)
2378{
2379 const QModelIndex proxyIndex = itemView->indexAt(pos);
2380 const QModelIndex dirIndex = proxyModel->mapToSource(proxyIndex);
2381 KFileItem item = dirModel->itemForIndex(dirIndex);
2382
2383 if (item.isNull())
2384 return;
2385
2386 parent->activatedMenu(item, QCursor::pos());
2387}
2388
2389void KDirOperator::Private::_k_triggerPreview(const QModelIndex& index)
2390{
2391 if ((preview != 0 && !preview->isHidden()) && index.isValid() && (index.column() == KDirModel::Name)) {
2392 const QModelIndex dirIndex = proxyModel->mapToSource(index);
2393 const KFileItem item = dirModel->itemForIndex(dirIndex);
2394
2395 if (item.isNull())
2396 return;
2397
2398 if (!item.isDir()) {
2399 previewUrl = item.url();
2400 _k_showPreview();
2401 } else {
2402 preview->clearPreview();
2403 }
2404 }
2405}
2406
2407void KDirOperator::Private::_k_showPreview()
2408{
2409 if (preview != 0) {
2410 preview->showPreview(previewUrl);
2411 }
2412}
2413
2414void KDirOperator::Private::_k_slotSplitterMoved(int, int)
2415{
2416 const QList<int> sizes = splitter->sizes();
2417 if (sizes.count() == 2) {
2418 // remember the width of the preview widget (see KDirOperator::resizeEvent())
2419 previewWidth = sizes[1];
2420 }
2421}
2422
2423void KDirOperator::Private::_k_assureVisibleSelection()
2424{
2425 if (itemView == 0) {
2426 return;
2427 }
2428
2429 QItemSelectionModel* selModel = itemView->selectionModel();
2430 if (selModel->hasSelection()) {
2431 const QModelIndex index = selModel->currentIndex();
2432 itemView->scrollTo(index, QAbstractItemView::EnsureVisible);
2433 _k_triggerPreview(index);
2434 }
2435}
2436
2437
2438void KDirOperator::Private::_k_synchronizeSortingState(int logicalIndex, Qt::SortOrder order)
2439{
2440 QDir::SortFlags newSort = sorting & ~(QDirSortMask | QDir::Reversed);
2441
2442 switch (logicalIndex) {
2443 case KDirModel::Name:
2444 newSort |= QDir::Name;
2445 break;
2446 case KDirModel::Size:
2447 newSort |= QDir::Size;
2448 break;
2449 case KDirModel::ModifiedTime:
2450 newSort |= QDir::Time;
2451 break;
2452 case KDirModel::Type:
2453 newSort |= QDir::Type;
2454 break;
2455 default:
2456 Q_ASSERT(false);
2457 }
2458
2459 if (order == Qt::DescendingOrder) {
2460 newSort |= QDir::Reversed;
2461 }
2462
2463 updateSorting(newSort);
2464
2465 QMetaObject::invokeMethod(parent, "_k_assureVisibleSelection", Qt::QueuedConnection);
2466}
2467
2468void KDirOperator::Private::_k_slotChangeDecorationPosition()
2469{
2470 if (!itemView) {
2471 return;
2472 }
2473
2474 QListView *view = qobject_cast<QListView*>(itemView);
2475
2476 if (!view) {
2477 return;
2478 }
2479
2480 const bool leftChecked = actionCollection->action("decorationAtLeft")->isChecked();
2481
2482 if (leftChecked) {
2483 decorationPosition = QStyleOptionViewItem::Left;
2484 view->setFlow(QListView::TopToBottom);
2485 } else {
2486 decorationPosition = QStyleOptionViewItem::Top;
2487 view->setFlow(QListView::LeftToRight);
2488 }
2489
2490 updateListViewGrid();
2491
2492 itemView->update();
2493}
2494
2495void KDirOperator::Private::_k_slotExpandToUrl(const QModelIndex &index)
2496{
2497 QTreeView *treeView = qobject_cast<QTreeView*>(itemView);
2498
2499 if (!treeView) {
2500 return;
2501 }
2502
2503 const KFileItem item = dirModel->itemForIndex(index);
2504
2505 if (item.isNull()) {
2506 return;
2507 }
2508
2509 if (!item.isDir()) {
2510 const QModelIndex proxyIndex = proxyModel->mapFromSource(index);
2511
2512 KUrl::List::Iterator it = itemsToBeSetAsCurrent.begin();
2513 while (it != itemsToBeSetAsCurrent.end()) {
2514 const KUrl url = *it;
2515 if (url.isParentOf(item.url())) {
2516 const KFileItem _item = dirLister->findByUrl(url);
2517 if (!_item.isNull() && _item.isDir()) {
2518 const QModelIndex _index = dirModel->indexForItem(_item);
2519 const QModelIndex _proxyIndex = proxyModel->mapFromSource(_index);
2520 treeView->expand(_proxyIndex);
2521
2522 // if we have expanded the last parent of this item, select it
2523 if (item.url().directory() == url.path(KUrl::RemoveTrailingSlash)) {
2524 treeView->selectionModel()->select(proxyIndex, QItemSelectionModel::Select);
2525 }
2526 }
2527 it = itemsToBeSetAsCurrent.erase(it);
2528 } else {
2529 ++it;
2530 }
2531 }
2532 } else if (!itemsToBeSetAsCurrent.contains(item.url())) {
2533 itemsToBeSetAsCurrent << item.url();
2534 }
2535}
2536
2537void KDirOperator::Private::_k_slotItemsChanged()
2538{
2539 completeListDirty = true;
2540}
2541
2542void KDirOperator::Private::updateListViewGrid()
2543{
2544 if (!itemView) {
2545 return;
2546 }
2547
2548 QListView *view = qobject_cast<QListView*>(itemView);
2549
2550 if (!view) {
2551 return;
2552 }
2553
2554 const bool leftChecked = actionCollection->action("decorationAtLeft")->isChecked();
2555
2556 if (leftChecked) {
2557 view->setGridSize(QSize());
2558 KFileItemDelegate *delegate = qobject_cast<KFileItemDelegate*>(view->itemDelegate());
2559 if (delegate) {
2560 delegate->setMaximumSize(QSize());
2561 }
2562 } else {
2563 const QFontMetrics metrics(itemView->viewport()->font());
2564 int size = itemView->iconSize().height() + metrics.height() * 2;
2565 // some heuristics for good looking. let's guess width = height * (3 / 2) is nice
2566 view->setGridSize(QSize(size * (3.0 / 2.0), size + metrics.height()));
2567 KFileItemDelegate *delegate = qobject_cast<KFileItemDelegate*>(view->itemDelegate());
2568 if (delegate) {
2569 delegate->setMaximumSize(QSize(size * (3.0 / 2.0), size + metrics.height()));
2570 }
2571 }
2572}
2573
2574int KDirOperator::Private::iconSizeForViewType(QAbstractItemView *itemView) const
2575{
2576 if (!itemView || !configGroup) {
2577 return 0;
2578 }
2579
2580 if (qobject_cast<QListView*>(itemView)) {
2581 return configGroup->readEntry("listViewIconSize", 0);
2582 } else {
2583 return configGroup->readEntry("detailedViewIconSize", 0);
2584 }
2585}
2586
2587void KDirOperator::setViewConfig(KConfigGroup& configGroup)
2588{
2589 delete d->configGroup;
2590 d->configGroup = new KConfigGroup(configGroup);
2591}
2592
2593KConfigGroup* KDirOperator::viewConfigGroup() const
2594{
2595 return d->configGroup;
2596}
2597
2598void KDirOperator::setShowHiddenFiles(bool s)
2599{
2600 d->actionCollection->action("show hidden")->setChecked(s);
2601}
2602
2603bool KDirOperator::showHiddenFiles() const
2604{
2605 return d->actionCollection->action("show hidden")->isChecked();
2606}
2607
2608QStyleOptionViewItem::Position KDirOperator::decorationPosition() const
2609{
2610 return d->decorationPosition;
2611}
2612
2613void KDirOperator::setDecorationPosition(QStyleOptionViewItem::Position position)
2614{
2615 d->decorationPosition = position;
2616 const bool decorationAtLeft = d->decorationPosition == QStyleOptionViewItem::Left;
2617 d->actionCollection->action("decorationAtLeft")->setChecked(decorationAtLeft);
2618 d->actionCollection->action("decorationAtTop")->setChecked(!decorationAtLeft);
2619}
2620
2621// ### temporary code
2622#include <dirent.h>
2623bool KDirOperator::Private::isReadable(const KUrl& url)
2624{
2625 if (!url.isLocalFile())
2626 return true; // what else can we say?
2627
2628 KDE_struct_stat buf;
2629#ifdef Q_WS_WIN
2630 QString ts = url.toLocalFile();
2631#else
2632 QString ts = url.path(KUrl::AddTrailingSlash);
2633#endif
2634 bool readable = (KDE::stat(ts, &buf) == 0);
2635 if (readable) { // further checks
2636 DIR *test;
2637 test = opendir(QFile::encodeName(ts)); // we do it just to test here
2638 readable = (test != 0);
2639 if (test)
2640 closedir(test);
2641 }
2642 return readable;
2643}
2644
2645void KDirOperator::Private::_k_slotDirectoryCreated(const KUrl& url)
2646{
2647 parent->setUrl(url, true);
2648}
2649
2650#include "kdiroperator.moc"
2651