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