]> cloud.milkyroute.net Git - dolphin.git/blob - src/views/dolphinview.cpp
Improved drag and drop support
[dolphin.git] / src / views / dolphinview.cpp
1 /***************************************************************************
2 * Copyright (C) 2006-2009 by Peter Penz <peter.penz19@gmail.com> *
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
23 #include <QAbstractItemView>
24 #include <QApplication>
25 #include <QBoxLayout>
26 #include <QClipboard>
27 #include <QDropEvent>
28 #include <QGraphicsSceneDragDropEvent>
29 #include <QKeyEvent>
30 #include <QItemSelection>
31 #include <QTimer>
32 #include <QScrollBar>
33
34 #include <KActionCollection>
35 #include <KColorScheme>
36 #include <KDirLister>
37 #include <KDirModel>
38 #include <KIconEffect>
39 #include <KFileItem>
40 #include <KFileItemListProperties>
41 #include <KLocale>
42 #include <kitemviews/kfileitemmodel.h>
43 #include <kitemviews/kfileitemlistview.h>
44 #include <kitemviews/kitemlistselectionmanager.h>
45 #include <kitemviews/kitemlistview.h>
46 #include <kitemviews/kitemlistcontroller.h>
47 #include <KIO/DeleteJob>
48 #include <KIO/NetAccess>
49 #include <KIO/PreviewJob>
50 #include <KJob>
51 #include <KMenu>
52 #include <KMessageBox>
53 #include <konq_fileitemcapabilities.h>
54 #include <konq_operations.h>
55 #include <konqmimedata.h>
56 #include <KToggleAction>
57 #include <KUrl>
58
59 #include "additionalinfoaccessor.h"
60 #include "dolphindirlister.h"
61 #include "dolphinnewfilemenuobserver.h"
62 #include "dolphin_detailsmodesettings.h"
63 #include "dolphin_generalsettings.h"
64 #include "dolphinitemlistcontainer.h"
65 #include "draganddrophelper.h"
66 #include "renamedialog.h"
67 #include "settings/dolphinsettings.h"
68 #include "viewmodecontroller.h"
69 #include "viewproperties.h"
70 #include "views/tooltips/tooltipmanager.h"
71 #include "zoomlevelinfo.h"
72
73 namespace {
74 const int MaxModeEnum = DolphinView::CompactView;
75 const int MaxSortingEnum = DolphinView::SortByPath;
76 };
77
78 DolphinView::DolphinView(const KUrl& url, QWidget* parent) :
79 QWidget(parent),
80 m_active(true),
81 m_tabsForFiles(false),
82 m_assureVisibleCurrentIndex(false),
83 m_isFolderWritable(true),
84 m_url(url),
85 m_mode(DolphinView::IconsView),
86 m_additionalInfoList(),
87 m_topLayout(0),
88 m_dirLister(0),
89 m_container(0),
90 m_toolTipManager(0),
91 m_selectionChangedTimer(0),
92 m_currentItemIndex(-1),
93 m_restoredContentsPosition(),
94 m_createdItemUrl(),
95 m_selectedItems()
96 {
97 m_topLayout = new QVBoxLayout(this);
98 m_topLayout->setSpacing(0);
99 m_topLayout->setMargin(0);
100
101 //m_dolphinViewController = new DolphinViewController(this);
102
103 //m_viewModeController = new ViewModeController(this);
104 //m_viewModeController->setUrl(url);
105
106 /*connect(m_viewModeController, SIGNAL(urlChanged(KUrl)),
107 this, SIGNAL(urlChanged(KUrl)));
108
109 connect(m_dolphinViewController, SIGNAL(requestContextMenu(QPoint,QList<QAction*>)),
110 this, SLOT(openContextMenu(QPoint,QList<QAction*>)));
111 connect(m_dolphinViewController, SIGNAL(urlsDropped(KFileItem,KUrl,QDropEvent*)),
112 this, SLOT(dropUrls(KFileItem,KUrl,QDropEvent*)));
113 connect(m_dolphinViewController, SIGNAL(sortingChanged(DolphinView::Sorting)),
114 this, SLOT(updateSorting(DolphinView::Sorting)));
115 connect(m_dolphinViewController, SIGNAL(sortOrderChanged(Qt::SortOrder)),
116 this, SLOT(updateSortOrder(Qt::SortOrder)));
117 connect(m_dolphinViewController, SIGNAL(sortFoldersFirstChanged(bool)),
118 this, SLOT(updateSortFoldersFirst(bool)));
119 connect(m_dolphinViewController, SIGNAL(additionalInfoChanged(QList<DolphinView::AdditionalInfo>)),
120 this, SLOT(updateAdditionalInfo(QList<DolphinView::AdditionalInfo>)));*/
121 //connect(m_dolphinViewController, SIGNAL(itemActivated(KFileItem)),
122 // this, SLOT(triggerItem(KFileItem)));
123 //connect(m_dolphinViewController, SIGNAL(tabRequested(KUrl)),
124 // this, SIGNAL(tabRequested(KUrl)));
125 /*connect(m_dolphinViewController, SIGNAL(activated()),
126 this, SLOT(activate()));
127 connect(m_dolphinViewController, SIGNAL(itemEntered(KFileItem)),
128 this, SLOT(showHoverInformation(KFileItem)));
129 connect(m_dolphinViewController, SIGNAL(viewportEntered()),
130 this, SLOT(clearHoverInformation()));
131 connect(m_dolphinViewController, SIGNAL(urlChangeRequested(KUrl)),
132 this, SLOT(slotUrlChangeRequested(KUrl)));*/
133
134 // When a new item has been created by the "Create New..." menu, the item should
135 // get selected and it must be assured that the item will get visible. As the
136 // creation is done asynchronously, several signals must be checked:
137 connect(&DolphinNewFileMenuObserver::instance(), SIGNAL(itemCreated(KUrl)),
138 this, SLOT(observeCreatedItem(KUrl)));
139
140 m_selectionChangedTimer = new QTimer(this);
141 m_selectionChangedTimer->setSingleShot(true);
142 m_selectionChangedTimer->setInterval(300);
143 connect(m_selectionChangedTimer, SIGNAL(timeout()),
144 this, SLOT(emitSelectionChangedSignal()));
145
146 m_dirLister = new DolphinDirLister(this);
147 m_dirLister->setAutoUpdate(true);
148 m_dirLister->setDelayedMimeTypes(true);
149
150 connect(m_dirLister, SIGNAL(redirection(KUrl,KUrl)), this, SLOT(slotRedirection(KUrl,KUrl)));
151 connect(m_dirLister, SIGNAL(started(KUrl)), this, SLOT(slotDirListerStarted(KUrl)));
152 connect(m_dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted()));
153 connect(m_dirLister, SIGNAL(refreshItems(QList<QPair<KFileItem,KFileItem> >)),
154 this, SLOT(slotRefreshItems()));
155
156 connect(m_dirLister, SIGNAL(clear()), this, SIGNAL(itemCountChanged()));
157 connect(m_dirLister, SIGNAL(newItems(KFileItemList)), this, SIGNAL(itemCountChanged()));
158 connect(m_dirLister, SIGNAL(infoMessage(QString)), this, SIGNAL(infoMessage(QString)));
159 connect(m_dirLister, SIGNAL(errorMessage(QString)), this, SIGNAL(infoMessage(QString)));
160 connect(m_dirLister, SIGNAL(percent(int)), this, SIGNAL(pathLoadingProgress(int)));
161 connect(m_dirLister, SIGNAL(urlIsFileError(KUrl)), this, SIGNAL(urlIsFileError(KUrl)));
162 connect(m_dirLister, SIGNAL(itemsDeleted(KFileItemList)), this, SIGNAL(itemCountChanged()));
163
164 m_container = new DolphinItemListContainer(m_dirLister, this);
165 QHash<QByteArray, int> visibleRoles;
166 visibleRoles.insert("name", 0);
167 m_container->setVisibleRoles(visibleRoles);
168
169 KItemListController* controller = m_container->controller();
170 controller->setSelectionBehavior(KItemListController::MultiSelection);
171 connect(controller, SIGNAL(itemActivated(int)),
172 this, SLOT(slotItemActivated(int)));
173 connect(controller, SIGNAL(itemMiddleClicked(int)), this, SLOT(slotItemMiddleClicked(int)));
174 connect(controller, SIGNAL(contextMenuRequested(int,QPointF)), this, SLOT(slotContextMenuRequested(int,QPointF)));
175 connect(controller, SIGNAL(itemExpansionToggleClicked(int)), this, SLOT(slotItemExpansionToggleClicked(int)));
176 connect(controller, SIGNAL(itemHovered(int)), this, SLOT(slotItemHovered(int)));
177 connect(controller, SIGNAL(itemUnhovered(int)), this, SLOT(slotItemUnhovered(int)));
178 connect(controller, SIGNAL(itemDropEvent(int,QGraphicsSceneDragDropEvent*)), this, SLOT(slotItemDropEvent(int,QGraphicsSceneDragDropEvent*)));
179
180 KItemListSelectionManager* selectionManager = controller->selectionManager();
181 connect(selectionManager, SIGNAL(selectionChanged(QSet<int>,QSet<int>)),
182 this, SLOT(slotSelectionChanged(QSet<int>,QSet<int>)));
183
184 m_toolTipManager = new ToolTipManager(this);
185
186 applyViewProperties();
187 m_topLayout->addWidget(m_container);
188
189 loadDirectory(url);
190 }
191
192 DolphinView::~DolphinView()
193 {
194 }
195
196 KUrl DolphinView::url() const
197 {
198 return m_url;
199 }
200
201 void DolphinView::setActive(bool active)
202 {
203 if (active == m_active) {
204 return;
205 }
206
207 m_active = active;
208
209 QColor color = KColorScheme(QPalette::Active, KColorScheme::View).background().color();
210 if (!active) {
211 color.setAlpha(150);
212 }
213
214 /*QAbstractItemView* view = m_viewAccessor.itemView();
215 QWidget* viewport = view ? view->viewport() : 0;
216 if (viewport) {
217 QPalette palette;
218 palette.setColor(viewport->backgroundRole(), color);
219 viewport->setPalette(palette);
220 }*/
221
222 update();
223
224 if (active) {
225 //if (view) {
226 // view->setFocus();
227 //}
228 emit activated();
229 emit writeStateChanged(m_isFolderWritable);
230 }
231
232 //m_viewModeController->indicateActivationChange(active);
233 }
234
235 bool DolphinView::isActive() const
236 {
237 return m_active;
238 }
239
240 void DolphinView::setMode(Mode mode)
241 {
242 if (mode != m_mode) {
243 ViewProperties props(url());
244 props.setViewMode(mode);
245 props.save();
246
247 applyViewProperties();
248 }
249 }
250
251 DolphinView::Mode DolphinView::mode() const
252 {
253 return m_mode;
254 }
255
256 bool DolphinView::previewsShown() const
257 {
258 return m_container->previewsShown();
259 }
260
261 bool DolphinView::hiddenFilesShown() const
262 {
263 return m_dirLister->showingDotFiles();
264 }
265
266 bool DolphinView::categorizedSorting() const
267 {
268 return false; //m_storedCategorizedSorting;
269 }
270
271 KFileItemList DolphinView::items() const
272 {
273 return m_dirLister->items();
274 }
275
276 KFileItemList DolphinView::selectedItems() const
277 {
278 const KFileItemModel* model = fileItemModel();
279 const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
280 const QSet<int> selectedIndexes = selectionManager->selectedItems();
281
282 KFileItemList selectedItems;
283 QSetIterator<int> it(selectedIndexes);
284 while (it.hasNext()) {
285 const int index = it.next();
286 selectedItems.append(model->fileItem(index));
287 }
288 return selectedItems;
289 }
290
291 int DolphinView::selectedItemsCount() const
292 {
293 const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
294 return selectionManager->selectedItems().count();
295 }
296
297 void DolphinView::markUrlsAsSelected(const QList<KUrl>& urls)
298 {
299 foreach (const KUrl& url, urls) {
300 KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
301 m_selectedItems.append(item);
302 }
303 }
304
305 void DolphinView::setItemSelectionEnabled(const QRegExp& pattern, bool enabled)
306 {
307 const KItemListSelectionManager::SelectionMode mode = enabled
308 ? KItemListSelectionManager::Select
309 : KItemListSelectionManager::Deselect;
310 const KFileItemModel* model = fileItemModel();
311 KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
312
313 for (int index = 0; index < model->count(); index++) {
314 const KFileItem item = model->fileItem(index);
315 if (pattern.exactMatch(item.name())) {
316 // An alternative approach would be to store the matching items in a QSet<int> and
317 // select them in one go after the loop, but we'd need a new function
318 // KItemListSelectionManager::setSelected(QSet<int>, SelectionMode mode)
319 // for that.
320 selectionManager->setSelected(index, 1, mode);
321 }
322 }
323 }
324
325 void DolphinView::setZoomLevel(int level)
326 {
327 const int oldZoomLevel = zoomLevel();
328 m_container->setZoomLevel(level);
329 if (zoomLevel() != oldZoomLevel) {
330 emit zoomLevelChanged(zoomLevel(), oldZoomLevel);
331 }
332 }
333
334 int DolphinView::zoomLevel() const
335 {
336 return m_container->zoomLevel();
337 }
338
339 void DolphinView::setSorting(Sorting sorting)
340 {
341 if (sorting != this->sorting()) {
342 updateSorting(sorting);
343 }
344 }
345
346 DolphinView::Sorting DolphinView::sorting() const
347 {
348 return DolphinView::SortByName;
349 //return m_viewAccessor.proxyModel()->sorting();
350 }
351
352 void DolphinView::setSortOrder(Qt::SortOrder order)
353 {
354 if (sortOrder() != order) {
355 updateSortOrder(order);
356 }
357 }
358
359 Qt::SortOrder DolphinView::sortOrder() const
360 {
361 return Qt::AscendingOrder; // m_viewAccessor.proxyModel()->sortOrder();
362 }
363
364 void DolphinView::setSortFoldersFirst(bool foldersFirst)
365 {
366 if (sortFoldersFirst() != foldersFirst) {
367 updateSortFoldersFirst(foldersFirst);
368 }
369 }
370
371 bool DolphinView::sortFoldersFirst() const
372 {
373 return true; // m_viewAccessor.proxyModel()->sortFoldersFirst();
374 }
375
376 void DolphinView::setAdditionalInfoList(const QList<AdditionalInfo>& info)
377 {
378 const QList<AdditionalInfo> previousList = info;
379
380 ViewProperties props(url());
381 props.setAdditionalInfoList(info);
382
383 m_additionalInfoList = info;
384 applyAdditionalInfoListToView();
385
386 emit additionalInfoListChanged(m_additionalInfoList, previousList);
387 }
388
389 QList<DolphinView::AdditionalInfo> DolphinView::additionalInfoList() const
390 {
391 return m_additionalInfoList;
392 }
393
394 void DolphinView::reload()
395 {
396 QByteArray viewState;
397 QDataStream saveStream(&viewState, QIODevice::WriteOnly);
398 saveState(saveStream);
399 m_selectedItems= selectedItems();
400
401 setUrl(url());
402 loadDirectory(url(), true);
403
404 QDataStream restoreStream(viewState);
405 restoreState(restoreStream);
406 }
407
408 void DolphinView::stopLoading()
409 {
410 m_dirLister->stop();
411 }
412
413 void DolphinView::refresh()
414 {
415 const bool oldActivationState = m_active;
416 const int oldZoomLevel = zoomLevel();
417 m_active = true;
418
419 applyViewProperties();
420 reload();
421
422 setActive(oldActivationState);
423 updateZoomLevel(oldZoomLevel);
424 }
425
426 void DolphinView::setNameFilter(const QString& nameFilter)
427 {
428 Q_UNUSED(nameFilter);
429 //m_viewModeController->setNameFilter(nameFilter);
430 }
431
432 QString DolphinView::nameFilter() const
433 {
434 return QString(); //m_viewModeController->nameFilter();
435 }
436
437 void DolphinView::calculateItemCount(int& fileCount,
438 int& folderCount,
439 KIO::filesize_t& totalFileSize) const
440 {
441 foreach (const KFileItem& item, m_dirLister->items()) {
442 if (item.isDir()) {
443 ++folderCount;
444 } else {
445 ++fileCount;
446 totalFileSize += item.size();
447 }
448 }
449 }
450
451 QString DolphinView::statusBarText() const
452 {
453 QString text;
454 int folderCount = 0;
455 int fileCount = 0;
456 KIO::filesize_t totalFileSize = 0;
457
458 if (hasSelection()) {
459 // give a summary of the status of the selected files
460 const KFileItemList list = selectedItems();
461 if (list.isEmpty()) {
462 // when an item is triggered, it is temporary selected but selectedItems()
463 // will return an empty list
464 return text;
465 }
466
467 KFileItemList::const_iterator it = list.begin();
468 const KFileItemList::const_iterator end = list.end();
469 while (it != end) {
470 const KFileItem& item = *it;
471 if (item.isDir()) {
472 ++folderCount;
473 } else {
474 ++fileCount;
475 totalFileSize += item.size();
476 }
477 ++it;
478 }
479
480 if (folderCount + fileCount == 1) {
481 // if only one item is selected, show the filename
482 const QString name = list.first().text();
483 text = (folderCount == 1) ? i18nc("@info:status", "<filename>%1</filename> selected", name) :
484 i18nc("@info:status", "<filename>%1</filename> selected (%2)",
485 name, KIO::convertSize(totalFileSize));
486 } else {
487 // at least 2 items are selected
488 const QString foldersText = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount);
489 const QString filesText = i18ncp("@info:status", "1 File selected", "%1 Files selected", fileCount);
490 if ((folderCount > 0) && (fileCount > 0)) {
491 text = i18nc("@info:status folders, files (size)", "%1, %2 (%3)",
492 foldersText, filesText, KIO::convertSize(totalFileSize));
493 } else if (fileCount > 0) {
494 text = i18nc("@info:status files (size)", "%1 (%2)", filesText, KIO::convertSize(totalFileSize));
495 } else {
496 Q_ASSERT(folderCount > 0);
497 text = foldersText;
498 }
499 }
500 } else {
501 calculateItemCount(fileCount, folderCount, totalFileSize);
502 text = KIO::itemsSummaryString(fileCount + folderCount,
503 fileCount, folderCount,
504 totalFileSize, true);
505 }
506
507 return text;
508 }
509
510 QList<QAction*> DolphinView::versionControlActions(const KFileItemList& items) const
511 {
512 Q_UNUSED(items);
513 return QList<QAction*>(); //m_dolphinViewController->versionControlActions(items);
514 }
515
516 void DolphinView::setUrl(const KUrl& url)
517 {
518 if (url == m_url) {
519 return;
520 }
521
522 emit urlAboutToBeChanged(url);
523 m_url = url;
524
525 if (GeneralSettings::showToolTips()) {
526 m_toolTipManager->hideToolTip();
527 }
528
529 // It is important to clear the items from the model before
530 // applying the view properties, otherwise expensive operations
531 // might be done on the existing items although they get cleared
532 // anyhow afterwards by loadDirectory().
533 fileItemModel()->clear();
534 applyViewProperties();
535 loadDirectory(url);
536
537 emit urlChanged(url);
538 }
539
540 void DolphinView::selectAll()
541 {
542 KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
543 selectionManager->setSelected(0, fileItemModel()->count());
544 }
545
546 void DolphinView::invertSelection()
547 {
548 KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
549 selectionManager->setSelected(0, fileItemModel()->count(), KItemListSelectionManager::Toggle);
550 }
551
552 void DolphinView::clearSelection()
553 {
554 m_container->controller()->selectionManager()->clearSelection();
555 }
556
557 void DolphinView::renameSelectedItems()
558 {
559 KFileItemList items = selectedItems();
560 const int itemCount = items.count();
561 if (itemCount < 1) {
562 return;
563 }
564
565 /*if ((itemCount == 1) && DolphinSettings::instance().generalSettings()->renameInline()) {
566 const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForItem(items.first());
567 const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->mapFromSource(dirIndex);
568 m_viewAccessor.itemView()->edit(proxyIndex);
569 } else {*/
570 RenameDialog* dialog = new RenameDialog(this, items);
571 dialog->setAttribute(Qt::WA_DeleteOnClose);
572 dialog->show();
573 dialog->raise();
574 dialog->activateWindow();
575 //}
576
577 // assure that the current index remains visible when KDirLister
578 // will notify the view about changed items
579 m_assureVisibleCurrentIndex = true;
580 }
581
582 void DolphinView::trashSelectedItems()
583 {
584 const KUrl::List list = simplifiedSelectedUrls();
585 KonqOperations::del(this, KonqOperations::TRASH, list);
586 }
587
588 void DolphinView::deleteSelectedItems()
589 {
590 const KUrl::List list = simplifiedSelectedUrls();
591 const bool del = KonqOperations::askDeleteConfirmation(list,
592 KonqOperations::DEL,
593 KonqOperations::DEFAULT_CONFIRMATION,
594 this);
595
596 if (del) {
597 KIO::Job* job = KIO::del(list);
598 connect(job, SIGNAL(result(KJob*)),
599 this, SLOT(slotDeleteFileFinished(KJob*)));
600 }
601 }
602
603 void DolphinView::cutSelectedItems()
604 {
605 QMimeData* mimeData = selectionMimeData();
606 KonqMimeData::addIsCutSelection(mimeData, true);
607 QApplication::clipboard()->setMimeData(mimeData);
608 }
609
610 void DolphinView::copySelectedItems()
611 {
612 QMimeData* mimeData = selectionMimeData();
613 QApplication::clipboard()->setMimeData(mimeData);
614 }
615
616 void DolphinView::paste()
617 {
618 pasteToUrl(url());
619 }
620
621 void DolphinView::pasteIntoFolder()
622 {
623 const KFileItemList items = selectedItems();
624 if ((items.count() == 1) && items.first().isDir()) {
625 pasteToUrl(items.first().url());
626 }
627 }
628
629 void DolphinView::setPreviewsShown(bool show)
630 {
631 if (previewsShown() == show) {
632 return;
633 }
634
635 ViewProperties props(url());
636 props.setPreviewsShown(show);
637
638 m_container->setPreviewsShown(show);
639 emit previewsShownChanged(show);
640 }
641
642 void DolphinView::setHiddenFilesShown(bool show)
643 {
644 if (m_dirLister->showingDotFiles() == show) {
645 return;
646 }
647
648 m_selectedItems = selectedItems();
649
650 ViewProperties props(url());
651 props.setHiddenFilesShown(show);
652
653 m_dirLister->setShowingDotFiles(show);
654 m_dirLister->emitChanges();
655 emit hiddenFilesShownChanged(show);
656 }
657
658 void DolphinView::setCategorizedSorting(bool categorized)
659 {
660 if (categorized == categorizedSorting()) {
661 return;
662 }
663
664 ViewProperties props(url());
665 props.setCategorizedSorting(categorized);
666 props.save();
667
668 //m_viewAccessor.proxyModel()->setCategorizedModel(categorized);
669
670 emit categorizedSortingChanged(categorized);
671 }
672
673 void DolphinView::mouseReleaseEvent(QMouseEvent* event)
674 {
675 QWidget::mouseReleaseEvent(event);
676 setActive(true);
677 }
678
679 void DolphinView::contextMenuEvent(QContextMenuEvent* event)
680 {
681 Q_UNUSED(event);
682
683 const QPoint pos = m_container->mapFromGlobal(QCursor::pos());
684 const KItemListView* view = m_container->controller()->view();
685 if (view->itemAt(pos) < 0) {
686 // Only open the context-menu if the cursor is above the viewport
687 // (the context-menu for items is handled in slotContextMenuRequested())
688 requestContextMenu(KFileItem(), url(), QList<QAction*>());
689 }
690 }
691
692 void DolphinView::wheelEvent(QWheelEvent* event)
693 {
694 if (event->modifiers().testFlag(Qt::ControlModifier)) {
695 const int numDegrees = event->delta() / 8;
696 const int numSteps = numDegrees / 15;
697
698 setZoomLevel(zoomLevel() + numSteps);
699 }
700 event->accept();
701 }
702
703 void DolphinView::activate()
704 {
705 setActive(true);
706 }
707
708 void DolphinView::slotItemActivated(int index)
709 {
710 Q_UNUSED(index);
711
712 const KFileItemList items = selectedItems();
713 if (items.isEmpty()) {
714 return;
715 }
716
717 if (items.count() == 1) {
718 emit itemActivated(items.at(0)); // caught by DolphinViewContainer or DolphinPart
719 } else {
720 foreach (const KFileItem& item, items) {
721 if (item.isDir()) {
722 emit tabRequested(item.url());
723 } else {
724 emit itemActivated(item);
725 }
726 }
727 }
728 }
729
730 void DolphinView::slotItemMiddleClicked(int index)
731 {
732 const KFileItem item = fileItemModel()->fileItem(index);
733 if (item.isDir() || isTabsForFilesEnabled()) {
734 emit tabRequested(item.url());
735 }
736 }
737
738 void DolphinView::slotContextMenuRequested(int index, const QPointF& pos)
739 {
740 Q_UNUSED(pos);
741 if (GeneralSettings::showToolTips()) {
742 m_toolTipManager->hideToolTip();
743 }
744 const KFileItem item = fileItemModel()->fileItem(index);
745 emit requestContextMenu(item, url(), QList<QAction*>());
746 }
747
748 void DolphinView::slotItemExpansionToggleClicked(int index)
749 {
750 // TODO: When doing a model->setExpanded(false) it should
751 // be checked here whether the current index is part of the
752 // closed sub-tree. If this is the case, the current index
753 // should be adjusted to the parent index.
754 KFileItemModel* model = fileItemModel();
755 const bool expanded = model->isExpanded(index);
756 model->setExpanded(index, !expanded);
757 }
758
759 void DolphinView::slotItemHovered(int index)
760 {
761 const KFileItem item = fileItemModel()->fileItem(index);
762
763 if (GeneralSettings::showToolTips()) {
764 QRectF itemRect = m_container->controller()->view()->itemBoundingRect(index);
765 const QPoint pos = m_container->mapToGlobal(itemRect.topLeft().toPoint());
766 itemRect.moveTo(pos);
767
768 m_toolTipManager->showToolTip(item, itemRect);
769 }
770
771 emit requestItemInfo(item);
772 }
773
774 void DolphinView::slotItemUnhovered(int index)
775 {
776 Q_UNUSED(index);
777 if (GeneralSettings::showToolTips()) {
778 m_toolTipManager->hideToolTip();
779 }
780 emit requestItemInfo(KFileItem());
781 }
782
783 void DolphinView::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event)
784 {
785 const KFileItem destItem = fileItemModel()->fileItem(index);
786
787 QDropEvent dropEvent(event->pos().toPoint(),
788 event->possibleActions(),
789 event->mimeData(),
790 event->buttons(),
791 event->modifiers());
792
793 DragAndDropHelper::dropUrls(destItem, url(), &dropEvent, this);
794 }
795
796 void DolphinView::slotSelectionChanged(const QSet<int>& current, const QSet<int>& previous)
797 {
798 const int currentCount = current.count();
799 const int previousCount = previous.count();
800 const bool selectionStateChanged = (currentCount == 0 && previousCount > 0) ||
801 (currentCount > 0 && previousCount == 0);
802
803 // If nothing has been selected before and something got selected (or if something
804 // was selected before and now nothing is selected) the selectionChangedSignal must
805 // be emitted asynchronously as fast as possible to update the edit-actions.
806 m_selectionChangedTimer->setInterval(selectionStateChanged ? 0 : 300);
807 m_selectionChangedTimer->start();
808 }
809
810 void DolphinView::emitSelectionChangedSignal()
811 {
812 m_selectionChangedTimer->stop();
813 emit selectionChanged(selectedItems());
814 }
815
816 void DolphinView::openContextMenu(const QPoint& pos,
817 const QList<QAction*>& customActions)
818 {
819 KFileItem item;
820 const int index = m_container->controller()->view()->itemAt(pos);
821 if (index >= 0) {
822 item = fileItemModel()->fileItem(index);
823 }
824
825 emit requestContextMenu(item, url(), customActions);
826 }
827
828 void DolphinView::dropUrls(const KFileItem& destItem,
829 const KUrl& destPath,
830 QDropEvent* event)
831 {
832 Q_UNUSED(destItem);
833 Q_UNUSED(destPath);
834 markPastedUrlsAsSelected(event->mimeData());
835 //DragAndDropHelper::instance().dropUrls(destItem, destPath, event, this);
836 }
837
838 void DolphinView::updateSorting(DolphinView::Sorting sorting)
839 {
840 ViewProperties props(url());
841 props.setSorting(sorting);
842
843 KItemModelBase* model = m_container->controller()->model();
844 model->setSortRole(sortRoleForSorting(sorting));
845
846 emit sortingChanged(sorting);
847 }
848
849 void DolphinView::updateSortOrder(Qt::SortOrder order)
850 {
851 ViewProperties props(url());
852 props.setSortOrder(order);
853
854 //m_viewAccessor.proxyModel()->setSortOrder(order);
855
856 emit sortOrderChanged(order);
857 }
858
859 void DolphinView::updateSortFoldersFirst(bool foldersFirst)
860 {
861 ViewProperties props(url());
862 props.setSortFoldersFirst(foldersFirst);
863
864 //m_viewAccessor.proxyModel()->setSortFoldersFirst(foldersFirst);
865
866 emit sortFoldersFirstChanged(foldersFirst);
867 }
868
869 QPair<bool, QString> DolphinView::pasteInfo() const
870 {
871 return KonqOperations::pasteInfo(url());
872 }
873
874 void DolphinView::setTabsForFilesEnabled(bool tabsForFiles)
875 {
876 m_tabsForFiles = tabsForFiles;
877 }
878
879 bool DolphinView::isTabsForFilesEnabled() const
880 {
881 return m_tabsForFiles;
882 }
883
884 bool DolphinView::itemsExpandable() const
885 {
886 return m_mode == DetailsView;
887 }
888
889 void DolphinView::restoreState(QDataStream& stream)
890 {
891 // Restore the current item that had the keyboard focus
892 stream >> m_currentItemIndex;
893
894 // Restore the view position
895 stream >> m_restoredContentsPosition;
896
897 // Restore expanded folders (only relevant for the details view - will be ignored by the view in other view modes)
898 QSet<KUrl> urlsToExpand;
899 stream >> urlsToExpand;
900 /*const DolphinDetailsViewExpander* expander = m_viewAccessor.setExpandedUrls(urlsToExpand);
901 if (expander) {
902 m_expanderActive = true;
903 connect (expander, SIGNAL(completed()), this, SLOT(slotLoadingCompleted()));
904 }
905 else {
906 m_expanderActive = false;
907 }*/
908 }
909
910 void DolphinView::saveState(QDataStream& stream)
911 {
912 // Save the current item that has the keyboard focus
913 stream << m_container->controller()->selectionManager()->currentItem();
914
915 // Save view position
916 const qreal x = m_container->horizontalScrollBar()->value();
917 const qreal y = m_container->verticalScrollBar()->value();
918 stream << QPoint(x, y);
919
920 // Save expanded folders (only relevant for the details view - the set will be empty in other view modes)
921 //stream << m_viewAccessor.expandedUrls();
922 }
923
924 bool DolphinView::hasSelection() const
925 {
926 return m_container->controller()->selectionManager()->hasSelection();
927 }
928
929 KFileItem DolphinView::rootItem() const
930 {
931 return m_dirLister->rootItem();
932 }
933
934 void DolphinView::observeCreatedItem(const KUrl& url)
935 {
936 m_createdItemUrl = url;
937 //connect(m_dirModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
938 // this, SLOT(selectAndScrollToCreatedItem()));
939 }
940
941 void DolphinView::selectAndScrollToCreatedItem()
942 {
943 /*const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForUrl(m_createdItemUrl);
944 if (dirIndex.isValid()) {
945 const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->mapFromSource(dirIndex);
946 QAbstractItemView* view = m_viewAccessor.itemView();
947 if (view) {
948 view->setCurrentIndex(proxyIndex);
949 }
950 }
951
952 disconnect(m_viewAccessor.dirModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
953 this, SLOT(selectAndScrollToCreatedItem()));*/
954 m_createdItemUrl = KUrl();
955 }
956
957 void DolphinView::slotRedirection(const KUrl& oldUrl, const KUrl& newUrl)
958 {
959 if (oldUrl.equals(url(), KUrl::CompareWithoutTrailingSlash)) {
960 emit redirection(oldUrl, newUrl);
961 m_url = newUrl; // #186947
962 }
963 }
964
965 void DolphinView::updateViewState()
966 {
967 if (m_currentItemIndex >= 0) {
968 KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
969 selectionManager->setCurrentItem(m_currentItemIndex);
970 m_currentItemIndex =-1;
971 }
972
973 if (!m_restoredContentsPosition.isNull()) {
974 const int x = m_restoredContentsPosition.x();
975 const int y = m_restoredContentsPosition.y();
976 m_restoredContentsPosition = QPoint();
977
978 m_container->horizontalScrollBar()->setValue(x);
979 m_container->verticalScrollBar()->setValue(y);
980 }
981
982 if (!m_selectedItems.isEmpty()) {
983 KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
984 QSet<int> selectedItems = selectionManager->selectedItems();
985 const KFileItemModel* model = fileItemModel();
986
987 foreach (const KFileItem& selectedItem, m_selectedItems) {
988 const int index = model->index(selectedItem);
989 if (index >= 0) {
990 selectedItems.insert(index);
991 }
992 }
993
994 selectionManager->setSelectedItems(selectedItems);
995 m_selectedItems.clear();
996 }
997 }
998
999 void DolphinView::showHoverInformation(const KFileItem& item)
1000 {
1001 emit requestItemInfo(item);
1002 }
1003
1004 void DolphinView::clearHoverInformation()
1005 {
1006 emit requestItemInfo(KFileItem());
1007 }
1008
1009 void DolphinView::slotDeleteFileFinished(KJob* job)
1010 {
1011 if (job->error() == 0) {
1012 emit operationCompletedMessage(i18nc("@info:status", "Delete operation completed."));
1013 } else if (job->error() != KIO::ERR_USER_CANCELED) {
1014 emit errorMessage(job->errorString());
1015 }
1016 }
1017
1018 void DolphinView::slotDirListerStarted(const KUrl& url)
1019 {
1020 // Disable the writestate temporary until it can be determined in a fast way
1021 // in DolphinView::slotDirListerCompleted()
1022 if (m_isFolderWritable) {
1023 m_isFolderWritable = false;
1024 emit writeStateChanged(m_isFolderWritable);
1025 }
1026
1027 emit startedPathLoading(url);
1028 }
1029
1030 void DolphinView::slotDirListerCompleted()
1031 {
1032 // Update the view-state. This has to be done using a Qt::QueuedConnection
1033 // because the view might not be in its final state yet (the view also
1034 // listens to the completed()-signal from KDirLister and the order of
1035 // of slots is undefined).
1036 QTimer::singleShot(0, this, SLOT(updateViewState()));
1037
1038 emit finishedPathLoading(url());
1039
1040 updateWritableState();
1041 }
1042
1043 void DolphinView::slotRefreshItems()
1044 {
1045 if (m_assureVisibleCurrentIndex) {
1046 m_assureVisibleCurrentIndex = false;
1047 //QAbstractItemView* view = m_viewAccessor.itemView();
1048 //if (view) {
1049 // m_viewAccessor.itemView()->scrollTo(m_viewAccessor.itemView()->currentIndex());
1050 //}
1051 }
1052 }
1053
1054 KFileItemModel* DolphinView::fileItemModel() const
1055 {
1056 return static_cast<KFileItemModel*>(m_container->controller()->model());
1057 }
1058
1059 void DolphinView::loadDirectory(const KUrl& url, bool reload)
1060 {
1061 if (!url.isValid()) {
1062 const QString location(url.pathOrUrl());
1063 if (location.isEmpty()) {
1064 emit errorMessage(i18nc("@info:status", "The location is empty."));
1065 } else {
1066 emit errorMessage(i18nc("@info:status", "The location '%1' is invalid.", location));
1067 }
1068 return;
1069 }
1070
1071 m_dirLister->openUrl(url, reload ? KDirLister::Reload : KDirLister::NoFlags);
1072 }
1073
1074 void DolphinView::applyViewProperties()
1075 {
1076 m_container->beginTransaction();
1077
1078 const ViewProperties props(url());
1079
1080 const Mode mode = props.viewMode();
1081 if (m_mode != mode) {
1082 const Mode previousMode = m_mode;
1083 m_mode = mode;
1084
1085 // Changing the mode might result in changing
1086 // the zoom level. Remember the old zoom level so
1087 // that zoomLevelChanged() can get emitted.
1088 const int oldZoomLevel = m_container->zoomLevel();
1089
1090 switch (m_mode) {
1091 case IconsView: m_container->setItemLayout(KFileItemListView::IconsLayout); break;
1092 case CompactView: m_container->setItemLayout(KFileItemListView::CompactLayout); break;
1093 case DetailsView: m_container->setItemLayout(KFileItemListView::DetailsLayout); break;
1094 default: Q_ASSERT(false); break;
1095 }
1096
1097 emit modeChanged(m_mode, previousMode);
1098
1099 if (m_container->zoomLevel() != oldZoomLevel) {
1100 emit zoomLevelChanged(m_container->zoomLevel(), oldZoomLevel);
1101 }
1102 }
1103
1104 const bool hiddenFilesShown = props.hiddenFilesShown();
1105 if (hiddenFilesShown != m_dirLister->showingDotFiles()) {
1106 m_dirLister->setShowingDotFiles(hiddenFilesShown);
1107 m_dirLister->emitChanges();
1108 emit hiddenFilesShownChanged(hiddenFilesShown);
1109 }
1110
1111 /* m_storedCategorizedSorting = props.categorizedSorting();
1112 const bool categorized = m_storedCategorizedSorting && supportsCategorizedSorting();
1113 if (categorized != m_viewAccessor.proxyModel()->isCategorizedModel()) {
1114 m_viewAccessor.proxyModel()->setCategorizedModel(categorized);
1115 emit categorizedSortingChanged();
1116 }*/
1117
1118 const DolphinView::Sorting sorting = props.sorting();
1119 KItemModelBase* model = m_container->controller()->model();
1120 const QByteArray newSortRole = sortRoleForSorting(sorting);
1121 if (newSortRole != model->sortRole()) {
1122 model->setSortRole(newSortRole);
1123 emit sortingChanged(sorting);
1124 }
1125 /*
1126 const Qt::SortOrder sortOrder = props.sortOrder();
1127 if (sortOrder != m_viewAccessor.proxyModel()->sortOrder()) {
1128 m_viewAccessor.proxyModel()->setSortOrder(sortOrder);
1129 emit sortOrderChanged(sortOrder);
1130 }
1131
1132 const bool sortFoldersFirst = props.sortFoldersFirst();
1133 if (sortFoldersFirst != m_viewAccessor.proxyModel()->sortFoldersFirst()) {
1134 m_viewAccessor.proxyModel()->setSortFoldersFirst(sortFoldersFirst);
1135 emit sortFoldersFirstChanged(sortFoldersFirst);
1136 }
1137 */
1138 const QList<DolphinView::AdditionalInfo> infoList = props.additionalInfoList();
1139 if (infoList != m_additionalInfoList) {
1140 const QList<DolphinView::AdditionalInfo> previousList = m_additionalInfoList;
1141 m_additionalInfoList = infoList;
1142 applyAdditionalInfoListToView();
1143 emit additionalInfoListChanged(m_additionalInfoList, previousList);
1144 }
1145
1146 const bool previewsShown = props.previewsShown();
1147 if (previewsShown != m_container->previewsShown()) {
1148 const int oldZoomLevel = zoomLevel();
1149
1150 m_container->setPreviewsShown(previewsShown);
1151 emit previewsShownChanged(previewsShown);
1152
1153 // Changing the preview-state might result in a changed zoom-level
1154 if (oldZoomLevel != zoomLevel()) {
1155 emit zoomLevelChanged(zoomLevel(), oldZoomLevel);
1156 }
1157 }
1158
1159 m_container->endTransaction();
1160 }
1161
1162 void DolphinView::applyAdditionalInfoListToView()
1163 {
1164 const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance();
1165
1166 QHash<QByteArray, int> visibleRoles;
1167 visibleRoles.insert("name", 0);
1168
1169 int index = 1;
1170 foreach (AdditionalInfo info, m_additionalInfoList) {
1171 visibleRoles.insert(infoAccessor.role(info), index);
1172 ++index;
1173 }
1174
1175 m_container->setVisibleRoles(visibleRoles);
1176 }
1177
1178 void DolphinView::pasteToUrl(const KUrl& url)
1179 {
1180 markPastedUrlsAsSelected(QApplication::clipboard()->mimeData());
1181 KonqOperations::doPaste(this, url);
1182 }
1183
1184 void DolphinView::updateZoomLevel(int oldZoomLevel)
1185 {
1186 Q_UNUSED(oldZoomLevel);
1187 /* const int newZoomLevel = ZoomLevelInfo::zoomLevelForIconSize(m_viewAccessor.itemView()->iconSize());
1188 if (oldZoomLevel != newZoomLevel) {
1189 m_viewModeController->setZoomLevel(newZoomLevel);
1190 emit zoomLevelChanged(newZoomLevel);
1191 }*/
1192 }
1193
1194 KUrl::List DolphinView::simplifiedSelectedUrls() const
1195 {
1196 KUrl::List urls;
1197
1198 const KFileItemList items = selectedItems();
1199 foreach (const KFileItem &item, items) {
1200 urls.append(item.url());
1201 }
1202
1203 if (itemsExpandable()) {
1204 // TODO: Check if we still need KDirModel for this in KDE 5.0
1205 urls = KDirModel::simplifiedUrlList(urls);
1206 }
1207
1208 return urls;
1209 }
1210
1211 QMimeData* DolphinView::selectionMimeData() const
1212 {
1213 const KFileItemModel* model = fileItemModel();
1214 const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
1215 const QSet<int> selectedIndexes = selectionManager->selectedItems();
1216
1217 return model->createMimeData(selectedIndexes);
1218 }
1219
1220 void DolphinView::markPastedUrlsAsSelected(const QMimeData* mimeData)
1221 {
1222 const KUrl::List urls = KUrl::List::fromMimeData(mimeData);
1223 markUrlsAsSelected(urls);
1224 }
1225
1226 void DolphinView::updateWritableState()
1227 {
1228 const bool wasFolderWritable = m_isFolderWritable;
1229 m_isFolderWritable = true;
1230
1231 const KFileItem item = m_dirLister->rootItem();
1232 if (!item.isNull()) {
1233 KFileItemListProperties capabilities(KFileItemList() << item);
1234 m_isFolderWritable = capabilities.supportsWriting();
1235 }
1236 if (m_isFolderWritable != wasFolderWritable) {
1237 emit writeStateChanged(m_isFolderWritable);
1238 }
1239 }
1240
1241 QByteArray DolphinView::sortRoleForSorting(Sorting sorting) const
1242 {
1243 switch (sorting) {
1244 case SortByName: return "name";
1245 case SortBySize: return "size";
1246 case SortByDate: return "date";
1247 case SortByPermissions: return "permissions";
1248 case SortByOwner: return "owner";
1249 case SortByGroup: return "group";
1250 case SortByType: return "type";
1251 case SortByDestination: return "destination";
1252 case SortByPath: return "path";
1253 default: break;
1254 }
1255
1256 return QByteArray();
1257 }
1258
1259 #include "dolphinview.moc"