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