]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphinview.cpp
Cleanup: let the DolphinView be aware about the used action collection. This will...
[dolphin.git] / src / dolphinview.cpp
1 /***************************************************************************
2 * Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at> *
3 * Copyright (C) 2006 by Gregor Kališnik <gregor@podnapisi.net> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program 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 *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
19 ***************************************************************************/
20
21 #include "dolphinview.h"
22 #include <ktoggleaction.h>
23 #include <kactioncollection.h>
24
25 #include <QApplication>
26 #include <QClipboard>
27 #include <QKeyEvent>
28 #include <QItemSelection>
29 #include <QBoxLayout>
30 #include <QTimer>
31 #include <QScrollBar>
32
33 #include <kcolorscheme.h>
34 #include <kdirlister.h>
35 #include <kfileitemdelegate.h>
36 #include <kiconeffect.h>
37 #include <klocale.h>
38 #include <kio/deletejob.h>
39 #include <kio/netaccess.h>
40 #include <kio/previewjob.h>
41 #include <kjob.h>
42 #include <kmenu.h>
43 #include <kmimetyperesolver.h>
44 #include <konq_operations.h>
45 #include <konqmimedata.h>
46 #include <kurl.h>
47
48 #include "dolphindropcontroller.h"
49 #include "dolphinmodel.h"
50 #include "dolphincolumnview.h"
51 #include "dolphincontroller.h"
52 #include "dolphinsortfilterproxymodel.h"
53 #include "dolphindetailsview.h"
54 #include "dolphiniconsview.h"
55 #include "dolphinsettings.h"
56 #include "dolphin_generalsettings.h"
57 #include "iconmanager.h"
58 #include "renamedialog.h"
59 #include "viewproperties.h"
60
61 DolphinView::DolphinView(QWidget* parent,
62 const KUrl& url,
63 KDirLister* dirLister,
64 DolphinModel* dolphinModel,
65 DolphinSortFilterProxyModel* proxyModel,
66 KActionCollection* actionCollection) :
67 QWidget(parent),
68 m_active(true),
69 m_showPreview(false),
70 m_loadingDirectory(false),
71 m_storedCategorizedSorting(false),
72 m_mode(DolphinView::IconsView),
73 m_topLayout(0),
74 m_controller(0),
75 m_iconsView(0),
76 m_detailsView(0),
77 m_columnView(0),
78 m_fileItemDelegate(0),
79 m_selectionModel(0),
80 m_dolphinModel(dolphinModel),
81 m_dirLister(dirLister),
82 m_proxyModel(proxyModel),
83 m_iconManager(0)
84 {
85 setFocusPolicy(Qt::StrongFocus);
86 m_topLayout = new QVBoxLayout(this);
87 m_topLayout->setSpacing(0);
88 m_topLayout->setMargin(0);
89
90 connect(m_dirLister, SIGNAL(completed()),
91 this, SLOT(updateCutItems()));
92
93 m_controller = new DolphinController(this);
94 m_controller->setUrl(url);
95
96 // Receiver of the DolphinView signal 'urlChanged()' don't need
97 // to care whether the internal controller changed the URL already or whether
98 // the controller just requested an URL change and will be updated later.
99 // In both cases the URL has been changed:
100 connect(m_controller, SIGNAL(urlChanged(const KUrl&)),
101 this, SIGNAL(urlChanged(const KUrl&)));
102 connect(m_controller, SIGNAL(requestUrlChange(const KUrl&)),
103 this, SIGNAL(urlChanged(const KUrl&)));
104
105 connect(m_controller, SIGNAL(requestContextMenu(const QPoint&)),
106 this, SLOT(openContextMenu(const QPoint&)));
107 connect(m_controller, SIGNAL(urlsDropped(const KUrl::List&, const KUrl&, const KFileItem&)),
108 this, SLOT(dropUrls(const KUrl::List&, const KUrl&, const KFileItem&)));
109 connect(m_controller, SIGNAL(sortingChanged(DolphinView::Sorting)),
110 this, SLOT(updateSorting(DolphinView::Sorting)));
111 connect(m_controller, SIGNAL(sortOrderChanged(Qt::SortOrder)),
112 this, SLOT(updateSortOrder(Qt::SortOrder)));
113 connect(m_controller, SIGNAL(additionalInfoChanged(const KFileItemDelegate::InformationList&)),
114 this, SLOT(updateAdditionalInfo(const KFileItemDelegate::InformationList&)));
115 connect(m_controller, SIGNAL(itemTriggered(const KFileItem&)),
116 this, SLOT(triggerItem(const KFileItem&)));
117 connect(m_controller, SIGNAL(activated()),
118 this, SLOT(activate()));
119 connect(m_controller, SIGNAL(itemEntered(const KFileItem&)),
120 this, SLOT(showHoverInformation(const KFileItem&)));
121 connect(m_controller, SIGNAL(viewportEntered()),
122 this, SLOT(clearHoverInformation()));
123
124 applyViewProperties(url);
125 m_topLayout->addWidget(itemView());
126
127 Q_ASSERT(actionCollection != 0);
128 if (actionCollection->action("create_dir") == 0) {
129 // This action doesn't appear in the GUI, it's for the shortcut only.
130 // KNewMenu takes care of the GUI stuff.
131 KAction* newDirAction = actionCollection->addAction("create_dir");
132 newDirAction->setText(i18n("Create Folder..."));
133 connect(newDirAction, SIGNAL(triggered()), SLOT(createDir()));
134 newDirAction->setShortcut(Qt::Key_F10);
135 }
136 }
137
138 DolphinView::~DolphinView()
139 {
140 }
141
142 const KUrl& DolphinView::url() const
143 {
144 return m_controller->url();
145 }
146
147 KUrl DolphinView::rootUrl() const
148 {
149 return isColumnViewActive() ? m_columnView->rootUrl() : url();
150 }
151
152 void DolphinView::setActive(bool active)
153 {
154 if (active == m_active) {
155 return;
156 }
157
158 m_active = active;
159 m_selectionModel->clearSelection();
160
161 QColor color = KColorScheme(QPalette::Active, KColorScheme::View).background().color();
162 if (active) {
163 // TODO: emitting urlChanged() is a hack, as the URL hasn't really changed. It
164 // bypasses the problem when having a split view and changing the active view to
165 // update the some URL dependent states. A nicer approach should be no big deal...
166 emit urlChanged(url());
167 emit selectionChanged(selectedItems());
168 } else {
169 color.setAlpha(150);
170 }
171
172 QWidget* viewport = itemView()->viewport();
173 QPalette palette;
174 palette.setColor(viewport->backgroundRole(), color);
175 viewport->setPalette(palette);
176
177 update();
178
179 if (active) {
180 emit activated();
181 }
182
183 m_controller->indicateActivationChange(active);
184 }
185
186 bool DolphinView::isActive() const
187 {
188 return m_active;
189 }
190
191 void DolphinView::setMode(Mode mode)
192 {
193 if (mode == m_mode) {
194 return; // the wished mode is already set
195 }
196
197 m_mode = mode;
198
199 if (isColumnViewActive()) {
200 // When changing the mode in the column view, it makes sense
201 // to go back to the root URL of the column view automatically.
202 // Otherwise there it would not be possible to turn off the column view
203 // without focusing the first column.
204 const KUrl root = rootUrl();
205 setUrl(root);
206 m_controller->setUrl(root);
207 }
208
209 deleteView();
210
211 const KUrl viewPropsUrl = viewPropertiesUrl();
212 ViewProperties props(viewPropsUrl);
213 props.setViewMode(m_mode);
214 createView();
215
216 // the file item delegate has been recreated, apply the current
217 // additional information manually
218 const KFileItemDelegate::InformationList infoList = props.additionalInfo();
219 m_fileItemDelegate->setShowInformation(infoList);
220 emit additionalInfoChanged(infoList);
221
222 // Not all view modes support categorized sorting. Adjust the sorting model
223 // if changing the view mode results in a change of the categorized sorting
224 // capabilities.
225 m_storedCategorizedSorting = props.categorizedSorting();
226 const bool categorized = m_storedCategorizedSorting && supportsCategorizedSorting();
227 if (categorized != m_proxyModel->isCategorizedModel()) {
228 m_proxyModel->setCategorizedModel(categorized);
229 emit categorizedSortingChanged();
230 }
231
232 emit modeChanged();
233 }
234
235 DolphinView::Mode DolphinView::mode() const
236 {
237 return m_mode;
238 }
239
240 void DolphinView::setShowPreview(bool show)
241 {
242 if (m_showPreview == show) {
243 return;
244 }
245
246 const KUrl viewPropsUrl = viewPropertiesUrl();
247 ViewProperties props(viewPropsUrl);
248 props.setShowPreview(show);
249
250 m_showPreview = show;
251 m_iconManager->setShowPreview(show);
252 emit showPreviewChanged();
253
254 loadDirectory(viewPropsUrl, true);
255 }
256
257 bool DolphinView::showPreview() const
258 {
259 return m_showPreview;
260 }
261
262 void DolphinView::setShowHiddenFiles(bool show)
263 {
264 if (m_dirLister->showingDotFiles() == show) {
265 return;
266 }
267
268 const KUrl viewPropsUrl = viewPropertiesUrl();
269 ViewProperties props(viewPropsUrl);
270 props.setShowHiddenFiles(show);
271
272 m_dirLister->setShowingDotFiles(show);
273 emit showHiddenFilesChanged();
274
275 loadDirectory(viewPropsUrl, true);
276 }
277
278 bool DolphinView::showHiddenFiles() const
279 {
280 return m_dirLister->showingDotFiles();
281 }
282
283 void DolphinView::setCategorizedSorting(bool categorized)
284 {
285 if (categorized == categorizedSorting()) {
286 return;
287 }
288
289 // setCategorizedSorting(true) may only get invoked
290 // if the view supports categorized sorting
291 Q_ASSERT(!categorized || supportsCategorizedSorting());
292
293 ViewProperties props(viewPropertiesUrl());
294 props.setCategorizedSorting(categorized);
295 props.save();
296
297 m_storedCategorizedSorting = categorized;
298 m_proxyModel->setCategorizedModel(categorized);
299
300 emit categorizedSortingChanged();
301 }
302
303 bool DolphinView::categorizedSorting() const
304 {
305 // If all view modes would support categorized sorting, returning
306 // m_proxyModel->isCategorizedModel() would be the way to go. As
307 // currently only the icons view supports caterized sorting, we remember
308 // the stored view properties state in m_storedCategorizedSorting and
309 // return this state. The application takes care to disable the corresponding
310 // checkbox by checking DolphinView::supportsCategorizedSorting() to indicate
311 // that this setting is not applied to the current view mode.
312 return m_storedCategorizedSorting;
313 }
314
315 bool DolphinView::supportsCategorizedSorting() const
316 {
317 return m_iconsView != 0;
318 }
319
320 void DolphinView::selectAll()
321 {
322 QAbstractItemView* view = itemView();
323 // TODO: there seems to be a bug in QAbstractItemView::selectAll(); if
324 // the Ctrl-key is pressed (e. g. for Ctrl+A), selectAll() inverts the
325 // selection instead of selecting all items. This is bypassed for KDE 4.0
326 // by invoking clearSelection() first.
327 view->clearSelection();
328 view->selectAll();
329 }
330
331 void DolphinView::invertSelection()
332 {
333 if (isColumnViewActive()) {
334 // QAbstractItemView does not offer a virtual method invertSelection()
335 // as counterpart to QAbstractItemView::selectAll(). This makes it
336 // necessary to delegate the inverting of the selection to the
337 // column view, as only the selection of the active column should
338 // get inverted.
339 m_columnView->invertSelection();
340 } else {
341 QItemSelectionModel* selectionModel = itemView()->selectionModel();
342 const QAbstractItemModel* itemModel = selectionModel->model();
343
344 const QModelIndex topLeft = itemModel->index(0, 0);
345 const QModelIndex bottomRight = itemModel->index(itemModel->rowCount() - 1,
346 itemModel->columnCount() - 1);
347
348 const QItemSelection selection(topLeft, bottomRight);
349 selectionModel->select(selection, QItemSelectionModel::Toggle);
350 }
351 }
352
353 bool DolphinView::hasSelection() const
354 {
355 return itemView()->selectionModel()->hasSelection();
356 }
357
358 void DolphinView::clearSelection()
359 {
360 itemView()->selectionModel()->clear();
361 }
362
363 KFileItemList DolphinView::selectedItems() const
364 {
365 const QAbstractItemView* view = itemView();
366
367 // Our view has a selection, we will map them back to the DolphinModel
368 // and then fill the KFileItemList.
369 Q_ASSERT((view != 0) && (view->selectionModel() != 0));
370
371 const QItemSelection selection = m_proxyModel->mapSelectionToSource(view->selectionModel()->selection());
372 KFileItemList itemList;
373
374 const QModelIndexList indexList = selection.indexes();
375 foreach (QModelIndex index, indexList) {
376 KFileItem item = m_dolphinModel->itemForIndex(index);
377 if (!item.isNull()) {
378 itemList.append(item);
379 }
380 }
381
382 return itemList;
383 }
384
385 KUrl::List DolphinView::selectedUrls() const
386 {
387 KUrl::List urls;
388 const KFileItemList list = selectedItems();
389 foreach (KFileItem item, list) {
390 urls.append(item.url());
391 }
392 return urls;
393 }
394
395 KFileItem DolphinView::fileItem(const QModelIndex& index) const
396 {
397 const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
398 return m_dolphinModel->itemForIndex(dolphinModelIndex);
399 }
400
401 void DolphinView::setContentsPosition(int x, int y)
402 {
403 QAbstractItemView* view = itemView();
404
405 // the ColumnView takes care itself for the horizontal scrolling
406 if (!isColumnViewActive()) {
407 view->horizontalScrollBar()->setValue(x);
408 }
409 view->verticalScrollBar()->setValue(y);
410
411 m_loadingDirectory = false;
412 }
413
414 QPoint DolphinView::contentsPosition() const
415 {
416 const int x = itemView()->horizontalScrollBar()->value();
417 const int y = itemView()->verticalScrollBar()->value();
418 return QPoint(x, y);
419 }
420
421 void DolphinView::zoomIn()
422 {
423 m_controller->triggerZoomIn();
424 }
425
426 void DolphinView::zoomOut()
427 {
428 m_controller->triggerZoomOut();
429 }
430
431 bool DolphinView::isZoomInPossible() const
432 {
433 return m_controller->isZoomInPossible();
434 }
435
436 bool DolphinView::isZoomOutPossible() const
437 {
438 return m_controller->isZoomOutPossible();
439 }
440
441 void DolphinView::setSorting(Sorting sorting)
442 {
443 if (sorting != this->sorting()) {
444 updateSorting(sorting);
445 }
446 }
447
448 DolphinView::Sorting DolphinView::sorting() const
449 {
450 return m_proxyModel->sorting();
451 }
452
453 void DolphinView::setSortOrder(Qt::SortOrder order)
454 {
455 if (sortOrder() != order) {
456 updateSortOrder(order);
457 }
458 }
459
460 Qt::SortOrder DolphinView::sortOrder() const
461 {
462 return m_proxyModel->sortOrder();
463 }
464
465 void DolphinView::setAdditionalInfo(KFileItemDelegate::InformationList info)
466 {
467 const KUrl viewPropsUrl = viewPropertiesUrl();
468 ViewProperties props(viewPropsUrl);
469 props.setAdditionalInfo(info);
470 m_fileItemDelegate->setShowInformation(info);
471
472 emit additionalInfoChanged(info);
473
474 if (itemView() != m_detailsView) {
475 // the details view requires no reloading of the directory, as it maps
476 // the file item delegate info to its columns internally
477 loadDirectory(viewPropsUrl, true);
478 }
479 }
480
481 KFileItemDelegate::InformationList DolphinView::additionalInfo() const
482 {
483 return m_fileItemDelegate->showInformation();
484 }
485
486 void DolphinView::reload()
487 {
488 setUrl(url());
489 loadDirectory(url(), true);
490 }
491
492 void DolphinView::refresh()
493 {
494 const bool oldActivationState = m_active;
495 m_active = true;
496
497 createView();
498 applyViewProperties(m_controller->url());
499 reload();
500
501 setActive(oldActivationState);
502 }
503
504 void DolphinView::updateView(const KUrl& url, const KUrl& rootUrl)
505 {
506 if (m_controller->url() == url) {
507 return;
508 }
509
510 m_controller->setUrl(url); // emits urlChanged, which we forward
511
512 if (!rootUrl.isEmpty() && rootUrl.isParentOf(url)) {
513 applyViewProperties(rootUrl);
514 loadDirectory(rootUrl);
515 if (itemView() == m_columnView) {
516 m_columnView->setRootUrl(rootUrl);
517 m_columnView->showColumn(url);
518 }
519 } else {
520 applyViewProperties(url);
521 loadDirectory(url);
522 }
523
524 emit startedPathLoading(url);
525 }
526
527 void DolphinView::setNameFilter(const QString& nameFilter)
528 {
529 m_proxyModel->setFilterRegExp(nameFilter);
530
531 if (isColumnViewActive()) {
532 // adjusting the directory lister is not enough in the case of the
533 // column view, as each column has its own directory lister internally...
534 m_columnView->setNameFilter(nameFilter);
535 }
536 }
537
538 void DolphinView::calculateItemCount(int& fileCount, int& folderCount)
539 {
540 foreach (KFileItem item, m_dirLister->items()) {
541 if (item.isDir()) {
542 ++folderCount;
543 } else {
544 ++fileCount;
545 }
546 }
547 }
548
549 void DolphinView::setUrl(const KUrl& url)
550 {
551 updateView(url, KUrl());
552 }
553
554 void DolphinView::mouseReleaseEvent(QMouseEvent* event)
555 {
556 QWidget::mouseReleaseEvent(event);
557 setActive(true);
558 }
559 void DolphinView::activate()
560 {
561 setActive(true);
562 }
563
564 void DolphinView::triggerItem(const KFileItem& item)
565 {
566 const Qt::KeyboardModifiers modifier = QApplication::keyboardModifiers();
567 if ((modifier & Qt::ShiftModifier) || (modifier & Qt::ControlModifier)) {
568 // items are selected by the user, hence don't trigger the
569 // item specified by 'index'
570 return;
571 }
572
573 if (item.isNull()) {
574 return;
575 }
576
577 emit itemTriggered(item); // caught by DolphinViewContainer or DolphinPart
578 }
579
580 void DolphinView::emitSelectionChangedSignal()
581 {
582 emit selectionChanged(DolphinView::selectedItems());
583 }
584
585 void DolphinView::loadDirectory(const KUrl& url, bool reload)
586 {
587 if (!url.isValid()) {
588 const QString location(url.pathOrUrl());
589 if (location.isEmpty()) {
590 emit errorMessage(i18nc("@info:status", "The location is empty."));
591 } else {
592 emit errorMessage(i18nc("@info:status", "The location '%1' is invalid.", location));
593 }
594 return;
595 }
596
597 m_loadingDirectory = true;
598
599 m_dirLister->stop();
600 m_dirLister->openUrl(url, reload ? KDirLister::Reload : KDirLister::NoFlags);
601
602 if (isColumnViewActive()) {
603 // adjusting the directory lister is not enough in the case of the
604 // column view, as each column has its own directory lister internally...
605 if (reload) {
606 m_columnView->reload();
607 } else {
608 m_columnView->showColumn(url);
609 }
610 }
611 }
612
613 KUrl DolphinView::viewPropertiesUrl() const
614 {
615 if (isColumnViewActive()) {
616 return m_dirLister->url();
617 }
618
619 return url();
620 }
621
622 void DolphinView::applyViewProperties(const KUrl& url)
623 {
624 if (isColumnViewActive() && rootUrl().isParentOf(url)) {
625 // The column view is active, hence don't apply the view properties
626 // of sub directories (represented by columns) to the view. The
627 // view always represents the properties of the first column.
628 return;
629 }
630
631 const ViewProperties props(url);
632
633 const Mode mode = props.viewMode();
634 if (m_mode != mode) {
635 m_mode = mode;
636 createView();
637 emit modeChanged();
638 }
639 if (itemView() == 0) {
640 createView();
641 }
642 Q_ASSERT(itemView() != 0);
643 Q_ASSERT(m_fileItemDelegate != 0);
644
645 const bool showHiddenFiles = props.showHiddenFiles();
646 if (showHiddenFiles != m_dirLister->showingDotFiles()) {
647 m_dirLister->setShowingDotFiles(showHiddenFiles);
648 emit showHiddenFilesChanged();
649 }
650
651 m_storedCategorizedSorting = props.categorizedSorting();
652 const bool categorized = m_storedCategorizedSorting && supportsCategorizedSorting();
653 if (categorized != m_proxyModel->isCategorizedModel()) {
654 m_proxyModel->setCategorizedModel(categorized);
655 emit categorizedSortingChanged();
656 }
657
658 const DolphinView::Sorting sorting = props.sorting();
659 if (sorting != m_proxyModel->sorting()) {
660 m_proxyModel->setSorting(sorting);
661 emit sortingChanged(sorting);
662 }
663
664 const Qt::SortOrder sortOrder = props.sortOrder();
665 if (sortOrder != m_proxyModel->sortOrder()) {
666 m_proxyModel->setSortOrder(sortOrder);
667 emit sortOrderChanged(sortOrder);
668 }
669
670 KFileItemDelegate::InformationList info = props.additionalInfo();
671 if (info != m_fileItemDelegate->showInformation()) {
672 m_fileItemDelegate->setShowInformation(info);
673 emit additionalInfoChanged(info);
674 }
675
676 const bool showPreview = props.showPreview();
677 if (showPreview != m_showPreview) {
678 m_showPreview = showPreview;
679 m_iconManager->setShowPreview(showPreview);
680 emit showPreviewChanged();
681 }
682 }
683
684 void DolphinView::changeSelection(const KFileItemList& selection)
685 {
686 clearSelection();
687 if (selection.isEmpty()) {
688 return;
689 }
690 const KUrl& baseUrl = url();
691 KUrl url;
692 QItemSelection new_selection;
693 foreach(const KFileItem& item, selection) {
694 url = item.url().upUrl();
695 if (baseUrl.equals(url, KUrl::CompareWithoutTrailingSlash)) {
696 QModelIndex index = m_proxyModel->mapFromSource(m_dolphinModel->indexForItem(item));
697 new_selection.select(index, index);
698 }
699 }
700 itemView()->selectionModel()->select(new_selection,
701 QItemSelectionModel::ClearAndSelect
702 | QItemSelectionModel::Current);
703 }
704
705 void DolphinView::openContextMenu(const QPoint& pos)
706 {
707 KFileItem item;
708
709 const QModelIndex index = itemView()->indexAt(pos);
710 if (index.isValid() && (index.column() == DolphinModel::Name)) {
711 item = fileItem(index);
712 }
713
714 emit requestContextMenu(item, url());
715 }
716
717 void DolphinView::dropUrls(const KUrl::List& urls,
718 const KUrl& destPath,
719 const KFileItem& destItem)
720 {
721 Q_ASSERT(!urls.isEmpty());
722 const KUrl& destination = !destItem.isNull() && destItem.isDir() ?
723 destItem.url() : destPath;
724 const KUrl sourceDir = KUrl(urls.first().directory());
725 if (sourceDir != destination) {
726 dropUrls(urls, destination);
727 }
728 }
729
730 void DolphinView::dropUrls(const KUrl::List& urls,
731 const KUrl& destination)
732 {
733 DolphinDropController dropController(this);
734 // forward doingOperation signal up to the mainwindow
735 connect(&dropController, SIGNAL(doingOperation(KonqFileUndoManager::CommandType)),
736 this, SIGNAL(doingOperation(KonqFileUndoManager::CommandType)));
737 dropController.dropUrls(urls, destination);
738 }
739
740 void DolphinView::updateSorting(DolphinView::Sorting sorting)
741 {
742 ViewProperties props(viewPropertiesUrl());
743 props.setSorting(sorting);
744
745 m_proxyModel->setSorting(sorting);
746
747 emit sortingChanged(sorting);
748 }
749
750 void DolphinView::updateSortOrder(Qt::SortOrder order)
751 {
752 ViewProperties props(viewPropertiesUrl());
753 props.setSortOrder(order);
754
755 m_proxyModel->setSortOrder(order);
756
757 emit sortOrderChanged(order);
758 }
759
760 void DolphinView::updateAdditionalInfo(const KFileItemDelegate::InformationList& info)
761 {
762 ViewProperties props(viewPropertiesUrl());
763 props.setAdditionalInfo(info);
764 props.save();
765
766 m_fileItemDelegate->setShowInformation(info);
767
768 emit additionalInfoChanged(info);
769
770 }
771
772 void DolphinView::emitContentsMoved()
773 {
774 // only emit the contents moved signal if:
775 // - no directory loading is ongoing (this would reset the contents position
776 // always to (0, 0))
777 // - if the Column View is active: the column view does an automatic
778 // positioning during the loading operation, which must be remembered
779 if (!m_loadingDirectory || isColumnViewActive()) {
780 const QPoint pos(contentsPosition());
781 emit contentsMoved(pos.x(), pos.y());
782 }
783 }
784
785 void DolphinView::showHoverInformation(const KFileItem& item)
786 {
787 if (hasSelection() || !m_active) {
788 return;
789 }
790
791 emit requestItemInfo(item);
792 }
793
794 void DolphinView::clearHoverInformation()
795 {
796 if (m_active) {
797 emit requestItemInfo(KFileItem());
798 }
799 }
800
801 void DolphinView::createView()
802 {
803 deleteView();
804 Q_ASSERT(m_iconsView == 0);
805 Q_ASSERT(m_detailsView == 0);
806 Q_ASSERT(m_columnView == 0);
807
808 QAbstractItemView* view = 0;
809 switch (m_mode) {
810 case IconsView: {
811 m_iconsView = new DolphinIconsView(this, m_controller);
812 view = m_iconsView;
813 break;
814 }
815
816 case DetailsView:
817 m_detailsView = new DolphinDetailsView(this, m_controller);
818 view = m_detailsView;
819 break;
820
821 case ColumnView:
822 m_columnView = new DolphinColumnView(this, m_controller);
823 view = m_columnView;
824 break;
825 }
826
827 Q_ASSERT(view != 0);
828
829 m_fileItemDelegate = new KFileItemDelegate(view);
830 view->setItemDelegate(m_fileItemDelegate);
831
832 view->setModel(m_proxyModel);
833 if (m_selectionModel != 0) {
834 view->setSelectionModel(m_selectionModel);
835 } else {
836 m_selectionModel = view->selectionModel();
837 }
838
839 // reparent the selection model, as it should not be deleted
840 // when deleting the model
841 m_selectionModel->setParent(this);
842
843 view->setSelectionMode(QAbstractItemView::ExtendedSelection);
844
845 new KMimeTypeResolver(view, m_dolphinModel);
846 m_iconManager = new IconManager(view, m_proxyModel);
847 m_iconManager->setShowPreview(m_showPreview);
848
849 m_topLayout->insertWidget(1, view);
850
851 connect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
852 this, SLOT(emitSelectionChangedSignal()));
853 connect(view->verticalScrollBar(), SIGNAL(valueChanged(int)),
854 this, SLOT(emitContentsMoved()));
855 connect(view->horizontalScrollBar(), SIGNAL(valueChanged(int)),
856 this, SLOT(emitContentsMoved()));
857 }
858
859 void DolphinView::deleteView()
860 {
861 QAbstractItemView* view = itemView();
862 if (view != 0) {
863 m_topLayout->removeWidget(view);
864 view->close();
865 view->deleteLater();
866 view = 0;
867 m_iconsView = 0;
868 m_detailsView = 0;
869 m_columnView = 0;
870 m_fileItemDelegate = 0;
871 m_iconManager = 0;
872 }
873 }
874
875 QAbstractItemView* DolphinView::itemView() const
876 {
877 if (m_detailsView != 0) {
878 return m_detailsView;
879 } else if (m_columnView != 0) {
880 return m_columnView;
881 }
882
883 return m_iconsView;
884 }
885
886 bool DolphinView::isCutItem(const KFileItem& item) const
887 {
888 const QMimeData* mimeData = QApplication::clipboard()->mimeData();
889 const KUrl::List cutUrls = KUrl::List::fromMimeData(mimeData);
890
891 const KUrl& itemUrl = item.url();
892 KUrl::List::const_iterator it = cutUrls.begin();
893 const KUrl::List::const_iterator end = cutUrls.end();
894 while (it != end) {
895 if (*it == itemUrl) {
896 return true;
897 }
898 ++it;
899 }
900
901 return false;
902 }
903
904 KToggleAction* DolphinView::iconsModeAction(KActionCollection* actionCollection)
905 {
906 KToggleAction* iconsView = actionCollection->add<KToggleAction>("icons");
907 iconsView->setText(i18nc("@action:inmenu View Mode", "Icons"));
908 iconsView->setShortcut(Qt::CTRL | Qt::Key_1);
909 iconsView->setIcon(KIcon("view-list-icons"));
910 iconsView->setData(QVariant::fromValue(IconsView));
911 return iconsView;
912 }
913
914 KToggleAction* DolphinView::detailsModeAction(KActionCollection* actionCollection)
915 {
916 KToggleAction* detailsView = actionCollection->add<KToggleAction>("details");
917 detailsView->setText(i18nc("@action:inmenu View Mode", "Details"));
918 detailsView->setShortcut(Qt::CTRL | Qt::Key_2);
919 detailsView->setIcon(KIcon("view-list-details"));
920 detailsView->setData(QVariant::fromValue(DetailsView));
921 return detailsView;
922 }
923
924 KToggleAction* DolphinView::columnsModeAction(KActionCollection* actionCollection)
925 {
926 KToggleAction* columnView = actionCollection->add<KToggleAction>("columns");
927 columnView->setText(i18nc("@action:inmenu View Mode", "Columns"));
928 columnView->setShortcut(Qt::CTRL | Qt::Key_3);
929 columnView->setIcon(KIcon("view-file-columns"));
930 columnView->setData(QVariant::fromValue(ColumnView));
931 return columnView;
932 }
933
934 QString DolphinView::currentViewModeActionName() const
935 {
936 switch (m_mode) {
937 case DolphinView::IconsView:
938 return "icons";
939 case DolphinView::DetailsView:
940 return "details";
941 case DolphinView::ColumnView:
942 return "columns";
943 }
944 return QString(); // can't happen
945 }
946
947 void DolphinView::renameSelectedItems()
948 {
949 const KFileItemList items = selectedItems();
950 if (items.count() > 1) {
951 // More than one item has been selected for renaming. Open
952 // a rename dialog and rename all items afterwards.
953 RenameDialog dialog(this, items);
954 if (dialog.exec() == QDialog::Rejected) {
955 return;
956 }
957
958 const QString newName = dialog.newName();
959 if (newName.isEmpty()) {
960 emit errorMessage(dialog.errorString());
961 } else {
962 // TODO: check how this can be integrated into KonqFileUndoManager/KonqOperations
963 // as one operation instead of n rename operations like it is done now...
964 Q_ASSERT(newName.contains('#'));
965
966 // iterate through all selected items and rename them...
967 int index = 1;
968 foreach (KFileItem item, items) {
969 const KUrl& oldUrl = item.url();
970 QString number;
971 number.setNum(index++);
972
973 QString name = newName;
974 name.replace('#', number);
975
976 if (oldUrl.fileName() != name) {
977 KUrl newUrl = oldUrl;
978 newUrl.setFileName(name);
979 KonqOperations::rename(this, oldUrl, newUrl);
980 emit doingOperation(KonqFileUndoManager::RENAME);
981 }
982 }
983 }
984 } else {
985 // Only one item has been selected for renaming. Use the custom
986 // renaming mechanism from the views.
987 Q_ASSERT(items.count() == 1);
988
989 // TODO: Think about using KFileItemDelegate as soon as it supports editing.
990 // Currently the RenameDialog is used, but I'm not sure whether inline renaming
991 // is a benefit for the user at all -> let's wait for some input first...
992 RenameDialog dialog(this, items);
993 if (dialog.exec() == QDialog::Rejected) {
994 return;
995 }
996
997 const QString& newName = dialog.newName();
998 if (newName.isEmpty()) {
999 emit errorMessage(dialog.errorString());
1000 } else {
1001 const KUrl& oldUrl = items.first().url();
1002 KUrl newUrl = oldUrl;
1003 newUrl.setFileName(newName);
1004 KonqOperations::rename(this, oldUrl, newUrl);
1005 emit doingOperation(KonqFileUndoManager::RENAME);
1006 }
1007 }
1008 }
1009
1010 void DolphinView::trashSelectedItems()
1011 {
1012 emit doingOperation(KonqFileUndoManager::TRASH);
1013 KonqOperations::del(this, KonqOperations::TRASH, selectedUrls());
1014 }
1015
1016 void DolphinView::deleteSelectedItems()
1017 {
1018 const KUrl::List list = selectedUrls();
1019 const bool del = KonqOperations::askDeleteConfirmation(list,
1020 KonqOperations::DEL,
1021 KonqOperations::DEFAULT_CONFIRMATION,
1022 this);
1023
1024 if (del) {
1025 KIO::Job* job = KIO::del(list);
1026 connect(job, SIGNAL(result(KJob*)),
1027 this, SLOT(slotDeleteFileFinished(KJob*)));
1028 }
1029 }
1030
1031 void DolphinView::slotDeleteFileFinished(KJob* job)
1032 {
1033 if (job->error() == 0) {
1034 emit operationCompletedMessage(i18nc("@info:status", "Delete operation completed."));
1035 } else {
1036 emit errorMessage(job->errorString());
1037 }
1038 }
1039
1040 void DolphinView::createDir()
1041 {
1042 KonqOperations::newDir(this, url());
1043 }
1044
1045 void DolphinView::cutSelectedItems()
1046 {
1047 QMimeData* mimeData = new QMimeData();
1048 const KUrl::List kdeUrls = selectedUrls();
1049 const KUrl::List mostLocalUrls;
1050 KonqMimeData::populateMimeData(mimeData, kdeUrls, mostLocalUrls, true);
1051 QApplication::clipboard()->setMimeData(mimeData);
1052 }
1053
1054 void DolphinView::copySelectedItems()
1055 {
1056 QMimeData* mimeData = new QMimeData();
1057 const KUrl::List kdeUrls = selectedUrls();
1058 const KUrl::List mostLocalUrls;
1059 KonqMimeData::populateMimeData(mimeData, kdeUrls, mostLocalUrls, false);
1060 QApplication::clipboard()->setMimeData(mimeData);
1061 }
1062
1063 void DolphinView::paste()
1064 {
1065 QClipboard* clipboard = QApplication::clipboard();
1066 const QMimeData* mimeData = clipboard->mimeData();
1067
1068 const KUrl::List sourceUrls = KUrl::List::fromMimeData(mimeData);
1069
1070 // per default the pasting is done into the current Url of the view
1071 KUrl destUrl(url());
1072
1073 // check whether the pasting should be done into a selected directory
1074 const KUrl::List selectedUrls = this->selectedUrls();
1075 if (selectedUrls.count() == 1) {
1076 const KFileItem fileItem(S_IFDIR,
1077 KFileItem::Unknown,
1078 selectedUrls.first(),
1079 true);
1080 if (fileItem.isDir()) {
1081 // only one item is selected which is a directory, hence paste
1082 // into this directory
1083 destUrl = selectedUrls.first();
1084 }
1085 }
1086
1087 if (KonqMimeData::decodeIsCutSelection(mimeData)) {
1088 KonqOperations::copy(this, KonqOperations::MOVE, sourceUrls, destUrl);
1089 emit doingOperation(KonqFileUndoManager::MOVE);
1090 clipboard->clear();
1091 } else {
1092 KonqOperations::copy(this, KonqOperations::COPY, sourceUrls, destUrl);
1093 emit doingOperation(KonqFileUndoManager::COPY);
1094 }
1095 }
1096
1097 QPair<bool, QString> DolphinView::pasteInfo() const
1098 {
1099 QPair<bool, QString> ret;
1100 QClipboard* clipboard = QApplication::clipboard();
1101 const QMimeData* mimeData = clipboard->mimeData();
1102
1103 KUrl::List urls = KUrl::List::fromMimeData(mimeData);
1104 if (!urls.isEmpty()) {
1105 ret.first = true;
1106 ret.second = i18ncp("@action:inmenu", "Paste One File", "Paste %1 Files", urls.count());
1107 } else {
1108 ret.first = false;
1109 ret.second = i18nc("@action:inmenu", "Paste");
1110 }
1111
1112 if (ret.first) {
1113 const KFileItemList items = selectedItems();
1114 const uint count = items.count();
1115 if (count > 1) {
1116 // pasting should not be allowed when more than one file
1117 // is selected
1118 ret.first = false;
1119 } else if (count == 1) {
1120 // Only one file is selected. Pasting is only allowed if this
1121 // file is a directory.
1122 ret.first = items.first().isDir();
1123 }
1124 }
1125 return ret;
1126 }
1127
1128 KAction* DolphinView::createRenameAction(KActionCollection* collection)
1129 {
1130 KAction* rename = collection->addAction("rename");
1131 rename->setText(i18nc("@action:inmenu File", "Rename..."));
1132 rename->setShortcut(Qt::Key_F2);
1133 return rename;
1134 }
1135
1136 KAction* DolphinView::createMoveToTrashAction(KActionCollection* collection)
1137 {
1138 KAction* moveToTrash = collection->addAction("move_to_trash");
1139 moveToTrash->setText(i18nc("@action:inmenu File", "Move to Trash"));
1140 moveToTrash->setIcon(KIcon("user-trash"));
1141 moveToTrash->setShortcut(QKeySequence::Delete);
1142 return moveToTrash;
1143 }
1144
1145 KAction* DolphinView::createDeleteAction(KActionCollection* collection)
1146 {
1147 KAction* deleteAction = collection->addAction("delete");
1148 deleteAction->setIcon(KIcon("edit-delete"));
1149 deleteAction->setText(i18nc("@action:inmenu File", "Delete"));
1150 deleteAction->setShortcut(Qt::SHIFT | Qt::Key_Delete);
1151 return deleteAction;
1152 }
1153
1154 #include "dolphinview.moc"