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