]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphinview.cpp
when having split views: darken the background color of the inactive view
[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
23 #include <QtGui/QApplication>
24 #include <QtGui/QClipboard>
25 #include <QtGui/QKeyEvent>
26 #include <QtGui/QItemSelection>
27 #include <QtGui/QBoxLayout>
28 #include <QtCore/QTimer>
29 #include <QtGui/QScrollBar>
30
31 #include <kdirmodel.h>
32 #include <kfileitemdelegate.h>
33 #include <kfileplacesmodel.h>
34 #include <kglobalsettings.h>
35 #include <klocale.h>
36 #include <kiconeffect.h>
37 #include <kio/netaccess.h>
38 #include <kio/renamedialog.h>
39 #include <kio/previewjob.h>
40 #include <kmimetyperesolver.h>
41 #include <konqmimedata.h>
42 #include <konq_operations.h>
43 #include <kurl.h>
44
45 #include "dolphincolumnview.h"
46 #include "dolphincontroller.h"
47 #include "dolphinstatusbar.h"
48 #include "dolphinmainwindow.h"
49 #include "dolphindirlister.h"
50 #include "dolphinsortfilterproxymodel.h"
51 #include "dolphindetailsview.h"
52 #include "dolphiniconsview.h"
53 #include "dolphincontextmenu.h"
54 #include "dolphinitemcategorizer.h"
55 #include "filterbar.h"
56 #include "renamedialog.h"
57 #include "kurlnavigator.h"
58 #include "viewproperties.h"
59 #include "dolphinsettings.h"
60 #include "dolphin_generalsettings.h"
61
62 DolphinView::DolphinView(DolphinMainWindow* mainWindow,
63 QWidget* parent,
64 const KUrl& url,
65 Mode mode,
66 bool showHiddenFiles) :
67 QWidget(parent),
68 m_showProgress(false),
69 m_blockContentsMovedSignal(false),
70 m_initializeColumnView(false),
71 m_mode(mode),
72 m_iconSize(0),
73 m_folderCount(0),
74 m_fileCount(0),
75 m_mainWindow(mainWindow),
76 m_topLayout(0),
77 m_urlNavigator(0),
78 m_controller(0),
79 m_iconsView(0),
80 m_detailsView(0),
81 m_columnView(0),
82 m_fileItemDelegate(0),
83 m_filterBar(0),
84 m_statusBar(0),
85 m_dirModel(0),
86 m_dirLister(0),
87 m_proxyModel(0)
88 {
89 hide();
90 setFocusPolicy(Qt::StrongFocus);
91 m_topLayout = new QVBoxLayout(this);
92 m_topLayout->setSpacing(0);
93 m_topLayout->setMargin(0);
94
95 connect(m_mainWindow, SIGNAL(activeViewChanged()),
96 this, SLOT(updateActivationState()));
97
98 QClipboard* clipboard = QApplication::clipboard();
99 connect(clipboard, SIGNAL(dataChanged()),
100 this, SLOT(updateCutItems()));
101
102 m_urlNavigator = new KUrlNavigator(DolphinSettings::instance().placesModel(), url, this);
103 m_urlNavigator->setUrlEditable(DolphinSettings::instance().generalSettings()->editableUrl());
104 m_urlNavigator->setHomeUrl(DolphinSettings::instance().generalSettings()->homeUrl());
105 connect(m_urlNavigator, SIGNAL(urlChanged(const KUrl&)),
106 this, SLOT(changeDirectory(const KUrl&)));
107 connect(m_urlNavigator, SIGNAL(urlsDropped(const KUrl::List&, const KUrl&)),
108 this, SLOT(dropUrls(const KUrl::List&, const KUrl&)));
109 connect(m_urlNavigator, SIGNAL(activated()),
110 this, SLOT(requestActivation()));
111 connect(this, SIGNAL(contentsMoved(int, int)),
112 m_urlNavigator, SLOT(savePosition(int, int)));
113
114 m_statusBar = new DolphinStatusBar(this);
115
116 m_dirLister = new DolphinDirLister();
117 m_dirLister->setAutoUpdate(true);
118 m_dirLister->setMainWindow(this);
119 m_dirLister->setShowingDotFiles(showHiddenFiles);
120 m_dirLister->setDelayedMimeTypes(true);
121
122 connect(m_dirLister, SIGNAL(clear()),
123 this, SLOT(updateStatusBar()));
124 connect(m_dirLister, SIGNAL(percent(int)),
125 this, SLOT(updateProgress(int)));
126 connect(m_dirLister, SIGNAL(deleteItem(KFileItem*)),
127 this, SLOT(updateStatusBar()));
128 connect(m_dirLister, SIGNAL(completed()),
129 this, SLOT(updateItemCount()));
130 connect(m_dirLister, SIGNAL(completed()),
131 this, SLOT(updateCutItems()));
132 connect(m_dirLister, SIGNAL(newItems(const KFileItemList&)),
133 this, SLOT(generatePreviews(const KFileItemList&)));
134 connect(m_dirLister, SIGNAL(infoMessage(const QString&)),
135 this, SLOT(showInfoMessage(const QString&)));
136 connect(m_dirLister, SIGNAL(errorMessage(const QString&)),
137 this, SLOT(showErrorMessage(const QString&)));
138
139 m_dirModel = new KDirModel();
140 m_dirModel->setDirLister(m_dirLister);
141 m_dirModel->setDropsAllowed(KDirModel::DropOnDirectory);
142
143 m_proxyModel = new DolphinSortFilterProxyModel(this);
144 m_proxyModel->setSourceModel(m_dirModel);
145
146 m_controller = new DolphinController(this);
147 connect(m_controller, SIGNAL(requestContextMenu(const QPoint&)),
148 this, SLOT(openContextMenu(const QPoint&)));
149 connect(m_controller, SIGNAL(urlsDropped(const KUrl::List&, const QModelIndex&, QWidget*)),
150 this, SLOT(dropUrls(const KUrl::List&, const QModelIndex&, QWidget*)));
151 connect(m_controller, SIGNAL(sortingChanged(DolphinView::Sorting)),
152 this, SLOT(updateSorting(DolphinView::Sorting)));
153 connect(m_controller, SIGNAL(sortOrderChanged(Qt::SortOrder)),
154 this, SLOT(updateSortOrder(Qt::SortOrder)));
155 connect(m_controller, SIGNAL(itemTriggered(const QModelIndex&)),
156 this, SLOT(triggerItem(const QModelIndex&)));
157 connect(m_controller, SIGNAL(selectionChanged()),
158 this, SLOT(emitSelectionChangedSignal()));
159 connect(m_controller, SIGNAL(activated()),
160 this, SLOT(requestActivation()));
161 connect(m_controller, SIGNAL(itemEntered(const QModelIndex&)),
162 this, SLOT(showHoverInformation(const QModelIndex&)));
163 connect(m_controller, SIGNAL(viewportEntered()),
164 this, SLOT(clearHoverInformation()));
165
166 createView();
167
168 m_iconSize = K3Icon::SizeMedium;
169
170 m_filterBar = new FilterBar(this);
171 m_filterBar->hide();
172 connect(m_filterBar, SIGNAL(filterChanged(const QString&)),
173 this, SLOT(changeNameFilter(const QString&)));
174 connect(m_filterBar, SIGNAL(closeRequest()),
175 this, SLOT(closeFilterBar()));
176
177 m_topLayout->addWidget(m_urlNavigator);
178 m_topLayout->addWidget(itemView());
179 m_topLayout->addWidget(m_filterBar);
180 m_topLayout->addWidget(m_statusBar);
181 }
182
183 DolphinView::~DolphinView()
184 {
185 delete m_dirLister;
186 m_dirLister = 0;
187 }
188
189 void DolphinView::setUrl(const KUrl& url)
190 {
191 m_urlNavigator->setUrl(url);
192 m_controller->setUrl(url);
193 }
194
195 const KUrl& DolphinView::url() const
196 {
197 return m_urlNavigator->url();
198 }
199
200 bool DolphinView::isActive() const
201 {
202 return m_mainWindow->activeView() == this;
203 }
204
205 void DolphinView::setMode(Mode mode)
206 {
207 if (mode == m_mode) {
208 return; // the wished mode is already set
209 }
210
211 m_mode = mode;
212
213 if (isColumnViewActive()) {
214 // When changing the mode in the column view, it makes sense
215 // to go back to the root URL of the column view automatically.
216 // Otherwise there it would not be possible to turn off the column view
217 // without focusing the first column.
218 setUrl(m_dirLister->url());
219 }
220
221 ViewProperties props(m_urlNavigator->url());
222 props.setViewMode(m_mode);
223
224 createView();
225 startDirLister(m_urlNavigator->url());
226
227 emit modeChanged();
228 }
229
230 DolphinView::Mode DolphinView::mode() const
231 {
232 return m_mode;
233 }
234
235 void DolphinView::setShowPreview(bool show)
236 {
237 ViewProperties props(m_urlNavigator->url());
238 props.setShowPreview(show);
239
240 m_controller->setShowPreview(show);
241 emit showPreviewChanged();
242
243 startDirLister(m_urlNavigator->url(), true);
244 }
245
246 bool DolphinView::showPreview() const
247 {
248 return m_controller->showPreview();
249 }
250
251 void DolphinView::setShowHiddenFiles(bool show)
252 {
253 if (m_dirLister->showingDotFiles() == show) {
254 return;
255 }
256
257 ViewProperties props(m_urlNavigator->url());
258 props.setShowHiddenFiles(show);
259
260 m_dirLister->setShowingDotFiles(show);
261 emit showHiddenFilesChanged();
262
263 startDirLister(m_urlNavigator->url(), true);
264 }
265
266 bool DolphinView::showHiddenFiles() const
267 {
268 return m_dirLister->showingDotFiles();
269 }
270
271 void DolphinView::setCategorizedSorting(bool categorized)
272 {
273 if (!supportsCategorizedSorting() || (categorized == categorizedSorting())) {
274 return;
275 }
276
277 Q_ASSERT(m_iconsView != 0);
278 if (categorized) {
279 Q_ASSERT(m_iconsView->itemCategorizer() == 0);
280 m_iconsView->setItemCategorizer(new DolphinItemCategorizer());
281 } else {
282 KItemCategorizer* categorizer = m_iconsView->itemCategorizer();
283 m_iconsView->setItemCategorizer(0);
284 delete categorizer;
285 }
286
287 ViewProperties props(m_urlNavigator->url());
288 props.setCategorizedSorting(categorized);
289 props.save();
290
291 emit categorizedSortingChanged();
292 }
293
294 bool DolphinView::categorizedSorting() const
295 {
296 if (!supportsCategorizedSorting()) {
297 return false;
298 }
299
300 Q_ASSERT(m_iconsView != 0);
301 return m_iconsView->itemCategorizer() != 0;
302 }
303
304 bool DolphinView::supportsCategorizedSorting() const
305 {
306 return m_iconsView != 0;
307 }
308
309 void DolphinView::renameSelectedItems()
310 {
311 DolphinView* view = mainWindow()->activeView();
312 const KUrl::List urls = selectedUrls();
313 if (urls.count() > 1) {
314 // More than one item has been selected for renaming. Open
315 // a rename dialog and rename all items afterwards.
316 RenameDialog dialog(urls);
317 if (dialog.exec() == QDialog::Rejected) {
318 return;
319 }
320
321 const QString& newName = dialog.newName();
322 if (newName.isEmpty()) {
323 view->statusBar()->setMessage(dialog.errorString(),
324 DolphinStatusBar::Error);
325 } else {
326 // TODO: check how this can be integrated into KonqUndoManager/KonqOperations
327 // as one operation instead of n rename operations like it is done now...
328 Q_ASSERT(newName.contains('#'));
329
330 // iterate through all selected items and rename them...
331 const int replaceIndex = newName.indexOf('#');
332 Q_ASSERT(replaceIndex >= 0);
333 int index = 1;
334
335 KUrl::List::const_iterator it = urls.begin();
336 KUrl::List::const_iterator end = urls.end();
337 while (it != end) {
338 const KUrl& oldUrl = *it;
339 QString number;
340 number.setNum(index++);
341
342 QString name(newName);
343 name.replace(replaceIndex, 1, number);
344
345 if (oldUrl.fileName() != name) {
346 KUrl newUrl = oldUrl;
347 newUrl.setFileName(name);
348 m_mainWindow->rename(oldUrl, newUrl);
349 }
350 ++it;
351 }
352 }
353 } else {
354 // Only one item has been selected for renaming. Use the custom
355 // renaming mechanism from the views.
356 Q_ASSERT(urls.count() == 1);
357
358 // TODO: Think about using KFileItemDelegate as soon as it supports editing.
359 // Currently the RenameDialog is used, but I'm not sure whether inline renaming
360 // is a benefit for the user at all -> let's wait for some input first...
361 RenameDialog dialog(urls);
362 if (dialog.exec() == QDialog::Rejected) {
363 return;
364 }
365
366 const QString& newName = dialog.newName();
367 if (newName.isEmpty()) {
368 view->statusBar()->setMessage(dialog.errorString(),
369 DolphinStatusBar::Error);
370 } else {
371 const KUrl& oldUrl = urls.first();
372 KUrl newUrl = oldUrl;
373 newUrl.setFileName(newName);
374 m_mainWindow->rename(oldUrl, newUrl);
375 }
376 }
377 }
378
379 void DolphinView::selectAll()
380 {
381 selectAll(QItemSelectionModel::Select);
382 }
383
384 void DolphinView::invertSelection()
385 {
386 selectAll(QItemSelectionModel::Toggle);
387 }
388
389 DolphinStatusBar* DolphinView::statusBar() const
390 {
391 return m_statusBar;
392 }
393
394 int DolphinView::contentsX() const
395 {
396 return itemView()->horizontalScrollBar()->value();
397 }
398
399 int DolphinView::contentsY() const
400 {
401 return itemView()->verticalScrollBar()->value();
402 }
403
404 void DolphinView::emitRequestItemInfo(const KUrl& url)
405 {
406 emit requestItemInfo(url);
407 }
408
409 bool DolphinView::isFilterBarVisible() const
410 {
411 return m_filterBar->isVisible();
412 }
413
414 bool DolphinView::isUrlEditable() const
415 {
416 return m_urlNavigator->isUrlEditable();
417 }
418
419 void DolphinView::zoomIn()
420 {
421 m_controller->triggerZoomIn();
422 }
423
424 void DolphinView::zoomOut()
425 {
426 m_controller->triggerZoomOut();
427 }
428
429 bool DolphinView::isZoomInPossible() const
430 {
431 return m_controller->isZoomInPossible();
432 }
433
434 bool DolphinView::isZoomOutPossible() const
435 {
436 return m_controller->isZoomOutPossible();
437 }
438
439 void DolphinView::setSorting(Sorting sorting)
440 {
441 if (sorting != this->sorting()) {
442 updateSorting(sorting);
443 }
444 }
445
446 DolphinView::Sorting DolphinView::sorting() const
447 {
448 return m_proxyModel->sorting();
449 }
450
451 void DolphinView::setSortOrder(Qt::SortOrder order)
452 {
453 if (sortOrder() != order) {
454 updateSortOrder(order);
455 }
456 }
457
458 Qt::SortOrder DolphinView::sortOrder() const
459 {
460 return m_proxyModel->sortOrder();
461 }
462
463 void DolphinView::setAdditionalInfo(KFileItemDelegate::AdditionalInformation info)
464 {
465 ViewProperties props(m_urlNavigator->url());
466 props.setAdditionalInfo(info);
467
468 m_controller->setShowAdditionalInfo(info != KFileItemDelegate::NoInformation);
469 m_fileItemDelegate->setAdditionalInformation(info);
470
471 emit additionalInfoChanged(info);
472 startDirLister(m_urlNavigator->url(), true);
473 }
474
475 KFileItemDelegate::AdditionalInformation DolphinView::additionalInfo() const
476 {
477 return m_fileItemDelegate->additionalInformation();
478 }
479
480 void DolphinView::goBack()
481 {
482 m_urlNavigator->goBack();
483 }
484
485 void DolphinView::goForward()
486 {
487 m_urlNavigator->goForward();
488 }
489
490 void DolphinView::goUp()
491 {
492 m_urlNavigator->goUp();
493 }
494
495 void DolphinView::goHome()
496 {
497 m_urlNavigator->goHome();
498 }
499
500 void DolphinView::setUrlEditable(bool editable)
501 {
502 m_urlNavigator->setUrlEditable(editable);
503 }
504
505 bool DolphinView::hasSelection() const
506 {
507 return itemView()->selectionModel()->hasSelection();
508 }
509
510 void DolphinView::clearSelection()
511 {
512 itemView()->selectionModel()->clear();
513 }
514
515 KFileItemList DolphinView::selectedItems() const
516 {
517 const QAbstractItemView* view = itemView();
518
519 // Our view has a selection, we will map them back to the DirModel
520 // and then fill the KFileItemList.
521 Q_ASSERT((view != 0) && (view->selectionModel() != 0));
522
523 const QItemSelection selection = m_proxyModel->mapSelectionToSource(view->selectionModel()->selection());
524 KFileItemList itemList;
525
526 const QModelIndexList indexList = selection.indexes();
527 QModelIndexList::const_iterator end = indexList.end();
528 for (QModelIndexList::const_iterator it = indexList.begin(); it != end; ++it) {
529 Q_ASSERT((*it).isValid());
530
531 KFileItem* item = m_dirModel->itemForIndex(*it);
532 if (item != 0) {
533 itemList.append(item);
534 }
535 }
536
537 return itemList;
538 }
539
540 KUrl::List DolphinView::selectedUrls() const
541 {
542 KUrl::List urls;
543
544 const KFileItemList list = selectedItems();
545 KFileItemList::const_iterator it = list.begin();
546 const KFileItemList::const_iterator end = list.end();
547 while (it != end) {
548 KFileItem* item = *it;
549 urls.append(item->url());
550 ++it;
551 }
552
553 return urls;
554 }
555
556 KFileItem* DolphinView::fileItem(const QModelIndex index) const
557 {
558 const QModelIndex dirModelIndex = m_proxyModel->mapToSource(index);
559 return m_dirModel->itemForIndex(dirModelIndex);
560 }
561
562 void DolphinView::rename(const KUrl& source, const QString& newName)
563 {
564 bool ok = false;
565
566 if (newName.isEmpty() || (source.fileName() == newName)) {
567 return;
568 }
569
570 KUrl dest(source.upUrl());
571 dest.addPath(newName);
572
573 const bool destExists = KIO::NetAccess::exists(dest,
574 false,
575 mainWindow()->activeView());
576 if (destExists) {
577 // the destination already exists, hence ask the user
578 // how to proceed...
579 KIO::RenameDialog renameDialog(this,
580 i18n("File Already Exists"),
581 source.path(),
582 dest.path(),
583 KIO::M_OVERWRITE);
584 switch (renameDialog.exec()) {
585 case KIO::R_OVERWRITE:
586 // the destination should be overwritten
587 ok = KIO::NetAccess::file_move(source, dest, -1, true);
588 break;
589
590 case KIO::R_RENAME: {
591 // a new name for the destination has been used
592 KUrl newDest(renameDialog.newDestUrl());
593 ok = KIO::NetAccess::file_move(source, newDest);
594 break;
595 }
596
597 default:
598 // the renaming operation has been canceled
599 return;
600 }
601 } else {
602 // no destination exists, hence just move the file to
603 // do the renaming
604 ok = KIO::NetAccess::file_move(source, dest);
605 }
606
607 const QString destFileName = dest.fileName();
608 if (ok) {
609 m_statusBar->setMessage(i18n("Renamed file '%1' to '%2'.", source.fileName(), destFileName),
610 DolphinStatusBar::OperationCompleted);
611
612 KonqOperations::rename(this, source, destFileName);
613 } else {
614 m_statusBar->setMessage(i18n("Renaming of file '%1' to '%2' failed.", source.fileName(), destFileName),
615 DolphinStatusBar::Error);
616 }
617 }
618
619 void DolphinView::reload()
620 {
621 const KUrl& url = m_urlNavigator->url();
622 changeDirectory(url);
623 startDirLister(url, true);
624 }
625
626 void DolphinView::refresh()
627 {
628 createView();
629 reload();
630 }
631
632 void DolphinView::mouseReleaseEvent(QMouseEvent* event)
633 {
634 QWidget::mouseReleaseEvent(event);
635 mainWindow()->setActiveView(this);
636 }
637
638 DolphinMainWindow* DolphinView::mainWindow() const
639 {
640 return m_mainWindow;
641 }
642
643 void DolphinView::changeDirectory(const KUrl& url)
644 {
645 if (!isActive()) {
646 requestActivation();
647 }
648
649 const ViewProperties props(url);
650
651 const Mode mode = props.viewMode();
652 bool changeMode = (m_mode != mode);
653 if (changeMode && isColumnViewActive()) {
654 // The column view is active. Only change the
655 // mode if the current URL is no child of the column view.
656 if (m_dirLister->url().isParentOf(url)) {
657 changeMode = false;
658 }
659 }
660
661 if (changeMode) {
662 m_mode = mode;
663 createView();
664 emit modeChanged();
665
666 if (m_mode == ColumnView) {
667 // The mode has been changed to the Column View. When starting the dir
668 // lister with DolphinView::startDirLister() it is important to give a
669 // hint that the dir lister may not keep the current directory
670 // although this is the default for showing a hierarchy.
671 m_initializeColumnView = true;
672 }
673 }
674
675 const bool showHiddenFiles = props.showHiddenFiles();
676 if (showHiddenFiles != m_dirLister->showingDotFiles()) {
677 m_dirLister->setShowingDotFiles(showHiddenFiles);
678 emit showHiddenFilesChanged();
679 }
680
681 const bool categorized = props.categorizedSorting();
682 if (categorized != categorizedSorting()) {
683 if (supportsCategorizedSorting()) {
684 Q_ASSERT(m_iconsView != 0);
685 if (categorized) {
686 Q_ASSERT(m_iconsView->itemCategorizer() == 0);
687 m_iconsView->setItemCategorizer(new DolphinItemCategorizer());
688 } else {
689 KItemCategorizer* categorizer = m_iconsView->itemCategorizer();
690 m_iconsView->setItemCategorizer(0);
691 delete categorizer;
692 }
693 }
694 emit categorizedSortingChanged();
695 }
696
697 const DolphinView::Sorting sorting = props.sorting();
698 if (sorting != m_proxyModel->sorting()) {
699 m_proxyModel->setSorting(sorting);
700 emit sortingChanged(sorting);
701 }
702
703 const Qt::SortOrder sortOrder = props.sortOrder();
704 if (sortOrder != m_proxyModel->sortOrder()) {
705 m_proxyModel->setSortOrder(sortOrder);
706 emit sortOrderChanged(sortOrder);
707 }
708
709 KFileItemDelegate::AdditionalInformation info = props.additionalInfo();
710 if (info != m_fileItemDelegate->additionalInformation()) {
711 m_controller->setShowAdditionalInfo(info != KFileItemDelegate::NoInformation);
712 m_fileItemDelegate->setAdditionalInformation(info);
713 emit additionalInfoChanged(info);
714 }
715
716 const bool showPreview = props.showPreview();
717 if (showPreview != m_controller->showPreview()) {
718 m_controller->setShowPreview(showPreview);
719 emit showPreviewChanged();
720 }
721
722 startDirLister(url);
723 emit urlChanged(url);
724
725 m_statusBar->clear();
726 }
727
728 void DolphinView::triggerItem(const QModelIndex& index)
729 {
730 if (!isValidNameIndex(index)) {
731 return;
732 }
733
734 const Qt::KeyboardModifiers modifier = QApplication::keyboardModifiers();
735 if ((modifier & Qt::ShiftModifier) || (modifier & Qt::ControlModifier)) {
736 // items are selected by the user, hence don't trigger the
737 // item specified by 'index'
738 return;
739 }
740
741 KFileItem* item = m_dirModel->itemForIndex(m_proxyModel->mapToSource(index));
742 if (item == 0) {
743 return;
744 }
745
746 // Prefer the local path over the URL. This assures that the
747 // volume space information is correct. Assuming that the URL is media:/sda1,
748 // and the local path is /windows/C: For the URL the space info is related
749 // to the root partition (and hence wrong) and for the local path the space
750 // info is related to the windows partition (-> correct).
751 const QString localPath(item->localPath());
752 KUrl url;
753 if (localPath.isEmpty()) {
754 url = item->url();
755 } else {
756 url = localPath;
757 }
758
759 if (item->isDir()) {
760 setUrl(url);
761 } else if (item->isFile()) {
762 // allow to browse through ZIP and tar files
763 KMimeType::Ptr mime = item->mimeTypePtr();
764 if (mime->is("application/zip")) {
765 url.setProtocol("zip");
766 setUrl(url);
767 } else if (mime->is("application/x-tar") ||
768 mime->is("application/x-tarz") ||
769 mime->is("application/x-bzip-compressed-tar") ||
770 mime->is("application/x-compressed-tar") ||
771 mime->is("application/x-tzo")) {
772 url.setProtocol("tar");
773 setUrl(url);
774 } else {
775 item->run();
776 }
777 } else {
778 item->run();
779 }
780 }
781
782 void DolphinView::updateProgress(int percent)
783 {
784 if (m_showProgress) {
785 m_statusBar->setProgress(percent);
786 }
787 }
788
789 void DolphinView::updateItemCount()
790 {
791 if (m_showProgress) {
792 m_statusBar->setProgressText(QString());
793 m_statusBar->setProgress(100);
794 m_showProgress = false;
795 }
796
797 KFileItemList items(m_dirLister->items());
798 KFileItemList::const_iterator it = items.begin();
799 const KFileItemList::const_iterator end = items.end();
800
801 m_fileCount = 0;
802 m_folderCount = 0;
803
804 while (it != end) {
805 KFileItem* item = *it;
806 if (item->isDir()) {
807 ++m_folderCount;
808 } else {
809 ++m_fileCount;
810 }
811 ++it;
812 }
813
814 updateStatusBar();
815
816 m_blockContentsMovedSignal = false;
817 QTimer::singleShot(0, this, SLOT(restoreContentsPos()));
818 }
819
820 void DolphinView::generatePreviews(const KFileItemList& items)
821 {
822 if (m_controller->showPreview()) {
823
824 // Must turn QList<KFileItem *> to QList<KFileItem>...
825 QList<KFileItem> itemsToPreview;
826 foreach( KFileItem* it, items )
827 itemsToPreview.append( *it );
828
829 KIO::PreviewJob* job = KIO::filePreview(itemsToPreview, 128);
830 connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)),
831 this, SLOT(showPreview(const KFileItem&, const QPixmap&)));
832 }
833 }
834
835 void DolphinView::showPreview(const KFileItem& item, const QPixmap& pixmap)
836 {
837 Q_ASSERT(!item.isNull());
838 if (item.url().directory() != m_dirLister->url().path()) {
839 // the preview job is still working on items of an older URL, hence
840 // the item is not part of the directory model anymore
841 return;
842 }
843
844 const QModelIndex idx = m_dirModel->indexForItem(item);
845 if (idx.isValid() && (idx.column() == 0)) {
846 const QMimeData* mimeData = QApplication::clipboard()->mimeData();
847 if (KonqMimeData::decodeIsCutSelection(mimeData) && isCutItem(item)) {
848 KIconEffect iconEffect;
849 const QPixmap cutPixmap = iconEffect.apply(pixmap, K3Icon::Desktop, K3Icon::DisabledState);
850 m_dirModel->setData(idx, QIcon(cutPixmap), Qt::DecorationRole);
851 } else {
852 m_dirModel->setData(idx, QIcon(pixmap), Qt::DecorationRole);
853 }
854 }
855 }
856
857 void DolphinView::restoreContentsPos()
858 {
859 KUrl currentUrl = m_urlNavigator->url();
860 if (!currentUrl.isEmpty()) {
861 QAbstractItemView* view = itemView();
862 // TODO: view->setCurrentItem(m_urlNavigator->currentFileName());
863 QPoint pos = m_urlNavigator->savedPosition();
864 view->horizontalScrollBar()->setValue(pos.x());
865 view->verticalScrollBar()->setValue(pos.y());
866 }
867 }
868
869 void DolphinView::showInfoMessage(const QString& msg)
870 {
871 m_statusBar->setMessage(msg, DolphinStatusBar::Information);
872 }
873
874 void DolphinView::showErrorMessage(const QString& msg)
875 {
876 m_statusBar->setMessage(msg, DolphinStatusBar::Error);
877 }
878
879 void DolphinView::emitSelectionChangedSignal()
880 {
881 emit selectionChanged(DolphinView::selectedItems());
882 }
883
884 void DolphinView::closeFilterBar()
885 {
886 m_filterBar->hide();
887 emit showFilterBarChanged(false);
888 }
889
890 void DolphinView::startDirLister(const KUrl& url, bool reload)
891 {
892 if (!url.isValid()) {
893 const QString location(url.pathOrUrl());
894 if (location.isEmpty()) {
895 m_statusBar->setMessage(i18n("The location is empty."), DolphinStatusBar::Error);
896 } else {
897 m_statusBar->setMessage(i18n("The location '%1' is invalid.", location),
898 DolphinStatusBar::Error);
899 }
900 return;
901 }
902
903 // Only show the directory loading progress if the status bar does
904 // not contain another progress information. This means that
905 // the directory loading progress information has the lowest priority.
906 const QString progressText(m_statusBar->progressText());
907 m_showProgress = progressText.isEmpty() ||
908 (progressText == i18n("Loading folder..."));
909 if (m_showProgress) {
910 m_statusBar->setProgressText(i18n("Loading folder..."));
911 m_statusBar->setProgress(0);
912 }
913
914 m_cutItemsCache.clear();
915 m_blockContentsMovedSignal = true;
916 m_dirLister->stop();
917
918 bool openDir = true;
919 bool keepOldDirs = isColumnViewActive() && !m_initializeColumnView;
920 m_initializeColumnView = false;
921
922 if (keepOldDirs) {
923 if (reload) {
924 keepOldDirs = false;
925
926 const KUrl& dirListerUrl = m_dirLister->url();
927 if (dirListerUrl.isValid()) {
928 const KUrl::List dirs = m_dirLister->directories();
929 KUrl url;
930 foreach(url, dirs) {
931 m_dirLister->updateDirectory(url);
932 }
933 openDir = false;
934 }
935 } else if (m_dirLister->directories().contains(url)) {
936 // The dir lister contains the directory already, so
937 // KDirLister::openUrl() may not been invoked twice.
938 m_dirLister->updateDirectory(url);
939 openDir = false;
940 } else {
941 const KUrl& dirListerUrl = m_dirLister->url();
942 if ((dirListerUrl == url) || !m_dirLister->url().isParentOf(url)) {
943 // The current URL is not a child of the dir lister
944 // URL. This may happen when e. g. a bookmark has been selected
945 // and hence the view must be reset.
946 keepOldDirs = false;
947 }
948 }
949 }
950
951 if (openDir) {
952 m_dirLister->openUrl(url, keepOldDirs, reload);
953 }
954 }
955
956 QString DolphinView::defaultStatusBarText() const
957 {
958 return KIO::itemsSummaryString(m_fileCount + m_folderCount,
959 m_fileCount,
960 m_folderCount,
961 0, false);
962 }
963
964 QString DolphinView::selectionStatusBarText() const
965 {
966 QString text;
967 const KFileItemList list = selectedItems();
968 if (list.isEmpty()) {
969 // when an item is triggered, it is temporary selected but selectedItems()
970 // will return an empty list
971 return QString();
972 }
973
974 int fileCount = 0;
975 int folderCount = 0;
976 KIO::filesize_t byteSize = 0;
977 KFileItemList::const_iterator it = list.begin();
978 const KFileItemList::const_iterator end = list.end();
979 while (it != end) {
980 KFileItem* item = *it;
981 if (item->isDir()) {
982 ++folderCount;
983 } else {
984 ++fileCount;
985 byteSize += item->size();
986 }
987 ++it;
988 }
989
990 if (folderCount > 0) {
991 text = i18np("1 Folder selected", "%1 Folders selected", folderCount);
992 if (fileCount > 0) {
993 text += ", ";
994 }
995 }
996
997 if (fileCount > 0) {
998 const QString sizeText(KIO::convertSize(byteSize));
999 text += i18np("1 File selected (%2)", "%1 Files selected (%2)", fileCount, sizeText);
1000 }
1001
1002 return text;
1003 }
1004
1005 void DolphinView::showFilterBar(bool show)
1006 {
1007 Q_ASSERT(m_filterBar != 0);
1008 if (show) {
1009 m_filterBar->show();
1010 } else {
1011 m_filterBar->hide();
1012 }
1013 }
1014
1015 void DolphinView::updateStatusBar()
1016 {
1017 // As the item count information is less important
1018 // in comparison with other messages, it should only
1019 // be shown if:
1020 // - the status bar is empty or
1021 // - shows already the item count information or
1022 // - shows only a not very important information
1023 // - if any progress is given don't show the item count info at all
1024 const QString msg(m_statusBar->message());
1025 const bool updateStatusBarMsg = (msg.isEmpty() ||
1026 (msg == m_statusBar->defaultText()) ||
1027 (m_statusBar->type() == DolphinStatusBar::Information)) &&
1028 (m_statusBar->progress() == 100);
1029
1030 const QString text(hasSelection() ? selectionStatusBarText() : defaultStatusBarText());
1031 m_statusBar->setDefaultText(text);
1032
1033 if (updateStatusBarMsg) {
1034 m_statusBar->setMessage(text, DolphinStatusBar::Default);
1035 }
1036 }
1037
1038 void DolphinView::requestActivation()
1039 {
1040 m_mainWindow->setActiveView(this);
1041 }
1042
1043 void DolphinView::changeSelection(const KFileItemList& selection)
1044 {
1045 clearSelection();
1046 if (selection.isEmpty()) {
1047 return;
1048 }
1049 KUrl baseUrl = url();
1050 KUrl url;
1051 QItemSelection new_selection;
1052 foreach(KFileItem* item, selection) {
1053 url = item->url().upUrl();
1054 if (baseUrl.equals(url, KUrl::CompareWithoutTrailingSlash)) {
1055 QModelIndex index = m_proxyModel->mapFromSource(m_dirModel->indexForItem(*item));
1056 new_selection.select(index, index);
1057 }
1058 }
1059 itemView()->selectionModel()->select(new_selection,
1060 QItemSelectionModel::ClearAndSelect
1061 | QItemSelectionModel::Current);
1062 }
1063
1064 void DolphinView::changeNameFilter(const QString& nameFilter)
1065 {
1066 // The name filter of KDirLister does a 'hard' filtering, which
1067 // means that only the items are shown where the names match
1068 // exactly the filter. This is non-transparent for the user, which
1069 // just wants to have a 'soft' filtering: does the name contain
1070 // the filter string?
1071 QString adjustedFilter(nameFilter);
1072 adjustedFilter.insert(0, '*');
1073 adjustedFilter.append('*');
1074
1075 // Use the ProxyModel to filter:
1076 // This code is #ifdefed as setNameFilter behaves
1077 // slightly different than the QSortFilterProxyModel
1078 // as it will not remove directories. I will ask
1079 // our beloved usability experts for input
1080 // -- z.
1081 #if 0
1082 m_dirLister->setNameFilter(adjustedFilter);
1083 m_dirLister->emitChanges();
1084 #else
1085 m_proxyModel->setFilterRegExp(nameFilter);
1086 #endif
1087 }
1088
1089 void DolphinView::openContextMenu(const QPoint& pos)
1090 {
1091 KFileItem* item = 0;
1092
1093 const QModelIndex index = itemView()->indexAt(pos);
1094 if (isValidNameIndex(index)) {
1095 item = fileItem(index);
1096 }
1097
1098 DolphinContextMenu contextMenu(m_mainWindow, item, url());
1099 contextMenu.open();
1100 }
1101
1102 void DolphinView::dropUrls(const KUrl::List& urls,
1103 const QModelIndex& index,
1104 QWidget* source)
1105 {
1106 KFileItem* directory = 0;
1107 if (isValidNameIndex(index)) {
1108 KFileItem* item = fileItem(index);
1109 Q_ASSERT(item != 0);
1110 if (item->isDir()) {
1111 // the URLs are dropped above a directory
1112 directory = item;
1113 }
1114 }
1115
1116 if ((directory == 0) && (source == itemView())) {
1117 // The dropping is done into the same viewport where
1118 // the dragging has been started. Just ignore this...
1119 return;
1120 }
1121
1122 const KUrl& destination = (directory == 0) ? url() :
1123 directory->url();
1124 dropUrls(urls, destination);
1125 }
1126
1127 void DolphinView::dropUrls(const KUrl::List& urls,
1128 const KUrl& destination)
1129 {
1130 m_mainWindow->dropUrls(urls, destination);
1131 }
1132
1133 void DolphinView::updateSorting(DolphinView::Sorting sorting)
1134 {
1135 ViewProperties props(url());
1136 props.setSorting(sorting);
1137
1138 m_proxyModel->setSorting(sorting);
1139
1140 emit sortingChanged(sorting);
1141 }
1142
1143 void DolphinView::updateSortOrder(Qt::SortOrder order)
1144 {
1145 ViewProperties props(url());
1146 props.setSortOrder(order);
1147
1148 m_proxyModel->setSortOrder(order);
1149
1150 emit sortOrderChanged(order);
1151 }
1152
1153 void DolphinView::emitContentsMoved()
1154 {
1155 if (!m_blockContentsMovedSignal) {
1156 emit contentsMoved(contentsX(), contentsY());
1157 }
1158 }
1159
1160 void DolphinView::updateActivationState()
1161 {
1162 m_urlNavigator->setActive(isActive());
1163
1164 QColor color = KGlobalSettings::baseColor();
1165 if (isActive()) {
1166 emit urlChanged(url());
1167 emit selectionChanged(selectedItems());
1168 } else {
1169 // darken the background if the view is inactive
1170 // TODO: does not work for a black background
1171 color = color.darker(105);
1172 }
1173
1174 QWidget* viewport = itemView()->viewport();
1175 QPalette palette;
1176 palette.setColor(viewport->backgroundRole(), color);
1177 viewport->setPalette(palette);
1178
1179 update();
1180 }
1181
1182 void DolphinView::updateCutItems()
1183 {
1184 // restore the icons of all previously selected items to the
1185 // original state...
1186 QList<CutItem>::const_iterator it = m_cutItemsCache.begin();
1187 QList<CutItem>::const_iterator end = m_cutItemsCache.end();
1188 while (it != end) {
1189 const QModelIndex index = m_dirModel->indexForUrl((*it).url);
1190 if (index.isValid()) {
1191 m_dirModel->setData(index, QIcon((*it).pixmap), Qt::DecorationRole);
1192 }
1193 ++it;
1194 }
1195 m_cutItemsCache.clear();
1196
1197 // ... and apply an item effect to all currently cut items
1198 applyCutItemEffect();
1199 }
1200
1201 void DolphinView::showHoverInformation(const QModelIndex& index)
1202 {
1203 if (hasSelection()) {
1204 return;
1205 }
1206
1207 const KFileItem* item = fileItem(index);
1208 if (item != 0) {
1209 m_statusBar->setMessage(item->getStatusBarInfo(), DolphinStatusBar::Default);
1210 emit requestItemInfo(item->url());
1211 }
1212 }
1213
1214 void DolphinView::clearHoverInformation()
1215 {
1216 m_statusBar->clear();
1217 }
1218
1219
1220 void DolphinView::createView()
1221 {
1222 // delete current view
1223 QAbstractItemView* view = itemView();
1224 if (view != 0) {
1225 m_topLayout->removeWidget(view);
1226 view->close();
1227 if (view == m_iconsView) {
1228 KItemCategorizer* categorizer = m_iconsView->itemCategorizer();
1229 m_iconsView->setItemCategorizer(0);
1230 delete categorizer;
1231 }
1232 view->deleteLater();
1233 view = 0;
1234 m_iconsView = 0;
1235 m_detailsView = 0;
1236 m_columnView = 0;
1237 m_fileItemDelegate = 0;
1238 }
1239
1240 Q_ASSERT(m_iconsView == 0);
1241 Q_ASSERT(m_detailsView == 0);
1242 Q_ASSERT(m_columnView == 0);
1243
1244 // ... and recreate it representing the current mode
1245 switch (m_mode) {
1246 case IconsView:
1247 m_iconsView = new DolphinIconsView(this, m_controller);
1248 view = m_iconsView;
1249 break;
1250
1251 case DetailsView:
1252 m_detailsView = new DolphinDetailsView(this, m_controller);
1253 view = m_detailsView;
1254 break;
1255
1256 case ColumnView:
1257 m_columnView = new DolphinColumnView(this, m_controller);
1258 view = m_columnView;
1259 break;
1260 }
1261
1262 Q_ASSERT(view != 0);
1263
1264 m_fileItemDelegate = new KFileItemDelegate(view);
1265 view->setItemDelegate(m_fileItemDelegate);
1266
1267 view->setModel(m_proxyModel);
1268 view->setSelectionMode(QAbstractItemView::ExtendedSelection);
1269
1270 new KMimeTypeResolver(view, m_dirModel);
1271 m_topLayout->insertWidget(1, view);
1272
1273 connect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
1274 m_controller, SLOT(indicateSelectionChange()));
1275 connect(view->verticalScrollBar(), SIGNAL(valueChanged(int)),
1276 this, SLOT(emitContentsMoved()));
1277 connect(view->horizontalScrollBar(), SIGNAL(valueChanged(int)),
1278 this, SLOT(emitContentsMoved()));
1279 }
1280
1281 void DolphinView::selectAll(QItemSelectionModel::SelectionFlags flags)
1282 {
1283 QItemSelectionModel* selectionModel = itemView()->selectionModel();
1284 const QAbstractItemModel* itemModel = selectionModel->model();
1285
1286 const QModelIndex topLeft = itemModel->index(0, 0);
1287 const QModelIndex bottomRight = itemModel->index(itemModel->rowCount() - 1,
1288 itemModel->columnCount() - 1);
1289
1290 QItemSelection selection(topLeft, bottomRight);
1291 selectionModel->select(selection, flags);
1292 }
1293
1294 QAbstractItemView* DolphinView::itemView() const
1295 {
1296 if (m_detailsView != 0) {
1297 return m_detailsView;
1298 } else if (m_columnView != 0) {
1299 return m_columnView;
1300 }
1301
1302 return m_iconsView;
1303 }
1304
1305 bool DolphinView::isValidNameIndex(const QModelIndex& index) const
1306 {
1307 return index.isValid() && (index.column() == KDirModel::Name);
1308 }
1309
1310 bool DolphinView::isCutItem(const KFileItem& item) const
1311 {
1312 const QMimeData* mimeData = QApplication::clipboard()->mimeData();
1313 const KUrl::List cutUrls = KUrl::List::fromMimeData(mimeData);
1314
1315 const KUrl& itemUrl = item.url();
1316 KUrl::List::const_iterator it = cutUrls.begin();
1317 const KUrl::List::const_iterator end = cutUrls.end();
1318 while (it != end) {
1319 if (*it == itemUrl) {
1320 return true;
1321 }
1322 ++it;
1323 }
1324
1325 return false;
1326 }
1327
1328 void DolphinView::applyCutItemEffect()
1329 {
1330 const QMimeData* mimeData = QApplication::clipboard()->mimeData();
1331 if (!KonqMimeData::decodeIsCutSelection(mimeData)) {
1332 return;
1333 }
1334
1335 KFileItemList items(m_dirLister->items());
1336 KFileItemList::const_iterator it = items.begin();
1337 const KFileItemList::const_iterator end = items.end();
1338 while (it != end) {
1339 KFileItem* item = *it;
1340 if (isCutItem(*item)) {
1341 const QModelIndex index = m_dirModel->indexForItem(*item);
1342 const KFileItem* item = m_dirModel->itemForIndex(index);
1343 const QVariant value = m_dirModel->data(index, Qt::DecorationRole);
1344 if ((value.type() == QVariant::Icon) && (item != 0)) {
1345 const QIcon icon(qvariant_cast<QIcon>(value));
1346 QPixmap pixmap = icon.pixmap(128, 128);
1347
1348 // remember current pixmap for the item to be able
1349 // to restore it when other items get cut
1350 CutItem cutItem;
1351 cutItem.url = item->url();
1352 cutItem.pixmap = pixmap;
1353 m_cutItemsCache.append(cutItem);
1354
1355 // apply icon effect to the cut item
1356 KIconEffect iconEffect;
1357 pixmap = iconEffect.apply(pixmap, K3Icon::Desktop, K3Icon::DisabledState);
1358 m_dirModel->setData(index, QIcon(pixmap), Qt::DecorationRole);
1359 }
1360 }
1361 ++it;
1362 }
1363 }
1364
1365 #include "dolphinview.moc"