┌   ┐
54
└   ┘

summaryrefslogtreecommitdiff
path: root/src/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/views')
-rw-r--r--src/views/dolphincategorydrawer.cpp378
-rw-r--r--src/views/dolphincategorydrawer.h85
-rw-r--r--src/views/dolphincolumnview.cpp466
-rw-r--r--src/views/dolphincolumnview.h170
-rw-r--r--src/views/dolphincolumnviewcontainer.cpp416
-rw-r--r--src/views/dolphincolumnviewcontainer.h147
-rw-r--r--src/views/dolphindetailsview.cpp1113
-rw-r--r--src/views/dolphindetailsview.h287
-rw-r--r--src/views/dolphindetailsviewexpander.cpp87
-rw-r--r--src/views/dolphindetailsviewexpander.h77
-rw-r--r--src/views/dolphinfileitemdelegate.cpp180
-rw-r--r--src/views/dolphinfileitemdelegate.h90
-rw-r--r--src/views/dolphiniconsview.cpp532
-rw-r--r--src/views/dolphiniconsview.h122
-rw-r--r--src/views/dolphinview.cpp1569
-rw-r--r--src/views/dolphinview.h818
-rw-r--r--src/views/dolphinviewautoscroller.cpp223
-rw-r--r--src/views/dolphinviewautoscroller.h79
-rw-r--r--src/views/dolphinviewcontroller.cpp249
-rw-r--r--src/views/dolphinviewcontroller.h314
-rw-r--r--src/views/selectionmanager.cpp186
-rw-r--r--src/views/selectionmanager.h74
-rw-r--r--src/views/selectiontoggle.cpp233
-rw-r--r--src/views/selectiontoggle.h91
-rw-r--r--src/views/viewextensionsfactory.cpp244
-rw-r--r--src/views/viewextensionsfactory.h105
-rw-r--r--src/views/viewmodecontroller.cpp88
-rw-r--r--src/views/viewmodecontroller.h124
-rw-r--r--src/views/zoomlevelinfo.cpp60
-rw-r--r--src/views/zoomlevelinfo.h51
30 files changed, 8658 insertions, 0 deletions
diff --git a/src/views/dolphincategorydrawer.cpp b/src/views/dolphincategorydrawer.cpp
new file mode 100644
index 000000000..59743b7f5
--- /dev/null
+++ b/src/views/dolphincategorydrawer.cpp
@@ -0,0 +1,378 @@
+/*
+ * This file is part of the KDE project
+ * Copyright (C) 2007 Rafael Fernández López <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "dolphincategorydrawer.h"
+
+#include <config-nepomuk.h>
+
+#include <QPainter>
+#include <QFile>
+#include <QDir>
+#include <QApplication>
+#include <QStyleOption>
+
+#ifdef HAVE_NEPOMUK
+#include <nepomuk/kratingpainter.h>
+#endif
+
+#include <kiconloader.h>
+#include <kiconeffect.h>
+#include <kcategorizedsortfilterproxymodel.h>
+#include <qimageblitz.h>
+#include <kuser.h>
+#include <kcategorizedview.h>
+
+#include "dolphinview.h"
+#include "dolphinmodel.h"
+
+#define HORIZONTAL_HINT 3
+
+DolphinCategoryDrawer::DolphinCategoryDrawer(KCategorizedView *view)
+ : KCategoryDrawerV3(view)
+ , hotSpotPressed(NoneHotSpot)
+ , selectAll(KIconLoader::global()->loadIcon("list-add", KIconLoader::Desktop, 16))
+ , selectAllHovered(KIconLoader::global()->iconEffect()->apply(selectAll, KIconLoader::Desktop, KIconLoader::ActiveState))
+ , selectAllDisabled(KIconLoader::global()->iconEffect()->apply(selectAll, KIconLoader::Desktop, KIconLoader::DisabledState))
+ , unselectAll(KIconLoader::global()->loadIcon("list-remove", KIconLoader::Desktop, 16))
+ , unselectAllHovered(KIconLoader::global()->iconEffect()->apply(unselectAll, KIconLoader::Desktop, KIconLoader::ActiveState))
+ , unselectAllDisabled(KIconLoader::global()->iconEffect()->apply(unselectAll, KIconLoader::Desktop, KIconLoader::DisabledState))
+{
+}
+
+DolphinCategoryDrawer::~DolphinCategoryDrawer()
+{
+}
+
+bool DolphinCategoryDrawer::allCategorySelected(const QString &category) const
+{
+ const QModelIndexList list = view()->block(category);
+ foreach (const QModelIndex &index, list) {
+ if (!view()->selectionModel()->isSelected(index)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DolphinCategoryDrawer::someCategorySelected(const QString &category) const
+{
+ const QModelIndexList list = view()->block(category);
+ foreach (const QModelIndex &index, list) {
+ if (view()->selectionModel()->isSelected(index)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void DolphinCategoryDrawer::drawCategory(const QModelIndex &index, int sortRole,
+ const QStyleOption &option, QPainter *painter) const
+{
+ Q_UNUSED(sortRole);
+ painter->setRenderHint(QPainter::Antialiasing);
+
+ if (!index.isValid()) {
+ return;
+ }
+
+ const QString category = index.model()->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
+ const QRect optRect = option.rect;
+ QFont font(QApplication::font());
+ font.setBold(true);
+ const QFontMetrics fontMetrics = QFontMetrics(font);
+
+ QColor outlineColor = option.palette.text().color();
+ outlineColor.setAlphaF(0.35);
+
+ //BEGIN: top left corner
+ {
+ painter->save();
+ painter->setPen(outlineColor);
+ const QPointF topLeft(optRect.topLeft());
+ QRectF arc(topLeft, QSizeF(4, 4));
+ arc.translate(0.5, 0.5);
+ painter->drawArc(arc, 1440, 1440);
+ painter->restore();
+ }
+ //END: top left corner
+
+ //BEGIN: left vertical line
+ {
+ QPoint start(optRect.topLeft());
+ start.ry() += 3;
+ QPoint verticalGradBottom(optRect.topLeft());
+ verticalGradBottom.ry() += fontMetrics.height() + 5;
+ QLinearGradient gradient(start, verticalGradBottom);
+ gradient.setColorAt(0, outlineColor);
+ gradient.setColorAt(1, Qt::transparent);
+ painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient);
+ }
+ //END: left vertical line
+
+ //BEGIN: horizontal line
+ {
+ QPoint start(optRect.topLeft());
+ start.rx() += 3;
+ QPoint horizontalGradTop(optRect.topLeft());
+ horizontalGradTop.rx() += optRect.width() - 6;
+ painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor);
+ }
+ //END: horizontal line
+
+ //BEGIN: top right corner
+ {
+ painter->save();
+ painter->setPen(outlineColor);
+ QPointF topRight(optRect.topRight());
+ topRight.rx() -= 4;
+ QRectF arc(topRight, QSizeF(4, 4));
+ arc.translate(0.5, 0.5);
+ painter->drawArc(arc, 0, 1440);
+ painter->restore();
+ }
+ //END: top right corner
+
+ //BEGIN: right vertical line
+ {
+ QPoint start(optRect.topRight());
+ start.ry() += 3;
+ QPoint verticalGradBottom(optRect.topRight());
+ verticalGradBottom.ry() += fontMetrics.height() + 5;
+ QLinearGradient gradient(start, verticalGradBottom);
+ gradient.setColorAt(0, outlineColor);
+ gradient.setColorAt(1, Qt::transparent);
+ painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient);
+ }
+ //END: right vertical line
+
+ const int iconSize = KIconLoader::global()->currentSize(KIconLoader::Small);
+
+ //BEGIN: select/unselect all
+ {
+ if (this->category == category) {
+ QRect iconAllRect(option.rect);
+ iconAllRect.setTop(iconAllRect.top() + 4);
+ iconAllRect.setLeft(iconAllRect.right() - 16 - 7);
+ iconAllRect.setSize(QSize(iconSize, iconSize));
+ if (!allCategorySelected(category)) {
+ if (iconAllRect.contains(pos)) {
+ painter->drawPixmap(iconAllRect, selectAllHovered);
+ } else {
+ painter->drawPixmap(iconAllRect, selectAll);
+ }
+ } else {
+ painter->drawPixmap(iconAllRect, selectAllDisabled);
+ }
+ QRect iconNoneRect(option.rect);
+ iconNoneRect.setTop(iconNoneRect.top() + 4);
+ iconNoneRect.setLeft(iconNoneRect.right() - 16 * 2 - 7 * 2);
+ iconNoneRect.setSize(QSize(iconSize, iconSize));
+ if (someCategorySelected(category)) {
+ if (iconNoneRect.contains(pos)) {
+ painter->drawPixmap(iconNoneRect, unselectAllHovered);
+ } else {
+ painter->drawPixmap(iconNoneRect, unselectAll);
+ }
+ } else {
+ painter->drawPixmap(iconNoneRect, unselectAllDisabled);
+ }
+ }
+ }
+ //END: select/unselect all
+
+ //BEGIN: category information
+ {
+ bool paintIcon;
+ QPixmap icon;
+ switch (index.column()) {
+ case KDirModel::Owner: {
+ paintIcon = true;
+ KUser user(category);
+ const QString faceIconPath = user.faceIconPath();
+ if (faceIconPath.isEmpty()) {
+ icon = KIconLoader::global()->loadIcon("user-identity", KIconLoader::NoGroup, iconSize);
+ } else {
+ icon = QPixmap::fromImage(QImage(faceIconPath).scaledToHeight(iconSize, Qt::SmoothTransformation));
+ }
+ }
+ break;
+ case KDirModel::Type: {
+ paintIcon = true;
+ const KCategorizedSortFilterProxyModel *proxyModel = static_cast<const KCategorizedSortFilterProxyModel*>(index.model());
+ const DolphinModel *model = static_cast<const DolphinModel*>(proxyModel->sourceModel());
+ KFileItem item = model->itemForIndex(proxyModel->mapToSource(index));
+ // This is the only way of getting the icon right. Others will fail on corner
+ // cases like the item representing this group has been set a different icon,
+ // so the group icon drawn is that one particularly. This way assures the drawn
+ // icon is the one of the mimetype of the group itself. (ereslibre)
+ icon = KIconLoader::global()->loadMimeTypeIcon(item.mimeTypePtr()->iconName(), KIconLoader::NoGroup, iconSize);
+ }
+ break;
+ default:
+ paintIcon = false;
+ }
+
+ if (paintIcon) {
+ QRect iconRect(option.rect);
+ iconRect.setTop(iconRect.top() + 4);
+ iconRect.setLeft(iconRect.left() + 7);
+ iconRect.setSize(QSize(iconSize, iconSize));
+
+ painter->drawPixmap(iconRect, icon);
+ }
+
+ //BEGIN: text
+ {
+ QRect textRect(option.rect);
+ textRect.setTop(textRect.top() + 7);
+ textRect.setLeft(textRect.left() + 7 + (paintIcon ? (iconSize + 6) : 0));
+ textRect.setHeight(qMax(fontMetrics.height(), iconSize));
+ textRect.setRight(textRect.right() - 7);
+ textRect.setBottom(textRect.bottom() - 5); // only one pixel separation here (no gradient)
+
+ painter->save();
+ painter->setFont(font);
+ QColor penColor(option.palette.text().color());
+ penColor.setAlphaF(0.6);
+ painter->setPen(penColor);
+ painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, category);
+ painter->restore();
+ }
+ //END: text
+ }
+ //BEGIN: category information
+}
+
+int DolphinCategoryDrawer::categoryHeight(const QModelIndex &index, const QStyleOption &) const
+{
+ int iconSize = KIconLoader::global()->currentSize(KIconLoader::Small);
+ QFont font(QApplication::font());
+ font.setBold(true);
+ const QFontMetrics fontMetrics = QFontMetrics(font);
+ int heightWithoutIcon = fontMetrics.height() + (iconSize / 4) * 2 + 1; /* 1 pixel-width gradient */
+ bool paintIcon;
+
+ switch (index.column()) {
+ case KDirModel::Owner:
+ case KDirModel::Type:
+ paintIcon = true;
+ break;
+ default:
+ paintIcon = false;
+ }
+
+ if (paintIcon) {
+ return qMax(heightWithoutIcon + 5, iconSize + 1 /* 1 pixel-width gradient */
+ + 5 /* top and bottom separation */);
+ }
+
+ return heightWithoutIcon + 5;
+}
+
+void DolphinCategoryDrawer::mouseButtonPressed(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event)
+{
+ if (!index.isValid()) {
+ event->ignore();
+ return;
+ }
+ const QString category = index.model()->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
+ int iconSize = KIconLoader::global()->currentSize(KIconLoader::Small);
+ if (this->category == category) {
+ QRect iconAllRect(blockRect);
+ iconAllRect.setTop(iconAllRect.top() + 4);
+ iconAllRect.setLeft(iconAllRect.right() - 16 - 7);
+ iconAllRect.setSize(QSize(iconSize, iconSize));
+ if (iconAllRect.contains(pos)) {
+ event->accept();
+ hotSpotPressed = SelectAllHotSpot;
+ categoryPressed = index;
+ return;
+ }
+ QRect iconNoneRect(blockRect);
+ iconNoneRect.setTop(iconNoneRect.top() + 4);
+ iconNoneRect.setLeft(iconNoneRect.right() - 16 * 2 - 7 * 2);
+ iconNoneRect.setSize(QSize(iconSize, iconSize));
+ if (iconNoneRect.contains(pos)) {
+ event->accept();
+ hotSpotPressed = UnselectAllHotSpot;
+ categoryPressed = index;
+ return;
+ }
+ }
+ event->ignore();
+}
+
+void DolphinCategoryDrawer::mouseButtonReleased(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event)
+{
+ if (!index.isValid() || hotSpotPressed == NoneHotSpot || categoryPressed != index) {
+ event->ignore();
+ return;
+ }
+ categoryPressed = QModelIndex();
+ const QString category = index.model()->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
+ int iconSize = KIconLoader::global()->currentSize(KIconLoader::Small);
+ if (this->category == category) {
+ QRect iconAllRect(blockRect);
+ iconAllRect.setTop(iconAllRect.top() + 4);
+ iconAllRect.setLeft(iconAllRect.right() - 16 - 7);
+ iconAllRect.setSize(QSize(iconSize, iconSize));
+ if (iconAllRect.contains(pos)) {
+ if (hotSpotPressed == SelectAllHotSpot) {
+ event->accept();
+ emit actionRequested(SelectAll, index);
+ } else {
+ event->ignore();
+ hotSpotPressed = NoneHotSpot;
+ }
+ return;
+ }
+ QRect iconNoneRect(blockRect);
+ iconNoneRect.setTop(iconNoneRect.top() + 4);
+ iconNoneRect.setLeft(iconNoneRect.right() - 16 * 2 - 7 * 2);
+ iconNoneRect.setSize(QSize(iconSize, iconSize));
+ if (iconNoneRect.contains(pos)) {
+ if (hotSpotPressed == UnselectAllHotSpot) {
+ event->accept();
+ emit actionRequested(UnselectAll, index);
+ } else {
+ event->ignore();
+ hotSpotPressed = NoneHotSpot;
+ }
+ return;
+ }
+ }
+ event->ignore();
+}
+
+void DolphinCategoryDrawer::mouseMoved(const QModelIndex &index, const QRect &, QMouseEvent *event)
+{
+ event->ignore();
+ if (!index.isValid()) {
+ return;
+ }
+ pos = event->pos();
+ category = index.model()->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
+}
+
+void DolphinCategoryDrawer::mouseLeft(const QModelIndex &, const QRect &)
+{
+ pos = QPoint();
+ category = QString();
+}
diff --git a/src/views/dolphincategorydrawer.h b/src/views/dolphincategorydrawer.h
new file mode 100644
index 000000000..d9849727e
--- /dev/null
+++ b/src/views/dolphincategorydrawer.h
@@ -0,0 +1,85 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2007 Rafael Fernández López <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef DOLPHINCATEGORYDRAWER_H
+#define DOLPHINCATEGORYDRAWER_H
+
+#include <kcategorydrawer.h>
+
+#include <QStyleOption>
+#include <QModelIndex>
+
+#include <libdolphin_export.h>
+
+class LIBDOLPHINPRIVATE_EXPORT DolphinCategoryDrawer
+ : public KCategoryDrawerV3
+{
+public:
+ using KCategoryDrawerV2::mouseButtonPressed;
+ using KCategoryDrawerV2::mouseButtonReleased;
+
+ enum Action {
+ SelectAll = 0,
+ UnselectAll
+ };
+
+ DolphinCategoryDrawer(KCategorizedView *view);
+
+ virtual ~DolphinCategoryDrawer();
+
+ bool allCategorySelected(const QString &category) const;
+
+ bool someCategorySelected(const QString &category) const;
+
+ virtual void drawCategory(const QModelIndex &index, int sortRole,
+ const QStyleOption &option, QPainter *painter) const;
+
+ virtual int categoryHeight(const QModelIndex &index, const QStyleOption &option) const;
+
+protected:
+ virtual void mouseButtonPressed(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event);
+
+ virtual void mouseButtonReleased(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event);
+
+ virtual void mouseMoved(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event);
+
+ virtual void mouseLeft(const QModelIndex &index,const QRect &blockRect);
+
+private:
+ enum HotSpot {
+ NoneHotSpot = 0,
+ SelectAllHotSpot,
+ UnselectAllHotSpot
+ };
+
+ HotSpot hotSpotPressed;
+ QModelIndex categoryPressed;
+
+ QPixmap selectAll;
+ QPixmap selectAllHovered;
+ QPixmap selectAllDisabled;
+ QPixmap unselectAll;
+ QPixmap unselectAllHovered;
+ QPixmap unselectAllDisabled;
+
+ QPoint pos;
+ QString category;
+};
+
+#endif // DOLPHINCATEGORYDRAWER_H
diff --git a/src/views/dolphincolumnview.cpp b/src/views/dolphincolumnview.cpp
new file mode 100644
index 000000000..7e2b522b4
--- /dev/null
+++ b/src/views/dolphincolumnview.cpp
@@ -0,0 +1,466 @@
+/***************************************************************************
+ * Copyright (C) 2007-2009 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphincolumnview.h"
+
+#include "dolphinmodel.h"
+#include "dolphincolumnviewcontainer.h"
+#include "dolphinviewcontroller.h"
+#include "dolphindirlister.h"
+#include "dolphinsortfilterproxymodel.h"
+#include "settings/dolphinsettings.h"
+#include "dolphinviewautoscroller.h"
+#include "dolphin_columnmodesettings.h"
+#include "dolphin_generalsettings.h"
+#include "draganddrophelper.h"
+#include "folderexpander.h"
+#include "tooltips/tooltipmanager.h"
+#include "viewextensionsfactory.h"
+#include "viewmodecontroller.h"
+#include "zoomlevelinfo.h"
+
+#include <kcolorscheme.h>
+#include <kdirlister.h>
+#include <kfileitem.h>
+#include <kio/previewjob.h>
+#include <kiconeffect.h>
+#include <kjob.h>
+#include <konqmimedata.h>
+
+#include <QApplication>
+#include <QClipboard>
+#include <QPainter>
+#include <QPoint>
+#include <QScrollBar>
+
+DolphinColumnView::DolphinColumnView(QWidget* parent,
+ DolphinColumnViewContainer* container,
+ const KUrl& url) :
+ QListView(parent),
+ m_active(false),
+ m_container(container),
+ m_extensionsFactory(0),
+ m_url(url),
+ m_childUrl(),
+ m_font(),
+ m_decorationSize(),
+ m_dirLister(0),
+ m_dolphinModel(0),
+ m_proxyModel(0),
+ m_dropRect()
+{
+ setMouseTracking(true);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ setSelectionBehavior(SelectItems);
+ setSelectionMode(QAbstractItemView::ExtendedSelection);
+ setDragDropMode(QAbstractItemView::DragDrop);
+ setDropIndicatorShown(false);
+ setSelectionRectVisible(true);
+ setEditTriggers(QAbstractItemView::NoEditTriggers);
+
+ setVerticalScrollMode(QListView::ScrollPerPixel);
+ setHorizontalScrollMode(QListView::ScrollPerPixel);
+
+ // apply the column mode settings to the widget
+ const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
+ Q_ASSERT(settings != 0);
+
+ if (settings->useSystemFont()) {
+ m_font = KGlobalSettings::generalFont();
+ } else {
+ m_font = QFont(settings->fontFamily(),
+ qRound(settings->fontSize()),
+ settings->fontWeight(),
+ settings->italicFont());
+ m_font.setPointSizeF(settings->fontSize());
+ }
+
+ connect(this, SIGNAL(viewportEntered()),
+ m_container->m_dolphinViewController, SLOT(emitViewportEntered()));
+ connect(this, SIGNAL(entered(const QModelIndex&)),
+ this, SLOT(slotEntered(const QModelIndex&)));
+
+ const DolphinView* dolphinView = m_container->m_dolphinViewController->view();
+ connect(dolphinView, SIGNAL(showPreviewChanged()),
+ this, SLOT(slotShowPreviewChanged()));
+
+ m_dirLister = new DolphinDirLister();
+ m_dirLister->setAutoUpdate(true);
+ m_dirLister->setMainWindow(window());
+ m_dirLister->setDelayedMimeTypes(true);
+ const bool showHiddenFiles = m_container->m_dolphinViewController->view()->showHiddenFiles();
+ m_dirLister->setShowingDotFiles(showHiddenFiles);
+
+ m_dolphinModel = new DolphinModel(this);
+ m_dolphinModel->setDirLister(m_dirLister);
+ m_dolphinModel->setDropsAllowed(DolphinModel::DropOnDirectory);
+
+ m_proxyModel = new DolphinSortFilterProxyModel(this);
+ m_proxyModel->setSourceModel(m_dolphinModel);
+ m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+
+ m_proxyModel->setSorting(dolphinView->sorting());
+ m_proxyModel->setSortOrder(dolphinView->sortOrder());
+ m_proxyModel->setSortFoldersFirst(dolphinView->sortFoldersFirst());
+
+ setModel(m_proxyModel);
+
+ connect(KGlobalSettings::self(), SIGNAL(kdisplayFontChanged()),
+ this, SLOT(updateFont()));
+
+ const ViewModeController* viewModeController = m_container->m_viewModeController;
+ connect(viewModeController, SIGNAL(zoomLevelChanged(int)),
+ this, SLOT(setZoomLevel(int)));
+ const QString nameFilter = viewModeController->nameFilter();
+ if (!nameFilter.isEmpty()) {
+ m_proxyModel->setFilterFixedString(nameFilter);
+ }
+
+ updateDecorationSize(dolphinView->showPreview());
+ updateBackground();
+
+ DolphinViewController* dolphinViewController = m_container->m_dolphinViewController;
+ m_extensionsFactory = new ViewExtensionsFactory(this, dolphinViewController, viewModeController);
+
+ m_dirLister->openUrl(url, KDirLister::NoFlags);
+}
+
+DolphinColumnView::~DolphinColumnView()
+{
+ delete m_proxyModel;
+ m_proxyModel = 0;
+ delete m_dolphinModel;
+ m_dolphinModel = 0;
+ m_dirLister = 0; // deleted by m_dolphinModel
+}
+
+void DolphinColumnView::setActive(bool active)
+{
+ if (m_active != active) {
+ m_active = active;
+
+ if (active) {
+ activate();
+ } else {
+ deactivate();
+ }
+ }
+}
+
+void DolphinColumnView::updateBackground()
+{
+ // TODO: The alpha-value 150 is copied from DolphinView::setActive(). When
+ // cleaning up the cut-indication of DolphinColumnView with the code from
+ // DolphinView a common helper-class should be available which can be shared
+ // by all view implementations -> no hardcoded value anymore
+ const QPalette::ColorRole role = viewport()->backgroundRole();
+ QColor color = viewport()->palette().color(role);
+ color.setAlpha((m_active && m_container->m_active) ? 255 : 150);
+
+ QPalette palette = viewport()->palette();
+ palette.setColor(role, color);
+ viewport()->setPalette(palette);
+
+ update();
+}
+
+KFileItem DolphinColumnView::itemAt(const QPoint& pos) const
+{
+ KFileItem item;
+ const QModelIndex index = indexAt(pos);
+ if (index.isValid() && (index.column() == DolphinModel::Name)) {
+ const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
+ item = m_dolphinModel->itemForIndex(dolphinModelIndex);
+ }
+ return item;
+}
+
+void DolphinColumnView::setSelectionModel(QItemSelectionModel* model)
+{
+ // If a change of the selection is done although the view is not active
+ // (e. g. by the selection markers), the column must be activated. This
+ // is done by listening to the current selectionChanged() signal.
+ if (selectionModel() != 0) {
+ disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+ this, SLOT(requestActivation()));
+ }
+
+ QListView::setSelectionModel(model);
+
+ connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+ this, SLOT(requestActivation()));
+}
+
+QStyleOptionViewItem DolphinColumnView::viewOptions() const
+{
+ QStyleOptionViewItem viewOptions = QListView::viewOptions();
+ viewOptions.font = m_font;
+ viewOptions.fontMetrics = QFontMetrics(m_font);
+ viewOptions.decorationSize = m_decorationSize;
+ viewOptions.showDecorationSelected = true;
+ return viewOptions;
+}
+
+void DolphinColumnView::startDrag(Qt::DropActions supportedActions)
+{
+ DragAndDropHelper::instance().startDrag(this, supportedActions, m_container->m_dolphinViewController);
+}
+
+void DolphinColumnView::dragEnterEvent(QDragEnterEvent* event)
+{
+ if (DragAndDropHelper::instance().isMimeDataSupported(event->mimeData())) {
+ event->acceptProposedAction();
+ requestActivation();
+ }
+}
+
+void DolphinColumnView::dragLeaveEvent(QDragLeaveEvent* event)
+{
+ QListView::dragLeaveEvent(event);
+ setDirtyRegion(m_dropRect);
+}
+
+void DolphinColumnView::dragMoveEvent(QDragMoveEvent* event)
+{
+ QListView::dragMoveEvent(event);
+
+ // TODO: remove this code when the issue #160611 is solved in Qt 4.4
+ const QModelIndex index = indexAt(event->pos());
+ setDirtyRegion(m_dropRect);
+
+ m_dropRect.setSize(QSize()); // set as invalid
+ if (index.isValid()) {
+ m_container->m_dolphinViewController->setItemView(this);
+ const KFileItem item = m_container->m_dolphinViewController->itemForIndex(index);
+ if (!item.isNull() && item.isDir()) {
+ m_dropRect = visualRect(index);
+ }
+ }
+ setDirtyRegion(m_dropRect);
+
+ if (DragAndDropHelper::instance().isMimeDataSupported(event->mimeData())) {
+ // accept url drops, independently from the destination item
+ event->acceptProposedAction();
+ }
+}
+
+void DolphinColumnView::dropEvent(QDropEvent* event)
+{
+ const QModelIndex index = indexAt(event->pos());
+ m_container->m_dolphinViewController->setItemView(this);
+ const KFileItem item = m_container->m_dolphinViewController->itemForIndex(index);
+ m_container->m_dolphinViewController->indicateDroppedUrls(item, url(), event);
+ QListView::dropEvent(event);
+}
+
+void DolphinColumnView::paintEvent(QPaintEvent* event)
+{
+ if (!m_childUrl.isEmpty()) {
+ // indicate the shown URL of the next column by highlighting the shown folder item
+ const QModelIndex dirIndex = m_dolphinModel->indexForUrl(m_childUrl);
+ const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
+ if (proxyIndex.isValid() && !selectionModel()->isSelected(proxyIndex)) {
+ const QRect itemRect = visualRect(proxyIndex);
+ QPainter painter(viewport());
+ QColor color = KColorScheme(QPalette::Active, KColorScheme::View).foreground().color();
+ color.setAlpha(32);
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(color);
+ painter.drawRect(itemRect);
+ }
+ }
+
+ QListView::paintEvent(event);
+}
+
+void DolphinColumnView::mousePressEvent(QMouseEvent* event)
+{
+ requestActivation();
+ if (!indexAt(event->pos()).isValid()) {
+ if (QApplication::mouseButtons() & Qt::MidButton) {
+ m_container->m_dolphinViewController->replaceUrlByClipboard();
+ }
+ } else if (event->button() == Qt::LeftButton) {
+ // TODO: see comment in DolphinIconsView::mousePressEvent()
+ setState(QAbstractItemView::DraggingState);
+ }
+ QListView::mousePressEvent(event);
+}
+
+void DolphinColumnView::keyPressEvent(QKeyEvent* event)
+{
+ QListView::keyPressEvent(event);
+
+ DolphinViewController* controller = m_container->m_dolphinViewController;
+ controller->handleKeyPressEvent(event);
+ switch (event->key()) {
+ case Qt::Key_Right: {
+ // Special key handling for the column: A Key_Right should
+ // open a new column for the currently selected folder.
+ const QModelIndex index = currentIndex();
+ const KFileItem item = controller->itemForIndex(index);
+ if (!item.isNull() && item.isDir()) {
+ controller->emitItemTriggered(item);
+ }
+ break;
+ }
+
+ case Qt::Key_Escape:
+ selectionModel()->setCurrentIndex(selectionModel()->currentIndex(),
+ QItemSelectionModel::Current |
+ QItemSelectionModel::Clear);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void DolphinColumnView::contextMenuEvent(QContextMenuEvent* event)
+{
+ requestActivation();
+ QListView::contextMenuEvent(event);
+ m_container->m_dolphinViewController->triggerContextMenuRequest(event->pos());
+}
+
+void DolphinColumnView::wheelEvent(QWheelEvent* event)
+{
+ const int step = m_decorationSize.height();
+ verticalScrollBar()->setSingleStep(step);
+ QListView::wheelEvent(event);
+}
+
+void DolphinColumnView::leaveEvent(QEvent* event)
+{
+ QListView::leaveEvent(event);
+ // if the mouse is above an item and moved very fast outside the widget,
+ // no viewportEntered() signal might be emitted although the mouse has been moved
+ // above the viewport
+ m_container->m_dolphinViewController->emitViewportEntered();
+}
+
+void DolphinColumnView::currentChanged(const QModelIndex& current, const QModelIndex& previous)
+{
+ QListView::currentChanged(current, previous);
+ m_extensionsFactory->handleCurrentIndexChange(current, previous);
+}
+
+void DolphinColumnView::setZoomLevel(int level)
+{
+ const int size = ZoomLevelInfo::iconSizeForZoomLevel(level);
+ ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
+
+ const bool showPreview = m_container->m_dolphinViewController->view()->showPreview();
+ if (showPreview) {
+ settings->setPreviewSize(size);
+ } else {
+ settings->setIconSize(size);
+ }
+
+ updateDecorationSize(showPreview);
+}
+
+void DolphinColumnView::slotEntered(const QModelIndex& index)
+{
+ m_container->m_dolphinViewController->setItemView(this);
+ m_container->m_dolphinViewController->emitItemEntered(index);
+}
+
+void DolphinColumnView::requestActivation()
+{
+ m_container->m_dolphinViewController->requestActivation();
+ if (!m_active) {
+ m_container->requestActivation(this);
+ selectionModel()->clear();
+ }
+}
+
+void DolphinColumnView::updateFont()
+{
+ const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
+ Q_ASSERT(settings != 0);
+
+ if (settings->useSystemFont()) {
+ m_font = KGlobalSettings::generalFont();
+ }
+}
+
+void DolphinColumnView::slotShowPreviewChanged()
+{
+ const DolphinView* view = m_container->m_dolphinViewController->view();
+ updateDecorationSize(view->showPreview());
+}
+
+void DolphinColumnView::activate()
+{
+ setFocus(Qt::OtherFocusReason);
+
+ if (KGlobalSettings::singleClick()) {
+ connect(this, SIGNAL(clicked(const QModelIndex&)),
+ m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
+ } else {
+ connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
+ m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
+ }
+
+ if (selectionModel() && selectionModel()->currentIndex().isValid()) {
+ selectionModel()->setCurrentIndex(selectionModel()->currentIndex(), QItemSelectionModel::SelectCurrent);
+ }
+
+ updateBackground();
+}
+
+void DolphinColumnView::deactivate()
+{
+ clearFocus();
+ if (KGlobalSettings::singleClick()) {
+ disconnect(this, SIGNAL(clicked(const QModelIndex&)),
+ m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
+ } else {
+ disconnect(this, SIGNAL(doubleClicked(const QModelIndex&)),
+ m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
+ }
+
+ // It is important to disconnect the connection to requestActivation() temporary, otherwise the internal
+ // clearing of the selection would result in activating the column again.
+ disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+ this, SLOT(requestActivation()));
+ const QModelIndex current = selectionModel()->currentIndex();
+ selectionModel()->clear();
+ selectionModel()->setCurrentIndex(current, QItemSelectionModel::NoUpdate);
+ connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+ this, SLOT(requestActivation()));
+
+ updateBackground();
+}
+
+void DolphinColumnView::updateDecorationSize(bool showPreview)
+{
+ ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
+ const int iconSize = showPreview ? settings->previewSize() : settings->iconSize();
+ const QSize size(iconSize, iconSize);
+ setIconSize(size);
+
+ m_decorationSize = size;
+
+ doItemsLayout();
+}
+
+#include "dolphincolumnview.moc"
diff --git a/src/views/dolphincolumnview.h b/src/views/dolphincolumnview.h
new file mode 100644
index 000000000..64feac4f9
--- /dev/null
+++ b/src/views/dolphincolumnview.h
@@ -0,0 +1,170 @@
+/***************************************************************************
+ * Copyright (C) 2007-2009 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHINCOLUMNVIEW_H
+#define DOLPHINCOLUMNVIEW_H
+
+#include "dolphinview.h"
+
+#include <QFont>
+#include <QListView>
+#include <QSize>
+#include <QStyleOption>
+
+#include <kurl.h>
+
+class DolphinColumnViewContainer;
+class DolphinModel;
+class DolphinSortFilterProxyModel;
+class DolphinDirLister;
+class KFileItem;
+class SelectionManager;
+class ViewExtensionsFactory;
+
+/**
+ * Represents one column inside the DolphinColumnViewContainer.
+ */
+class DolphinColumnView : public QListView
+{
+ Q_OBJECT
+
+public:
+ DolphinColumnView(QWidget* parent,
+ DolphinColumnViewContainer* container,
+ const KUrl& url);
+ virtual ~DolphinColumnView();
+
+ /**
+ * An active column is defined as column, which shows the same URL
+ * as indicated by the URL navigator. The active column is usually
+ * drawn in a lighter color. All operations are applied to this column.
+ */
+ void setActive(bool active);
+ bool isActive() const;
+
+ /**
+ * Sets the directory URL of the child column that is shown next to
+ * this column. This property is only used for a visual indication
+ * of the shown directory, it does not trigger a loading of the model.
+ */
+ void setChildUrl(const KUrl& url);
+ KUrl childUrl() const;
+
+ /** Sets the directory URL that is shown inside the column widget. */
+ void setUrl(const KUrl& url);
+
+ /** Returns the directory URL that is shown inside the column widget. */
+ KUrl url() const;
+
+ /**
+ * Updates the background color dependent from the activation state
+ * \a isViewActive of the column view.
+ */
+ void updateBackground();
+
+ /**
+ * Returns the item on the position \a pos. The KFileItem instance
+ * is null if no item is below the position.
+ */
+ KFileItem itemAt(const QPoint& pos) const;
+
+ virtual void setSelectionModel(QItemSelectionModel* model);
+
+protected:
+ virtual QStyleOptionViewItem viewOptions() const;
+ virtual void startDrag(Qt::DropActions supportedActions);
+ virtual void dragEnterEvent(QDragEnterEvent* event);
+ virtual void dragLeaveEvent(QDragLeaveEvent* event);
+ virtual void dragMoveEvent(QDragMoveEvent* event);
+ virtual void dropEvent(QDropEvent* event);
+ virtual void paintEvent(QPaintEvent* event);
+ virtual void mousePressEvent(QMouseEvent* event);
+ virtual void keyPressEvent(QKeyEvent* event);
+ virtual void contextMenuEvent(QContextMenuEvent* event);
+ virtual void wheelEvent(QWheelEvent* event);
+ virtual void leaveEvent(QEvent* event);
+ virtual void currentChanged(const QModelIndex& current, const QModelIndex& previous);
+
+private slots:
+ void setZoomLevel(int level);
+
+ void slotEntered(const QModelIndex& index);
+ void requestActivation();
+ void updateFont();
+
+ void slotShowPreviewChanged();
+
+private:
+ /** Used by DolphinColumnView::setActive(). */
+ void activate();
+
+ /** Used by DolphinColumnView::setActive(). */
+ void deactivate();
+
+ void updateDecorationSize(bool showPreview);
+
+private:
+ bool m_active;
+ DolphinColumnViewContainer* m_container;
+ SelectionManager* m_selectionManager;
+ ViewExtensionsFactory* m_extensionsFactory;
+ KUrl m_url; // URL of the directory that is shown
+ KUrl m_childUrl; // URL of the next column that is shown
+
+ QFont m_font;
+ QSize m_decorationSize;
+
+ DolphinDirLister* m_dirLister;
+ DolphinModel* m_dolphinModel;
+ DolphinSortFilterProxyModel* m_proxyModel;
+
+ QRect m_dropRect;
+
+ friend class DolphinColumnViewContainer;
+};
+
+inline bool DolphinColumnView::isActive() const
+{
+ return m_active;
+}
+
+inline void DolphinColumnView::setChildUrl(const KUrl& url)
+{
+ m_childUrl = url;
+}
+
+inline KUrl DolphinColumnView::childUrl() const
+{
+ return m_childUrl;
+}
+
+inline void DolphinColumnView::setUrl(const KUrl& url)
+{
+ if (url != m_url) {
+ m_url = url;
+ //reload();
+ }
+}
+
+inline KUrl DolphinColumnView::url() const
+{
+ return m_url;
+}
+
+#endif
diff --git a/src/views/dolphincolumnviewcontainer.cpp b/src/views/dolphincolumnviewcontainer.cpp
new file mode 100644
index 000000000..344d38d8a
--- /dev/null
+++ b/src/views/dolphincolumnviewcontainer.cpp
@@ -0,0 +1,416 @@
+/***************************************************************************
+ * Copyright (C) 2007-2009 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphincolumnviewcontainer.h"
+
+#include "dolphin_columnmodesettings.h"
+
+#include "dolphincolumnview.h"
+#include "dolphinviewcontroller.h"
+#include "dolphinsortfilterproxymodel.h"
+#include "draganddrophelper.h"
+#include "settings/dolphinsettings.h"
+#include "viewmodecontroller.h"
+
+#include <QPoint>
+#include <QScrollBar>
+#include <QTimeLine>
+#include <QTimer>
+
+DolphinColumnViewContainer::DolphinColumnViewContainer(QWidget* parent,
+ DolphinViewController* dolphinViewController,
+ const ViewModeController* viewModeController) :
+ QScrollArea(parent),
+ m_dolphinViewController(dolphinViewController),
+ m_viewModeController(viewModeController),
+ m_active(false),
+ m_index(-1),
+ m_contentX(0),
+ m_columns(),
+ m_emptyViewport(0),
+ m_animation(0),
+ m_dragSource(0),
+ m_activeUrlTimer(0)
+{
+ Q_ASSERT(dolphinViewController != 0);
+ Q_ASSERT(viewModeController != 0);
+
+ setAcceptDrops(true);
+ setFocusPolicy(Qt::NoFocus);
+ setFrameShape(QFrame::NoFrame);
+ setLayoutDirection(Qt::LeftToRight);
+
+ connect(viewModeController, SIGNAL(activationChanged(bool)),
+ this, SLOT(updateColumnsBackground(bool)));
+
+ connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(moveContentHorizontally(int)));
+
+ m_animation = new QTimeLine(500, this);
+ connect(m_animation, SIGNAL(frameChanged(int)), horizontalScrollBar(), SLOT(setValue(int)));
+
+ m_activeUrlTimer = new QTimer(this);
+ m_activeUrlTimer->setSingleShot(true);
+ m_activeUrlTimer->setInterval(200);
+ connect(m_activeUrlTimer, SIGNAL(timeout()),
+ this, SLOT(updateActiveUrl()));
+
+ DolphinColumnView* column = new DolphinColumnView(viewport(), this, viewModeController->url());
+ m_columns.append(column);
+ requestActivation(column);
+
+ m_emptyViewport = new QFrame(viewport());
+ m_emptyViewport->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+
+ updateColumnsBackground(true);
+
+}
+
+DolphinColumnViewContainer::~DolphinColumnViewContainer()
+{
+ delete m_dragSource;
+ m_dragSource = 0;
+}
+
+KUrl DolphinColumnViewContainer::rootUrl() const
+{
+ return m_columns[0]->url();
+}
+
+QAbstractItemView* DolphinColumnViewContainer::activeColumn() const
+{
+ return m_columns[m_index];
+}
+
+void DolphinColumnViewContainer::showColumn(const KUrl& url)
+{
+ if (!rootUrl().isParentOf(url)) {
+ removeAllColumns();
+ m_columns[0]->setUrl(url);
+ return;
+ }
+
+ int columnIndex = 0;
+ foreach (DolphinColumnView* column, m_columns) {
+ if (column->url() == url) {
+ // the column represents already the requested URL, hence activate it
+ requestActivation(column);
+ layoutColumns();
+ return;
+ } else if (!column->url().isParentOf(url)) {
+ // the column is no parent of the requested URL, hence
+ // just delete all remaining columns
+ if (columnIndex > 0) {
+ QList<DolphinColumnView*>::iterator start = m_columns.begin() + columnIndex;
+ QList<DolphinColumnView*>::iterator end = m_columns.end();
+ for (QList<DolphinColumnView*>::iterator it = start; it != end; ++it) {
+ deleteColumn(*it);
+ }
+ m_columns.erase(start, end);
+
+ const int maxIndex = m_columns.count() - 1;
+ Q_ASSERT(maxIndex >= 0);
+ if (m_index > maxIndex) {
+ m_index = maxIndex;
+ }
+ break;
+ }
+ }
+ ++columnIndex;
+ }
+
+ // Create missing columns. Assuming that the path is "/home/peter/Temp/" and
+ // the target path is "/home/peter/Temp/a/b/c/", then the columns "a", "b" and
+ // "c" will be created.
+ const int lastIndex = m_columns.count() - 1;
+ Q_ASSERT(lastIndex >= 0);
+
+ const KUrl& activeUrl = m_columns[lastIndex]->url();
+ Q_ASSERT(activeUrl.isParentOf(url));
+ Q_ASSERT(activeUrl != url);
+
+ QString path = activeUrl.url(KUrl::AddTrailingSlash);
+ const QString targetPath = url.url(KUrl::AddTrailingSlash);
+
+ columnIndex = lastIndex;
+ int slashIndex = path.count('/');
+ bool hasSubPath = (slashIndex >= 0);
+ while (hasSubPath) {
+ const QString subPath = targetPath.section('/', slashIndex, slashIndex);
+ if (subPath.isEmpty()) {
+ hasSubPath = false;
+ } else {
+ path += subPath + '/';
+ ++slashIndex;
+
+ const KUrl childUrl = KUrl(path);
+ m_columns[columnIndex]->setChildUrl(childUrl);
+ columnIndex++;
+
+ DolphinColumnView* column = new DolphinColumnView(viewport(), this, childUrl);
+ m_columns.append(column);
+
+ // Before invoking layoutColumns() the column must be set visible temporary.
+ // To prevent a flickering the initial geometry is set to a hidden position.
+ column->setGeometry(QRect(-1, -1, 1, 1));
+ column->show();
+ layoutColumns();
+ updateScrollBar();
+ }
+ }
+
+ requestActivation(m_columns[columnIndex]);
+}
+
+void DolphinColumnViewContainer::mousePressEvent(QMouseEvent* event)
+{
+ m_dolphinViewController->requestActivation();
+ QScrollArea::mousePressEvent(event);
+}
+
+void DolphinColumnViewContainer::keyPressEvent(QKeyEvent* event)
+{
+ if (event->key() == Qt::Key_Left) {
+ if (m_index > 0) {
+ requestActivation(m_columns[m_index - 1]);
+ }
+ } else {
+ QScrollArea::keyPressEvent(event);
+ }
+}
+
+void DolphinColumnViewContainer::resizeEvent(QResizeEvent* event)
+{
+ QScrollArea::resizeEvent(event);
+ layoutColumns();
+ updateScrollBar();
+ assureVisibleActiveColumn();
+}
+
+void DolphinColumnViewContainer::wheelEvent(QWheelEvent* event)
+{
+ // let Ctrl+wheel events propagate to the DolphinView for icon zooming
+ if ((event->modifiers() & Qt::ControlModifier) == Qt::ControlModifier) {
+ event->ignore();
+ } else {
+ QScrollArea::wheelEvent(event);
+ }
+}
+
+void DolphinColumnViewContainer::moveContentHorizontally(int x)
+{
+ m_contentX = isRightToLeft() ? +x : -x;
+ layoutColumns();
+}
+
+void DolphinColumnViewContainer::updateColumnsBackground(bool active)
+{
+ if (active == m_active) {
+ return;
+ }
+
+ m_active = active;
+
+ // dim the background of the viewport
+ const QPalette::ColorRole role = viewport()->backgroundRole();
+ QColor background = viewport()->palette().color(role);
+ background.setAlpha(0); // make background transparent
+
+ QPalette palette = viewport()->palette();
+ palette.setColor(role, background);
+ viewport()->setPalette(palette);
+
+ foreach (DolphinColumnView* column, m_columns) {
+ column->updateBackground();
+ }
+}
+
+void DolphinColumnViewContainer::updateActiveUrl()
+{
+ const KUrl activeUrl = m_columns[m_index]->url();
+ m_dolphinViewController->requestUrlChange(activeUrl);
+}
+
+void DolphinColumnViewContainer::layoutColumns()
+{
+ const int gap = 4;
+
+ ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
+ const int columnWidth = settings->columnWidth();
+
+ QRect emptyViewportRect;
+ if (isRightToLeft()) {
+ int x = viewport()->width() - columnWidth + m_contentX;
+ foreach (DolphinColumnView* column, m_columns) {
+ column->setGeometry(QRect(x, 0, columnWidth - gap, viewport()->height()));
+ x -= columnWidth;
+ }
+ emptyViewportRect = QRect(0, 0, x + columnWidth - gap, viewport()->height());
+ } else {
+ int x = m_contentX;
+ foreach (DolphinColumnView* column, m_columns) {
+ column->setGeometry(QRect(x, 0, columnWidth - gap, viewport()->height()));
+ x += columnWidth;
+ }
+ emptyViewportRect = QRect(x, 0, viewport()->width() - x - gap, viewport()->height());
+ }
+
+ if (emptyViewportRect.isValid()) {
+ m_emptyViewport->show();
+ m_emptyViewport->setGeometry(emptyViewportRect);
+ } else {
+ m_emptyViewport->hide();
+ }
+}
+
+void DolphinColumnViewContainer::updateScrollBar()
+{
+ ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
+ const int contentWidth = m_columns.count() * settings->columnWidth();
+
+ horizontalScrollBar()->setPageStep(contentWidth);
+ horizontalScrollBar()->setRange(0, contentWidth - viewport()->width());
+}
+
+void DolphinColumnViewContainer::assureVisibleActiveColumn()
+{
+ const int viewportWidth = viewport()->width();
+ const int x = activeColumn()->x();
+
+ ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
+ const int width = settings->columnWidth();
+
+ if (x + width > viewportWidth) {
+ const int newContentX = m_contentX - x - width + viewportWidth;
+ if (isRightToLeft()) {
+ m_animation->setFrameRange(m_contentX, newContentX);
+ } else {
+ m_animation->setFrameRange(-m_contentX, -newContentX);
+ }
+ if (m_animation->state() != QTimeLine::Running) {
+ m_animation->start();
+ }
+ } else if (x < 0) {
+ const int newContentX = m_contentX - x;
+ if (isRightToLeft()) {
+ m_animation->setFrameRange(m_contentX, newContentX);
+ } else {
+ m_animation->setFrameRange(-m_contentX, -newContentX);
+ }
+ if (m_animation->state() != QTimeLine::Running) {
+ m_animation->start();
+ }
+ }
+}
+
+void DolphinColumnViewContainer::requestActivation(DolphinColumnView* column)
+{
+ if (m_dolphinViewController->itemView() != column) {
+ m_dolphinViewController->setItemView(column);
+ }
+ if (focusProxy() != column) {
+ setFocusProxy(column);
+ }
+
+ if (column->isActive()) {
+ assureVisibleActiveColumn();
+ } else {
+ // Deactivate the currently active column
+ if (m_index >= 0) {
+ m_columns[m_index]->setActive(false);
+ }
+
+ // Get the index of the column that should get activated
+ int index = 0;
+ foreach (DolphinColumnView* currColumn, m_columns) {
+ if (currColumn == column) {
+ break;
+ }
+ ++index;
+ }
+
+ Q_ASSERT(index != m_index);
+ Q_ASSERT(index < m_columns.count());
+
+ // Activate the requested column
+ m_index = index;
+ m_columns[m_index]->setActive(true);
+
+ assureVisibleActiveColumn();
+ m_activeUrlTimer->start(); // calls slot updateActiveUrl()
+ }
+}
+
+void DolphinColumnViewContainer::removeAllColumns()
+{
+ QList<DolphinColumnView*>::iterator start = m_columns.begin() + 1;
+ QList<DolphinColumnView*>::iterator end = m_columns.end();
+ for (QList<DolphinColumnView*>::iterator it = start; it != end; ++it) {
+ deleteColumn(*it);
+ }
+ m_columns.erase(start, end);
+ m_index = 0;
+ m_columns[0]->setActive(true);
+ assureVisibleActiveColumn();
+}
+
+QPoint DolphinColumnViewContainer::columnPosition(DolphinColumnView* column, const QPoint& point) const
+{
+ const QPoint topLeft = column->frameGeometry().topLeft();
+ return QPoint(point.x() - topLeft.x(), point.y() - topLeft.y());
+}
+
+void DolphinColumnViewContainer::deleteColumn(DolphinColumnView* column)
+{
+ if (column == 0) {
+ return;
+ }
+
+ if (m_dolphinViewController->itemView() == column) {
+ m_dolphinViewController->setItemView(0);
+ }
+ // deleteWhenNotDragSource(column) does not necessarily delete column,
+ // and we want its preview generator destroyed immediately.
+ column->hide();
+ // Prevent automatic destruction of column when this DolphinColumnViewContainer
+ // is destroyed.
+ column->setParent(0);
+ column->disconnect();
+
+ if (DragAndDropHelper::instance().isDragSource(column)) {
+ // The column is a drag source (the feature "Open folders
+ // during drag operations" is used). Deleting the view
+ // during an ongoing drag operation is not allowed, so
+ // this will postponed.
+ if (m_dragSource != 0) {
+ // the old stored view is obviously not the drag source anymore
+ m_dragSource->deleteLater();
+ m_dragSource = 0;
+ }
+ column->hide();
+ column->setParent(0);
+ column->disconnect();
+
+ m_dragSource = column;
+ } else {
+ column->deleteLater();
+ }
+}
+
+#include "dolphincolumnviewcontainer.moc"
diff --git a/src/views/dolphincolumnviewcontainer.h b/src/views/dolphincolumnviewcontainer.h
new file mode 100644
index 000000000..c67fb3cff
--- /dev/null
+++ b/src/views/dolphincolumnviewcontainer.h
@@ -0,0 +1,147 @@
+/***************************************************************************
+ * Copyright (C) 2007-2009 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHINCOLUMNVIEWCONTAINER_H
+#define DOLPHINCOLUMNVIEWCONTAINER_H
+
+#include "dolphinview.h"
+
+#include <kurl.h>
+
+#include <QList>
+#include <QScrollArea>
+#include <QString>
+
+class DolphinColumnView;
+class DolphinViewController;
+class QFrame;
+class QTimeLine;
+class QTimer;
+
+/**
+ * @brief Represents a container for columns represented as instances
+ * of DolphinColumnView.
+ *
+ * @see DolphinColumnView
+ */
+class DolphinColumnViewContainer : public QScrollArea
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @param parent Parent widget.
+ * @param dolphinViewController Allows the DolphinColumnView to control the
+ * DolphinView in a limited way.
+ * @param viewModeController Controller that is used by the DolphinView
+ * to control the DolphinColumnView. The DolphinColumnView
+ * only has read access to the controller.
+ * @param model Directory that is shown.
+ */
+ explicit DolphinColumnViewContainer(QWidget* parent,
+ DolphinViewController* dolphinViewController,
+ const ViewModeController* viewModeController);
+ virtual ~DolphinColumnViewContainer();
+
+ KUrl rootUrl() const;
+
+ QAbstractItemView* activeColumn() const;
+
+ /**
+ * Shows the column which represents the URL \a url. If the column
+ * is already shown, it gets activated, otherwise it will be created.
+ */
+ void showColumn(const KUrl& url);
+
+protected:
+ virtual void mousePressEvent(QMouseEvent* event);
+ virtual void keyPressEvent(QKeyEvent* event);
+ virtual void resizeEvent(QResizeEvent* event);
+ virtual void wheelEvent(QWheelEvent* event);
+
+private slots:
+ /**
+ * Moves the content of the columns view to represent
+ * the scrollbar position \a x.
+ */
+ void moveContentHorizontally(int x);
+
+ /**
+ * Updates the background color of the columns to respect
+ * the current activation state \a active.
+ */
+ void updateColumnsBackground(bool active);
+
+ /**
+ * Tells the Dolphin controller to update the active URL
+ * to m_activeUrl. The slot is called asynchronously with a
+ * small delay, as this prevents a flickering when a directory
+ * from an inactive column gets selected.
+ */
+ void updateActiveUrl();
+
+private:
+ void layoutColumns();
+ void updateScrollBar();
+
+ /**
+ * Assures that the currently active column is fully visible
+ * by adjusting the horizontal position of the content.
+ */
+ void assureVisibleActiveColumn();
+
+ /**
+ * Request the activation for the column \a column. It is assured
+ * that the columns gets fully visible by adjusting the horizontal
+ * position of the content.
+ */
+ void requestActivation(DolphinColumnView* column);
+
+ /** Removes all columns except of the root column. */
+ void removeAllColumns();
+
+ /**
+ * Returns the position of the point \a point relative to the column
+ * \a column.
+ */
+ QPoint columnPosition(DolphinColumnView* column, const QPoint& point) const;
+
+ /**
+ * Deletes the column. If the itemview of the controller is set to the column,
+ * the controllers itemview is set to 0.
+ */
+ void deleteColumn(DolphinColumnView* column);
+
+private:
+ DolphinViewController* m_dolphinViewController;
+ const ViewModeController* m_viewModeController;
+ bool m_active;
+ int m_index;
+ int m_contentX;
+ QList<DolphinColumnView*> m_columns;
+ QFrame* m_emptyViewport;
+ QTimeLine* m_animation;
+ QAbstractItemView* m_dragSource;
+
+ QTimer* m_activeUrlTimer;
+
+ friend class DolphinColumnView;
+};
+
+#endif
diff --git a/src/views/dolphindetailsview.cpp b/src/views/dolphindetailsview.cpp
new file mode 100644
index 000000000..961bd7872
--- /dev/null
+++ b/src/views/dolphindetailsview.cpp
@@ -0,0 +1,1113 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Peter Penz ([email protected]) *
+ * Copyright (C) 2008 by Simon St. James ([email protected]) *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphindetailsview.h"
+
+#include "additionalinfoaccessor.h"
+#include "dolphinmodel.h"
+#include "dolphinviewcontroller.h"
+#include "dolphinfileitemdelegate.h"
+#include "settings/dolphinsettings.h"
+#include "dolphinsortfilterproxymodel.h"
+#include "dolphinviewautoscroller.h"
+#include "draganddrophelper.h"
+#include "viewextensionsfactory.h"
+#include "viewmodecontroller.h"
+#include "viewproperties.h"
+#include "zoomlevelinfo.h"
+
+#include "dolphin_detailsmodesettings.h"
+#include "dolphin_generalsettings.h"
+
+#include <kdirmodel.h>
+#include <klocale.h>
+#include <kmenu.h>
+
+#include <QAction>
+#include <QApplication>
+#include <QHeaderView>
+#include <QRubberBand>
+#include <QPainter>
+#include <QScrollBar>
+
+DolphinDetailsView::DolphinDetailsView(QWidget* parent,
+ DolphinViewController* dolphinViewController,
+ const ViewModeController* viewModeController,
+ DolphinSortFilterProxyModel* proxyModel) :
+ QTreeView(parent),
+ m_autoResize(true),
+ m_expandingTogglePressed(false),
+ m_keyPressed(false),
+ m_useDefaultIndexAt(true),
+ m_ignoreScrollTo(false),
+ m_dolphinViewController(dolphinViewController),
+ m_viewModeController(viewModeController),
+ m_extensionsFactory(0),
+ m_expandableFoldersAction(0),
+ m_expandedUrls(),
+ m_font(),
+ m_decorationSize(),
+ m_band()
+{
+ const DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
+ Q_ASSERT(settings != 0);
+ Q_ASSERT(dolphinViewController != 0);
+ Q_ASSERT(viewModeController != 0);
+
+ setLayoutDirection(Qt::LeftToRight);
+ setAcceptDrops(true);
+ setSortingEnabled(true);
+ setUniformRowHeights(true);
+ setSelectionBehavior(SelectItems);
+ setDragDropMode(QAbstractItemView::DragDrop);
+ setDropIndicatorShown(false);
+ setAlternatingRowColors(true);
+ setRootIsDecorated(settings->expandableFolders());
+ setItemsExpandable(settings->expandableFolders());
+ setEditTriggers(QAbstractItemView::NoEditTriggers);
+ setModel(proxyModel);
+
+ setMouseTracking(true);
+
+ const ViewProperties props(viewModeController->url());
+ setSortIndicatorSection(props.sorting());
+ setSortIndicatorOrder(props.sortOrder());
+
+ QHeaderView* headerView = header();
+ connect(headerView, SIGNAL(sectionClicked(int)),
+ this, SLOT(synchronizeSortingState(int)));
+ headerView->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(headerView, SIGNAL(customContextMenuRequested(const QPoint&)),
+ this, SLOT(configureSettings(const QPoint&)));
+ connect(headerView, SIGNAL(sectionResized(int, int, int)),
+ this, SLOT(slotHeaderSectionResized(int, int, int)));
+ connect(headerView, SIGNAL(sectionHandleDoubleClicked(int)),
+ this, SLOT(disableAutoResizing()));
+
+ connect(parent, SIGNAL(sortingChanged(DolphinView::Sorting)),
+ this, SLOT(setSortIndicatorSection(DolphinView::Sorting)));
+ connect(parent, SIGNAL(sortOrderChanged(Qt::SortOrder)),
+ this, SLOT(setSortIndicatorOrder(Qt::SortOrder)));
+
+ connect(this, SIGNAL(clicked(const QModelIndex&)),
+ dolphinViewController, SLOT(requestTab(const QModelIndex&)));
+ if (KGlobalSettings::singleClick()) {
+ connect(this, SIGNAL(clicked(const QModelIndex&)),
+ dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
+ } else {
+ connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
+ dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
+ }
+
+ connect(this, SIGNAL(entered(const QModelIndex&)),
+ this, SLOT(slotEntered(const QModelIndex&)));
+ connect(this, SIGNAL(viewportEntered()),
+ dolphinViewController, SLOT(emitViewportEntered()));
+ connect(viewModeController, SIGNAL(zoomLevelChanged(int)),
+ this, SLOT(setZoomLevel(int)));
+ connect(dolphinViewController->view(), SIGNAL(additionalInfoChanged()),
+ this, SLOT(updateColumnVisibility()));
+ connect(viewModeController, SIGNAL(activationChanged(bool)),
+ this, SLOT(slotActivationChanged(bool)));
+
+ if (settings->useSystemFont()) {
+ m_font = KGlobalSettings::generalFont();
+ } else {
+ m_font = QFont(settings->fontFamily(),
+ qRound(settings->fontSize()),
+ settings->fontWeight(),
+ settings->italicFont());
+ m_font.setPointSizeF(settings->fontSize());
+ }
+
+ setVerticalScrollMode(QTreeView::ScrollPerPixel);
+ setHorizontalScrollMode(QTreeView::ScrollPerPixel);
+
+ const DolphinView* view = dolphinViewController->view();
+ connect(view, SIGNAL(showPreviewChanged()),
+ this, SLOT(slotShowPreviewChanged()));
+
+
+ setFocus();
+ viewport()->installEventFilter(this);
+
+ connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)),
+ this, SLOT(slotGlobalSettingsChanged(int)));
+
+ m_useDefaultIndexAt = false;
+
+ m_expandableFoldersAction = new QAction(i18nc("@option:check", "Expandable Folders"), this);
+ m_expandableFoldersAction->setCheckable(true);
+ connect(m_expandableFoldersAction, SIGNAL(toggled(bool)),
+ this, SLOT(setFoldersExpandable(bool)));
+
+ connect(this, SIGNAL(expanded(const QModelIndex&)), this, SLOT(slotExpanded(const QModelIndex&)));
+ connect(this, SIGNAL(collapsed(const QModelIndex&)), this, SLOT(slotCollapsed(const QModelIndex&)));
+
+ updateDecorationSize(view->showPreview());
+
+ m_extensionsFactory = new ViewExtensionsFactory(this, dolphinViewController, viewModeController);
+ m_extensionsFactory->fileItemDelegate()->setMinimizedNameColumn(true);
+ m_extensionsFactory->setAutoFolderExpandingEnabled(settings->expandableFolders());
+}
+
+DolphinDetailsView::~DolphinDetailsView()
+{
+}
+
+QSet<KUrl> DolphinDetailsView::expandedUrls() const
+{
+ return m_expandedUrls;
+}
+
+QRegion DolphinDetailsView::visualRegionForSelection(const QItemSelection& selection) const
+{
+ // We have to make sure that the visualRect of each model index is inside the region.
+ // QTreeView::visualRegionForSelection does not do it right because it assumes implicitly
+ // that all visualRects have the same width, which is in general not the case here.
+ QRegion selectionRegion;
+ const QModelIndexList indexes = selection.indexes();
+
+ foreach(const QModelIndex& index, indexes) {
+ selectionRegion += visualRect(index);
+ }
+
+ return selectionRegion;
+}
+
+bool DolphinDetailsView::event(QEvent* event)
+{
+ switch (event->type()) {
+ case QEvent::Polish:
+ header()->setResizeMode(QHeaderView::Interactive);
+ updateColumnVisibility();
+ break;
+
+ case QEvent::FocusOut:
+ // If a key-press triggers an action that e. g. opens a dialog, the
+ // widget gets no key-release event. Assure that the pressed state
+ // is reset to prevent accidently setting the current index during a selection.
+ m_keyPressed = false;
+ break;
+
+ default:
+ break;
+ }
+
+ return QTreeView::event(event);
+}
+
+QStyleOptionViewItem DolphinDetailsView::viewOptions() const
+{
+ QStyleOptionViewItem viewOptions = QTreeView::viewOptions();
+ viewOptions.font = m_font;
+ viewOptions.fontMetrics = QFontMetrics(m_font);
+ viewOptions.showDecorationSelected = true;
+ viewOptions.decorationSize = m_decorationSize;
+ return viewOptions;
+}
+
+void DolphinDetailsView::contextMenuEvent(QContextMenuEvent* event)
+{
+ QTreeView::contextMenuEvent(event);
+
+ DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
+ m_expandableFoldersAction->setChecked(settings->expandableFolders());
+ m_dolphinViewController->triggerContextMenuRequest(event->pos(),
+ QList<QAction*>() << m_expandableFoldersAction);
+}
+
+void DolphinDetailsView::mousePressEvent(QMouseEvent* event)
+{
+ m_dolphinViewController->requestActivation();
+
+ const QModelIndex current = currentIndex();
+ QTreeView::mousePressEvent(event);
+
+ m_expandingTogglePressed = isAboveExpandingToggle(event->pos());
+
+ const QModelIndex index = indexAt(event->pos());
+ const bool updateState = index.isValid() &&
+ (index.column() == DolphinModel::Name) &&
+ (event->button() == Qt::LeftButton);
+ if (updateState) {
+ setState(QAbstractItemView::DraggingState);
+ }
+
+ if (!index.isValid() || (index.column() != DolphinModel::Name)) {
+ // the mouse press is done somewhere outside the filename column
+ if (QApplication::mouseButtons() & Qt::MidButton) {
+ m_dolphinViewController->replaceUrlByClipboard();
+ }
+
+ const Qt::KeyboardModifiers mod = QApplication::keyboardModifiers();
+ if (!m_expandingTogglePressed && !(mod & Qt::ShiftModifier) && !(mod & Qt::ControlModifier)) {
+ clearSelection();
+ }
+
+ // restore the current index, other columns are handled as viewport area.
+ // setCurrentIndex(...) implicitly calls scrollTo(...), which we want to ignore.
+ m_ignoreScrollTo = true;
+ selectionModel()->setCurrentIndex(current, QItemSelectionModel::Current);
+ m_ignoreScrollTo = false;
+
+ if ((event->button() == Qt::LeftButton) && !m_expandingTogglePressed) {
+ // Inform Qt about what we are doing - otherwise it starts dragging items around!
+ setState(DragSelectingState);
+ m_band.show = true;
+ // Incremental update data will not be useful - start from scratch.
+ m_band.ignoreOldInfo = true;
+ const QPoint scrollPos(horizontalScrollBar()->value(), verticalScrollBar()->value());
+ m_band.origin = event->pos() + scrollPos;
+ m_band.destination = m_band.origin;
+ m_band.originalSelection = selectionModel()->selection();
+ }
+ }
+}
+
+void DolphinDetailsView::mouseMoveEvent(QMouseEvent* event)
+{
+ if (m_expandingTogglePressed) {
+ // Per default QTreeView starts either a selection or a drag operation when dragging
+ // the expanding toggle button (Qt-issue - see TODO comment in DolphinIconsView::mousePressEvent()).
+ // Turn off this behavior in Dolphin to stay predictable:
+ setState(QAbstractItemView::NoState);
+ return;
+ }
+
+ if (m_band.show) {
+ const QPoint mousePos = event->pos();
+ const QModelIndex index = indexAt(mousePos);
+ if (!index.isValid()) {
+ // the destination of the selection rectangle is above the viewport. In this
+ // case QTreeView does no selection at all, which is not the wanted behavior
+ // in Dolphin -> select all items within the elastic band rectangle
+ updateElasticBandSelection();
+ }
+
+ // TODO: enable QTreeView::mouseMoveEvent(event) again, as soon
+ // as the Qt-issue #199631 has been fixed.
+ // QTreeView::mouseMoveEvent(event);
+ QAbstractItemView::mouseMoveEvent(event);
+ updateElasticBand();
+ } else {
+ // TODO: enable QTreeView::mouseMoveEvent(event) again, as soon
+ // as the Qt-issue #199631 has been fixed.
+ // QTreeView::mouseMoveEvent(event);
+ QAbstractItemView::mouseMoveEvent(event);
+ }
+}
+
+void DolphinDetailsView::mouseReleaseEvent(QMouseEvent* event)
+{
+ if (!m_expandingTogglePressed) {
+ const QModelIndex index = indexAt(event->pos());
+ if (index.isValid() && (index.column() == DolphinModel::Name)) {
+ QTreeView::mouseReleaseEvent(event);
+ } else {
+ // don't change the current index if the cursor is released
+ // above any other column than the name column, as the other
+ // columns act as viewport
+ const QModelIndex current = currentIndex();
+ QTreeView::mouseReleaseEvent(event);
+ selectionModel()->setCurrentIndex(current, QItemSelectionModel::Current);
+ }
+ }
+ m_expandingTogglePressed = false;
+
+ if (m_band.show) {
+ setState(NoState);
+ updateElasticBand();
+ m_band.show = false;
+ }
+}
+
+void DolphinDetailsView::startDrag(Qt::DropActions supportedActions)
+{
+ DragAndDropHelper::instance().startDrag(this, supportedActions, m_dolphinViewController);
+ m_band.show = false;
+}
+
+void DolphinDetailsView::dragEnterEvent(QDragEnterEvent* event)
+{
+ if (DragAndDropHelper::instance().isMimeDataSupported(event->mimeData())) {
+ event->acceptProposedAction();
+ }
+
+ if (m_band.show) {
+ updateElasticBand();
+ m_band.show = false;
+ }
+}
+
+void DolphinDetailsView::dragLeaveEvent(QDragLeaveEvent* event)
+{
+ QTreeView::dragLeaveEvent(event);
+ setDirtyRegion(m_dropRect);
+}
+
+void DolphinDetailsView::dragMoveEvent(QDragMoveEvent* event)
+{
+ QTreeView::dragMoveEvent(event);
+
+ // TODO: remove this code when the issue #160611 is solved in Qt 4.4
+ setDirtyRegion(m_dropRect);
+ const QModelIndex index = indexAt(event->pos());
+ if (index.isValid() && (index.column() == DolphinModel::Name)) {
+ const KFileItem item = m_dolphinViewController->itemForIndex(index);
+ if (!item.isNull() && item.isDir()) {
+ m_dropRect = visualRect(index);
+ } else {
+ m_dropRect.setSize(QSize()); // set as invalid
+ }
+ setDirtyRegion(m_dropRect);
+ }
+
+ if (DragAndDropHelper::instance().isMimeDataSupported(event->mimeData())) {
+ // accept url drops, independently from the destination item
+ event->acceptProposedAction();
+ }
+}
+
+void DolphinDetailsView::dropEvent(QDropEvent* event)
+{
+ const QModelIndex index = indexAt(event->pos());
+ KFileItem item;
+ if (index.isValid() && (index.column() == DolphinModel::Name)) {
+ item = m_dolphinViewController->itemForIndex(index);
+ }
+ m_dolphinViewController->indicateDroppedUrls(item, m_viewModeController->url(), event);
+ QTreeView::dropEvent(event);
+}
+
+void DolphinDetailsView::paintEvent(QPaintEvent* event)
+{
+ QTreeView::paintEvent(event);
+ if (m_band.show) {
+ // The following code has been taken from QListView
+ // and adapted to DolphinDetailsView.
+ // (C) 1992-2007 Trolltech ASA
+ QStyleOptionRubberBand opt;
+ opt.initFrom(this);
+ opt.shape = QRubberBand::Rectangle;
+ opt.opaque = false;
+ opt.rect = elasticBandRect();
+
+ QPainter painter(viewport());
+ painter.save();
+ style()->drawControl(QStyle::CE_RubberBand, &opt, &painter);
+ painter.restore();
+ }
+}
+
+void DolphinDetailsView::keyPressEvent(QKeyEvent* event)
+{
+ // If the Control modifier is pressed, a multiple selection
+ // is done and DolphinDetailsView::currentChanged() may not
+ // not change the selection in a custom way.
+ m_keyPressed = !(event->modifiers() & Qt::ControlModifier);
+
+ QTreeView::keyPressEvent(event);
+ m_dolphinViewController->handleKeyPressEvent(event);
+}
+
+void DolphinDetailsView::keyReleaseEvent(QKeyEvent* event)
+{
+ QTreeView::keyReleaseEvent(event);
+ m_keyPressed = false;
+}
+
+void DolphinDetailsView::resizeEvent(QResizeEvent* event)
+{
+ QTreeView::resizeEvent(event);
+ if (m_autoResize) {
+ resizeColumns();
+ }
+}
+
+void DolphinDetailsView::wheelEvent(QWheelEvent* event)
+{
+ const int step = m_decorationSize.height();
+ verticalScrollBar()->setSingleStep(step);
+ QTreeView::wheelEvent(event);
+}
+
+void DolphinDetailsView::currentChanged(const QModelIndex& current, const QModelIndex& previous)
+{
+ QTreeView::currentChanged(current, previous);
+ m_extensionsFactory->handleCurrentIndexChange(current, previous);
+
+ // Stay consistent with QListView: When changing the current index by key presses,
+ // also change the selection.
+ if (m_keyPressed) {
+ setCurrentIndex(current);
+ }
+
+ // If folders are expanded, the width which is available for editing may have changed
+ // because it depends on the level of the current item in the folder hierarchy.
+ adjustMaximumSizeForEditing(current);
+}
+
+bool DolphinDetailsView::eventFilter(QObject* watched, QEvent* event)
+{
+ if ((watched == viewport()) && (event->type() == QEvent::Leave)) {
+ // if the mouse is above an item and moved very fast outside the widget,
+ // no viewportEntered() signal might be emitted although the mouse has been moved
+ // above the viewport
+ m_dolphinViewController->emitViewportEntered();
+ }
+
+ return QTreeView::eventFilter(watched, event);
+}
+
+QModelIndex DolphinDetailsView::indexAt(const QPoint& point) const
+{
+ // the blank portion of the name column counts as empty space
+ const QModelIndex index = QTreeView::indexAt(point);
+ const bool isAboveEmptySpace = !m_useDefaultIndexAt &&
+ (index.column() == KDirModel::Name) && !visualRect(index).contains(point);
+ return isAboveEmptySpace ? QModelIndex() : index;
+}
+
+QRect DolphinDetailsView::visualRect(const QModelIndex& index) const
+{
+ QRect rect = QTreeView::visualRect(index);
+ const KFileItem item = m_dolphinViewController->itemForIndex(index);
+ if (!item.isNull()) {
+ const int width = DolphinFileItemDelegate::nameColumnWidth(item.text(), viewOptions());
+ rect.setWidth(width);
+ }
+
+ return rect;
+}
+
+void DolphinDetailsView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command)
+{
+ // We must override setSelection() as Qt calls it internally and when this happens
+ // we must ensure that the default indexAt() is used.
+ if (!m_band.show) {
+ m_useDefaultIndexAt = true;
+ QTreeView::setSelection(rect, command);
+ m_useDefaultIndexAt = false;
+ } else {
+ // Use our own elastic band selection algorithm
+ updateElasticBandSelection();
+ }
+}
+
+void DolphinDetailsView::scrollTo(const QModelIndex & index, ScrollHint hint)
+{
+ if (!m_ignoreScrollTo) {
+ QTreeView::scrollTo(index, hint);
+ }
+}
+
+void DolphinDetailsView::setSortIndicatorSection(DolphinView::Sorting sorting)
+{
+ header()->setSortIndicator(sorting, header()->sortIndicatorOrder());
+}
+
+void DolphinDetailsView::setSortIndicatorOrder(Qt::SortOrder sortOrder)
+{
+ header()->setSortIndicator(header()->sortIndicatorSection(), sortOrder);
+}
+
+void DolphinDetailsView::synchronizeSortingState(int column)
+{
+ // The sorting has already been changed in QTreeView if this slot is
+ // invoked, but Dolphin is not informed about this.
+ DolphinView::Sorting sorting = DolphinSortFilterProxyModel::sortingForColumn(column);
+ const Qt::SortOrder sortOrder = header()->sortIndicatorOrder();
+ m_dolphinViewController->indicateSortingChange(sorting);
+ m_dolphinViewController->indicateSortOrderChange(sortOrder);
+}
+
+void DolphinDetailsView::slotEntered(const QModelIndex& index)
+{
+ if (index.column() == DolphinModel::Name) {
+ m_dolphinViewController->emitItemEntered(index);
+ } else {
+ m_dolphinViewController->emitViewportEntered();
+ }
+}
+
+void DolphinDetailsView::updateElasticBand()
+{
+ if (m_band.show) {
+ QRect dirtyRegion(elasticBandRect());
+ const QPoint scrollPos(horizontalScrollBar()->value(), verticalScrollBar()->value());
+ m_band.destination = viewport()->mapFromGlobal(QCursor::pos()) + scrollPos;
+ // Going above the (logical) top-left of the view causes complications during selection;
+ // we may as well prevent it.
+ if (m_band.destination.y() < 0) {
+ m_band.destination.setY(0);
+ }
+ if (m_band.destination.x() < 0) {
+ m_band.destination.setX(0);
+ }
+ dirtyRegion = dirtyRegion.united(elasticBandRect());
+ setDirtyRegion(dirtyRegion);
+ }
+}
+
+QRect DolphinDetailsView::elasticBandRect() const
+{
+ const QPoint scrollPos(horizontalScrollBar()->value(), verticalScrollBar()->value());
+
+ const QPoint topLeft = m_band.origin - scrollPos;
+ const QPoint bottomRight = m_band.destination - scrollPos;
+ return QRect(topLeft, bottomRight).normalized();
+}
+
+void DolphinDetailsView::setZoomLevel(int level)
+{
+ const int size = ZoomLevelInfo::iconSizeForZoomLevel(level);
+ DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
+
+ const bool showPreview = m_dolphinViewController->view()->showPreview();
+ if (showPreview) {
+ settings->setPreviewSize(size);
+ } else {
+ settings->setIconSize(size);
+ }
+
+ updateDecorationSize(showPreview);
+}
+
+void DolphinDetailsView::slotShowPreviewChanged()
+{
+ const DolphinView* view = m_dolphinViewController->view();
+ updateDecorationSize(view->showPreview());
+}
+
+void DolphinDetailsView::configureSettings(const QPoint& pos)
+{
+ KMenu popup(this);
+ popup.addTitle(i18nc("@title:menu", "Columns"));
+
+ // add checkbox items for each column
+ QHeaderView* headerView = header();
+ const int columns = model()->columnCount();
+ for (int i = 0; i < columns; ++i) {
+ const int logicalIndex = headerView->logicalIndex(i);
+ const QString text = model()->headerData(logicalIndex, Qt::Horizontal).toString();
+ if (!text.isEmpty()) {
+ QAction* action = popup.addAction(text);
+ action->setCheckable(true);
+ action->setChecked(!headerView->isSectionHidden(logicalIndex));
+ action->setData(logicalIndex);
+ action->setEnabled(logicalIndex != DolphinModel::Name);
+ }
+ }
+ popup.addSeparator();
+
+ QAction* activatedAction = popup.exec(header()->mapToGlobal(pos));
+ if (activatedAction != 0) {
+ const bool show = activatedAction->isChecked();
+ const int columnIndex = activatedAction->data().toInt();
+
+ KFileItemDelegate::InformationList list = m_dolphinViewController->view()->additionalInfo();
+ const KFileItemDelegate::Information info = infoForColumn(columnIndex);
+ if (show) {
+ Q_ASSERT(!list.contains(info));
+ list.append(info);
+ } else {
+ Q_ASSERT(list.contains(info));
+ const int index = list.indexOf(info);
+ list.removeAt(index);
+ }
+
+ m_dolphinViewController->indicateAdditionalInfoChange(list);
+ setColumnHidden(columnIndex, !show);
+ resizeColumns();
+ }
+}
+
+void DolphinDetailsView::updateColumnVisibility()
+{
+ QHeaderView* headerView = header();
+ disconnect(headerView, SIGNAL(sectionMoved(int, int, int)),
+ this, SLOT(saveColumnPositions()));
+
+ const DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
+ const QList<int> columnPositions = settings->columnPositions();
+
+ const KFileItemDelegate::InformationList list = m_dolphinViewController->view()->additionalInfo();
+ for (int i = DolphinModel::Name; i < DolphinModel::ExtraColumnCount; ++i) {
+ const KFileItemDelegate::Information info = infoForColumn(i);
+ const bool hide = !list.contains(info) && (i != DolphinModel::Name);
+ if (isColumnHidden(i) != hide) {
+ setColumnHidden(i, hide);
+ }
+
+ // If the list columnPositions has been written by an older Dolphin version,
+ // its length might be smaller than DolphinModel::ExtraColumnCount. Therefore,
+ // we have to check if item number i exists before accessing it.
+ if (i < columnPositions.length()) {
+ const int position = columnPositions[i];
+
+ // The position might be outside the correct range if the list columnPositions
+ // has been written by a newer Dolphin version with more columns.
+ if (position < DolphinModel::ExtraColumnCount) {
+ const int from = headerView->visualIndex(i);
+ headerView->moveSection(from, position);
+ }
+ }
+ }
+
+ resizeColumns();
+
+ connect(headerView, SIGNAL(sectionMoved(int, int, int)),
+ this, SLOT(saveColumnPositions()));
+}
+
+void DolphinDetailsView::saveColumnPositions()
+{
+ QList<int> columnPositions;
+ for (int i = DolphinModel::Name; i < DolphinModel::ExtraColumnCount; ++i) {
+ columnPositions.append(header()->visualIndex(i));
+ }
+
+ DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
+ settings->setColumnPositions(columnPositions);
+}
+
+void DolphinDetailsView::slotHeaderSectionResized(int logicalIndex, int oldSize, int newSize)
+{
+ Q_UNUSED(logicalIndex);
+ Q_UNUSED(oldSize);
+ Q_UNUSED(newSize);
+ // If the user changes the size of the headers, the autoresize feature should be
+ // turned off. As there is no dedicated interface to find out whether the header
+ // section has been resized by the user or by a resize event, another approach is used.
+ // Attention: Take care when changing the if-condition to verify that there is no
+ // regression in combination with bug 178630 (see fix in comment #8).
+ if ((QApplication::mouseButtons() & Qt::LeftButton) && header()->underMouse()) {
+ disableAutoResizing();
+ }
+
+ adjustMaximumSizeForEditing(currentIndex());
+}
+
+void DolphinDetailsView::slotActivationChanged(bool active)
+{
+ setAlternatingRowColors(active);
+}
+
+void DolphinDetailsView::disableAutoResizing()
+{
+ m_autoResize = false;
+}
+
+void DolphinDetailsView::requestActivation()
+{
+ m_dolphinViewController->requestActivation();
+}
+
+void DolphinDetailsView::slotGlobalSettingsChanged(int category)
+{
+ Q_UNUSED(category);
+
+ const DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
+ Q_ASSERT(settings != 0);
+ if (settings->useSystemFont()) {
+ m_font = KGlobalSettings::generalFont();
+ }
+ //Disconnect then reconnect, since the settings have been changed, the connection requirements may have also.
+ disconnect(this, SIGNAL(clicked(QModelIndex)), m_dolphinViewController, SLOT(triggerItem(QModelIndex)));
+ disconnect(this, SIGNAL(doubleClicked(QModelIndex)), m_dolphinViewController, SLOT(triggerItem(QModelIndex)));
+ if (KGlobalSettings::singleClick()) {
+ connect(this, SIGNAL(clicked(QModelIndex)), m_dolphinViewController, SLOT(triggerItem(QModelIndex)));
+ } else {
+ connect(this, SIGNAL(doubleClicked(QModelIndex)), m_dolphinViewController, SLOT(triggerItem(QModelIndex)));
+ }
+}
+
+void DolphinDetailsView::updateElasticBandSelection()
+{
+ if (!m_band.show) {
+ return;
+ }
+
+ // Ensure the elastic band itself is up-to-date, in
+ // case we are being called due to e.g. a drag event.
+ updateElasticBand();
+
+ // Clip horizontally to the name column, as some filenames will be
+ // longer than the column. We don't clip vertically as origin
+ // may be above or below the current viewport area.
+ const int nameColumnX = header()->sectionPosition(DolphinModel::Name);
+ const int nameColumnWidth = header()->sectionSize(DolphinModel::Name);
+ QRect selRect = elasticBandRect().normalized();
+ QRect nameColumnArea(nameColumnX, selRect.y(), nameColumnWidth, selRect.height());
+ selRect = nameColumnArea.intersect(selRect).normalized();
+ // Get the last elastic band rectangle, expressed in viewpoint coordinates.
+ const QPoint scrollPos(horizontalScrollBar()->value(), verticalScrollBar()->value());
+ QRect oldSelRect = QRect(m_band.lastSelectionOrigin - scrollPos, m_band.lastSelectionDestination - scrollPos).normalized();
+
+ if (selRect.isNull()) {
+ selectionModel()->select(m_band.originalSelection, QItemSelectionModel::ClearAndSelect);
+ m_band.ignoreOldInfo = true;
+ return;
+ }
+
+ if (!m_band.ignoreOldInfo) {
+ // Do some quick checks to see if we can rule out the need to
+ // update the selection.
+ Q_ASSERT(uniformRowHeights());
+ QModelIndex dummyIndex = model()->index(0, 0);
+ if (!dummyIndex.isValid()) {
+ // No items in the model presumably.
+ return;
+ }
+
+ // If the elastic band does not cover the same rows as before, we'll
+ // need to re-check, and also invalidate the old item distances.
+ const int rowHeight = QTreeView::rowHeight(dummyIndex);
+ const bool coveringSameRows =
+ (selRect.top() / rowHeight == oldSelRect.top() / rowHeight) &&
+ (selRect.bottom() / rowHeight == oldSelRect.bottom() / rowHeight);
+ if (coveringSameRows) {
+ // Covering the same rows, but have we moved far enough horizontally
+ // that we might have (de)selected some other items?
+ const bool itemSelectionChanged =
+ ((selRect.left() > oldSelRect.left()) &&
+ (selRect.left() > m_band.insideNearestLeftEdge)) ||
+ ((selRect.left() < oldSelRect.left()) &&
+ (selRect.left() <= m_band.outsideNearestLeftEdge)) ||
+ ((selRect.right() < oldSelRect.right()) &&
+ (selRect.left() >= m_band.insideNearestRightEdge)) ||
+ ((selRect.right() > oldSelRect.right()) &&
+ (selRect.right() >= m_band.outsideNearestRightEdge));
+
+ if (!itemSelectionChanged) {
+ return;
+ }
+ }
+ } else {
+ // This is the only piece of optimization data that needs to be explicitly
+ // discarded.
+ m_band.lastSelectionOrigin = QPoint();
+ m_band.lastSelectionDestination = QPoint();
+ oldSelRect = selRect;
+ }
+
+ // Do the selection from scratch. Force a update of the horizontal distances info.
+ m_band.insideNearestLeftEdge = nameColumnX + nameColumnWidth + 1;
+ m_band.insideNearestRightEdge = nameColumnX - 1;
+ m_band.outsideNearestLeftEdge = nameColumnX - 1;
+ m_band.outsideNearestRightEdge = nameColumnX + nameColumnWidth + 1;
+
+ // Include the old selection rect as well, so we can deselect
+ // items that were inside it but not in the new selRect.
+ const QRect boundingRect = selRect.united(oldSelRect).normalized();
+ if (boundingRect.isNull()) {
+ return;
+ }
+
+ // Get the index of the item in this row in the name column.
+ // TODO - would this still work if the columns could be re-ordered?
+ QModelIndex startIndex = QTreeView::indexAt(boundingRect.topLeft());
+ if (startIndex.parent().isValid()) {
+ startIndex = startIndex.parent().child(startIndex.row(), KDirModel::Name);
+ } else {
+ startIndex = model()->index(startIndex.row(), KDirModel::Name);
+ }
+ if (!startIndex.isValid()) {
+ selectionModel()->select(m_band.originalSelection, QItemSelectionModel::ClearAndSelect);
+ m_band.ignoreOldInfo = true;
+ return;
+ }
+
+ // Go through all indexes between the top and bottom of boundingRect, and
+ // update the selection.
+ const int verticalCutoff = boundingRect.bottom();
+ QModelIndex currIndex = startIndex;
+ QModelIndex lastIndex;
+ bool allItemsInBoundDone = false;
+
+ // Calling selectionModel()->select(...) for each item that needs to be
+ // toggled is slow as each call emits selectionChanged(...) so store them
+ // and do the selection toggle in one batch.
+ QItemSelection itemsToToggle;
+ // QItemSelection's deal with continuous ranges of indexes better than
+ // single indexes, so try to portion items that need to be toggled into ranges.
+ bool formingToggleIndexRange = false;
+ QModelIndex toggleIndexRangeBegin = QModelIndex();
+
+ do {
+ QRect currIndexRect = visualRect(currIndex);
+
+ // Update some optimization info as we go.
+ const int cr = currIndexRect.right();
+ const int cl = currIndexRect.left();
+ const int sl = selRect.left();
+ const int sr = selRect.right();
+ // "The right edge of the name is outside of the rect but nearer than m_outsideNearestLeft", etc
+ if ((cr < sl && cr > m_band.outsideNearestLeftEdge)) {
+ m_band.outsideNearestLeftEdge = cr;
+ }
+ if ((cl > sr && cl < m_band.outsideNearestRightEdge)) {
+ m_band.outsideNearestRightEdge = cl;
+ }
+ if ((cl >= sl && cl <= sr && cl > m_band.insideNearestRightEdge)) {
+ m_band.insideNearestRightEdge = cl;
+ }
+ if ((cr >= sl && cr <= sr && cr < m_band.insideNearestLeftEdge)) {
+ m_band.insideNearestLeftEdge = cr;
+ }
+
+ bool currentlySelected = selectionModel()->isSelected(currIndex);
+ bool originallySelected = m_band.originalSelection.contains(currIndex);
+ bool intersectsSelectedRect = currIndexRect.intersects(selRect);
+ bool shouldBeSelected = (intersectsSelectedRect && !originallySelected) || (!intersectsSelectedRect && originallySelected);
+ bool needToToggleItem = (currentlySelected && !shouldBeSelected) || (!currentlySelected && shouldBeSelected);
+ if (needToToggleItem && !formingToggleIndexRange) {
+ toggleIndexRangeBegin = currIndex;
+ formingToggleIndexRange = true;
+ }
+
+ // NOTE: indexBelow actually walks up and down expanded trees for us.
+ QModelIndex nextIndex = indexBelow(currIndex);
+ allItemsInBoundDone = !nextIndex.isValid() || currIndexRect.top() > verticalCutoff;
+
+ const bool commitToggleIndexRange = formingToggleIndexRange &&
+ (!needToToggleItem ||
+ allItemsInBoundDone ||
+ currIndex.parent() != toggleIndexRangeBegin.parent());
+ if (commitToggleIndexRange) {
+ formingToggleIndexRange = false;
+ // If this is the last item in the bounds and it is also the beginning of a range,
+ // don't toggle lastIndex - it will already have been dealt with.
+ if (!allItemsInBoundDone || toggleIndexRangeBegin != currIndex) {
+ itemsToToggle.select(toggleIndexRangeBegin, lastIndex);
+ }
+ // Need to start a new range immediately with currIndex?
+ if (needToToggleItem) {
+ toggleIndexRangeBegin = currIndex;
+ formingToggleIndexRange = true;
+ }
+ if (allItemsInBoundDone && needToToggleItem) {
+ // Toggle the very last item in the bounds.
+ itemsToToggle.select(currIndex, currIndex);
+ }
+ }
+
+ // next item
+ lastIndex = currIndex;
+ currIndex = nextIndex;
+ } while (!allItemsInBoundDone);
+
+
+ selectionModel()->select(itemsToToggle, QItemSelectionModel::Toggle);
+
+ m_band.lastSelectionOrigin = m_band.origin;
+ m_band.lastSelectionDestination = m_band.destination;
+ m_band.ignoreOldInfo = false;
+}
+
+void DolphinDetailsView::setFoldersExpandable(bool expandable)
+{
+ if (!expandable) {
+ // collapse all expanded folders, as QTreeView::setItemsExpandable(false)
+ // does not do this task
+ const int rowCount = model()->rowCount();
+ for (int row = 0; row < rowCount; ++row) {
+ setExpanded(model()->index(row, 0), false);
+ }
+ }
+ DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
+ settings->setExpandableFolders(expandable);
+ setRootIsDecorated(expandable);
+ setItemsExpandable(expandable);
+
+ // The width of the space which is available for editing has changed
+ // because of the (dis)appearance of the expanding toggles
+ adjustMaximumSizeForEditing(currentIndex());
+}
+
+void DolphinDetailsView::slotExpanded(const QModelIndex& index)
+{
+ KFileItem item = m_dolphinViewController->itemForIndex(index);
+ if (!item.isNull()) {
+ m_expandedUrls.insert(item.url());
+ }
+}
+
+void DolphinDetailsView::slotCollapsed(const QModelIndex& index)
+{
+ KFileItem item = m_dolphinViewController->itemForIndex(index);
+ if (!item.isNull()) {
+ m_expandedUrls.remove(item.url());
+ }
+}
+
+void DolphinDetailsView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+{
+ removeExpandedIndexes(parent, start, end);
+ QTreeView::rowsAboutToBeRemoved(parent, start, end);
+}
+
+void DolphinDetailsView::removeExpandedIndexes(const QModelIndex& parent, int start, int end)
+{
+ if (m_expandedUrls.isEmpty()) {
+ return;
+ }
+
+ for (int row = start; row <= end; row++) {
+ const QModelIndex index = model()->index(row, 0, parent);
+ if (isExpanded(index)) {
+ slotCollapsed(index);
+ removeExpandedIndexes(index, 0, model()->rowCount(index) - 1);
+ }
+ }
+}
+
+void DolphinDetailsView::updateDecorationSize(bool showPreview)
+{
+ DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
+ const int iconSize = showPreview ? settings->previewSize() : settings->iconSize();
+ setIconSize(QSize(iconSize, iconSize));
+ m_decorationSize = QSize(iconSize, iconSize);
+
+ doItemsLayout();
+}
+
+KFileItemDelegate::Information DolphinDetailsView::infoForColumn(int columnIndex) const
+{
+ return AdditionalInfoAccessor::instance().keyForColumn(columnIndex);
+}
+
+void DolphinDetailsView::resizeColumns()
+{
+ // Using the resize mode QHeaderView::ResizeToContents is too slow (it takes
+ // around 3 seconds for each (!) resize operation when having > 10000 items).
+ // This gets a problem especially when opening large directories, where several
+ // resize operations are received for showing the currently available items during
+ // loading (the application hangs around 20 seconds when loading > 10000 items).
+
+ QHeaderView* headerView = header();
+ QFontMetrics fontMetrics(viewport()->font());
+
+ // Calculate the required with for each column and store it in columnWidth[]
+ int columnWidth[DolphinModel::ExtraColumnCount];
+ const int defaultWidth = fontMetrics.width("xxxxxxxxxx");
+
+ for (int i = 0; i < DolphinModel::ExtraColumnCount; ++i) {
+ const int logicalIndex = headerView->logicalIndex(i);
+ const QString headline = model()->headerData(logicalIndex, Qt::Horizontal).toString();
+ const int headlineWidth = fontMetrics.width(headline);
+
+ columnWidth[i] = qMax(defaultWidth, headlineWidth);
+ }
+
+ const int defaultSizeWidth = fontMetrics.width("00000 Items");
+ if (defaultSizeWidth > columnWidth[DolphinModel::Size]) {
+ columnWidth[DolphinModel::Size] = defaultSizeWidth;
+ }
+
+ const int defaultTimeWidth = fontMetrics.width("0000-00-00 00:00");
+ if (defaultTimeWidth > columnWidth[DolphinModel::ModifiedTime]) {
+ columnWidth[DolphinModel::ModifiedTime] = defaultTimeWidth;
+ }
+
+ int requiredWidth = 0;
+ for (int i = KDirModel::Size; i < DolphinModel::ExtraColumnCount; ++i) {
+ if (!isColumnHidden(i)) {
+ columnWidth[i] += 20; // provide a default gap
+ requiredWidth += columnWidth[i];
+ headerView->resizeSection(i, columnWidth[i]);
+ }
+ }
+
+ // Resize the name column in a way that the whole available width is used
+ columnWidth[KDirModel::Name] = viewport()->width() - requiredWidth;
+
+ const int minNameWidth = 300;
+ if (columnWidth[KDirModel::Name] < minNameWidth) {
+ columnWidth[KDirModel::Name] = minNameWidth;
+
+ // It might be possible that the name column width can be
+ // decreased without clipping any text. For performance
+ // reasons the exact necessary width for full visible names is
+ // only checked for up to 200 items:
+ const int rowCount = model()->rowCount();
+ if (rowCount > 0 && rowCount < 200) {
+ const int nameWidth = sizeHintForColumn(DolphinModel::Name);
+ if (nameWidth + requiredWidth <= viewport()->width()) {
+ columnWidth[KDirModel::Name] = viewport()->width() - requiredWidth;
+ } else if (nameWidth < minNameWidth) {
+ columnWidth[KDirModel::Name] = nameWidth;
+ }
+ }
+ }
+
+ headerView->resizeSection(KDirModel::Name, columnWidth[KDirModel::Name]);
+}
+
+bool DolphinDetailsView::isAboveExpandingToggle(const QPoint& pos) const
+{
+ // QTreeView offers no public API to get the information whether an index has an
+ // expanding toggle and what boundaries the toggle has. The following approach
+ // also assumes a toggle for file items.
+ if (itemsExpandable()) {
+ const QModelIndex index = QTreeView::indexAt(pos);
+ if (index.isValid() && (index.column() == KDirModel::Name)) {
+ QRect rect = visualRect(index);
+ const int toggleSize = rect.height();
+ if (isRightToLeft()) {
+ rect.moveRight(rect.right());
+ } else {
+ rect.moveLeft(rect.x() - toggleSize);
+ }
+ rect.setWidth(toggleSize);
+
+ QStyleOption opt;
+ opt.initFrom(this);
+ opt.rect = rect;
+ rect = style()->subElementRect(QStyle::SE_TreeViewDisclosureItem, &opt, this);
+
+ return rect.contains(pos);
+ }
+ }
+ return false;
+}
+
+void DolphinDetailsView::adjustMaximumSizeForEditing(const QModelIndex& index)
+{
+ // Make sure that the full width of the "Name" column is available for "Rename Inline"
+ m_extensionsFactory->fileItemDelegate()->setMaximumSize(QTreeView::visualRect(index).size());
+}
+
+DolphinDetailsView::ElasticBand::ElasticBand() :
+ show(false),
+ origin(),
+ destination(),
+ lastSelectionOrigin(),
+ lastSelectionDestination(),
+ ignoreOldInfo(true),
+ outsideNearestLeftEdge(0),
+ outsideNearestRightEdge(0),
+ insideNearestLeftEdge(0),
+ insideNearestRightEdge(0)
+{
+}
+
+#include "dolphindetailsview.moc"
diff --git a/src/views/dolphindetailsview.h b/src/views/dolphindetailsview.h
new file mode 100644
index 000000000..3ac08d337
--- /dev/null
+++ b/src/views/dolphindetailsview.h
@@ -0,0 +1,287 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Peter Penz ([email protected]) *
+ * Copyright (C) 2008 by Simon St. James ([email protected]) *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHINDETAILSVIEW_H
+#define DOLPHINDETAILSVIEW_H
+
+#include <QTreeView>
+#include <libdolphin_export.h>
+#include <views/dolphinview.h>
+
+class DolphinViewController;
+class DolphinSortFilterProxyModel;
+class ViewExtensionsFactory;
+
+/**
+ * @brief Represents the details view which shows the name, size,
+ * date, permissions, owner and group of an item.
+ *
+ * The width of the columns is automatically adjusted in a way
+ * that full available width of the view is used by stretching the width
+ * of the name column.
+ */
+class LIBDOLPHINPRIVATE_EXPORT DolphinDetailsView : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @param parent Parent widget.
+ * @param dolphinViewController Allows the DolphinDetailsView to control the
+ * DolphinView in a limited way.
+ * @param viewModeController Controller that is used by the DolphinView
+ * to control the DolphinDetailsView. The DolphinDetailsView
+ * only has read access to the controller.
+ * @param model Directory that is shown.
+ */
+ explicit DolphinDetailsView(QWidget* parent,
+ DolphinViewController* dolphinViewController,
+ const ViewModeController* viewModeController,
+ DolphinSortFilterProxyModel* model);
+ virtual ~DolphinDetailsView();
+
+ /**
+ * Returns a set containing the URLs of all expanded items.
+ */
+ QSet<KUrl> expandedUrls() const;
+
+ virtual QRegion visualRegionForSelection(const QItemSelection& selection) const;
+
+protected:
+ virtual bool event(QEvent* event);
+ virtual QStyleOptionViewItem viewOptions() const;
+ virtual void contextMenuEvent(QContextMenuEvent* event);
+ virtual void mousePressEvent(QMouseEvent* event);
+ virtual void mouseMoveEvent(QMouseEvent* event);
+ virtual void mouseReleaseEvent(QMouseEvent* event);
+ virtual void startDrag(Qt::DropActions supportedActions);
+ virtual void dragEnterEvent(QDragEnterEvent* event);
+ virtual void dragLeaveEvent(QDragLeaveEvent* event);
+ virtual void dragMoveEvent(QDragMoveEvent* event);
+ virtual void dropEvent(QDropEvent* event);
+ virtual void paintEvent(QPaintEvent* event);
+ virtual void keyPressEvent(QKeyEvent* event);
+ virtual void keyReleaseEvent(QKeyEvent* event);
+ virtual void resizeEvent(QResizeEvent* event);
+ virtual void wheelEvent(QWheelEvent* event);
+ virtual void currentChanged(const QModelIndex& current, const QModelIndex& previous);
+ virtual bool eventFilter(QObject* watched, QEvent* event);
+ virtual QModelIndex indexAt (const QPoint& point) const;
+ virtual QRect visualRect(const QModelIndex& index) const;
+ virtual void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command);
+ virtual void scrollTo(const QModelIndex& index, ScrollHint hint = EnsureVisible);
+
+private slots:
+ /**
+ * Sets the sort indicator section of the header view
+ * corresponding to \a sorting.
+ */
+ void setSortIndicatorSection(DolphinView::Sorting sorting);
+
+ /**
+ * Sets the sort indicator order of the header view
+ * corresponding to \a sortOrder.
+ */
+ void setSortIndicatorOrder(Qt::SortOrder sortOrder);
+
+ /**
+ * Synchronizes the sorting state of the Dolphin menu 'View -> Sort'
+ * with the current state of the details view.
+ * @param column Index of the current sorting column.
+ */
+ void synchronizeSortingState(int column);
+
+ /**
+ * Is invoked when the mouse cursor has entered an item. The controller
+ * gets informed to emit the itemEntered() signal if the mouse cursor
+ * is above the name column. Otherwise the controller gets informed
+ * to emit the itemViewportEntered() signal (all other columns should
+ * behave as viewport area).
+ */
+ void slotEntered(const QModelIndex& index);
+
+ /**
+ * Updates the destination \a destination from
+ * the elastic band to the current mouse position and triggers
+ * an update.
+ */
+ void updateElasticBand();
+
+ /**
+ * Returns the rectangle for the elastic band dependent from the
+ * origin \a origin, the current destination
+ * \a destination and the viewport position.
+ */
+ QRect elasticBandRect() const;
+
+ void setZoomLevel(int level);
+
+ void slotShowPreviewChanged();
+
+ /**
+ * Opens a context menu at the position \a pos and allows to
+ * configure the visibility of the header columns and whether
+ * expandable folders should be shown.
+ */
+ void configureSettings(const QPoint& pos);
+
+ /**
+ * Updates the visibilty state of columns and their order.
+ */
+ void updateColumnVisibility();
+
+ /**
+ * Saves order of the columns as global setting.
+ */
+ void saveColumnPositions();
+
+ /**
+ * Disables the automatical resizing of columns, if the user has resized the columns
+ * with the mouse.
+ */
+ void slotHeaderSectionResized(int logicalIndex, int oldSize, int newSize);
+
+ /**
+ * Changes the alternating row colors setting depending from
+ * the activation state \a active.
+ */
+ void slotActivationChanged(bool active);
+
+ /**
+ * Disables the automatical resizing of the columns. Per default all columns
+ * are resized to use the maximum available width of the view as good as possible.
+ */
+ void disableAutoResizing();
+
+ void requestActivation();
+
+ void slotGlobalSettingsChanged(int category);
+
+ /**
+ * If the elastic band is currently shown, update the elastic band based on
+ * the current mouse position and ensure that the selection is the set of items
+ * intersecting it.
+ */
+ void updateElasticBandSelection();
+
+ /**
+ * If \a expandable is true, the details view acts as tree view.
+ * The current expandable state is remembered in the settings.
+ */
+ void setFoldersExpandable(bool expandable);
+
+ /**
+ * These slots update the list of expanded items.
+ */
+ void slotExpanded(const QModelIndex& index);
+ void slotCollapsed(const QModelIndex& index);
+
+protected slots:
+
+ virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
+
+private:
+ /**
+ * Removes the URLs corresponding to the children of \a index in the rows
+ * between \a start and \a end inclusive from the set of expanded URLs.
+ */
+ void removeExpandedIndexes(const QModelIndex& parent, int start, int end);
+
+ /**
+ * Updates the size of the decoration dependent on the
+ * icon size of the DetailsModeSettings. The controller
+ * will get informed about possible zoom in/zoom out
+ * operations.
+ */
+ void updateDecorationSize(bool showPreview);
+
+ KFileItemDelegate::Information infoForColumn(int columnIndex) const;
+
+ /**
+ * Resizes all columns in a way to use the whole available width of the view.
+ */
+ void resizeColumns();
+
+ /**
+ * Returns true, if \a pos is within the expanding toggle of a tree.
+ */
+ bool isAboveExpandingToggle(const QPoint& pos) const;
+
+ /**
+ * Sets the maximum size available for editing in the delegate.
+ */
+ void adjustMaximumSizeForEditing(const QModelIndex& index);
+
+private:
+ bool m_autoResize : 1; // if true, the columns are resized automatically to the available width
+ bool m_expandingTogglePressed : 1;
+ bool m_keyPressed : 1; // true if a key is pressed currently; info used by currentChanged()
+ bool m_useDefaultIndexAt : 1; // true, if QTreeView::indexAt() should be used
+ bool m_ignoreScrollTo : 1; // true if calls to scrollTo(...) should do nothing.
+
+ DolphinViewController* m_dolphinViewController;
+ const ViewModeController* m_viewModeController;
+ ViewExtensionsFactory* m_extensionsFactory;
+ QAction* m_expandableFoldersAction;
+
+ // A set containing the URLs of all currently expanded folders.
+ // We cannot use a QSet<QModelIndex> because a QModelIndex is not guaranteed to remain valid over time.
+ // Also a QSet<QPersistentModelIndex> does not work as expected because it is not guaranteed that
+ // subsequent expand/collapse events of the same file item will yield the same QPersistentModelIndex.
+ QSet<KUrl> m_expandedUrls;
+
+ QFont m_font;
+ QSize m_decorationSize;
+
+ QRect m_dropRect;
+
+ struct ElasticBand
+ {
+ ElasticBand();
+
+ // Elastic band origin and destination coordinates are relative to t
+ // he origin of the view, not the viewport.
+ bool show;
+ QPoint origin;
+ QPoint destination;
+
+ // Optimization mechanisms for use with elastic band selection.
+ // Basically, allow "incremental" updates to the selection based
+ // on the last elastic band shape.
+ QPoint lastSelectionOrigin;
+ QPoint lastSelectionDestination;
+
+ // If true, compute the set of selected elements from scratch (slower)
+ bool ignoreOldInfo;
+
+ // Edges of the filenames that are closest to the edges of oldSelectionRect.
+ // Used to decide whether horizontal changes in the elastic band are likely
+ // to require us to re-check which items are selected.
+ int outsideNearestLeftEdge;
+ int outsideNearestRightEdge;
+ int insideNearestLeftEdge;
+ int insideNearestRightEdge;
+ // The set of items that were selected at the time this band was shown.
+ // NOTE: Unless CTRL was pressed when the band was created, this is always empty.
+ QItemSelection originalSelection;
+ } m_band;
+};
+
+#endif
diff --git a/src/views/dolphindetailsviewexpander.cpp b/src/views/dolphindetailsviewexpander.cpp
new file mode 100644
index 000000000..0f3d3fde1
--- /dev/null
+++ b/src/views/dolphindetailsviewexpander.cpp
@@ -0,0 +1,87 @@
+/***************************************************************************
+ * Copyright (C) 2009 by Frank Reininghaus ([email protected]) *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphindetailsviewexpander.h"
+
+#include "dolphindetailsview.h"
+#include "dolphinmodel.h"
+#include "dolphinsortfilterproxymodel.h"
+
+#include <kdirlister.h>
+#include <kdirmodel.h>
+
+DolphinDetailsViewExpander::DolphinDetailsViewExpander(DolphinDetailsView* parent,
+ const QSet<KUrl>& urlsToExpand) :
+ QObject(parent),
+ m_detailsView(parent),
+ m_dirLister(0),
+ m_dolphinModel(0),
+ m_proxyModel(0)
+{
+ Q_ASSERT(parent != 0);
+
+ m_proxyModel = qobject_cast<const DolphinSortFilterProxyModel*>(parent->model());
+ Q_ASSERT(m_proxyModel != 0);
+
+ m_dolphinModel = qobject_cast<const DolphinModel*>(m_proxyModel->sourceModel());
+ Q_ASSERT(m_dolphinModel != 0);
+
+ m_dirLister = m_dolphinModel->dirLister();
+ Q_ASSERT(m_dirLister != 0);
+
+ // The URLs must be sorted. E.g. /home/user/ cannot be expanded before /home/
+ // because it is not known to the dir model before.
+ m_urlsToExpand = urlsToExpand.toList();
+ qSort(m_urlsToExpand);
+
+ // The dir lister must have completed the folder listing before a subfolder can be expanded.
+ connect(m_dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted()));
+}
+
+DolphinDetailsViewExpander::~DolphinDetailsViewExpander()
+{
+}
+
+void DolphinDetailsViewExpander::stop()
+{
+ disconnect(m_dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted()));
+ deleteLater();
+}
+
+void DolphinDetailsViewExpander::slotDirListerCompleted()
+{
+ QModelIndex dirIndex;
+
+ while(!m_urlsToExpand.isEmpty() && !dirIndex.isValid()) {
+ const KUrl url = m_urlsToExpand.takeFirst();
+ dirIndex = m_dolphinModel->indexForUrl(url);
+ }
+
+ if(dirIndex.isValid()) {
+ // A valid model index was found. Note that only one item is expanded in each call of this slot
+ // because expanding any item will trigger KDirLister::openUrl(...) via KDirModel::fetchMore(...),
+ // and we can only continue when the dir lister is done.
+ const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
+ m_detailsView->expand(proxyIndex);
+ }
+ else {
+ emit completed();
+ stop();
+ }
+}
diff --git a/src/views/dolphindetailsviewexpander.h b/src/views/dolphindetailsviewexpander.h
new file mode 100644
index 000000000..b4dc2fc72
--- /dev/null
+++ b/src/views/dolphindetailsviewexpander.h
@@ -0,0 +1,77 @@
+/***************************************************************************
+ * Copyright (C) 2009 by Frank Reininghaus ([email protected]) *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHINDETAILSVIEWEXPANDER_H
+#define DOLPHINDETAILSVIEWEXPANDER_H
+
+#include <QObject>
+#include <QSet>
+#include <QList>
+
+class DolphinDetailsView;
+class KUrl;
+class KDirLister;
+class DolphinModel;
+class DolphinSortFilterProxyModel;
+
+/**
+ * @brief Expands a given set of subfolders in collaboration with the dir lister and the dir model.
+ *
+ * Note that only one subfolder can be expanded at a time. Each expansion triggers KDirLister::openUrl(...),
+ * and further expansions can only be done the next time the dir lister emits its completed() signal.
+ */
+class DolphinDetailsViewExpander : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit DolphinDetailsViewExpander(DolphinDetailsView* parent,
+ const QSet<KUrl>& urlsToExpand);
+
+ virtual ~DolphinDetailsViewExpander();
+
+ /**
+ * Stops the expansion and deletes the object via deleteLater().
+ */
+ void stop();
+
+private slots:
+ /**
+ * This slot is invoked every time the dir lister has completed a listing.
+ * It expands the first URL from the list m_urlsToExpand that can be found in the dir model.
+ * If the list is empty, stop() is called.
+ */
+ void slotDirListerCompleted();
+
+signals:
+ /**
+ * Is emitted when the expander has finished expanding URLs in the details view.
+ */
+ void completed();
+
+private:
+ QList<KUrl> m_urlsToExpand;
+
+ DolphinDetailsView* m_detailsView;
+ const KDirLister* m_dirLister;
+ const DolphinModel* m_dolphinModel;
+ const DolphinSortFilterProxyModel* m_proxyModel;
+};
+
+#endif
diff --git a/src/views/dolphinfileitemdelegate.cpp b/src/views/dolphinfileitemdelegate.cpp
new file mode 100644
index 000000000..17447d8cb
--- /dev/null
+++ b/src/views/dolphinfileitemdelegate.cpp
@@ -0,0 +1,180 @@
+/***************************************************************************
+ * Copyright (C) 2008 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphinfileitemdelegate.h"
+
+#include <dolphinmodel.h>
+#include <kfileitem.h>
+#include <kicon.h>
+#include <kiconloader.h>
+
+#include <QAbstractItemModel>
+#include <QAbstractProxyModel>
+#include <QFontMetrics>
+#include <QPalette>
+#include <QPainter>
+#include <QStyleOptionViewItemV4>
+
+DolphinFileItemDelegate::DolphinFileItemDelegate(QObject* parent) :
+ KFileItemDelegate(parent),
+ m_hasMinimizedNameColumn(false),
+ m_cachedSize(),
+ m_cachedEmblems()
+{
+ setJobTransfersVisible(true);
+}
+
+DolphinFileItemDelegate::~DolphinFileItemDelegate()
+{
+}
+
+void DolphinFileItemDelegate::paint(QPainter* painter,
+ const QStyleOptionViewItem& option,
+ const QModelIndex& index) const
+{
+ const QAbstractProxyModel* proxyModel = static_cast<const QAbstractProxyModel*>(index.model());
+ const DolphinModel* dolphinModel = static_cast<const DolphinModel*>(proxyModel->sourceModel());
+ const bool isNameColumn = (index.column() == KDirModel::Name);
+
+ QStyleOptionViewItemV4 opt(option);
+ if (m_hasMinimizedNameColumn && isNameColumn) {
+ adjustOptionWidth(opt, proxyModel, dolphinModel, index);
+ }
+
+ if (dolphinModel->hasVersionData() && isNameColumn) {
+ // The currently shown items are under revision control. Show the current revision
+ // state by adding an emblem and changing the text tintColor.
+ const QModelIndex dirIndex = proxyModel->mapToSource(index);
+ const QModelIndex revisionIndex = dolphinModel->index(dirIndex.row(), DolphinModel::Version, dirIndex.parent());
+ const QVariant data = dolphinModel->data(revisionIndex, Qt::DecorationRole);
+ const KVersionControlPlugin::VersionState state = static_cast<KVersionControlPlugin::VersionState>(data.toInt());
+
+ adjustOptionTextColor(opt, state);
+
+ KFileItemDelegate::paint(painter, opt, index);
+
+ if (state != KVersionControlPlugin::UnversionedVersion) {
+ const QRect rect = iconRect(option, index);
+ const QPixmap emblem = emblemForState(state, rect.size());
+ painter->drawPixmap(rect.x(), rect.y() + rect.height() - emblem.height(), emblem);
+ }
+ } else {
+ KFileItemDelegate::paint(painter, opt, index);
+ }
+}
+
+int DolphinFileItemDelegate::nameColumnWidth(const QString& name, const QStyleOptionViewItem& option)
+{
+ QFontMetrics fontMetrics(option.font);
+ int width = option.decorationSize.width() + fontMetrics.width(name) + 16;
+
+ const int defaultWidth = option.rect.width();
+ if ((defaultWidth > 0) && (defaultWidth < width)) {
+ width = defaultWidth;
+ }
+ return width;
+}
+
+void DolphinFileItemDelegate::adjustOptionWidth(QStyleOptionViewItemV4& option,
+ const QAbstractProxyModel* proxyModel,
+ const DolphinModel* dolphinModel,
+ const QModelIndex& index)
+{
+ const QModelIndex dirIndex = proxyModel->mapToSource(index);
+ const KFileItem item = dolphinModel->itemForIndex(dirIndex);
+ if (!item.isNull()) {
+ // symbolic links are displayed in an italic font
+ if (item.isLink()) {
+ option.font.setItalic(true);
+ }
+
+ const int width = nameColumnWidth(item.text(), option);
+ option.rect.setWidth(width);
+ }
+}
+
+void DolphinFileItemDelegate::adjustOptionTextColor(QStyleOptionViewItemV4& option,
+ KVersionControlPlugin::VersionState state)
+{
+ QColor tintColor;
+
+ // Using hardcoded colors is generally a bad idea. In this case the colors just act
+ // as tint colors and are mixed with the current set text color. The tint colors
+ // have been optimized for the base colors of the corresponding Oxygen emblems.
+ switch (state) {
+ case KVersionControlPlugin::UpdateRequiredVersion: tintColor = Qt::yellow; break;
+ case KVersionControlPlugin::LocallyModifiedVersion: tintColor = Qt::green; break;
+ case KVersionControlPlugin::AddedVersion: tintColor = Qt::darkGreen; break;
+ case KVersionControlPlugin::RemovedVersion: tintColor = Qt::darkRed; break;
+ case KVersionControlPlugin::ConflictingVersion: tintColor = Qt::red; break;
+ case KVersionControlPlugin::UnversionedVersion:
+ case KVersionControlPlugin::NormalVersion:
+ default:
+ // use the default text color
+ return;
+ }
+
+ QPalette palette = option.palette;
+ const QColor textColor = palette.color(QPalette::Text);
+ tintColor = QColor((tintColor.red() + textColor.red()) / 2,
+ (tintColor.green() + textColor.green()) / 2,
+ (tintColor.blue() + textColor.blue()) / 2,
+ (tintColor.alpha() + textColor.alpha()) / 2);
+ palette.setColor(QPalette::Text, tintColor);
+ option.palette = palette;
+}
+
+QPixmap DolphinFileItemDelegate::emblemForState(KVersionControlPlugin::VersionState state, const QSize& size) const
+{
+ Q_ASSERT(state <= KVersionControlPlugin::ConflictingVersion);
+ if (m_cachedSize != size) {
+ m_cachedSize = size;
+
+ const int iconHeight = size.height();
+ int emblemHeight = KIconLoader::SizeSmall;
+ if (iconHeight >= KIconLoader::SizeEnormous) {
+ emblemHeight = KIconLoader::SizeMedium;
+ } else if (iconHeight >= KIconLoader::SizeLarge) {
+ emblemHeight = KIconLoader::SizeSmallMedium;
+ } else if (iconHeight >= KIconLoader::SizeMedium) {
+ emblemHeight = KIconLoader::SizeSmall;
+ } else {
+ emblemHeight = KIconLoader::SizeSmall / 2;
+ }
+
+ const QSize emblemSize(emblemHeight, emblemHeight);
+ for (int i = KVersionControlPlugin::NormalVersion; i <= KVersionControlPlugin::ConflictingVersion; ++i) {
+ QString iconName;
+ switch (i) {
+ case KVersionControlPlugin::NormalVersion: iconName = "vcs-normal"; break;
+ case KVersionControlPlugin::UpdateRequiredVersion: iconName = "vcs-update-required"; break;
+ case KVersionControlPlugin::LocallyModifiedVersion: iconName = "vcs-locally-modified"; break;
+ case KVersionControlPlugin::AddedVersion: iconName = "vcs-added"; break;
+ case KVersionControlPlugin::RemovedVersion: iconName = "vcs-removed"; break;
+ case KVersionControlPlugin::ConflictingVersion: iconName = "vcs-conflicting"; break;
+ case KVersionControlPlugin::UnversionedVersion:
+ default: Q_ASSERT(false); break;
+ }
+
+ m_cachedEmblems[i] = KIcon(iconName).pixmap(emblemSize);
+ }
+ }
+ return m_cachedEmblems[state];
+}
+
diff --git a/src/views/dolphinfileitemdelegate.h b/src/views/dolphinfileitemdelegate.h
new file mode 100644
index 000000000..405f24916
--- /dev/null
+++ b/src/views/dolphinfileitemdelegate.h
@@ -0,0 +1,90 @@
+/***************************************************************************
+ * Copyright (C) 2008 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHINFILEITEMDELEGATE_H
+#define DOLPHINFILEITEMDELEGATE_H
+
+#include <dolphinmodel.h>
+#include <kfileitemdelegate.h>
+
+class QAbstractProxyModel;
+
+/**
+ * Extends KFileItemDelegate by the ability to show the hover effect
+ * and the selection in a minimized way for the name column of
+ * the details view.
+ *
+ * Note that this is a workaround, as Qt does not support having custom
+ * shapes within the visual rect of an item view. The visual part of
+ * workaround is handled inside DolphinFileItemDelegate, the behavior
+ * changes are handled in DolphinDetailsView.
+ */
+class DolphinFileItemDelegate : public KFileItemDelegate
+{
+public:
+ explicit DolphinFileItemDelegate(QObject* parent = 0);
+ virtual ~DolphinFileItemDelegate();
+
+ /**
+ * If \a minimized is true, the hover effect and the selection are
+ * only drawn above the icon and text of an item. Per default
+ * \a minimized is false, which means that the whole visual rect is
+ * used like in KFileItemDelegate.
+ */
+ void setMinimizedNameColumn(bool minimized);
+ bool hasMinimizedNameColumn() const;
+
+ virtual void paint(QPainter* painter,
+ const QStyleOptionViewItem& option,
+ const QModelIndex& index) const;
+
+ /**
+ * Returns the minimized width of the name column for the name \a name. This method
+ * is also used in DolphinDetailsView to handle the selection of items correctly.
+ */
+ static int nameColumnWidth(const QString& name, const QStyleOptionViewItem& option);
+
+private:
+ static void adjustOptionWidth(QStyleOptionViewItemV4& option,
+ const QAbstractProxyModel* proxyModel,
+ const DolphinModel* dolphinModel,
+ const QModelIndex& index);
+
+ static void adjustOptionTextColor(QStyleOptionViewItemV4& option,
+ KVersionControlPlugin::VersionState state);
+
+ QPixmap emblemForState(KVersionControlPlugin::VersionState state, const QSize& size) const;
+
+private:
+ bool m_hasMinimizedNameColumn;
+ mutable QSize m_cachedSize;
+ mutable QPixmap m_cachedEmblems[KVersionControlPlugin::ConflictingVersion + 1];
+};
+
+inline void DolphinFileItemDelegate::setMinimizedNameColumn(bool minimized)
+{
+ m_hasMinimizedNameColumn = minimized;
+}
+
+inline bool DolphinFileItemDelegate::hasMinimizedNameColumn() const
+{
+ return m_hasMinimizedNameColumn;
+}
+
+#endif
diff --git a/src/views/dolphiniconsview.cpp b/src/views/dolphiniconsview.cpp
new file mode 100644
index 000000000..636bdd66c
--- /dev/null
+++ b/src/views/dolphiniconsview.cpp
@@ -0,0 +1,532 @@
+/***************************************************************************
+ * Copyright (C) 2006-2009 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphiniconsview.h"
+
+#include "dolphincategorydrawer.h"
+#include "dolphinviewcontroller.h"
+#include "settings/dolphinsettings.h"
+#include "dolphinsortfilterproxymodel.h"
+#include "dolphin_iconsmodesettings.h"
+#include "dolphin_generalsettings.h"
+#include "draganddrophelper.h"
+#include "selectionmanager.h"
+#include "viewextensionsfactory.h"
+#include "viewmodecontroller.h"
+#include "zoomlevelinfo.h"
+
+#include <kcategorizedsortfilterproxymodel.h>
+#include <kdialog.h>
+#include <kfileitemdelegate.h>
+
+#include <QAbstractProxyModel>
+#include <QApplication>
+#include <QScrollBar>
+
+DolphinIconsView::DolphinIconsView(QWidget* parent,
+ DolphinViewController* dolphinViewController,
+ const ViewModeController* viewModeController,
+ DolphinSortFilterProxyModel* proxyModel) :
+ KCategorizedView(parent),
+ m_dolphinViewController(dolphinViewController),
+ m_viewModeController(viewModeController),
+ m_categoryDrawer(new DolphinCategoryDrawer(this)),
+ m_extensionsFactory(0),
+ m_font(),
+ m_decorationSize(),
+ m_decorationPosition(QStyleOptionViewItem::Top),
+ m_displayAlignment(Qt::AlignHCenter),
+ m_itemSize(),
+ m_dropRect()
+{
+ Q_ASSERT(dolphinViewController != 0);
+ Q_ASSERT(viewModeController != 0);
+
+ setModel(proxyModel);
+ setLayoutDirection(Qt::LeftToRight);
+ setViewMode(QListView::IconMode);
+ setResizeMode(QListView::Adjust);
+ setMovement(QListView::Static);
+ setDragEnabled(true);
+ setEditTriggers(QAbstractItemView::NoEditTriggers);
+ viewport()->setAcceptDrops(true);
+
+ setMouseTracking(true);
+
+ connect(this, SIGNAL(clicked(const QModelIndex&)),
+ dolphinViewController, SLOT(requestTab(const QModelIndex&)));
+ if (KGlobalSettings::singleClick()) {
+ connect(this, SIGNAL(clicked(const QModelIndex&)),
+ dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
+ } else {
+ connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
+ dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
+ }
+
+ connect(this, SIGNAL(entered(const QModelIndex&)),
+ dolphinViewController, SLOT(emitItemEntered(const QModelIndex&)));
+ connect(this, SIGNAL(viewportEntered()),
+ dolphinViewController, SLOT(emitViewportEntered()));
+ connect(viewModeController, SIGNAL(zoomLevelChanged(int)),
+ this, SLOT(setZoomLevel(int)));
+
+ const DolphinView* view = dolphinViewController->view();
+ connect(view, SIGNAL(showPreviewChanged()),
+ this, SLOT(slotShowPreviewChanged()));
+ connect(view, SIGNAL(additionalInfoChanged()),
+ this, SLOT(slotAdditionalInfoChanged()));
+
+ // apply the icons mode settings to the widget
+ const IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings();
+ Q_ASSERT(settings != 0);
+
+ if (settings->useSystemFont()) {
+ m_font = KGlobalSettings::generalFont();
+ } else {
+ m_font = QFont(settings->fontFamily(),
+ qRound(settings->fontSize()),
+ settings->fontWeight(),
+ settings->italicFont());
+ m_font.setPointSizeF(settings->fontSize());
+ }
+
+ setWordWrap(settings->numberOfTextlines() > 1);
+
+ if (settings->arrangement() == QListView::TopToBottom) {
+ setFlow(QListView::LeftToRight);
+ m_decorationPosition = QStyleOptionViewItem::Top;
+ m_displayAlignment = Qt::AlignHCenter;
+ } else {
+ setFlow(QListView::TopToBottom);
+ m_decorationPosition = QStyleOptionViewItem::Left;
+ m_displayAlignment = Qt::AlignLeft | Qt::AlignVCenter;
+ }
+
+ connect(m_categoryDrawer, SIGNAL(actionRequested(int,QModelIndex)), this, SLOT(categoryDrawerActionRequested(int,QModelIndex)));
+ setCategoryDrawer(m_categoryDrawer);
+
+ setFocus();
+
+ connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)),
+ this, SLOT(slotGlobalSettingsChanged(int)));
+
+ updateGridSize(view->showPreview(), 0);
+ m_extensionsFactory = new ViewExtensionsFactory(this, dolphinViewController, viewModeController);
+}
+
+DolphinIconsView::~DolphinIconsView()
+{
+}
+
+void DolphinIconsView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
+{
+ KCategorizedView::dataChanged(topLeft, bottomRight);
+
+ KCategorizedSortFilterProxyModel* proxyModel = dynamic_cast<KCategorizedSortFilterProxyModel*>(model());
+ if (!proxyModel->isCategorizedModel()) {
+ // bypass a QListView issue that items are not layout correctly if the decoration size of
+ // an index changes
+ scheduleDelayedItemsLayout();
+ }
+}
+
+QStyleOptionViewItem DolphinIconsView::viewOptions() const
+{
+ QStyleOptionViewItem viewOptions = KCategorizedView::viewOptions();
+ viewOptions.font = m_font;
+ viewOptions.fontMetrics = QFontMetrics(m_font);
+ viewOptions.decorationPosition = m_decorationPosition;
+ viewOptions.decorationSize = m_decorationSize;
+ viewOptions.displayAlignment = m_displayAlignment;
+ viewOptions.showDecorationSelected = true;
+ return viewOptions;
+}
+
+void DolphinIconsView::contextMenuEvent(QContextMenuEvent* event)
+{
+ KCategorizedView::contextMenuEvent(event);
+ m_dolphinViewController->triggerContextMenuRequest(event->pos());
+}
+
+void DolphinIconsView::mousePressEvent(QMouseEvent* event)
+{
+ m_dolphinViewController->requestActivation();
+ const QModelIndex index = indexAt(event->pos());
+ if (index.isValid() && (event->button() == Qt::LeftButton)) {
+ // TODO: It should not be necessary to manually set the dragging state, but I could
+ // not reproduce this issue with a Qt-only example yet to find the root cause.
+ // Issue description: start Dolphin, split the view and drag an item from the
+ // inactive view to the active view by a very fast mouse movement. Result:
+ // the item gets selected instead of being dragged...
+ setState(QAbstractItemView::DraggingState);
+ }
+
+ if (!index.isValid() && (QApplication::mouseButtons() & Qt::MidButton)) {
+ m_dolphinViewController->replaceUrlByClipboard();
+ }
+
+ KCategorizedView::mousePressEvent(event);
+}
+
+void DolphinIconsView::startDrag(Qt::DropActions supportedActions)
+{
+ DragAndDropHelper::instance().startDrag(this, supportedActions, m_dolphinViewController);
+}
+
+void DolphinIconsView::dragEnterEvent(QDragEnterEvent* event)
+{
+ if (DragAndDropHelper::instance().isMimeDataSupported(event->mimeData())) {
+ event->acceptProposedAction();
+ }
+}
+
+void DolphinIconsView::dragLeaveEvent(QDragLeaveEvent* event)
+{
+ KCategorizedView::dragLeaveEvent(event);
+ setDirtyRegion(m_dropRect);
+}
+
+void DolphinIconsView::dragMoveEvent(QDragMoveEvent* event)
+{
+ KCategorizedView::dragMoveEvent(event);
+
+ // TODO: remove this code when the issue #160611 is solved in Qt 4.4
+ const QModelIndex index = indexAt(event->pos());
+ setDirtyRegion(m_dropRect);
+
+ m_dropRect.setSize(QSize()); // set as invalid
+ if (index.isValid()) {
+ const KFileItem item = m_dolphinViewController->itemForIndex(index);
+ if (!item.isNull() && item.isDir()) {
+ m_dropRect = visualRect(index);
+ } else {
+ m_dropRect.setSize(QSize()); // set as invalid
+ }
+ }
+ if (DragAndDropHelper::instance().isMimeDataSupported(event->mimeData())) {
+ // accept url drops, independently from the destination item
+ event->acceptProposedAction();
+ }
+
+ setDirtyRegion(m_dropRect);
+}
+
+void DolphinIconsView::dropEvent(QDropEvent* event)
+{
+ const QModelIndex index = indexAt(event->pos());
+ const KFileItem item = m_dolphinViewController->itemForIndex(index);
+ m_dolphinViewController->indicateDroppedUrls(item, m_viewModeController->url(), event);
+ // don't call KCategorizedView::dropEvent(event), as it moves
+ // the items which is not wanted
+}
+
+QModelIndex DolphinIconsView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
+{
+ const QModelIndex oldCurrent = currentIndex();
+
+ QModelIndex newCurrent = KCategorizedView::moveCursor(cursorAction, modifiers);
+ if (newCurrent != oldCurrent) {
+ return newCurrent;
+ }
+
+ // The cursor has not been moved by the base implementation. Provide a
+ // wrap behavior, so that the cursor will go to the next item when reaching
+ // the border.
+ const IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings();
+ if (settings->arrangement() == QListView::LeftToRight) {
+ switch (cursorAction) {
+ case MoveUp:
+ if (newCurrent.row() == 0) {
+ return newCurrent;
+ }
+ newCurrent = KCategorizedView::moveCursor(MoveLeft, modifiers);
+ selectionModel()->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate);
+ newCurrent = KCategorizedView::moveCursor(MovePageDown, modifiers);
+ break;
+
+ case MoveDown:
+ if (newCurrent.row() == (model()->rowCount() - 1)) {
+ return newCurrent;
+ }
+ newCurrent = KCategorizedView::moveCursor(MovePageUp, modifiers);
+ selectionModel()->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate);
+ newCurrent = KCategorizedView::moveCursor(MoveRight, modifiers);
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ QModelIndex current = oldCurrent;
+ switch (cursorAction) {
+ case MoveLeft:
+ if (newCurrent.row() == 0) {
+ return newCurrent;
+ }
+ newCurrent = KCategorizedView::moveCursor(MoveUp, modifiers);
+ do {
+ selectionModel()->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate);
+ current = newCurrent;
+ newCurrent = KCategorizedView::moveCursor(MoveRight, modifiers);
+ } while (newCurrent != current);
+ break;
+
+ case MoveRight:
+ if (newCurrent.row() == (model()->rowCount() - 1)) {
+ return newCurrent;
+ }
+ do {
+ selectionModel()->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate);
+ current = newCurrent;
+ newCurrent = KCategorizedView::moveCursor(MoveLeft, modifiers);
+ } while (newCurrent != current);
+ newCurrent = KCategorizedView::moveCursor(MoveDown, modifiers);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Revert all changes of the current item to make sure that item selection works correctly
+ selectionModel()->setCurrentIndex(oldCurrent, QItemSelectionModel::NoUpdate);
+ return newCurrent;
+}
+
+void DolphinIconsView::keyPressEvent(QKeyEvent* event)
+{
+ KCategorizedView::keyPressEvent(event);
+ m_dolphinViewController->handleKeyPressEvent(event);
+}
+
+void DolphinIconsView::wheelEvent(QWheelEvent* event)
+{
+ horizontalScrollBar()->setSingleStep(m_itemSize.width() / 5);
+ verticalScrollBar()->setSingleStep(m_itemSize.height() / 5);
+
+ KCategorizedView::wheelEvent(event);
+ // if the icons are aligned left to right, the vertical wheel event should
+ // be applied to the horizontal scrollbar
+ const IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings();
+ const bool scrollHorizontal = (event->orientation() == Qt::Vertical) &&
+ (settings->arrangement() == QListView::LeftToRight);
+ if (scrollHorizontal) {
+ QWheelEvent horizEvent(event->pos(),
+ event->delta(),
+ event->buttons(),
+ event->modifiers(),
+ Qt::Horizontal);
+ QApplication::sendEvent(horizontalScrollBar(), &horizEvent);
+ }
+}
+
+void DolphinIconsView::showEvent(QShowEvent* event)
+{
+ KFileItemDelegate* delegate = dynamic_cast<KFileItemDelegate*>(itemDelegate());
+ delegate->setMaximumSize(m_itemSize);
+
+ KCategorizedView::showEvent(event);
+}
+
+void DolphinIconsView::leaveEvent(QEvent* event)
+{
+ KCategorizedView::leaveEvent(event);
+ // if the mouse is above an item and moved very fast outside the widget,
+ // no viewportEntered() signal might be emitted although the mouse has been moved
+ // above the viewport
+ m_dolphinViewController->emitViewportEntered();
+}
+
+void DolphinIconsView::currentChanged(const QModelIndex& current, const QModelIndex& previous)
+{
+ KCategorizedView::currentChanged(current, previous);
+ m_extensionsFactory->handleCurrentIndexChange(current, previous);
+}
+
+void DolphinIconsView::resizeEvent(QResizeEvent* event)
+{
+ KCategorizedView::resizeEvent(event);
+ const DolphinView* view = m_dolphinViewController->view();
+ updateGridSize(view->showPreview(), view->additionalInfo().count());
+}
+
+void DolphinIconsView::slotShowPreviewChanged()
+{
+ const DolphinView* view = m_dolphinViewController->view();
+ updateGridSize(view->showPreview(), additionalInfoCount());
+}
+
+void DolphinIconsView::slotAdditionalInfoChanged()
+{
+ const DolphinView* view = m_dolphinViewController->view();
+ const bool showPreview = view->showPreview();
+ updateGridSize(showPreview, view->additionalInfo().count());
+}
+
+void DolphinIconsView::setZoomLevel(int level)
+{
+ IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings();
+
+ const int oldIconSize = settings->iconSize();
+ int newIconSize = oldIconSize;
+
+ const bool showPreview = m_dolphinViewController->view()->showPreview();
+ if (showPreview) {
+ const int previewSize = ZoomLevelInfo::iconSizeForZoomLevel(level);
+ settings->setPreviewSize(previewSize);
+ } else {
+ newIconSize = ZoomLevelInfo::iconSizeForZoomLevel(level);
+ settings->setIconSize(newIconSize);
+ }
+
+ // increase also the grid size
+ const int diff = newIconSize - oldIconSize;
+ settings->setItemWidth(settings->itemWidth() + diff);
+ settings->setItemHeight(settings->itemHeight() + diff);
+
+ updateGridSize(showPreview, additionalInfoCount());
+}
+
+void DolphinIconsView::requestActivation()
+{
+ m_dolphinViewController->requestActivation();
+}
+
+void DolphinIconsView::slotGlobalSettingsChanged(int category)
+{
+ Q_UNUSED(category);
+
+ const IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings();
+ Q_ASSERT(settings != 0);
+ if (settings->useSystemFont()) {
+ m_font = KGlobalSettings::generalFont();
+ }
+
+ disconnect(this, SIGNAL(clicked(QModelIndex)), m_dolphinViewController, SLOT(triggerItem(QModelIndex)));
+ disconnect(this, SIGNAL(doubleClicked(QModelIndex)), m_dolphinViewController, SLOT(triggerItem(QModelIndex)));
+ if (KGlobalSettings::singleClick()) {
+ connect(this, SIGNAL(clicked(QModelIndex)), m_dolphinViewController, SLOT(triggerItem(QModelIndex)));
+ } else {
+ connect(this, SIGNAL(doubleClicked(QModelIndex)), m_dolphinViewController, SLOT(triggerItem(QModelIndex)));
+ }
+}
+
+void DolphinIconsView::categoryDrawerActionRequested(int action, const QModelIndex &index)
+{
+ const QSortFilterProxyModel *model = dynamic_cast<const QSortFilterProxyModel*>(index.model());
+ const QModelIndex topLeft = model->index(index.row(), modelColumn());
+ QModelIndex bottomRight = topLeft;
+ const QString category = model->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
+ QModelIndex current = topLeft;
+ while (true) {
+ current = model->index(current.row() + 1, modelColumn());
+ const QString curCategory = model->data(model->index(current.row(), index.column()), KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
+ if (!current.isValid() || category != curCategory) {
+ break;
+ }
+ bottomRight = current;
+ }
+ switch (action) {
+ case DolphinCategoryDrawer::SelectAll:
+ selectionModel()->select(QItemSelection(topLeft, bottomRight), QItemSelectionModel::Select);
+ break;
+ case DolphinCategoryDrawer::UnselectAll:
+ selectionModel()->select(QItemSelection(topLeft, bottomRight), QItemSelectionModel::Deselect);
+ break;
+ default:
+ break;
+ }
+}
+
+void DolphinIconsView::updateGridSize(bool showPreview, int additionalInfoCount)
+{
+ const IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings();
+ Q_ASSERT(settings != 0);
+
+ int itemWidth = settings->itemWidth();
+ int itemHeight = settings->itemHeight();
+ int size = settings->iconSize();
+
+ if (showPreview) {
+ const int previewSize = settings->previewSize();
+ const int diff = previewSize - size;
+ itemWidth += diff;
+ itemHeight += diff;
+
+ size = previewSize;
+ }
+
+ Q_ASSERT(additionalInfoCount >= 0);
+ itemHeight += additionalInfoCount * QFontMetrics(m_font).height();
+
+ // Optimize the item size of the grid in a way to prevent large gaps on the
+ // right border (= row arrangement) or the bottom border (= column arrangement).
+ // There is no public API in QListView to find out the used width of the viewport
+ // for the layout. The following calculation of 'contentWidth'/'contentHeight'
+ // is based on QListViewPrivate::prepareItemsLayout() (Copyright (C) 2009 Nokia Corporation).
+ int frameAroundContents = 0;
+ if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) {
+ frameAroundContents = style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2;
+ }
+ const int spacing = settings->gridSpacing();
+ if (settings->arrangement() == QListView::TopToBottom) {
+ const int contentWidth = viewport()->width() - 1
+ - frameAroundContents
+ - style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, horizontalScrollBar());
+ const int gridWidth = itemWidth + spacing * 2;
+ const int horizItemCount = contentWidth / gridWidth;
+ if (horizItemCount > 0) {
+ itemWidth += (contentWidth - horizItemCount * gridWidth) / horizItemCount;
+ }
+
+ // The decoration width indirectly defines the maximum
+ // width for the text wrapping. To use the maximum item width
+ // for text wrapping, it is used as decoration width.
+ m_decorationSize = QSize(itemWidth, size);
+ setIconSize(QSize(itemWidth, size));
+ } else {
+ const int contentHeight = viewport()->height() - 1
+ - frameAroundContents
+ - style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, verticalScrollBar());
+ const int gridHeight = itemHeight + spacing;
+ const int vertItemCount = contentHeight / gridHeight;
+ if (vertItemCount > 0) {
+ itemHeight += (contentHeight - vertItemCount * gridHeight) / vertItemCount;
+ }
+
+ m_decorationSize = QSize(size, size);
+ setIconSize(QSize(size, size));
+ }
+
+ m_itemSize = QSize(itemWidth, itemHeight);
+ setGridSizeOwn(QSize(itemWidth + spacing * 2, itemHeight + spacing));
+
+ KFileItemDelegate* delegate = dynamic_cast<KFileItemDelegate*>(itemDelegate());
+ if (delegate != 0) {
+ delegate->setMaximumSize(m_itemSize);
+ }
+}
+
+int DolphinIconsView::additionalInfoCount() const
+{
+ const DolphinView* view = m_dolphinViewController->view();
+ return view->additionalInfo().count();
+}
+
+#include "dolphiniconsview.moc"
diff --git a/src/views/dolphiniconsview.h b/src/views/dolphiniconsview.h
new file mode 100644
index 000000000..21e37ba02
--- /dev/null
+++ b/src/views/dolphiniconsview.h
@@ -0,0 +1,122 @@
+/***************************************************************************
+ * Copyright (C) 2006-2009 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHINICONSVIEW_H
+#define DOLPHINICONSVIEW_H
+
+#include <kcategorizedview.h>
+
+#include <kfileitem.h>
+#include <kfileitemdelegate.h>
+
+#include <QFont>
+#include <QSize>
+#include <QStyleOption>
+
+#include <libdolphin_export.h>
+
+class DolphinViewController;
+class DolphinCategoryDrawer;
+class DolphinSortFilterProxyModel;
+class ViewExtensionsFactory;
+class ViewModeController;
+
+/**
+ * @brief Represents the view, where each item is shown as an icon.
+ *
+ * It is also possible that instead of the icon a preview of the item
+ * content is shown.
+ */
+class LIBDOLPHINPRIVATE_EXPORT DolphinIconsView : public KCategorizedView
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @param parent Parent widget.
+ * @param dolphinViewController Allows the DolphinIconsView to control the
+ * DolphinView in a limited way.
+ * @param viewModeController Controller that is used by the DolphinView
+ * to control the DolphinIconsView. The DolphinIconsView
+ * only has read access to the controller.
+ * @param model Directory that is shown.
+ */
+ explicit DolphinIconsView(QWidget* parent,
+ DolphinViewController* dolphinViewController,
+ const ViewModeController* viewModeController,
+ DolphinSortFilterProxyModel* proxyModel);
+ virtual ~DolphinIconsView();
+
+protected slots:
+ virtual void dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
+
+protected:
+ virtual QStyleOptionViewItem viewOptions() const;
+ virtual void contextMenuEvent(QContextMenuEvent* event);
+ virtual void mousePressEvent(QMouseEvent* event);
+ virtual void startDrag(Qt::DropActions supportedActions);
+ virtual void dragEnterEvent(QDragEnterEvent* event);
+ virtual void dragLeaveEvent(QDragLeaveEvent* event);
+ virtual void dragMoveEvent(QDragMoveEvent* event);
+ virtual void dropEvent(QDropEvent* event);
+ virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
+ virtual void keyPressEvent(QKeyEvent* event);
+ virtual void wheelEvent(QWheelEvent* event);
+ virtual void showEvent(QShowEvent* event);
+ virtual void leaveEvent(QEvent* event);
+ virtual void currentChanged(const QModelIndex& current, const QModelIndex& previous);
+ virtual void resizeEvent(QResizeEvent* event);
+
+private slots:
+ void slotShowPreviewChanged();
+ void slotAdditionalInfoChanged();
+ void setZoomLevel(int level);
+ void requestActivation();
+ void slotGlobalSettingsChanged(int category);
+ void categoryDrawerActionRequested(int action, const QModelIndex &index);
+
+private:
+ /**
+ * Updates the size of the grid depending on the state
+ * of \a showPreview and \a additionalInfoCount.
+ */
+ void updateGridSize(bool showPreview, int additionalInfoCount);
+
+ /**
+ * Returns the number of additional information lines that should
+ * be shown below the item name.
+ */
+ int additionalInfoCount() const;
+
+private:
+ DolphinViewController* m_dolphinViewController;
+ const ViewModeController* m_viewModeController;
+ DolphinCategoryDrawer* m_categoryDrawer;
+ ViewExtensionsFactory* m_extensionsFactory;
+
+ QFont m_font;
+ QSize m_decorationSize;
+ QStyleOptionViewItem::Position m_decorationPosition;
+ Qt::Alignment m_displayAlignment;
+
+ QSize m_itemSize;
+ QRect m_dropRect;
+};
+
+#endif
diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp
new file mode 100644
index 000000000..73a0c63c3
--- /dev/null
+++ b/src/views/dolphinview.cpp
@@ -0,0 +1,1569 @@
+/***************************************************************************
+ * Copyright (C) 2006-2009 by Peter Penz <[email protected]> *
+ * Copyright (C) 2006 by Gregor Kališnik <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphinview.h"
+
+#include <QAbstractItemView>
+#include <QApplication>
+#include <QClipboard>
+#include <QKeyEvent>
+#include <QItemSelection>
+#include <QBoxLayout>
+#include <QTimer>
+#include <QScrollBar>
+
+#include <kactioncollection.h>
+#include <kcolorscheme.h>
+#include <kdirlister.h>
+#include <kiconeffect.h>
+#include <kfileitem.h>
+#include <klocale.h>
+#include <kio/deletejob.h>
+#include <kio/netaccess.h>
+#include <kio/previewjob.h>
+#include <kjob.h>
+#include <kmenu.h>
+#include <kmessagebox.h>
+#include <kmimetyperesolver.h>
+#include <konq_fileitemcapabilities.h>
+#include <konq_operations.h>
+#include <konqmimedata.h>
+#include <kstringhandler.h>
+#include <ktoggleaction.h>
+#include <kurl.h>
+
+#include "additionalinfoaccessor.h"
+#include "dolphinmodel.h"
+#include "dolphincolumnviewcontainer.h"
+#include "dolphinviewcontroller.h"
+#include "dolphindetailsview.h"
+#include "dolphinfileitemdelegate.h"
+#include "dolphinnewmenuobserver.h"
+#include "dolphinsortfilterproxymodel.h"
+#include "dolphin_detailsmodesettings.h"
+#include "dolphiniconsview.h"
+#include "dolphin_generalsettings.h"
+#include "draganddrophelper.h"
+#include "renamedialog.h"
+#include "settings/dolphinsettings.h"
+#include "viewmodecontroller.h"
+#include "viewproperties.h"
+#include "zoomlevelinfo.h"
+#include "dolphindetailsviewexpander.h"
+
+/**
+ * Helper function for sorting items with qSort() in
+ * DolphinView::renameSelectedItems().
+ */
+bool lessThan(const KFileItem& item1, const KFileItem& item2)
+{
+ return KStringHandler::naturalCompare(item1.name(), item2.name()) < 0;
+}
+
+DolphinView::DolphinView(QWidget* parent,
+ const KUrl& url,
+ DolphinSortFilterProxyModel* proxyModel) :
+ QWidget(parent),
+ m_active(true),
+ m_showPreview(false),
+ m_storedCategorizedSorting(false),
+ m_tabsForFiles(false),
+ m_isContextMenuOpen(false),
+ m_ignoreViewProperties(false),
+ m_assureVisibleCurrentIndex(false),
+ m_mode(DolphinView::IconsView),
+ m_topLayout(0),
+ m_dolphinViewController(0),
+ m_viewModeController(0),
+ m_viewAccessor(proxyModel),
+ m_selectionModel(0),
+ m_selectionChangedTimer(0),
+ m_rootUrl(),
+ m_activeItemUrl(),
+ m_restoredContentsPosition(),
+ m_createdItemUrl(),
+ m_selectedItems(),
+ m_newFileNames()
+{
+ m_topLayout = new QVBoxLayout(this);
+ m_topLayout->setSpacing(0);
+ m_topLayout->setMargin(0);
+
+ m_dolphinViewController = new DolphinViewController(this);
+
+ m_viewModeController = new ViewModeController(this);
+ m_viewModeController->setUrl(url);
+
+ connect(m_viewModeController, SIGNAL(urlChanged(const KUrl&)),
+ this, SIGNAL(urlChanged(const KUrl&)));
+
+ connect(m_dolphinViewController, SIGNAL(requestContextMenu(const QPoint&, const QList<QAction*>&)),
+ this, SLOT(openContextMenu(const QPoint&, const QList<QAction*>&)));
+ connect(m_dolphinViewController, SIGNAL(urlsDropped(const KFileItem&, const KUrl&, QDropEvent*)),
+ this, SLOT(dropUrls(const KFileItem&, const KUrl&, QDropEvent*)));
+ connect(m_dolphinViewController, SIGNAL(sortingChanged(DolphinView::Sorting)),
+ this, SLOT(updateSorting(DolphinView::Sorting)));
+ connect(m_dolphinViewController, SIGNAL(sortOrderChanged(Qt::SortOrder)),
+ this, SLOT(updateSortOrder(Qt::SortOrder)));
+ connect(m_dolphinViewController, SIGNAL(sortFoldersFirstChanged(bool)),
+ this, SLOT(updateSortFoldersFirst(bool)));
+ connect(m_dolphinViewController, SIGNAL(additionalInfoChanged(const KFileItemDelegate::InformationList&)),
+ this, SLOT(updateAdditionalInfo(const KFileItemDelegate::InformationList&)));
+ connect(m_dolphinViewController, SIGNAL(itemTriggered(const KFileItem&)),
+ this, SLOT(triggerItem(const KFileItem&)));
+ connect(m_dolphinViewController, SIGNAL(tabRequested(const KUrl&)),
+ this, SIGNAL(tabRequested(const KUrl&)));
+ connect(m_dolphinViewController, SIGNAL(activated()),
+ this, SLOT(activate()));
+ connect(m_dolphinViewController, SIGNAL(itemEntered(const KFileItem&)),
+ this, SLOT(showHoverInformation(const KFileItem&)));
+ connect(m_dolphinViewController, SIGNAL(viewportEntered()),
+ this, SLOT(clearHoverInformation()));
+ connect(m_dolphinViewController, SIGNAL(urlChangeRequested(KUrl)),
+ m_viewModeController, SLOT(setUrl(KUrl)));
+
+ KDirLister* dirLister = m_viewAccessor.dirLister();
+ connect(dirLister, SIGNAL(redirection(KUrl,KUrl)),
+ this, SLOT(slotRedirection(KUrl,KUrl)));
+ connect(dirLister, SIGNAL(completed()),
+ this, SLOT(slotDirListerCompleted()));
+ connect(dirLister, SIGNAL(refreshItems(const QList<QPair<KFileItem,KFileItem>>&)),
+ this, SLOT(slotRefreshItems()));
+
+ // When a new item has been created by the "Create New..." menu, the item should
+ // get selected and it must be assured that the item will get visible. As the
+ // creation is done asynchronously, several signals must be checked:
+ connect(&DolphinNewMenuObserver::instance(), SIGNAL(itemCreated(const KUrl&)),
+ this, SLOT(observeCreatedItem(const KUrl&)));
+
+ m_selectionChangedTimer = new QTimer(this);
+ m_selectionChangedTimer->setSingleShot(true);
+ m_selectionChangedTimer->setInterval(300);
+ connect(m_selectionChangedTimer, SIGNAL(timeout()),
+ this, SLOT(emitSelectionChangedSignal()));
+
+ applyViewProperties();
+ m_topLayout->addWidget(m_viewAccessor.layoutTarget());
+}
+
+DolphinView::~DolphinView()
+{
+}
+
+KUrl DolphinView::url() const
+{
+ return m_viewModeController->url();
+}
+
+KUrl DolphinView::rootUrl() const
+{
+ const KUrl viewUrl = url();
+ const KUrl root = m_viewAccessor.rootUrl();
+ if (root.isEmpty() || !root.isParentOf(viewUrl)) {
+ return viewUrl;
+ }
+ return root;
+}
+
+void DolphinView::setActive(bool active)
+{
+ if (active == m_active) {
+ return;
+ }
+
+ m_active = active;
+
+ QColor color = KColorScheme(QPalette::Active, KColorScheme::View).background().color();
+ if (active) {
+ emitSelectionChangedSignal();
+ } else {
+ color.setAlpha(150);
+ }
+
+ QWidget* viewport = m_viewAccessor.itemView()->viewport();
+ QPalette palette;
+ palette.setColor(viewport->backgroundRole(), color);
+ viewport->setPalette(palette);
+
+ update();
+
+ if (active) {
+ m_viewAccessor.itemView()->setFocus();
+ emit activated();
+ }
+
+ m_viewModeController->indicateActivationChange(active);
+}
+
+bool DolphinView::isActive() const
+{
+ return m_active;
+}
+
+void DolphinView::setMode(Mode mode)
+{
+ if (mode == m_mode) {
+ return; // the wished mode is already set
+ }
+
+ const int oldZoomLevel = m_viewModeController->zoomLevel();
+ m_mode = mode;
+
+ // remember the currently selected items, so that they will
+ // be restored after reloading the directory
+ m_selectedItems = selectedItems();
+
+ deleteView();
+
+ const KUrl viewPropsUrl = rootUrl();
+ ViewProperties props(viewPropsUrl);
+ props.setViewMode(m_mode);
+ createView();
+
+ // the file item delegate has been recreated, apply the current
+ // additional information manually
+ const KFileItemDelegate::InformationList infoList = props.additionalInfo();
+ m_viewAccessor.itemDelegate()->setShowInformation(infoList);
+ emit additionalInfoChanged();
+
+ // Not all view modes support categorized sorting. Adjust the sorting model
+ // if changing the view mode results in a change of the categorized sorting
+ // capabilities.
+ m_storedCategorizedSorting = props.categorizedSorting();
+ const bool categorized = m_storedCategorizedSorting && supportsCategorizedSorting();
+ if (categorized != m_viewAccessor.proxyModel()->isCategorizedModel()) {
+ m_viewAccessor.proxyModel()->setCategorizedModel(categorized);
+ emit categorizedSortingChanged();
+ }
+
+ emit modeChanged();
+
+ updateZoomLevel(oldZoomLevel);
+ loadDirectory(viewPropsUrl);
+}
+
+DolphinView::Mode DolphinView::mode() const
+{
+ return m_mode;
+}
+
+bool DolphinView::showPreview() const
+{
+ return m_showPreview;
+}
+
+bool DolphinView::showHiddenFiles() const
+{
+ return m_viewAccessor.dirLister()->showingDotFiles();
+}
+
+bool DolphinView::categorizedSorting() const
+{
+ // If all view modes would support categorized sorting, returning
+ // m_viewAccessor.proxyModel()->isCategorizedModel() would be the way to go. As
+ // currently only the icons view supports caterized sorting, we remember
+ // the stored view properties state in m_storedCategorizedSorting and
+ // return this state. The application takes care to disable the corresponding
+ // checkbox by checking DolphinView::supportsCategorizedSorting() to indicate
+ // that this setting is not applied to the current view mode.
+ return m_storedCategorizedSorting;
+}
+
+bool DolphinView::supportsCategorizedSorting() const
+{
+ return m_viewAccessor.supportsCategorizedSorting();
+}
+
+bool DolphinView::hasSelection() const
+{
+ const QAbstractItemView* view = m_viewAccessor.itemView();
+ return (view != 0) && view->selectionModel()->hasSelection();
+}
+
+void DolphinView::markUrlsAsSelected(const QList<KUrl>& urls)
+{
+ foreach (const KUrl& url, urls) {
+ KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
+ m_selectedItems.append(item);
+ }
+}
+
+KFileItemList DolphinView::selectedItems() const
+{
+ KFileItemList itemList;
+ const QAbstractItemView* view = m_viewAccessor.itemView();
+ if (view == 0) {
+ return itemList;
+ }
+
+ const QItemSelection selection = m_viewAccessor.proxyModel()->mapSelectionToSource(view->selectionModel()->selection());
+
+ const QModelIndexList indexList = selection.indexes();
+ foreach (const QModelIndex &index, indexList) {
+ KFileItem item = m_viewAccessor.dirModel()->itemForIndex(index);
+ if (!item.isNull()) {
+ itemList.append(item);
+ }
+ }
+
+ return itemList;
+}
+
+KUrl::List DolphinView::selectedUrls() const
+{
+ KUrl::List urls;
+ const KFileItemList list = selectedItems();
+ foreach (const KFileItem &item, list) {
+ urls.append(item.url());
+ }
+ return urls;
+}
+
+int DolphinView::selectedItemsCount() const
+{
+ const QAbstractItemView* view = m_viewAccessor.itemView();
+ if (view == 0) {
+ return 0;
+ }
+
+ return view->selectionModel()->selectedIndexes().count();
+}
+
+QItemSelectionModel* DolphinView::selectionModel() const
+{
+ return m_viewAccessor.itemView()->selectionModel();
+}
+
+void DolphinView::setZoomLevel(int level)
+{
+ if (level < ZoomLevelInfo::minimumLevel()) {
+ level = ZoomLevelInfo::minimumLevel();
+ } else if (level > ZoomLevelInfo::maximumLevel()) {
+ level = ZoomLevelInfo::maximumLevel();
+ }
+
+ if (level != zoomLevel()) {
+ m_viewModeController->setZoomLevel(level);
+ emit zoomLevelChanged(level);
+ }
+}
+
+int DolphinView::zoomLevel() const
+{
+ return m_viewModeController->zoomLevel();
+}
+
+void DolphinView::setSorting(Sorting sorting)
+{
+ if (sorting != this->sorting()) {
+ updateSorting(sorting);
+ }
+}
+
+DolphinView::Sorting DolphinView::sorting() const
+{
+ return m_viewAccessor.proxyModel()->sorting();
+}
+
+void DolphinView::setSortOrder(Qt::SortOrder order)
+{
+ if (sortOrder() != order) {
+ updateSortOrder(order);
+ }
+}
+
+Qt::SortOrder DolphinView::sortOrder() const
+{
+ return m_viewAccessor.proxyModel()->sortOrder();
+}
+
+void DolphinView::setSortFoldersFirst(bool foldersFirst)
+{
+ if (sortFoldersFirst() != foldersFirst) {
+ updateSortFoldersFirst(foldersFirst);
+ }
+}
+
+bool DolphinView::sortFoldersFirst() const
+{
+ return m_viewAccessor.proxyModel()->sortFoldersFirst();
+}
+
+void DolphinView::setAdditionalInfo(KFileItemDelegate::InformationList info)
+{
+ const KUrl viewPropsUrl = rootUrl();
+ ViewProperties props(viewPropsUrl);
+ props.setAdditionalInfo(info);
+ m_viewAccessor.itemDelegate()->setShowInformation(info);
+
+ emit additionalInfoChanged();
+
+ if (m_viewAccessor.reloadOnAdditionalInfoChange()) {
+ loadDirectory(viewPropsUrl);
+ }
+}
+
+KFileItemDelegate::InformationList DolphinView::additionalInfo() const
+{
+ return m_viewAccessor.itemDelegate()->showInformation();
+}
+
+void DolphinView::reload()
+{
+ QByteArray viewState;
+ QDataStream saveStream(&viewState, QIODevice::WriteOnly);
+ saveState(saveStream);
+ m_selectedItems= selectedItems();
+
+ setUrl(url());
+ loadDirectory(url(), true);
+
+ QDataStream restoreStream(viewState);
+ restoreState(restoreStream);
+}
+
+void DolphinView::refresh()
+{
+ m_ignoreViewProperties = false;
+
+ const bool oldActivationState = m_active;
+ const int oldZoomLevel = m_viewModeController->zoomLevel();
+ m_active = true;
+
+ createView();
+ applyViewProperties();
+ reload();
+
+ setActive(oldActivationState);
+ updateZoomLevel(oldZoomLevel);
+}
+
+void DolphinView::setNameFilter(const QString& nameFilter)
+{
+ m_viewModeController->setNameFilter(nameFilter);
+}
+
+void DolphinView::calculateItemCount(int& fileCount,
+ int& folderCount,
+ KIO::filesize_t& totalFileSize) const
+{
+ foreach (const KFileItem& item, m_viewAccessor.dirLister()->items()) {
+ if (item.isDir()) {
+ ++folderCount;
+ } else {
+ ++fileCount;
+ totalFileSize += item.size();
+ }
+ }
+}
+
+QString DolphinView::statusBarText() const
+{
+ QString text;
+ int folderCount = 0;
+ int fileCount = 0;
+ KIO::filesize_t totalFileSize = 0;
+
+ if (hasSelection()) {
+ // give a summary of the status of the selected files
+ const KFileItemList list = selectedItems();
+ if (list.isEmpty()) {
+ // when an item is triggered, it is temporary selected but selectedItems()
+ // will return an empty list
+ return text;
+ }
+
+ KFileItemList::const_iterator it = list.begin();
+ const KFileItemList::const_iterator end = list.end();
+ while (it != end) {
+ const KFileItem& item = *it;
+ if (item.isDir()) {
+ ++folderCount;
+ } else {
+ ++fileCount;
+ totalFileSize += item.size();
+ }
+ ++it;
+ }
+
+ if (folderCount + fileCount == 1) {
+ // if only one item is selected, show the filename
+ const QString name = list.first().text();
+ text = (folderCount == 1) ? i18nc("@info:status", "<filename>%1</filename> selected", name) :
+ i18nc("@info:status", "<filename>%1</filename> selected (%2)",
+ name, KIO::convertSize(totalFileSize));
+ } else {
+ // at least 2 items are selected
+ const QString foldersText = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount);
+ const QString filesText = i18ncp("@info:status", "1 File selected", "%1 Files selected", fileCount);
+ if ((folderCount > 0) && (fileCount > 0)) {
+ text = i18nc("@info:status folders, files (size)", "%1, %2 (%3)",
+ foldersText, filesText, KIO::convertSize(totalFileSize));
+ } else if (fileCount > 0) {
+ text = i18nc("@info:status files (size)", "%1 (%2)", filesText, KIO::convertSize(totalFileSize));
+ } else {
+ Q_ASSERT(folderCount > 0);
+ text = foldersText;
+ }
+ }
+ } else {
+ calculateItemCount(fileCount, folderCount, totalFileSize);
+ text = KIO::itemsSummaryString(fileCount + folderCount,
+ fileCount, folderCount,
+ totalFileSize, true);
+ }
+
+ return text;
+}
+
+QList<QAction*> DolphinView::versionControlActions(const KFileItemList& items) const
+{
+ return m_dolphinViewController->versionControlActions(items);
+}
+
+void DolphinView::setUrl(const KUrl& url)
+{
+ if (m_viewModeController->url() != url) {
+ m_newFileNames.clear();
+
+ m_viewModeController->setUrl(url); // emits urlChanged, which we forward
+ m_viewAccessor.prepareUrlChange(url);
+ applyViewProperties();
+ loadDirectory(url);
+
+ // When changing the URL there is no need to keep the version
+ // data of the previous URL.
+ m_viewAccessor.dirModel()->clearVersionData();
+
+ emit startedPathLoading(url);
+ }
+
+ // the selection model might have changed in the case of a column view
+ QItemSelectionModel* selectionModel = m_viewAccessor.itemView()->selectionModel();
+ if (m_selectionModel != selectionModel) {
+ disconnect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+ this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection)));
+ m_selectionModel = selectionModel;
+ connect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+ this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection)));
+ }
+}
+
+void DolphinView::selectAll()
+{
+ m_viewAccessor.itemView()->selectAll();
+}
+
+void DolphinView::invertSelection()
+{
+ QItemSelectionModel* selectionModel = m_viewAccessor.itemView()->selectionModel();
+ const QAbstractItemModel* itemModel = selectionModel->model();
+
+ const QModelIndex topLeft = itemModel->index(0, 0);
+ const QModelIndex bottomRight = itemModel->index(itemModel->rowCount() - 1,
+ itemModel->columnCount() - 1);
+
+ const QItemSelection selection(topLeft, bottomRight);
+ selectionModel->select(selection, QItemSelectionModel::Toggle);
+}
+
+void DolphinView::clearSelection()
+{
+ m_viewAccessor.itemView()->clearSelection();
+}
+
+void DolphinView::renameSelectedItems()
+{
+ KFileItemList items = selectedItems();
+ const int itemCount = items.count();
+ if (itemCount < 1) {
+ return;
+ }
+
+ if (itemCount > 1) {
+ // More than one item has been selected for renaming. Open
+ // a rename dialog and rename all items afterwards.
+ QPointer<RenameDialog> dialog = new RenameDialog(this, items);
+ if (dialog->exec() == QDialog::Rejected) {
+ delete dialog;
+ return;
+ }
+
+ const QString newName = dialog->newName();
+ if (newName.isEmpty()) {
+ emit errorMessage(dialog->errorString());
+ delete dialog;
+ return;
+ }
+ delete dialog;
+
+ // the selection would be invalid after renaming the items, so just clear
+ // it before
+ clearSelection();
+
+ // TODO: check how this can be integrated into KIO::FileUndoManager/KonqOperations
+ // as one operation instead of n rename operations like it is done now...
+ Q_ASSERT(newName.contains('#'));
+
+ // currently the items are sorted by the selection order, resort
+ // them by the file name
+ qSort(items.begin(), items.end(), lessThan);
+
+ // iterate through all selected items and rename them...
+ int index = 1;
+ foreach (const KFileItem& item, items) {
+ const KUrl& oldUrl = item.url();
+ QString number;
+ number.setNum(index++);
+
+ QString name = newName;
+ name.replace('#', number);
+
+ if (oldUrl.fileName() != name) {
+ KUrl newUrl = oldUrl;
+ newUrl.setFileName(name);
+ KonqOperations::rename(this, oldUrl, newUrl);
+ }
+ }
+ } else if (DolphinSettings::instance().generalSettings()->renameInline()) {
+ Q_ASSERT(itemCount == 1);
+ const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForItem(items.first());
+ const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->mapFromSource(dirIndex);
+ m_viewAccessor.itemView()->edit(proxyIndex);
+ } else {
+ Q_ASSERT(itemCount == 1);
+
+ QPointer<RenameDialog> dialog = new RenameDialog(this, items);
+ if (dialog->exec() == QDialog::Rejected) {
+ delete dialog;
+ return;
+ }
+
+ const QString newName = dialog->newName();
+ if (newName.isEmpty()) {
+ emit errorMessage(dialog->errorString());
+ delete dialog;
+ return;
+ }
+ delete dialog;
+
+ const KUrl& oldUrl = items.first().url();
+ KUrl newUrl = oldUrl;
+ newUrl.setFileName(newName);
+ KonqOperations::rename(this, oldUrl, newUrl);
+ }
+
+ // assure that the current index remains visible when KDirLister
+ // will notify the view about changed items
+ m_assureVisibleCurrentIndex = true;
+}
+
+void DolphinView::trashSelectedItems()
+{
+ const KUrl::List list = simplifiedSelectedUrls();
+ KonqOperations::del(this, KonqOperations::TRASH, list);
+}
+
+void DolphinView::deleteSelectedItems()
+{
+ const KUrl::List list = simplifiedSelectedUrls();
+ const bool del = KonqOperations::askDeleteConfirmation(list,
+ KonqOperations::DEL,
+ KonqOperations::DEFAULT_CONFIRMATION,
+ this);
+
+ if (del) {
+ KIO::Job* job = KIO::del(list);
+ connect(job, SIGNAL(result(KJob*)),
+ this, SLOT(slotDeleteFileFinished(KJob*)));
+ }
+}
+
+void DolphinView::cutSelectedItems()
+{
+ QMimeData* mimeData = selectionMimeData();
+ KonqMimeData::addIsCutSelection(mimeData, true);
+ QApplication::clipboard()->setMimeData(mimeData);
+}
+
+void DolphinView::copySelectedItems()
+{
+ QMimeData* mimeData = selectionMimeData();
+ QApplication::clipboard()->setMimeData(mimeData);
+}
+
+void DolphinView::paste()
+{
+ pasteToUrl(url());
+}
+
+void DolphinView::pasteIntoFolder()
+{
+ const KFileItemList items = selectedItems();
+ if ((items.count() == 1) && items.first().isDir()) {
+ pasteToUrl(items.first().url());
+ }
+}
+
+void DolphinView::setShowPreview(bool show)
+{
+ if (m_showPreview == show) {
+ return;
+ }
+
+ const KUrl viewPropsUrl = rootUrl();
+ ViewProperties props(viewPropsUrl);
+ props.setShowPreview(show);
+
+ m_showPreview = show;
+ const int oldZoomLevel = m_viewModeController->zoomLevel();
+ emit showPreviewChanged();
+
+ // Enabling or disabling the preview might change the icon size of the view.
+ // As the view does not emit a signal when the icon size has been changed,
+ // the used zoom level of the controller must be adjusted manually:
+ updateZoomLevel(oldZoomLevel);
+}
+
+void DolphinView::setShowHiddenFiles(bool show)
+{
+ if (m_viewAccessor.dirLister()->showingDotFiles() == show) {
+ return;
+ }
+
+ const KUrl viewPropsUrl = rootUrl();
+ ViewProperties props(viewPropsUrl);
+ props.setShowHiddenFiles(show);
+
+ m_viewAccessor.dirLister()->setShowingDotFiles(show);
+ emit showHiddenFilesChanged();
+}
+
+void DolphinView::setCategorizedSorting(bool categorized)
+{
+ if (categorized == categorizedSorting()) {
+ return;
+ }
+
+ // setCategorizedSorting(true) may only get invoked
+ // if the view supports categorized sorting
+ Q_ASSERT(!categorized || supportsCategorizedSorting());
+
+ ViewProperties props(rootUrl());
+ props.setCategorizedSorting(categorized);
+ props.save();
+
+ m_storedCategorizedSorting = categorized;
+ m_viewAccessor.proxyModel()->setCategorizedModel(categorized);
+
+ emit categorizedSortingChanged();
+}
+
+void DolphinView::toggleSortOrder()
+{
+ const Qt::SortOrder order = (sortOrder() == Qt::AscendingOrder) ?
+ Qt::DescendingOrder :
+ Qt::AscendingOrder;
+ setSortOrder(order);
+}
+
+void DolphinView::toggleSortFoldersFirst()
+{
+ setSortFoldersFirst(!sortFoldersFirst());
+}
+
+void DolphinView::toggleAdditionalInfo(QAction* action)
+{
+ const KFileItemDelegate::Information info =
+ static_cast<KFileItemDelegate::Information>(action->data().toInt());
+
+ KFileItemDelegate::InformationList list = additionalInfo();
+
+ const bool show = action->isChecked();
+
+ const int index = list.indexOf(info);
+ const bool containsInfo = (index >= 0);
+ if (show && !containsInfo) {
+ list.append(info);
+ setAdditionalInfo(list);
+ } else if (!show && containsInfo) {
+ list.removeAt(index);
+ setAdditionalInfo(list);
+ Q_ASSERT(list.indexOf(info) < 0);
+ }
+}
+
+void DolphinView::mouseReleaseEvent(QMouseEvent* event)
+{
+ QWidget::mouseReleaseEvent(event);
+ setActive(true);
+}
+
+bool DolphinView::eventFilter(QObject* watched, QEvent* event)
+{
+ switch (event->type()) {
+ case QEvent::FocusIn:
+ if (watched == m_viewAccessor.itemView()) {
+ m_dolphinViewController->requestActivation();
+ }
+ break;
+
+ case QEvent::DragEnter:
+ if (watched == m_viewAccessor.itemView()->viewport()) {
+ setActive(true);
+ }
+ break;
+
+ case QEvent::KeyPress:
+ if (watched == m_viewAccessor.itemView()) {
+ // clear the selection when Escape has been pressed
+ QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
+ if (keyEvent->key() == Qt::Key_Escape) {
+ clearSelection();
+ }
+ }
+ break;
+
+ case QEvent::Wheel:
+ if (watched == m_viewAccessor.itemView()->viewport()) {
+ // Ctrl+wheel events should cause icon zooming, but not if the left mouse button is pressed
+ // (the user is probably trying to scroll during a selection in that case)
+ QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event);
+ if (wheelEvent->modifiers() & Qt::ControlModifier && !(wheelEvent->buttons() & Qt::LeftButton)) {
+ const int delta = wheelEvent->delta();
+ const int level = zoomLevel();
+ if (delta > 0) {
+ setZoomLevel(level + 1);
+ } else if (delta < 0) {
+ setZoomLevel(level - 1);
+ }
+ return true;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return QWidget::eventFilter(watched, event);
+}
+
+void DolphinView::activate()
+{
+ setActive(true);
+}
+
+void DolphinView::triggerItem(const KFileItem& item)
+{
+ const Qt::KeyboardModifiers modifier = QApplication::keyboardModifiers();
+ if ((modifier & Qt::ShiftModifier) || (modifier & Qt::ControlModifier)) {
+ // items are selected by the user, hence don't trigger the
+ // item specified by 'index'
+ return;
+ }
+
+ // TODO: the m_isContextMenuOpen check is a workaround for Qt-issue 207192
+ if (item.isNull() || m_isContextMenuOpen) {
+ return;
+ }
+
+ emit itemTriggered(item); // caught by DolphinViewContainer or DolphinPart
+}
+
+void DolphinView::slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
+{
+ const int count = selectedItemsCount();
+ const bool selectionStateChanged = ((count > 0) && (selected.count() == count)) ||
+ ((count == 0) && !deselected.isEmpty());
+
+ // If nothing has been selected before and something got selected (or if something
+ // was selected before and now nothing is selected) the selectionChangedSignal must
+ // be emitted asynchronously as fast as possible to update the edit-actions.
+ m_selectionChangedTimer->setInterval(selectionStateChanged ? 0 : 300);
+ m_selectionChangedTimer->start();
+}
+
+void DolphinView::emitSelectionChangedSignal()
+{
+ emit selectionChanged(DolphinView::selectedItems());
+}
+
+void DolphinView::openContextMenu(const QPoint& pos,
+ const QList<QAction*>& customActions)
+{
+ KFileItem item;
+ const QModelIndex index = m_viewAccessor.itemView()->indexAt(pos);
+ if (index.isValid() && (index.column() == DolphinModel::Name)) {
+ const QModelIndex dolphinModelIndex = m_viewAccessor.proxyModel()->mapToSource(index);
+ item = m_viewAccessor.dirModel()->itemForIndex(dolphinModelIndex);
+ }
+
+ m_isContextMenuOpen = true; // TODO: workaround for Qt-issue 207192
+ emit requestContextMenu(item, url(), customActions);
+ m_isContextMenuOpen = false;
+}
+
+void DolphinView::dropUrls(const KFileItem& destItem,
+ const KUrl& destPath,
+ QDropEvent* event)
+{
+ addNewFileNames(event->mimeData());
+ DragAndDropHelper::instance().dropUrls(destItem, destPath, event, this);
+}
+
+void DolphinView::updateSorting(DolphinView::Sorting sorting)
+{
+ ViewProperties props(rootUrl());
+ props.setSorting(sorting);
+
+ m_viewAccessor.proxyModel()->setSorting(sorting);
+
+ emit sortingChanged(sorting);
+}
+
+void DolphinView::updateSortOrder(Qt::SortOrder order)
+{
+ ViewProperties props(rootUrl());
+ props.setSortOrder(order);
+
+ m_viewAccessor.proxyModel()->setSortOrder(order);
+
+ emit sortOrderChanged(order);
+}
+
+void DolphinView::updateSortFoldersFirst(bool foldersFirst)
+{
+ ViewProperties props(rootUrl());
+ props.setSortFoldersFirst(foldersFirst);
+
+ m_viewAccessor.proxyModel()->setSortFoldersFirst(foldersFirst);
+
+ emit sortFoldersFirstChanged(foldersFirst);
+}
+
+void DolphinView::updateAdditionalInfo(const KFileItemDelegate::InformationList& info)
+{
+ ViewProperties props(rootUrl());
+ props.setAdditionalInfo(info);
+ props.save();
+
+ m_viewAccessor.itemDelegate()->setShowInformation(info);
+
+ emit additionalInfoChanged();
+}
+
+void DolphinView::updateAdditionalInfoActions(KActionCollection* collection)
+{
+ const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance();
+
+ const KFileItemDelegate::InformationList checkedInfos = m_viewAccessor.itemDelegate()->showInformation();
+ const KFileItemDelegate::InformationList infos = infoAccessor.keys();
+
+ const bool enable = (m_mode == DolphinView::DetailsView) ||
+ (m_mode == DolphinView::IconsView);
+
+ foreach (const KFileItemDelegate::Information& info, infos) {
+ const QString name = infoAccessor.actionCollectionName(info, AdditionalInfoAccessor::AdditionalInfoType);
+ QAction* action = collection->action(name);
+ Q_ASSERT(action != 0);
+ action->setEnabled(enable);
+ action->setChecked(checkedInfos.contains(info));
+ }
+}
+
+QPair<bool, QString> DolphinView::pasteInfo() const
+{
+ return KonqOperations::pasteInfo(url());
+}
+
+void DolphinView::setTabsForFilesEnabled(bool tabsForFiles)
+{
+ m_tabsForFiles = tabsForFiles;
+}
+
+bool DolphinView::isTabsForFilesEnabled() const
+{
+ return m_tabsForFiles;
+}
+
+bool DolphinView::itemsExpandable() const
+{
+ return m_viewAccessor.itemsExpandable();
+}
+
+void DolphinView::restoreState(QDataStream& stream)
+{
+ // current item
+ stream >> m_activeItemUrl;
+
+ // view position
+ stream >> m_restoredContentsPosition;
+
+ // expanded folders (only relevant for the details view - will be ignored by the view in other view modes)
+ QSet<KUrl> urlsToExpand;
+ stream >> urlsToExpand;
+ const DolphinDetailsViewExpander* expander = m_viewAccessor.setExpandedUrls(urlsToExpand);
+ if (expander != 0) {
+ m_expanderActive = true;
+ connect (expander, SIGNAL(completed()), this, SLOT(slotLoadingCompleted()));
+ }
+ else {
+ m_expanderActive = false;
+ }
+}
+
+void DolphinView::saveState(QDataStream& stream)
+{
+ // current item
+ KFileItem currentItem;
+ const QAbstractItemView* view = m_viewAccessor.itemView();
+
+ if (view != 0) {
+ const QModelIndex proxyIndex = view->currentIndex();
+ const QModelIndex dirModelIndex = m_viewAccessor.proxyModel()->mapToSource(proxyIndex);
+ currentItem = m_viewAccessor.dirModel()->itemForIndex(dirModelIndex);
+ }
+
+ KUrl currentUrl;
+ if (!currentItem.isNull()) {
+ currentUrl = currentItem.url();
+ }
+
+ stream << currentUrl;
+
+ // view position
+ const int x = view->horizontalScrollBar()->value();
+ const int y = view->verticalScrollBar()->value();
+ stream << QPoint(x, y);
+
+ // expanded folders (only relevant for the details view - the set will be empty in other view modes)
+ stream << m_viewAccessor.expandedUrls();
+}
+
+void DolphinView::observeCreatedItem(const KUrl& url)
+{
+ m_createdItemUrl = url;
+ connect(m_viewAccessor.dirModel(), SIGNAL(rowsInserted(const QModelIndex&, int, int)),
+ this, SLOT(selectAndScrollToCreatedItem()));
+}
+
+void DolphinView::selectAndScrollToCreatedItem()
+{
+ const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForUrl(m_createdItemUrl);
+ if (dirIndex.isValid()) {
+ const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->mapFromSource(dirIndex);
+ m_viewAccessor.itemView()->setCurrentIndex(proxyIndex);
+ }
+
+ disconnect(m_viewAccessor.dirModel(), SIGNAL(rowsInserted(const QModelIndex&, int, int)),
+ this, SLOT(selectAndScrollToCreatedItem()));
+ m_createdItemUrl = KUrl();
+}
+
+void DolphinView::showHoverInformation(const KFileItem& item)
+{
+ emit requestItemInfo(item);
+}
+
+void DolphinView::clearHoverInformation()
+{
+ emit requestItemInfo(KFileItem());
+}
+
+void DolphinView::slotDeleteFileFinished(KJob* job)
+{
+ if (job->error() == 0) {
+ emit operationCompletedMessage(i18nc("@info:status", "Delete operation completed."));
+ } else if (job->error() != KIO::ERR_USER_CANCELED) {
+ emit errorMessage(job->errorString());
+ }
+}
+
+void DolphinView::slotDirListerCompleted()
+{
+ if (!m_expanderActive) {
+ slotLoadingCompleted();
+ }
+
+ if (!m_newFileNames.isEmpty()) {
+ // select all newly added items created by a paste operation or
+ // a drag & drop operation, and clear the previous selection
+ m_viewAccessor.itemView()->clearSelection();
+ const int rowCount = m_viewAccessor.proxyModel()->rowCount();
+ QItemSelection selection;
+ for (int row = 0; row < rowCount; ++row) {
+ const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->index(row, 0);
+ const QModelIndex dirIndex = m_viewAccessor.proxyModel()->mapToSource(proxyIndex);
+ const KUrl url = m_viewAccessor.dirModel()->itemForIndex(dirIndex).url();
+ if (m_newFileNames.contains(url.fileName())) {
+ selection.merge(QItemSelection(proxyIndex, proxyIndex), QItemSelectionModel::Select);
+ }
+ }
+ m_viewAccessor.itemView()->selectionModel()->select(selection, QItemSelectionModel::Select);
+
+ m_newFileNames.clear();
+ }
+}
+
+void DolphinView::slotLoadingCompleted()
+{
+ m_expanderActive = false;
+
+ if (!m_activeItemUrl.isEmpty()) {
+ // assure that the current item remains visible
+ const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForUrl(m_activeItemUrl);
+ if (dirIndex.isValid()) {
+ const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->mapFromSource(dirIndex);
+ QAbstractItemView* view = m_viewAccessor.itemView();
+ const bool clearSelection = !hasSelection();
+ view->setCurrentIndex(proxyIndex);
+ if (clearSelection) {
+ view->clearSelection();
+ }
+ m_activeItemUrl.clear();
+ }
+ }
+
+ if (!m_selectedItems.isEmpty()) {
+ const KUrl& baseUrl = url();
+ KUrl url;
+ QItemSelection newSelection;
+ foreach(const KFileItem& item, m_selectedItems) {
+ url = item.url().upUrl();
+ if (baseUrl.equals(url, KUrl::CompareWithoutTrailingSlash)) {
+ QModelIndex index = m_viewAccessor.proxyModel()->mapFromSource(m_viewAccessor.dirModel()->indexForItem(item));
+ newSelection.select(index, index);
+ }
+ }
+ m_viewAccessor.itemView()->selectionModel()->select(newSelection,
+ QItemSelectionModel::ClearAndSelect
+ | QItemSelectionModel::Current);
+ m_selectedItems.clear();
+ }
+
+ // Restore the contents position. This has to be done using a Qt::QueuedConnection
+ // because the view might not be in its final state yet.
+ QMetaObject::invokeMethod(this, "restoreContentsPosition", Qt::QueuedConnection);
+}
+
+void DolphinView::slotRefreshItems()
+{
+ if (m_assureVisibleCurrentIndex) {
+ m_assureVisibleCurrentIndex = false;
+ m_viewAccessor.itemView()->scrollTo(m_viewAccessor.itemView()->currentIndex());
+ }
+}
+
+void DolphinView::loadDirectory(const KUrl& url, bool reload)
+{
+ if (!url.isValid()) {
+ const QString location(url.pathOrUrl());
+ if (location.isEmpty()) {
+ emit errorMessage(i18nc("@info:status", "The location is empty."));
+ } else {
+ emit errorMessage(i18nc("@info:status", "The location '%1' is invalid.", location));
+ }
+ return;
+ }
+
+ KDirLister* dirLister = m_viewAccessor.dirLister();
+ dirLister->openUrl(url, reload ? KDirLister::Reload : KDirLister::NoFlags);
+}
+
+void DolphinView::applyViewProperties()
+{
+ if (m_ignoreViewProperties) {
+ return;
+ }
+
+ const ViewProperties props(rootUrl());
+
+ const Mode mode = props.viewMode();
+ if (m_mode != mode) {
+ const int oldZoomLevel = m_viewModeController->zoomLevel();
+
+ m_mode = mode;
+ createView();
+ emit modeChanged();
+
+ updateZoomLevel(oldZoomLevel);
+ }
+ if (m_viewAccessor.itemView() == 0) {
+ createView();
+ }
+ Q_ASSERT(m_viewAccessor.itemView() != 0);
+ Q_ASSERT(m_viewAccessor.itemDelegate() != 0);
+
+ const bool showHiddenFiles = props.showHiddenFiles();
+ if (showHiddenFiles != m_viewAccessor.dirLister()->showingDotFiles()) {
+ m_viewAccessor.dirLister()->setShowingDotFiles(showHiddenFiles);
+ emit showHiddenFilesChanged();
+ }
+
+ m_storedCategorizedSorting = props.categorizedSorting();
+ const bool categorized = m_storedCategorizedSorting && supportsCategorizedSorting();
+ if (categorized != m_viewAccessor.proxyModel()->isCategorizedModel()) {
+ m_viewAccessor.proxyModel()->setCategorizedModel(categorized);
+ emit categorizedSortingChanged();
+ }
+
+ const DolphinView::Sorting sorting = props.sorting();
+ if (sorting != m_viewAccessor.proxyModel()->sorting()) {
+ m_viewAccessor.proxyModel()->setSorting(sorting);
+ emit sortingChanged(sorting);
+ }
+
+ const Qt::SortOrder sortOrder = props.sortOrder();
+ if (sortOrder != m_viewAccessor.proxyModel()->sortOrder()) {
+ m_viewAccessor.proxyModel()->setSortOrder(sortOrder);
+ emit sortOrderChanged(sortOrder);
+ }
+
+ const bool sortFoldersFirst = props.sortFoldersFirst();
+ if (sortFoldersFirst != m_viewAccessor.proxyModel()->sortFoldersFirst()) {
+ m_viewAccessor.proxyModel()->setSortFoldersFirst(sortFoldersFirst);
+ emit sortFoldersFirstChanged(sortFoldersFirst);
+ }
+
+ KFileItemDelegate::InformationList info = props.additionalInfo();
+ if (info != m_viewAccessor.itemDelegate()->showInformation()) {
+ m_viewAccessor.itemDelegate()->setShowInformation(info);
+ emit additionalInfoChanged();
+ }
+
+ const bool showPreview = props.showPreview();
+ if (showPreview != m_showPreview) {
+ m_showPreview = showPreview;
+ const int oldZoomLevel = m_viewModeController->zoomLevel();
+ emit showPreviewChanged();
+
+ // Enabling or disabling the preview might change the icon size of the view.
+ // As the view does not emit a signal when the icon size has been changed,
+ // the used zoom level of the controller must be adjusted manually:
+ updateZoomLevel(oldZoomLevel);
+ }
+
+ if (DolphinSettings::instance().generalSettings()->globalViewProps()) {
+ // During the lifetime of a DolphinView instance the global view properties
+ // should not be changed. This allows e. g. to split a view and use different
+ // view properties for each view.
+ m_ignoreViewProperties = true;
+ }
+}
+
+void DolphinView::createView()
+{
+ deleteView();
+
+ Q_ASSERT(m_viewAccessor.itemView() == 0);
+ m_viewAccessor.createView(this, m_dolphinViewController, m_viewModeController, m_mode);
+
+ QAbstractItemView* view = m_viewAccessor.itemView();
+ Q_ASSERT(view != 0);
+ view->installEventFilter(this);
+ view->viewport()->installEventFilter(this);
+
+ m_dolphinViewController->setItemView(view);
+
+ // When changing the view mode, the selection is lost due to reinstantiating
+ // a new item view with a custom selection model. Pass the ownership of the
+ // selection model to DolphinView, so that it can be shared by all item views.
+ if (m_selectionModel != 0) {
+ view->setSelectionModel(m_selectionModel);
+ } else {
+ m_selectionModel = view->selectionModel();
+ }
+ m_selectionModel->setParent(this);
+ connect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+ this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection)));
+
+ setFocusProxy(m_viewAccessor.layoutTarget());
+ m_topLayout->insertWidget(1, m_viewAccessor.layoutTarget());
+}
+
+void DolphinView::deleteView()
+{
+ QAbstractItemView* view = m_viewAccessor.itemView();
+ if (view != 0) {
+ // It's important to set the keyboard focus to the parent
+ // before deleting the view: Otherwise when having a split
+ // view the other view will get the focus and will request
+ // an activation (see DolphinView::eventFilter()).
+ setFocusProxy(0);
+ setFocus();
+
+ m_topLayout->removeWidget(view);
+ view->close();
+
+ // disconnect all signal/slots
+ disconnect(view);
+ m_viewModeController->disconnect(view);
+ view->disconnect();
+
+ m_viewAccessor.deleteView();
+ }
+}
+
+void DolphinView::pasteToUrl(const KUrl& url)
+{
+ addNewFileNames(QApplication::clipboard()->mimeData());
+ KonqOperations::doPaste(this, url);
+}
+
+void DolphinView::updateZoomLevel(int oldZoomLevel)
+{
+ const int newZoomLevel = ZoomLevelInfo::zoomLevelForIconSize(m_viewAccessor.itemView()->iconSize());
+ if (oldZoomLevel != newZoomLevel) {
+ m_viewModeController->setZoomLevel(newZoomLevel);
+ emit zoomLevelChanged(newZoomLevel);
+ }
+}
+
+KUrl::List DolphinView::simplifiedSelectedUrls() const
+{
+ KUrl::List list = selectedUrls();
+ if (itemsExpandable() ) {
+ list = KDirModel::simplifiedUrlList(list);
+ }
+ return list;
+}
+
+QMimeData* DolphinView::selectionMimeData() const
+{
+ const QAbstractItemView* view = m_viewAccessor.itemView();
+ Q_ASSERT((view != 0) && (view->selectionModel() != 0));
+ const QItemSelection selection = m_viewAccessor.proxyModel()->mapSelectionToSource(view->selectionModel()->selection());
+ return m_viewAccessor.dirModel()->mimeData(selection.indexes());
+}
+
+void DolphinView::addNewFileNames(const QMimeData* mimeData)
+{
+ const KUrl::List urls = KUrl::List::fromMimeData(mimeData);
+ foreach (const KUrl& url, urls) {
+ m_newFileNames.insert(url.fileName());
+ }
+}
+
+DolphinView::ViewAccessor::ViewAccessor(DolphinSortFilterProxyModel* proxyModel) :
+ m_iconsView(0),
+ m_detailsView(0),
+ m_columnsContainer(0),
+ m_proxyModel(proxyModel),
+ m_dragSource(0)
+{
+}
+
+DolphinView::ViewAccessor::~ViewAccessor()
+{
+ delete m_dragSource;
+ m_dragSource = 0;
+}
+
+void DolphinView::ViewAccessor::createView(QWidget* parent,
+ DolphinViewController* dolphinViewController,
+ const ViewModeController* viewModeController,
+ Mode mode)
+{
+ Q_ASSERT(itemView() == 0);
+
+ switch (mode) {
+ case IconsView:
+ m_iconsView = new DolphinIconsView(parent,
+ dolphinViewController,
+ viewModeController,
+ m_proxyModel);
+ break;
+
+ case DetailsView:
+ m_detailsView = new DolphinDetailsView(parent,
+ dolphinViewController,
+ viewModeController,
+ m_proxyModel);
+ break;
+
+ case ColumnView:
+ m_columnsContainer = new DolphinColumnViewContainer(parent,
+ dolphinViewController,
+ viewModeController);
+ break;
+
+ default:
+ Q_ASSERT(false);
+ }
+}
+
+void DolphinView::ViewAccessor::deleteView()
+{
+ QAbstractItemView* view = itemView();
+ if (view != 0) {
+ if (DragAndDropHelper::instance().isDragSource(view)) {
+ // The view is a drag source (the feature "Open folders
+ // during drag operations" is used). Deleting the view
+ // during an ongoing drag operation is not allowed, so
+ // this will postponed.
+ if (m_dragSource != 0) {
+ // the old stored view is obviously not the drag source anymore
+ m_dragSource->deleteLater();
+ m_dragSource = 0;
+ }
+ view->hide();
+ m_dragSource = view;
+ } else {
+ view->deleteLater();
+ }
+ }
+
+ m_iconsView = 0;
+ m_detailsView = 0;
+
+ if (m_columnsContainer != 0) {
+ m_columnsContainer->deleteLater();
+ }
+ m_columnsContainer = 0;
+}
+
+
+void DolphinView::ViewAccessor::prepareUrlChange(const KUrl& url)
+{
+ if (m_columnsContainer != 0) {
+ m_columnsContainer->showColumn(url);
+ }
+}
+
+QAbstractItemView* DolphinView::ViewAccessor::itemView() const
+{
+ if (m_iconsView != 0) {
+ return m_iconsView;
+ }
+
+ if (m_detailsView != 0) {
+ return m_detailsView;
+ }
+
+ if (m_columnsContainer != 0) {
+ return m_columnsContainer->activeColumn();
+ }
+
+ return 0;
+}
+
+KFileItemDelegate* DolphinView::ViewAccessor::itemDelegate() const
+{
+ return static_cast<KFileItemDelegate*>(itemView()->itemDelegate());
+}
+
+QWidget* DolphinView::ViewAccessor::layoutTarget() const
+{
+ if (m_columnsContainer != 0) {
+ return m_columnsContainer;
+ }
+ return itemView();
+}
+
+KUrl DolphinView::ViewAccessor::rootUrl() const
+{
+ return (m_columnsContainer != 0) ? m_columnsContainer->rootUrl() : KUrl();
+}
+
+bool DolphinView::ViewAccessor::supportsCategorizedSorting() const
+{
+ return m_iconsView != 0;
+}
+
+bool DolphinView::ViewAccessor::itemsExpandable() const
+{
+ return (m_detailsView != 0) && m_detailsView->itemsExpandable();
+}
+
+
+QSet<KUrl> DolphinView::ViewAccessor::expandedUrls() const
+{
+ if (m_detailsView != 0) {
+ return m_detailsView->expandedUrls();
+ }
+
+ return QSet<KUrl>();
+}
+
+const DolphinDetailsViewExpander* DolphinView::ViewAccessor::setExpandedUrls(const QSet<KUrl>& urlsToExpand)
+{
+ if ((m_detailsView != 0) && m_detailsView->itemsExpandable() && !urlsToExpand.isEmpty()) {
+ // Check if another expander is already active and stop it if necessary.
+ if(!m_detailsViewExpander.isNull()) {
+ m_detailsViewExpander->stop();
+ }
+
+ m_detailsViewExpander = new DolphinDetailsViewExpander(m_detailsView, urlsToExpand);
+ return m_detailsViewExpander;
+ }
+ else {
+ return 0;
+ }
+}
+
+bool DolphinView::ViewAccessor::reloadOnAdditionalInfoChange() const
+{
+ // the details view requires no reloading of the directory, as it maps
+ // the file item delegate info to its columns internally
+ return m_detailsView != 0;
+}
+
+DolphinModel* DolphinView::ViewAccessor::dirModel() const
+{
+ return static_cast<DolphinModel*>(proxyModel()->sourceModel());
+}
+
+DolphinSortFilterProxyModel* DolphinView::ViewAccessor::proxyModel() const
+{
+ if (m_columnsContainer != 0) {
+ return static_cast<DolphinSortFilterProxyModel*>(m_columnsContainer->activeColumn()->model());
+ }
+ return m_proxyModel;
+}
+
+KDirLister* DolphinView::ViewAccessor::dirLister() const
+{
+ return dirModel()->dirLister();
+}
+
+void DolphinView::slotRedirection(const KUrl& oldUrl, const KUrl& newUrl)
+{
+ if (oldUrl.equals(url(), KUrl::CompareWithoutTrailingSlash)) {
+ emit redirection(oldUrl, newUrl);
+ m_viewModeController->redirectToUrl(newUrl); // #186947
+ }
+}
+
+void DolphinView::restoreContentsPosition()
+{
+ if (!m_restoredContentsPosition.isNull()) {
+ const int x = m_restoredContentsPosition.x();
+ const int y = m_restoredContentsPosition.y();
+ m_restoredContentsPosition = QPoint();
+
+ QAbstractItemView* view = m_viewAccessor.itemView();
+ Q_ASSERT(view != 0);
+ view->horizontalScrollBar()->setValue(x);
+ view->verticalScrollBar()->setValue(y);
+ }
+}
+
+#include "dolphinview.moc"
diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h
new file mode 100644
index 000000000..dbef511bf
--- /dev/null
+++ b/src/views/dolphinview.h
@@ -0,0 +1,818 @@
+/***************************************************************************
+ * Copyright (C) 2006-2009 by Peter Penz <[email protected]> *
+ * Copyright (C) 2006 by Gregor Kališnik <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+
+#ifndef DOLPHINVIEW_H
+#define DOLPHINVIEW_H
+
+#include <config-nepomuk.h>
+
+#include "libdolphin_export.h"
+
+#include <kparts/part.h>
+#include <kfileitem.h>
+#include <kfileitemdelegate.h>
+#include <kio/fileundomanager.h>
+#include <kio/job.h>
+
+#include <QBoxLayout>
+#include <QKeyEvent>
+#include <QLinkedList>
+#include <QListView>
+#include <QSet>
+#include <QWidget>
+
+typedef KIO::FileUndoManager::CommandType CommandType;
+
+class DolphinColumnViewContainer;
+class DolphinDetailsView;
+class DolphinIconsView;
+class DolphinModel;
+class DolphinSortFilterProxyModel;
+class DolphinViewController;
+class KAction;
+class KActionCollection;
+class KDirLister;
+class KUrl;
+class ViewModeController;
+class ViewProperties;
+class DolphinDetailsViewExpander;
+
+/**
+ * @short Represents a view for the directory content.
+ *
+ * View modes for icons, details and columns are supported. It's
+ * possible to adjust:
+ * - sort order
+ * - sort type
+ * - show hidden files
+ * - show previews
+ *
+ * @see DolphinIconsView
+ * @see DolphinDetailsView
+ * @see DolphinColumnView
+ */
+class LIBDOLPHINPRIVATE_EXPORT DolphinView : public QWidget
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Defines the view mode for a directory. The view mode
+ * can be defined when constructing a DolphinView. The
+ * view mode is automatically updated if the directory itself
+ * defines a view mode (see class ViewProperties for details).
+ */
+ enum Mode
+ {
+ /**
+ * The directory items are shown as icons including an
+ * icon name.
+ */
+ IconsView = 0,
+
+ /**
+ * The icon, the name and at least the size of the directory
+ * items are shown in a table. It is possible to add columns
+ * for date, group and permissions.
+ */
+ DetailsView = 1,
+
+ /**
+ * Each folder is shown in a separate column.
+ */
+ ColumnView = 2,
+ MaxModeEnum = ColumnView
+ };
+
+ /** Defines the sort order for the items of a directory. */
+ enum Sorting
+ {
+ SortByName = 0,
+ SortBySize,
+ SortByDate,
+ SortByPermissions,
+ SortByOwner,
+ SortByGroup,
+ SortByType,
+ SortByDestination,
+ SortByPath,
+ MaxSortingEnum = SortByPath
+ };
+
+ /**
+ * @param parent Parent widget of the view.
+ * @param url Specifies the content which should be shown.
+ * @param proxyModel Used proxy model which specifies the sorting. The
+ * model is not owned by the view and won't get
+ * deleted.
+ */
+ DolphinView(QWidget* parent,
+ const KUrl& url,
+ DolphinSortFilterProxyModel* proxyModel);
+
+ virtual ~DolphinView();
+
+ /**
+ * Returns the current active URL, where all actions are applied.
+ * The URL navigator is synchronized with this URL.
+ */
+ KUrl url() const;
+
+ /**
+ * Returns the root URL of the view, which is defined as the first
+ * visible path of DolphinView::url(). Usually the root URL is
+ * equal to DolphinView::url(), but in the case of the column view
+ * when 2 columns are shown, the root URL might be:
+ * /home/peter/Documents
+ * and DolphinView::url() might return
+ * /home/peter/Documents/Music/
+ */
+ KUrl rootUrl() const;
+
+ /**
+ * If \a active is true, the view will marked as active. The active
+ * view is defined as view where all actions are applied to.
+ */
+ void setActive(bool active);
+ bool isActive() const;
+
+ /**
+ * Changes the view mode for the current directory to \a mode.
+ * If the view properties should be remembered for each directory
+ * (GeneralSettings::globalViewProps() returns false), then the
+ * changed view mode will be stored automatically.
+ */
+ void setMode(Mode mode);
+ Mode mode() const;
+
+ /** See setShowPreview */
+ bool showPreview() const;
+
+ /** See setShowHiddenFiles */
+ bool showHiddenFiles() const;
+
+ /** See setCategorizedSorting */
+ bool categorizedSorting() const;
+
+ /**
+ * Returns true, if the categorized sorting is supported by the current
+ * used mode (see DolphinView::setMode()). Currently only DolphinView::IconsView
+ * supports categorizations. To check whether the categorized
+ * sorting is set, use DolphinView::categorizedSorting().
+ */
+ bool supportsCategorizedSorting() const;
+
+ /**
+ * Marks the items indicated by \p urls to get selected after the
+ * directory DolphinView::url() has been loaded. Note that nothing
+ * gets selected if no loading of a directory has been triggered
+ * by DolphinView::setUrl() or DolphinView::reload().
+ */
+ void markUrlsAsSelected(const QList<KUrl>& urls);
+
+ /**
+ * Returns the selected items. The list is empty if no item has been
+ * selected.
+ * @see DolphinView::selectedUrls()
+ */
+ KFileItemList selectedItems() const;
+
+ /**
+ * Returns a list of URLs for all selected items. An empty list
+ * is returned, if no item is selected.
+ * @see DolphinView::selectedItems()
+ */
+ KUrl::List selectedUrls() const;
+
+ /**
+ * Returns the number of selected items (this is faster than
+ * invoking selectedItems().count()).
+ */
+ int selectedItemsCount() const;
+
+ QItemSelectionModel* selectionModel() const;
+
+ /**
+ * Sets the zoom level to \a level. It is assured that the used
+ * level is adjusted to be inside the range ZoomLevelInfo::minimumLevel() and
+ * ZoomLevelInfo::maximumLevel().
+ */
+ void setZoomLevel(int level);
+ int zoomLevel() const;
+
+ /**
+ * Returns true, if zooming in is possible. If false is returned,
+ * the maximum zooming level has been reached.
+ */
+ bool isZoomInPossible() const;
+
+ /**
+ * Returns true, if zooming out is possible. If false is returned,
+ * the minimum zooming level has been reached.
+ */
+ bool isZoomOutPossible() const;
+
+ /** Sets the sort order of the items inside a directory (see DolphinView::Sorting). */
+ void setSorting(Sorting sorting);
+
+ /** Returns the sort order of the items inside a directory (see DolphinView::Sorting). */
+ Sorting sorting() const;
+
+ /** Sets the sort order (Qt::Ascending or Qt::Descending) for the items. */
+ void setSortOrder(Qt::SortOrder order);
+
+ /** Returns the current used sort order (Qt::Ascending or Qt::Descending). */
+ Qt::SortOrder sortOrder() const;
+
+ /** Sets a separate sorting with folders first (true) or a mixed sorting of files and folders (false). */
+ void setSortFoldersFirst(bool foldersFirst);
+
+ /** Returns if files and folders are sorted separately or not. */
+ bool sortFoldersFirst() const;
+
+ /** Sets the additional information which should be shown for the items. */
+ void setAdditionalInfo(KFileItemDelegate::InformationList info);
+
+ /** Returns the additional information which should be shown for the items. */
+ KFileItemDelegate::InformationList additionalInfo() const;
+
+ /** Reloads the current directory. */
+ void reload();
+
+ /**
+ * Refreshes the view to get synchronized with the (updated) Dolphin settings.
+ * This method only needs to get invoked if the view settings for the Icons View,
+ * Details View or Columns View have been changed.
+ */
+ void refresh();
+
+ /**
+ * Filters the currently shown items by \a nameFilter. All items
+ * which contain the given filter string will be shown.
+ */
+ void setNameFilter(const QString& nameFilter);
+
+ /**
+ * Calculates the number of currently shown files into
+ * \a fileCount and the number of folders into \a folderCount.
+ * The size of all files is written into \a totalFileSize.
+ * It is recommend using this method instead of asking the
+ * directory lister or the model directly, as it takes
+ * filtering and hierarchical previews into account.
+ */
+ void calculateItemCount(int& fileCount, int& folderCount, KIO::filesize_t& totalFileSize) const;
+
+ /**
+ * Returns a textual representation of the state of the current
+ * folder or selected items, suitable for use in the status bar.
+ */
+ QString statusBarText() const;
+
+ /**
+ * Returns the version control actions that are provided for the items \p items.
+ * Usually the actions are presented in the context menu.
+ */
+ QList<QAction*> versionControlActions(const KFileItemList& items) const;
+
+ /**
+ * Updates the state of the 'Additional Information' actions in \a collection.
+ */
+ void updateAdditionalInfoActions(KActionCollection* collection);
+
+ /**
+ * Returns the state of the paste action:
+ * first is whether the action should be enabled
+ * second is the text for the action
+ */
+ QPair<bool, QString> pasteInfo() const;
+
+ /**
+ * If \a tabsForFiles is true, the signal tabRequested() will also
+ * emitted also for files. Per default tabs for files is disabled
+ * and hence the signal tabRequested() will only be emitted for
+ * directories.
+ */
+ void setTabsForFilesEnabled(bool tabsForFiles);
+ bool isTabsForFilesEnabled() const;
+
+ /**
+ * Returns true if the current view allows folders to be expanded,
+ * i.e. presents a hierarchical view to the user.
+ */
+ bool itemsExpandable() const;
+
+ /**
+ * Restores the view state (current item, contents position, details view expansion state)
+ */
+ void restoreState(QDataStream& stream);
+
+ /**
+ * Saves the view state (current item, contents position, details view expansion state)
+ */
+ void saveState(QDataStream& stream);
+
+public slots:
+ /**
+ * Changes the directory to \a url. If the current directory is equal to
+ * \a url, nothing will be done (use DolphinView::reload() instead).
+ */
+ void setUrl(const KUrl& url);
+
+ /**
+ * Selects all items.
+ * @see DolphinView::selectedItems()
+ */
+ void selectAll();
+
+ /**
+ * Inverts the current selection: selected items get unselected,
+ * unselected items get selected.
+ * @see DolphinView::selectedItems()
+ */
+ void invertSelection();
+
+ /** Returns true, if at least one item is selected. */
+ bool hasSelection() const;
+
+ void clearSelection();
+
+ /**
+ * Triggers the renaming of the currently selected items, where
+ * the user must input a new name for the items.
+ */
+ void renameSelectedItems();
+
+ /**
+ * Moves all selected items to the trash.
+ */
+ void trashSelectedItems();
+
+ /**
+ * Deletes all selected items.
+ */
+ void deleteSelectedItems();
+
+ /**
+ * Copies all selected items to the clipboard and marks
+ * the items as cut.
+ */
+ void cutSelectedItems();
+
+ /** Copies all selected items to the clipboard. */
+ void copySelectedItems();
+
+ /** Pastes the clipboard data to this view. */
+ void paste();
+
+ /**
+ * Pastes the clipboard data into the currently selected
+ * folder. If the current selection is not exactly one folder, no
+ * paste operation is done.
+ */
+ void pasteIntoFolder();
+
+ /**
+ * Turns on the file preview for the all files of the current directory,
+ * if \a show is true.
+ * If the view properties should be remembered for each directory
+ * (GeneralSettings::globalViewProps() returns false), then the
+ * preview setting will be stored automatically.
+ */
+ void setShowPreview(bool show);
+
+ /**
+ * Shows all hidden files of the current directory,
+ * if \a show is true.
+ * If the view properties should be remembered for each directory
+ * (GeneralSettings::globalViewProps() returns false), then the
+ * show hidden file setting will be stored automatically.
+ */
+ void setShowHiddenFiles(bool show);
+
+ /**
+ * Summarizes all sorted items by their category \a categorized
+ * is true.
+ * If the view properties should be remembered for each directory
+ * (GeneralSettings::globalViewProps() returns false), then the
+ * categorized sorting setting will be stored automatically.
+ */
+ void setCategorizedSorting(bool categorized);
+
+ /** Switches between an ascending and descending sorting order. */
+ void toggleSortOrder();
+
+ /** Switches between a separate sorting (with folders first) and a mixed sorting of files and folders. */
+ void toggleSortFoldersFirst();
+
+ /**
+ * Switches on or off the displaying of additional information
+ * as specified by \a action.
+ */
+ void toggleAdditionalInfo(QAction* action);
+
+signals:
+ /**
+ * Is emitted if the view has been activated by e. g. a mouse click.
+ */
+ void activated();
+
+ /** Is emitted if URL of the view has been changed to \a url. */
+ void urlChanged(const KUrl& url);
+
+ /**
+ * Is emitted when clicking on an item with the left mouse button.
+ */
+ void itemTriggered(const KFileItem& item);
+
+ /**
+ * Is emitted if a new tab should be opened for the URL \a url.
+ */
+ void tabRequested(const KUrl& url);
+
+ /**
+ * Is emitted if the view mode (IconsView, DetailsView,
+ * PreviewsView) has been changed.
+ */
+ void modeChanged();
+
+ /** Is emitted if the 'show preview' property has been changed. */
+ void showPreviewChanged();
+
+ /** Is emitted if the 'show hidden files' property has been changed. */
+ void showHiddenFilesChanged();
+
+ /** Is emitted if the 'categorized sorting' property has been changed. */
+ void categorizedSortingChanged();
+
+ /** Is emitted if the sorting by name, size or date has been changed. */
+ void sortingChanged(DolphinView::Sorting sorting);
+
+ /** Is emitted if the sort order (ascending or descending) has been changed. */
+ void sortOrderChanged(Qt::SortOrder order);
+
+ /** Is emitted if the sorting of files and folders (separate with folders first or mixed) has been changed. */
+ void sortFoldersFirstChanged(bool foldersFirst);
+
+ /** Is emitted if the additional information shown for this view has been changed. */
+ void additionalInfoChanged();
+
+ /** Is emitted if the zoom level has been changed by zooming in or out. */
+ void zoomLevelChanged(int level);
+
+ /**
+ * Is emitted if information of an item is requested to be shown e. g. in the panel.
+ * If item is null, no item information request is pending.
+ */
+ void requestItemInfo(const KFileItem& item);
+
+ /**
+ * Is emitted whenever the selection has been changed.
+ */
+ void selectionChanged(const KFileItemList& selection);
+
+ /**
+ * Is emitted if a context menu is requested for the item \a item,
+ * which is part of \a url. If the item is null, the context menu
+ * for the URL should be shown and the custom actions \a customActions
+ * will be added.
+ */
+ void requestContextMenu(const KFileItem& item,
+ const KUrl& url,
+ const QList<QAction*>& customActions);
+
+ /**
+ * Is emitted if an information message with the content \a msg
+ * should be shown.
+ */
+ void infoMessage(const QString& msg);
+
+ /**
+ * Is emitted if an error message with the content \a msg
+ * should be shown.
+ */
+ void errorMessage(const QString& msg);
+
+ /**
+ * Is emitted if an "operation completed" message with the content \a msg
+ * should be shown.
+ */
+ void operationCompletedMessage(const QString& msg);
+
+ /**
+ * Is emitted after DolphinView::setUrl() has been invoked and
+ * the path \a url is currently loaded. If this signal is emitted,
+ * it is assured that the view contains already the correct root
+ * URL and property settings.
+ */
+ void startedPathLoading(const KUrl& url);
+
+ /**
+ * Emitted when KDirLister emits redirection.
+ * Testcase: fish://localhost
+ */
+ void redirection(const KUrl& oldUrl, const KUrl& newUrl);
+
+protected:
+ /** @see QWidget::mouseReleaseEvent */
+ virtual void mouseReleaseEvent(QMouseEvent* event);
+ virtual bool eventFilter(QObject* watched, QEvent* event);
+
+private slots:
+ /**
+ * Marks the view as active (DolphinView:isActive() will return true)
+ * and emits the 'activated' signal if it is not already active.
+ */
+ void activate();
+
+ /**
+ * If the item \a item is a directory, then this
+ * directory will be loaded. If the item is a file, the corresponding
+ * application will get started.
+ */
+ void triggerItem(const KFileItem& index);
+
+ /**
+ * Emits the signal \a selectionChanged() with a small delay. This is
+ * because getting all file items for the signal can be an expensive
+ * operation. Fast selection changes are collected in this case and
+ * the signal is emitted only after no selection change has been done
+ * within a small delay.
+ */
+ void slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
+
+ /**
+ * Is called by emitDelayedSelectionChangedSignal() and emits the
+ * signal \a selectionChanged() with all selected file items as parameter.
+ */
+ void emitSelectionChangedSignal();
+
+ /**
+ * Opens the context menu on position \a pos. The position
+ * is used to check whether the context menu is related to an
+ * item or to the viewport.
+ */
+ void openContextMenu(const QPoint& pos, const QList<QAction*>& customActions);
+
+ /**
+ * Drops dragged URLs to the destination path \a destPath. If
+ * the URLs are dropped above an item inside the destination path,
+ * the item is indicated by \a destItem.
+ */
+ void dropUrls(const KFileItem& destItem,
+ const KUrl& destPath,
+ QDropEvent* event);
+
+ /**
+ * Updates the view properties of the current URL to the
+ * sorting given by \a sorting.
+ */
+ void updateSorting(DolphinView::Sorting sorting);
+
+ /**
+ * Updates the view properties of the current URL to the
+ * sort order given by \a order.
+ */
+ void updateSortOrder(Qt::SortOrder order);
+
+ /**
+ * Updates the view properties of the current URL to the
+ * sorting of files and folders (separate with folders first or mixed) given by \a foldersFirst.
+ */
+ void updateSortFoldersFirst(bool foldersFirst);
+
+ /**
+ * Updates the view properties of the current URL to the
+ * additional information given by \a info.
+ */
+ void updateAdditionalInfo(const KFileItemDelegate::InformationList& info);
+
+ /**
+ * Updates the status bar to show hover information for the
+ * item \a item. If currently other items are selected,
+ * no hover information is shown.
+ * @see DolphinView::clearHoverInformation()
+ */
+ void showHoverInformation(const KFileItem& item);
+
+ /**
+ * Clears the hover information shown in the status bar.
+ * @see DolphinView::showHoverInformation().
+ */
+ void clearHoverInformation();
+
+ /**
+ * Indicates in the status bar that the delete operation
+ * of the job \a job has been finished.
+ */
+ void slotDeleteFileFinished(KJob* job);
+
+ /**
+ * Invoked when the directory lister has completed the loading of
+ * items. Assures that pasted items and renamed items get seleced.
+ */
+ void slotDirListerCompleted();
+
+ /**
+ * Invoked when the loading of the directory is finished.
+ * Restores the active item and the scroll position if possible.
+ */
+ void slotLoadingCompleted();
+
+ /**
+ * Is invoked when the KDirLister indicates refreshed items.
+ */
+ void slotRefreshItems();
+
+ /**
+ * Observes the item with the URL \a url. As soon as the directory
+ * model indicates that the item is available, the item will
+ * get selected and it is assure that the item stays visible.
+ *
+ * @see selectAndScrollToCreatedItem()
+ */
+ void observeCreatedItem(const KUrl& url);
+
+ /**
+ * Selects and scrolls to the item that got observed
+ * by observeCreatedItem().
+ */
+ void selectAndScrollToCreatedItem();
+
+ /**
+ * Called when a redirection happens.
+ * Testcase: fish://localhost
+ */
+ void slotRedirection(const KUrl& oldUrl, const KUrl& newUrl);
+
+ /**
+ * Restores the contents position, if history information about the old position is available.
+ */
+ void restoreContentsPosition();
+
+private:
+ void loadDirectory(const KUrl& url, bool reload = false);
+
+ /**
+ * Applies the view properties which are defined by the current URL
+ * to the DolphinView properties.
+ */
+ void applyViewProperties();
+
+ /**
+ * Creates a new view representing the given view mode (DolphinView::mode()).
+ * The current view will get deleted.
+ */
+ void createView();
+
+ void deleteView();
+
+ /**
+ * Helper method for DolphinView::paste() and DolphinView::pasteIntoFolder().
+ * Pastes the clipboard data into the URL \a url.
+ */
+ void pasteToUrl(const KUrl& url);
+
+ /**
+ * Checks whether the current item view has the same zoom level
+ * as \a oldZoomLevel. If this is not the case, the zoom level
+ * of the controller is updated and a zoomLevelChanged() signal
+ * is emitted.
+ */
+ void updateZoomLevel(int oldZoomLevel);
+
+ /**
+ * Returns a list of URLs for all selected items. The list is
+ * simplified, so that when the URLs are part of different tree
+ * levels, only the parent is returned.
+ */
+ KUrl::List simplifiedSelectedUrls() const;
+
+ /**
+ * Returns the MIME data for all selected items.
+ */
+ QMimeData* selectionMimeData() const;
+
+ /**
+ * Is invoked after a paste operation or a drag & drop
+ * operation and adds the filenames of all URLs from \a mimeData to
+ * m_newFileNames. This allows to select all newly added
+ * items in slotDirListerCompleted().
+ */
+ void addNewFileNames(const QMimeData* mimeData);
+
+private:
+ /**
+ * Abstracts the access to the different view implementations
+ * for icons-, details- and column-view.
+ */
+ class ViewAccessor
+ {
+ public:
+ ViewAccessor(DolphinSortFilterProxyModel* proxyModel);
+ ~ViewAccessor();
+
+ void createView(QWidget* parent,
+ DolphinViewController* dolphinViewController,
+ const ViewModeController* viewModeController,
+ Mode mode);
+ void deleteView();
+
+ /**
+ * Must be invoked before the URL has been changed and allows view implementations
+ * like the column view to create a new column.
+ */
+ void prepareUrlChange(const KUrl& url);
+
+ QAbstractItemView* itemView() const;
+ KFileItemDelegate* itemDelegate() const;
+
+ /**
+ * Returns the widget that should be added to the layout as target. Usually
+ * the item view itself is returned, but in the case of the column view
+ * a container widget is returned.
+ */
+ QWidget* layoutTarget() const;
+
+ KUrl rootUrl() const;
+
+ bool supportsCategorizedSorting() const;
+ bool itemsExpandable() const;
+ QSet<KUrl> expandedUrls() const;
+ const DolphinDetailsViewExpander* setExpandedUrls(const QSet<KUrl>& urlsToExpand);
+
+ /**
+ * Returns true, if a reloading of the items is required
+ * when the additional information properties have been changed
+ * by the user.
+ */
+ bool reloadOnAdditionalInfoChange() const;
+
+ DolphinModel* dirModel() const;
+ DolphinSortFilterProxyModel* proxyModel() const;
+ KDirLister* dirLister() const;
+
+ private:
+ DolphinIconsView* m_iconsView;
+ DolphinDetailsView* m_detailsView;
+ DolphinColumnViewContainer* m_columnsContainer;
+ DolphinSortFilterProxyModel* m_proxyModel;
+ QAbstractItemView* m_dragSource;
+ QPointer<DolphinDetailsViewExpander> m_detailsViewExpander;
+ };
+
+ bool m_active : 1;
+ bool m_showPreview : 1;
+ bool m_storedCategorizedSorting : 1;
+ bool m_tabsForFiles : 1;
+ bool m_isContextMenuOpen : 1; // TODO: workaround for Qt-issue 207192
+ bool m_ignoreViewProperties : 1;
+ bool m_assureVisibleCurrentIndex : 1;
+ bool m_expanderActive : 1;
+
+ Mode m_mode;
+
+ QVBoxLayout* m_topLayout;
+
+ DolphinViewController* m_dolphinViewController;
+ ViewModeController* m_viewModeController;
+ ViewAccessor m_viewAccessor;
+
+ QItemSelectionModel* m_selectionModel; // allow to switch views without losing the selection
+ QTimer* m_selectionChangedTimer;
+
+ KUrl m_rootUrl;
+ KUrl m_activeItemUrl;
+ QPoint m_restoredContentsPosition;
+ KUrl m_createdItemUrl; // URL for a new item that got created by the "Create New..." menu
+ KFileItemList m_selectedItems; // this is used for making the View to remember selections after F5
+
+ /**
+ * Remembers the filenames that have been added by a paste operation
+ * or a drag & drop operation. Allows to select the items in
+ * slotDirListerCompleted().
+ */
+ QSet<QString> m_newFileNames;
+};
+
+/// Allow using DolphinView::Mode in QVariant
+Q_DECLARE_METATYPE(DolphinView::Mode)
+
+#endif // DOLPHINVIEW_H
diff --git a/src/views/dolphinviewautoscroller.cpp b/src/views/dolphinviewautoscroller.cpp
new file mode 100644
index 000000000..45896a5eb
--- /dev/null
+++ b/src/views/dolphinviewautoscroller.cpp
@@ -0,0 +1,223 @@
+/***************************************************************************
+ * Copyright (C) 2008 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphinviewautoscroller.h"
+
+#include <QAbstractItemView>
+#include <QApplication>
+#include <QCursor>
+#include <QEvent>
+#include <QMouseEvent>
+#include <QScrollBar>
+#include <QTimer>
+#include <math.h>
+
+DolphinViewAutoScroller::DolphinViewAutoScroller(QAbstractItemView* parent) :
+ QObject(parent),
+ m_rubberBandSelection(false),
+ m_keyPressed(false),
+ m_initializedTimestamp(false),
+ m_horizontalScrollInc(0),
+ m_verticalScrollInc(0),
+ m_itemView(parent),
+ m_timer(0),
+ m_timestamp()
+{
+ m_itemView->setAutoScroll(false);
+ m_itemView->viewport()->installEventFilter(this);
+ m_itemView->installEventFilter(this);
+
+ m_timer = new QTimer(this);
+ m_timer->setSingleShot(false);
+ m_timer->setInterval(1000 / 25); // 25 frames per second
+ connect(m_timer, SIGNAL(timeout()), this, SLOT(scrollViewport()));
+}
+
+DolphinViewAutoScroller::~DolphinViewAutoScroller()
+{
+}
+
+bool DolphinViewAutoScroller::isActive() const
+{
+ return m_timer->isActive();
+}
+
+void DolphinViewAutoScroller::handleCurrentIndexChange(const QModelIndex& current,
+ const QModelIndex& previous)
+{
+ // When the autoscroller is inactive and a key has been pressed, it must be
+ // assured that the current item stays visible. The check whether the previous
+ // item is valid is important because of #197951. The keypress check is done
+ // because of #199833.
+ if (current.isValid() && (previous.isValid() || m_keyPressed) && !isActive()) {
+ m_itemView->scrollTo(current);
+ }
+}
+
+bool DolphinViewAutoScroller::eventFilter(QObject* watched, QEvent* event)
+{
+ if (watched == m_itemView->viewport()) {
+ switch (event->type()) {
+ case QEvent::MouseButtonPress:
+ if (static_cast<QMouseEvent*>(event)->button() == Qt::LeftButton) {
+ m_rubberBandSelection = true;
+ }
+ break;
+
+ case QEvent::MouseMove:
+ if (m_rubberBandSelection) {
+ triggerAutoScroll();
+ }
+ break;
+
+ case QEvent::MouseButtonRelease:
+ m_rubberBandSelection = false;
+ stopAutoScroll();
+ break;
+
+ case QEvent::DragEnter:
+ case QEvent::DragMove:
+ m_rubberBandSelection = false;
+ triggerAutoScroll();
+ break;
+
+ case QEvent::Drop:
+ case QEvent::DragLeave:
+ m_rubberBandSelection = false;
+ stopAutoScroll();
+ break;
+
+ default:
+ break;
+ }
+ } else if (watched == m_itemView) {
+ switch (event->type()) {
+ case QEvent::KeyPress:
+ m_keyPressed = true;
+ break;
+
+ case QEvent::KeyRelease:
+ m_keyPressed = false;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return QObject::eventFilter(watched, event);
+}
+
+void DolphinViewAutoScroller::scrollViewport()
+{
+ if (m_timestamp.elapsed() < QApplication::startDragTime()) {
+ return;
+ }
+
+ QScrollBar* verticalScrollBar = m_itemView->verticalScrollBar();
+ if (verticalScrollBar != 0) {
+ const int value = verticalScrollBar->value();
+ verticalScrollBar->setValue(value + m_verticalScrollInc);
+
+ }
+ QScrollBar* horizontalScrollBar = m_itemView->horizontalScrollBar();
+ if (horizontalScrollBar != 0) {
+ const int value = horizontalScrollBar->value();
+ horizontalScrollBar->setValue(value + m_horizontalScrollInc);
+
+ }
+
+ if (m_rubberBandSelection) {
+ // The scrolling does not lead to an update of the rubberband
+ // selection. Fake a mouse move event to let the QAbstractItemView
+ // update the rubberband.
+ QWidget* viewport = m_itemView->viewport();
+ const QPoint pos = viewport->mapFromGlobal(QCursor::pos());
+ QMouseEvent event(QEvent::MouseMove, pos, Qt::LeftButton, Qt::LeftButton, QApplication::keyboardModifiers());
+ QCoreApplication::sendEvent(viewport, &event);
+ }
+}
+
+void DolphinViewAutoScroller::triggerAutoScroll()
+{
+ const bool verticalScrolling = (m_itemView->verticalScrollBar() != 0) &&
+ m_itemView->verticalScrollBar()->isVisible();
+ const bool horizontalScrolling = (m_itemView->horizontalScrollBar() != 0) &&
+ m_itemView->horizontalScrollBar()->isVisible();
+ if (!verticalScrolling && !horizontalScrolling) {
+ // no scrollbars are shown at all, so no autoscrolling is necessary
+ stopAutoScroll();
+ return;
+ }
+
+ QWidget* viewport = m_itemView->viewport();
+ const QPoint pos = viewport->mapFromGlobal(QCursor::pos());
+ if (verticalScrolling) {
+ m_verticalScrollInc = calculateScrollIncrement(pos.y(), viewport->height());
+ }
+ if (horizontalScrolling) {
+ m_horizontalScrollInc = calculateScrollIncrement(pos.x(), viewport->width());
+ }
+
+ if (m_timer->isActive()) {
+ if ((m_horizontalScrollInc == 0) && (m_verticalScrollInc == 0)) {
+ stopAutoScroll();
+ }
+ } else if ((m_horizontalScrollInc != 0) || (m_verticalScrollInc != 0)) {
+ if (!m_initializedTimestamp) {
+ m_initializedTimestamp = true;
+ m_timestamp.start();
+ }
+ m_timer->start();
+ }
+}
+
+void DolphinViewAutoScroller::stopAutoScroll()
+{
+ m_timer->stop();
+ m_horizontalScrollInc = 0;
+ m_verticalScrollInc = 0;
+ m_initializedTimestamp = false;
+}
+
+int DolphinViewAutoScroller::calculateScrollIncrement(int cursorPos, int rangeSize) const
+{
+ int inc = 0;
+
+ const int minSpeed = 4;
+ const int maxSpeed = 768;
+ const int speedLimiter = 48;
+ const int autoScrollBorder = 64;
+
+ if (cursorPos < autoScrollBorder) {
+ inc = -minSpeed + qAbs(cursorPos - autoScrollBorder) * (cursorPos - autoScrollBorder) / speedLimiter;
+ if (inc < -maxSpeed) {
+ inc = -maxSpeed;
+ }
+ } else if (cursorPos > rangeSize - autoScrollBorder) {
+ inc = minSpeed + qAbs(cursorPos - rangeSize + autoScrollBorder) * (cursorPos - rangeSize + autoScrollBorder) / speedLimiter;
+ if (inc > maxSpeed) {
+ inc = maxSpeed;
+ }
+ }
+
+ return inc;
+}
+
+#include "dolphinviewautoscroller.moc"
diff --git a/src/views/dolphinviewautoscroller.h b/src/views/dolphinviewautoscroller.h
new file mode 100644
index 000000000..9fd35d494
--- /dev/null
+++ b/src/views/dolphinviewautoscroller.h
@@ -0,0 +1,79 @@
+/***************************************************************************
+ * Copyright (C) 2008 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHINVIEWAUTOSCROLLER_H
+#define DOLPHINVIEWAUTOSCROLLER_H
+
+#include <QTime>
+#include <QObject>
+
+class QAbstractItemView;
+class QModelIndex;
+class QTimer;
+
+/**
+ * @brief Assures that an autoscrolling is done for item views.
+ *
+ * This is a workaround as QAbstractItemView::setAutoScroll() is not usable
+ * when selecting items (see Qt issue #214542).
+ */
+class DolphinViewAutoScroller : public QObject
+{
+ Q_OBJECT
+
+public:
+ DolphinViewAutoScroller(QAbstractItemView* parent);
+ virtual ~DolphinViewAutoScroller();
+ bool isActive() const;
+
+ /**
+ * Must be invoked by the parent item view, when QAbstractItemView::currentChanged()
+ * has been called. Assures that the current item stays visible when it has been
+ * changed by the keyboard.
+ */
+ void handleCurrentIndexChange(const QModelIndex& current, const QModelIndex& previous);
+
+protected:
+ virtual bool eventFilter(QObject* watched, QEvent* event);
+
+private slots:
+ void scrollViewport();
+
+private:
+ void triggerAutoScroll();
+ void stopAutoScroll();
+
+ /**
+ * Calculates the scroll increment dependent from
+ * the cursor position \a cursorPos and the range 0 - \a rangeSize - 1.
+ */
+ int calculateScrollIncrement(int cursorPos, int rangeSize) const;
+
+private:
+ bool m_rubberBandSelection;
+ bool m_keyPressed;
+ bool m_initializedTimestamp;
+ int m_horizontalScrollInc;
+ int m_verticalScrollInc;
+ QAbstractItemView* m_itemView;
+ QTimer* m_timer;
+ QTime m_timestamp;
+};
+
+#endif
diff --git a/src/views/dolphinviewcontroller.cpp b/src/views/dolphinviewcontroller.cpp
new file mode 100644
index 000000000..6ef32f07f
--- /dev/null
+++ b/src/views/dolphinviewcontroller.cpp
@@ -0,0 +1,249 @@
+/***************************************************************************
+ * Copyright (C) 2010 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "dolphinviewcontroller.h"
+#include "zoomlevelinfo.h"
+
+#include <kdirmodel.h>
+#include <kfileitemactions.h>
+#include <QAbstractProxyModel>
+#include <QApplication>
+#include <QClipboard>
+#include <QDir>
+
+Qt::MouseButtons DolphinViewController::m_mouseButtons = Qt::NoButton;
+
+DolphinViewController::DolphinViewController(DolphinView* dolphinView) :
+ QObject(dolphinView),
+ m_dolphinView(dolphinView),
+ m_itemView(0),
+ m_versionControlActions()
+{
+}
+
+DolphinViewController::~DolphinViewController()
+{
+}
+
+const DolphinView* DolphinViewController::view() const
+{
+ return m_dolphinView;
+}
+
+void DolphinViewController::requestUrlChange(const KUrl& url)
+{
+ emit urlChangeRequested(url);
+}
+
+void DolphinViewController::setItemView(QAbstractItemView* view)
+{
+ if (m_itemView != 0) {
+ disconnect(m_itemView, SIGNAL(pressed(const QModelIndex&)),
+ this, SLOT(updateMouseButtonState()));
+ }
+
+ m_itemView = view;
+
+ if (m_itemView != 0) {
+ // TODO: this is a workaround until Qt-issue 176832 has been fixed
+ connect(m_itemView, SIGNAL(pressed(const QModelIndex&)),
+ this, SLOT(updateMouseButtonState()));
+ }
+}
+
+QAbstractItemView* DolphinViewController::itemView() const
+{
+ return m_itemView;
+}
+
+void DolphinViewController::triggerContextMenuRequest(const QPoint& pos,
+ const QList<QAction*>& customActions)
+{
+ emit activated();
+ emit requestContextMenu(pos, customActions);
+}
+
+void DolphinViewController::requestActivation()
+{
+ emit activated();
+}
+
+void DolphinViewController::indicateDroppedUrls(const KFileItem& destItem,
+ const KUrl& destPath,
+ QDropEvent* event)
+{
+ emit urlsDropped(destItem, destPath, event);
+}
+
+
+void DolphinViewController::indicateSortingChange(DolphinView::Sorting sorting)
+{
+ emit sortingChanged(sorting);
+}
+
+void DolphinViewController::indicateSortOrderChange(Qt::SortOrder order)
+{
+ emit sortOrderChanged(order);
+}
+
+void DolphinViewController::indicateSortFoldersFirstChange(bool foldersFirst)
+{
+ emit sortFoldersFirstChanged(foldersFirst);
+}
+
+void DolphinViewController::indicateAdditionalInfoChange(const KFileItemDelegate::InformationList& info)
+{
+ emit additionalInfoChanged(info);
+}
+
+void DolphinViewController::setVersionControlActions(QList<QAction*> actions)
+{
+ m_versionControlActions = actions;
+}
+
+QList<QAction*> DolphinViewController::versionControlActions(const KFileItemList& items)
+{
+ emit requestVersionControlActions(items);
+ // All view implementations are connected with the signal requestVersionControlActions()
+ // (see ViewExtensionFactory) and will invoke DolphinViewController::setVersionControlActions(),
+ // so that the context dependent actions can be returned.
+ return m_versionControlActions;
+}
+
+void DolphinViewController::handleKeyPressEvent(QKeyEvent* event)
+{
+ Q_ASSERT(m_itemView != 0);
+
+ const QItemSelectionModel* selModel = m_itemView->selectionModel();
+ const QModelIndex currentIndex = selModel->currentIndex();
+ const bool trigger = currentIndex.isValid()
+ && ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Enter))
+ && !selModel->selectedIndexes().isEmpty();
+ if (!trigger) {
+ return;
+ }
+
+ // Collect the non-directory files into a list and
+ // call runPreferredApplications for that list.
+ // Several selected directories are opened in separate tabs,
+ // one selected directory will get opened in the view.
+ QModelIndexList dirQueue;
+ const QModelIndexList indexList = selModel->selectedIndexes();
+ KFileItemList fileOpenList;
+ foreach (const QModelIndex& index, indexList) {
+ if (itemForIndex(index).isDir()) {
+ dirQueue << index;
+ } else {
+ fileOpenList << itemForIndex(index);
+ }
+ }
+
+ KFileItemActions fileItemActions;
+ fileItemActions.runPreferredApplications(fileOpenList, "DesktopEntryName != 'dolphin'");
+
+ if (dirQueue.length() == 1) {
+ // open directory in the view
+ emit itemTriggered(itemForIndex(dirQueue[0]));
+ } else {
+ // open directories in separate tabs
+ foreach(const QModelIndex& dir, dirQueue) {
+ emit tabRequested(itemForIndex(dir).url());
+ }
+ }
+}
+
+void DolphinViewController::replaceUrlByClipboard()
+{
+ const QClipboard* clipboard = QApplication::clipboard();
+ QString text;
+ if (clipboard->mimeData(QClipboard::Selection)->hasText()) {
+ text = clipboard->mimeData(QClipboard::Selection)->text();
+ } else if (clipboard->mimeData(QClipboard::Clipboard)->hasText()) {
+ text = clipboard->mimeData(QClipboard::Clipboard)->text();
+ }
+ if (!text.isEmpty() && QDir::isAbsolutePath(text)) {
+ m_dolphinView->setUrl(KUrl(text));
+ }
+}
+
+void DolphinViewController::requestToolTipHiding()
+{
+ emit hideToolTip();
+}
+
+void DolphinViewController::emitItemTriggered(const KFileItem& item)
+{
+ emit itemTriggered(item);
+}
+
+KFileItem DolphinViewController::itemForIndex(const QModelIndex& index) const
+{
+ Q_ASSERT(m_itemView != 0);
+
+ QAbstractProxyModel* proxyModel = static_cast<QAbstractProxyModel*>(m_itemView->model());
+ KDirModel* dirModel = static_cast<KDirModel*>(proxyModel->sourceModel());
+ const QModelIndex dirIndex = proxyModel->mapToSource(index);
+ return dirModel->itemForIndex(dirIndex);
+}
+
+void DolphinViewController::triggerItem(const QModelIndex& index)
+{
+ if (m_mouseButtons & Qt::LeftButton) {
+ const KFileItem item = itemForIndex(index);
+ if (index.isValid() && (index.column() == KDirModel::Name)) {
+ emit itemTriggered(item);
+ } else {
+ m_itemView->clearSelection();
+ emit itemEntered(KFileItem());
+ }
+ }
+}
+
+void DolphinViewController::requestTab(const QModelIndex& index)
+{
+ if (m_mouseButtons & Qt::MidButton) {
+ const KFileItem item = itemForIndex(index);
+ const bool validRequest = index.isValid() &&
+ (index.column() == KDirModel::Name) &&
+ (item.isDir() || m_dolphinView->isTabsForFilesEnabled());
+ if (validRequest) {
+ emit tabRequested(item.url());
+ }
+ }
+}
+
+void DolphinViewController::emitItemEntered(const QModelIndex& index)
+{
+ KFileItem item = itemForIndex(index);
+ if (!item.isNull()) {
+ emit itemEntered(item);
+ }
+}
+
+void DolphinViewController::emitViewportEntered()
+{
+ emit viewportEntered();
+}
+
+void DolphinViewController::updateMouseButtonState()
+{
+ m_mouseButtons = QApplication::mouseButtons();
+}
+
+#include "dolphinviewcontroller.moc"
diff --git a/src/views/dolphinviewcontroller.h b/src/views/dolphinviewcontroller.h
new file mode 100644
index 000000000..6eed68e1a
--- /dev/null
+++ b/src/views/dolphinviewcontroller.h
@@ -0,0 +1,314 @@
+/***************************************************************************
+ * Copyright (C) 2010 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef DOLPHINVIEWCONTROLLER_H
+#define DOLPHINVIEWCONTROLLER_H
+
+#include <views/dolphinview.h>
+#include <kurl.h>
+#include <QtCore/QObject>
+#include <libdolphin_export.h>
+
+class QAbstractItemView;
+class DolphinView;
+class KUrl;
+class QPoint;
+
+/**
+ * @brief Allows the view mode implementations (DolphinIconsView, DolphinDetailsView, DolphinColumnView)
+ * to do a limited control of the DolphinView.
+ *
+ * The DolphinView connects to the signals of DolphinViewController to react on changes
+ * that have been triggered by the view mode implementations. The view mode implementations
+ * have read access to the whole DolphinView by DolphinViewController::view(). Limited control of the
+ * DolphinView by the view mode implementations are defined by the public DolphinController methods.
+ */
+class LIBDOLPHINPRIVATE_EXPORT DolphinViewController : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit DolphinViewController(DolphinView* dolphinView);
+ virtual ~DolphinViewController();
+
+ /**
+ * Allows read access for the view mode implementation
+ * to the DolphinView.
+ */
+ const DolphinView* view() const;
+
+ /**
+ * Requests the DolphinView to change the URL to \p url. The signal
+ * urlChangeRequested will be emitted.
+ */
+ void requestUrlChange(const KUrl& url);
+
+ /**
+ * Changes the current view mode implementation where the controller is working.
+ * This is only necessary for views like the tree view, where internally
+ * several QAbstractItemView instances are used.
+ */
+ void setItemView(QAbstractItemView* view);
+ QAbstractItemView* itemView() const;
+
+ /**
+ * Requests a context menu for the position \a pos. DolphinView
+ * takes care itself to get the selected items depending from
+ * \a pos. It is possible to define a custom list of actions for
+ * the context menu by \a customActions.
+ */
+ void triggerContextMenuRequest(const QPoint& pos,
+ const QList<QAction*>& customActions = QList<QAction*>());
+
+ /**
+ * Requests an activation of the DolphinView and emits the signal
+ * activated(). This method should be invoked by the view mode implementation
+ * if e. g. a mouse click on the view has been done.
+ */
+ void requestActivation();
+
+ /**
+ * Indicates that URLs are dropped above a destination. The DolphinView
+ * will start the corresponding action (copy, move, link).
+ * @param destItem Item of the destination (can be null, see KFileItem::isNull()).
+ * @param destPath Path of the destination.
+ * @param event Drop event
+ */
+ void indicateDroppedUrls(const KFileItem& destItem,
+ const KUrl& destPath,
+ QDropEvent* event);
+
+ /**
+ * Informs the DolphinView about a sorting change done inside
+ * the view mode implementation.
+ */
+ void indicateSortingChange(DolphinView::Sorting sorting);
+
+ /**
+ * Informs the DolphinView about a sort order change done inside
+ * the view mode implementation.
+ */
+ void indicateSortOrderChange(Qt::SortOrder order);
+
+ /**
+ * Informs the DolphinView about a change between separate sorting
+ * (with folders first) and mixed sorting of files and folders done inside
+ * the view mode implementation.
+ */
+ void indicateSortFoldersFirstChange(bool foldersFirst);
+
+ /**
+ * Informs the DolphinView about an additional information change
+ * done inside the view mode implementation.
+ */
+ void indicateAdditionalInfoChange(const KFileItemDelegate::InformationList& info);
+
+ /**
+ * Sets the available version control actions. Is called by the view
+ * mode implementation as soon as the DolphinView has requested them by the signal
+ * requestVersionControlActions().
+ */
+ void setVersionControlActions(QList<QAction*> actions);
+
+ /**
+ * Emits the signal requestVersionControlActions(). The view mode implementation
+ * listens to the signal and will invoke a DolphinViewController::setVersionControlActions()
+ * and the result will be returned.
+ */
+ QList<QAction*> versionControlActions(const KFileItemList& items);
+
+ /**
+ * Must be be invoked in each view mode implementation whenever a key has been
+ * pressed. If the selection model of \a view is not empty and
+ * the return key has been pressed, the selected items will get triggered.
+ */
+ void handleKeyPressEvent(QKeyEvent* event);
+
+ /**
+ * Replaces the URL of the DolphinView with the content
+ * of the clipboard as URL. If the clipboard contains no text,
+ * nothing will be done.
+ */
+ void replaceUrlByClipboard();
+
+ /**
+ * Requests the view mode implementation to hide tooltips.
+ */
+ void requestToolTipHiding();
+
+ /**
+ * Emits the signal itemTriggered() for the item \a item.
+ * The method can be used by the view mode implementations to
+ * trigger an item directly without mouse interaction.
+ * If the item triggering is done by the mouse, it is recommended
+ * to use DolphinViewController::triggerItem(), as this will check
+ * the used mouse buttons to execute the correct action.
+ */
+ void emitItemTriggered(const KFileItem& item);
+
+ /**
+ * Returns the file item for the proxy index \a index of the DolphinView.
+ */
+ KFileItem itemForIndex(const QModelIndex& index) const;
+
+public slots:
+ /**
+ * Emits the signal itemTriggered() if the file item for the index \a index
+ * is not null and the left mouse button has been pressed. If the item is
+ * null, the signal itemEntered() is emitted.
+ * The method should be invoked by the view mode implementations whenever the
+ * user has triggered an item with the mouse (see
+ * QAbstractItemView::clicked() or QAbstractItemView::doubleClicked()).
+ */
+ void triggerItem(const QModelIndex& index);
+
+ /**
+ * Emits the signal tabRequested(), if the file item for the index \a index
+ * represents a directory and when the middle mouse button has been pressed.
+ */
+ void requestTab(const QModelIndex& index);
+
+ /**
+ * Emits the signal itemEntered() if the file item for the index \a index
+ * is not null. The method should be invoked by the view mode implementation
+ * whenever the mouse cursor is above an item.
+ */
+ void emitItemEntered(const QModelIndex& index);
+
+ /**
+ * Emits the signal viewportEntered(). The method should be invoked by
+ * the view mode implementation whenever the mouse cursor is above the viewport.
+ */
+ void emitViewportEntered();
+
+signals:
+ void urlChangeRequested(const KUrl& url);
+
+ /**
+ * Is emitted if a context menu should be opened (see triggerContextMenuRequest()).
+ * @param pos Position relative to the view widget where the
+ * context menu should be opened. It is recommended
+ * to get the corresponding model index from
+ * this position.
+ * @param customActions List of actions that is added to the context menu when
+ * the menu is opened above the viewport.
+ */
+ void requestContextMenu(const QPoint& pos, QList<QAction*> customActions);
+
+ /**
+ * Is emitted if the view has been activated by e. g. a mouse click.
+ */
+ void activated();
+
+ /**
+ * Is emitted if URLs have been dropped to the destination
+ * path \a destPath. If the URLs have been dropped above an item of
+ * the destination path, the item is indicated by \a destItem
+ * (can be null, see KFileItem::isNull()).
+ */
+ void urlsDropped(const KFileItem& destItem,
+ const KUrl& destPath,
+ QDropEvent* event);
+
+ /**
+ * Is emitted if the sorting has been changed to \a sorting by
+ * the view mode implementation (see indicateSortingChanged().
+ * The DolphinView connects to
+ * this signal to update its menu action.
+ */
+ void sortingChanged(DolphinView::Sorting sorting);
+
+ /**
+ * Is emitted if the sort order has been changed to \a order
+ * by the view mode implementation (see indicateSortOrderChanged().
+ * The DolphinView connects
+ * to this signal to update its menu actions.
+ */
+ void sortOrderChanged(Qt::SortOrder order);
+
+ /**
+ * Is emitted if 'sort folders first' has been changed to \a foldersFirst
+ * by the view mode implementation (see indicateSortOrderChanged().
+ * The DolphinView connects
+ * to this signal to update its menu actions.
+ */
+ void sortFoldersFirstChanged(bool foldersFirst);
+
+ /**
+ * Is emitted if the additional info has been changed to \a info
+ * by the view mode implementation. The DolphinView connects
+ * to this signal to update its menu actions.
+ */
+ void additionalInfoChanged(const KFileItemDelegate::InformationList& info);
+
+ /**
+ * Is emitted if the item \a item should be triggered. The abstract
+ * Dolphin view connects to this signal. If the item represents a directory,
+ * the directory is opened. On a file the corresponding application is opened.
+ * The item is null (see KFileItem::isNull()), when clicking on the viewport itself.
+ */
+ void itemTriggered(const KFileItem& item);
+
+ /**
+ * Is emitted if the mouse cursor has entered the item
+ * given by \a index (see emitItemEntered()).
+ */
+ void itemEntered(const KFileItem& item);
+
+ /**
+ * Is emitted if a new tab should be opened for the URL \a url.
+ */
+ void tabRequested(const KUrl& url);
+
+ /**
+ * Is emitted if the mouse cursor has entered
+ * the viewport (see emitViewportEntered()).
+ */
+ void viewportEntered();
+
+ /**
+ * Is emitted, if DolphinViewController::requestToolTipHiding() is invoked
+ * and requests to hide all tooltips.
+ */
+ void hideToolTip();
+
+ /**
+ * Is emitted if pending previews should be canceled (e. g. because of an URL change).
+ */
+ void cancelPreviews();
+
+ /**
+ * Requests the view mode implementation to invoke DolphinViewController::setVersionControlActions(),
+ * so that they can be returned with DolphinViewController::versionControlActions() for
+ * the DolphinView.
+ */
+ void requestVersionControlActions(const KFileItemList& items);
+
+private slots:
+ void updateMouseButtonState();
+
+private:
+ static Qt::MouseButtons m_mouseButtons; // TODO: this is a workaround until Qt-issue 176832 has been fixed
+
+ DolphinView* m_dolphinView;
+ QAbstractItemView* m_itemView;
+ QList<QAction*> m_versionControlActions;
+};
+
+#endif
diff --git a/src/views/selectionmanager.cpp b/src/views/selectionmanager.cpp
new file mode 100644
index 000000000..0d3efae09
--- /dev/null
+++ b/src/views/selectionmanager.cpp
@@ -0,0 +1,186 @@
+/***************************************************************************
+ * Copyright (C) 2008 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "selectionmanager.h"
+
+#include "dolphinmodel.h"
+#include "selectiontoggle.h"
+#include <kdirmodel.h>
+#include <kiconeffect.h>
+
+#include <QAbstractButton>
+#include <QAbstractItemView>
+#include <QAbstractProxyModel>
+#include <QApplication>
+#include <QModelIndex>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QRect>
+#include <QTimeLine>
+
+SelectionManager::SelectionManager(QAbstractItemView* parent) :
+ QObject(parent),
+ m_view(parent),
+ m_toggle(0),
+ m_connected(false)
+{
+ connect(parent, SIGNAL(entered(const QModelIndex&)),
+ this, SLOT(slotEntered(const QModelIndex&)));
+ connect(parent, SIGNAL(viewportEntered()),
+ this, SLOT(slotViewportEntered()));
+ m_toggle = new SelectionToggle(m_view->viewport());
+ m_toggle->setCheckable(true);
+ m_toggle->hide();
+ connect(m_toggle, SIGNAL(clicked(bool)),
+ this, SLOT(setItemSelected(bool)));
+}
+
+SelectionManager::~SelectionManager()
+{
+}
+
+void SelectionManager::reset()
+{
+ m_toggle->reset();
+}
+
+void SelectionManager::slotEntered(const QModelIndex& index)
+{
+ m_toggle->hide();
+ const bool showToggle = index.isValid() &&
+ (index.column() == DolphinModel::Name) &&
+ (QApplication::mouseButtons() == Qt::NoButton);
+ if (showToggle) {
+ m_toggle->setUrl(urlForIndex(index));
+
+ if (!m_connected) {
+ connect(m_view->model(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)),
+ this, SLOT(slotRowsRemoved(const QModelIndex&, int, int)));
+ connect(m_view->selectionModel(),
+ SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
+ this,
+ SLOT(slotSelectionChanged(const QItemSelection&, const QItemSelection&)));
+ m_connected = true;
+ }
+
+ // increase the size of the toggle for large items
+ const int height = m_view->iconSize().height();
+ if (height >= KIconLoader::SizeEnormous) {
+ m_toggle->resize(KIconLoader::SizeMedium, KIconLoader::SizeMedium);
+ } else if (height >= KIconLoader::SizeLarge) {
+ m_toggle->resize(KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium);
+ } else {
+ m_toggle->resize(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
+ }
+
+ const QRect rect = m_view->visualRect(index);
+ int x = rect.left();
+ int y = rect.top();
+ if (height < KIconLoader::SizeSmallMedium) {
+ // The height is nearly equal to the smallest toggle height.
+ // Assure that the toggle is vertically centered instead
+ // of aligned on the top and gets more horizontal gap.
+ x += 2;
+ y += (rect.height() - m_toggle->height()) / 2;
+ }
+ m_toggle->move(QPoint(x, y));
+
+ QItemSelectionModel* selModel = m_view->selectionModel();
+ m_toggle->setChecked(selModel->isSelected(index));
+ m_toggle->show();
+ } else {
+ m_toggle->setUrl(KUrl());
+ disconnect(m_view->model(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)),
+ this, SLOT(slotRowsRemoved(const QModelIndex&, int, int)));
+ disconnect(m_view->selectionModel(),
+ SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
+ this,
+ SLOT(slotSelectionChanged(const QItemSelection&, const QItemSelection&)));
+ m_connected = false;
+ }
+}
+
+void SelectionManager::slotViewportEntered()
+{
+ m_toggle->hide();
+}
+
+void SelectionManager::setItemSelected(bool selected)
+{
+ emit selectionChanged();
+
+ if (!m_toggle->url().isEmpty()) {
+ const QModelIndex index = indexForUrl(m_toggle->url());
+ if (index.isValid()) {
+ QItemSelectionModel* selModel = m_view->selectionModel();
+ if (selected) {
+ selModel->select(index, QItemSelectionModel::Select);
+ } else {
+ selModel->select(index, QItemSelectionModel::Deselect);
+ }
+ selModel->setCurrentIndex(index, QItemSelectionModel::Current);
+ }
+ }
+}
+
+void SelectionManager::slotRowsRemoved(const QModelIndex& parent, int start, int end)
+{
+ Q_UNUSED(parent);
+ Q_UNUSED(start);
+ Q_UNUSED(end);
+ m_toggle->hide();
+}
+
+void SelectionManager::slotSelectionChanged(const QItemSelection& selected,
+ const QItemSelection& deselected)
+{
+ // The selection has been changed outside the scope of the selection manager
+ // (e. g. by the rubberband or the "Select All" action). Take care updating
+ // the state of the toggle button.
+ if (!m_toggle->url().isEmpty()) {
+ const QModelIndex index = indexForUrl(m_toggle->url());
+ if (index.isValid()) {
+ if (selected.contains(index)) {
+ m_toggle->setChecked(true);
+ }
+
+ if (deselected.contains(index)) {
+ m_toggle->setChecked(false);
+ }
+ }
+ }
+}
+
+KUrl SelectionManager::urlForIndex(const QModelIndex& index) const
+{
+ QAbstractProxyModel* proxyModel = static_cast<QAbstractProxyModel*>(m_view->model());
+ KDirModel* dirModel = static_cast<KDirModel*>(proxyModel->sourceModel());
+ const QModelIndex dirIndex = proxyModel->mapToSource(index);
+ return dirModel->itemForIndex(dirIndex).url();
+}
+
+const QModelIndex SelectionManager::indexForUrl(const KUrl& url) const
+{
+ QAbstractProxyModel* proxyModel = static_cast<QAbstractProxyModel*>(m_view->model());
+ KDirModel* dirModel = static_cast<KDirModel*>(proxyModel->sourceModel());
+ const QModelIndex dirIndex = dirModel->indexForUrl(url);
+ return proxyModel->mapFromSource(dirIndex);
+}
+
+#include "selectionmanager.moc"
diff --git a/src/views/selectionmanager.h b/src/views/selectionmanager.h
new file mode 100644
index 000000000..c2fcc88b4
--- /dev/null
+++ b/src/views/selectionmanager.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * Copyright (C) 2008 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef SELECTIONMANAGER_H
+#define SELECTIONMANAGER_H
+
+#include <kfileitem.h>
+
+#include <QObject>
+
+class QAbstractItemView;
+class QModelIndex;
+class QItemSelection;
+class SelectionToggle;
+
+/**
+ * @brief Allows to select and deselect items for item views.
+ *
+ * Whenever an item is hovered by the mouse, a toggle button is shown
+ * which allows to select/deselect the current item.
+ */
+class SelectionManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ SelectionManager(QAbstractItemView* parent);
+ virtual ~SelectionManager();
+
+public slots:
+ /**
+ * Resets the selection manager so that the toggle button gets
+ * invisible.
+ */
+ void reset();
+
+signals:
+ /** Is emitted if the selection has been changed by the toggle button. */
+ void selectionChanged();
+
+private slots:
+ void slotEntered(const QModelIndex& index);
+ void slotViewportEntered();
+ void setItemSelected(bool selected);
+ void slotRowsRemoved(const QModelIndex& parent, int start, int end);
+ void slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
+
+private:
+ KUrl urlForIndex(const QModelIndex& index) const;
+ const QModelIndex indexForUrl(const KUrl& url) const;
+
+private:
+ QAbstractItemView* m_view;
+ SelectionToggle* m_toggle;
+ bool m_connected;
+};
+
+#endif
diff --git a/src/views/selectiontoggle.cpp b/src/views/selectiontoggle.cpp
new file mode 100644
index 000000000..6608b5821
--- /dev/null
+++ b/src/views/selectiontoggle.cpp
@@ -0,0 +1,233 @@
+/***************************************************************************
+ * Copyright (C) 2008 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "selectiontoggle.h"
+
+#include <kglobalsettings.h>
+#include <kicon.h>
+#include <kiconloader.h>
+#include <kiconeffect.h>
+#include <klocale.h>
+
+#include <QPainter>
+#include <QPaintEvent>
+#include <QRect>
+#include <QTimer>
+#include <QTimeLine>
+
+SelectionToggle::SelectionToggle(QWidget* parent) :
+ QAbstractButton(parent),
+ m_isHovered(false),
+ m_leftMouseButtonPressed(false),
+ m_fadingValue(0),
+ m_icon(),
+ m_fadingTimeLine(0)
+{
+ setFocusPolicy(Qt::NoFocus);
+ parent->installEventFilter(this);
+ resize(sizeHint());
+ setIconOverlay(isChecked());
+ connect(this, SIGNAL(toggled(bool)),
+ this, SLOT(setIconOverlay(bool)));
+ connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)),
+ this, SLOT(refreshIcon()));
+}
+
+SelectionToggle::~SelectionToggle()
+{
+}
+
+QSize SelectionToggle::sizeHint() const
+{
+ return QSize(16, 16);
+}
+
+void SelectionToggle::reset()
+{
+ m_url = KUrl();
+ hide();
+}
+
+void SelectionToggle::setUrl(const KUrl& url)
+{
+ m_url = url;
+ if (!url.isEmpty()) {
+ startFading();
+ }
+}
+
+KUrl SelectionToggle::url() const
+{
+ return m_url;
+}
+
+void SelectionToggle::setVisible(bool visible)
+{
+ QAbstractButton::setVisible(visible);
+
+ stopFading();
+ if (visible) {
+ startFading();
+ }
+
+}
+
+bool SelectionToggle::eventFilter(QObject* obj, QEvent* event)
+{
+ if (obj == parent()) {
+ switch (event->type()) {
+ case QEvent::Leave:
+ hide();
+ break;
+
+ case QEvent::MouseMove:
+ if (m_leftMouseButtonPressed) {
+ // Don't forward mouse move events to the viewport,
+ // otherwise a rubberband selection will be shown when
+ // clicking on the selection toggle and moving the mouse
+ // above the viewport.
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return QAbstractButton::eventFilter(obj, event);
+}
+
+void SelectionToggle::enterEvent(QEvent* event)
+{
+ QAbstractButton::enterEvent(event);
+
+ // if the mouse cursor is above the selection toggle, display
+ // it immediately without fading timer
+ m_isHovered = true;
+ if (m_fadingTimeLine != 0) {
+ m_fadingTimeLine->stop();
+ }
+ m_fadingValue = 255;
+ setToolTip(isChecked() ? i18nc("@info:tooltip", "Deselect Item") :
+ i18nc("@info:tooltip", "Select Item"));
+ update();
+}
+
+void SelectionToggle::leaveEvent(QEvent* event)
+{
+ QAbstractButton::leaveEvent(event);
+ m_isHovered = false;
+ update();
+}
+
+void SelectionToggle::mousePressEvent(QMouseEvent* event)
+{
+ QAbstractButton::mousePressEvent(event);
+ m_leftMouseButtonPressed = (event->buttons() & Qt::LeftButton);
+}
+
+void SelectionToggle::mouseReleaseEvent(QMouseEvent* event)
+{
+ QAbstractButton::mouseReleaseEvent(event);
+ m_leftMouseButtonPressed = (event->buttons() & Qt::LeftButton);
+}
+
+void SelectionToggle::resizeEvent(QResizeEvent* event)
+{
+ QAbstractButton::resizeEvent(event);
+ setIconOverlay(isChecked());
+}
+
+void SelectionToggle::paintEvent(QPaintEvent* event)
+{
+ QPainter painter(this);
+ painter.setClipRect(event->rect());
+
+ // draw the icon overlay
+ if (m_isHovered) {
+ KIconEffect iconEffect;
+ QPixmap activeIcon = iconEffect.apply(m_icon, KIconLoader::Desktop, KIconLoader::ActiveState);
+ painter.drawPixmap(0, 0, activeIcon);
+ } else {
+ if (m_fadingValue < 255) {
+ // apply an alpha mask respecting the fading value to the icon
+ QPixmap icon = m_icon;
+ QPixmap alphaMask(icon.width(), icon.height());
+ const QColor color(m_fadingValue, m_fadingValue, m_fadingValue);
+ alphaMask.fill(color);
+ icon.setAlphaChannel(alphaMask);
+ painter.drawPixmap(0, 0, icon);
+ } else {
+ // no fading is required
+ painter.drawPixmap(0, 0, m_icon);
+ }
+ }
+}
+
+void SelectionToggle::setFadingValue(int value)
+{
+ m_fadingValue = value;
+ if (m_fadingValue >= 255) {
+ Q_ASSERT(m_fadingTimeLine != 0);
+ m_fadingTimeLine->stop();
+ }
+ update();
+}
+
+void SelectionToggle::setIconOverlay(bool checked)
+{
+ const char* icon = checked ? "list-remove" : "list-add";
+ m_icon = KIconLoader::global()->loadIcon(icon,
+ KIconLoader::NoGroup,
+ qMin(width(), height()));
+ update();
+}
+
+void SelectionToggle::refreshIcon()
+{
+ setIconOverlay(isChecked());
+}
+
+void SelectionToggle::startFading()
+{
+ Q_ASSERT(m_fadingTimeLine == 0);
+
+ const bool animate = KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects;
+ const int duration = animate ? 600 : 1;
+
+ m_fadingTimeLine = new QTimeLine(duration, this);
+ connect(m_fadingTimeLine, SIGNAL(frameChanged(int)),
+ this, SLOT(setFadingValue(int)));
+ m_fadingTimeLine->setFrameRange(0, 255);
+ m_fadingTimeLine->start();
+ m_fadingValue = 0;
+}
+
+void SelectionToggle::stopFading()
+{
+ if (m_fadingTimeLine != 0) {
+ m_fadingTimeLine->stop();
+ delete m_fadingTimeLine;
+ m_fadingTimeLine = 0;
+ }
+ m_fadingValue = 0;
+}
+
+#include "selectiontoggle.moc"
diff --git a/src/views/selectiontoggle.h b/src/views/selectiontoggle.h
new file mode 100644
index 000000000..5519272b3
--- /dev/null
+++ b/src/views/selectiontoggle.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2008 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef SELECTIONTOGGLE_H
+#define SELECTIONTOGGLE_H
+
+#include <kurl.h>
+
+#include <QAbstractButton>
+#include <QPixmap>
+
+class QTimeLine;
+
+/**
+ * @brief Toggle button for changing the selection of an hovered item.
+ *
+ * The toggle button is visually invisible until it is displayed at least
+ * for one second.
+ *
+ * @see SelectionManager
+ */
+class SelectionToggle : public QAbstractButton
+{
+ Q_OBJECT
+
+public:
+ explicit SelectionToggle(QWidget* parent);
+ virtual ~SelectionToggle();
+ virtual QSize sizeHint() const;
+
+ /**
+ * Resets the selection toggle so that it is hidden and stays
+ * visually invisible for at least one second after it is shown again.
+ */
+ void reset();
+
+ void setUrl(const KUrl& url);
+ KUrl url() const;
+
+public slots:
+ virtual void setVisible(bool visible);
+
+protected:
+ virtual bool eventFilter(QObject* obj, QEvent* event);
+ virtual void enterEvent(QEvent* event);
+ virtual void leaveEvent(QEvent* event);
+ virtual void mousePressEvent(QMouseEvent* event);
+ virtual void mouseReleaseEvent(QMouseEvent* event);
+ virtual void resizeEvent(QResizeEvent* event);
+ virtual void paintEvent(QPaintEvent* event);
+
+private slots:
+ /**
+ * Sets the alpha value for the fading animation and is
+ * connected with m_fadingTimeLine.
+ */
+ void setFadingValue(int value);
+
+ void setIconOverlay(bool checked);
+ void refreshIcon();
+
+private:
+ void startFading();
+ void stopFading();
+
+private:
+ bool m_isHovered;
+ bool m_leftMouseButtonPressed;
+ int m_fadingValue;
+ QPixmap m_icon;
+ QTimeLine* m_fadingTimeLine;
+ KUrl m_url;
+};
+
+#endif
diff --git a/src/views/viewextensionsfactory.cpp b/src/views/viewextensionsfactory.cpp
new file mode 100644
index 000000000..e5638c03e
--- /dev/null
+++ b/src/views/viewextensionsfactory.cpp
@@ -0,0 +1,244 @@
+/***************************************************************************
+ * Copyright (C) 2009 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "viewextensionsfactory.h"
+
+#include "dolphinfileitemdelegate.h"
+#include "dolphinsortfilterproxymodel.h"
+#include "dolphinview.h"
+#include "dolphinviewcontroller.h"
+#include "dolphinviewautoscroller.h"
+#include "folderexpander.h"
+#include "selectionmanager.h"
+#include "settings/dolphinsettings.h"
+#include "tooltips/tooltipmanager.h"
+#include "versioncontrol/versioncontrolobserver.h"
+#include "viewmodecontroller.h"
+
+#include "dolphin_generalsettings.h"
+
+#include <kdirlister.h>
+#include <kdirmodel.h>
+#include <kfilepreviewgenerator.h>
+#include <QAbstractItemView>
+
+ViewExtensionsFactory::ViewExtensionsFactory(QAbstractItemView* view,
+ DolphinViewController* dolphinViewController,
+ const ViewModeController* viewModeController) :
+ QObject(view),
+ m_view(view),
+ m_dolphinViewController(dolphinViewController),
+ m_toolTipManager(0),
+ m_previewGenerator(0),
+ m_selectionManager(0),
+ m_autoScroller(0),
+ m_fileItemDelegate(0),
+ m_versionControlObserver(0)
+{
+ view->setSelectionMode(QAbstractItemView::ExtendedSelection);
+
+ GeneralSettings* settings = DolphinSettings::instance().generalSettings();
+
+ // initialize tooltips
+ if (settings->showToolTips()) {
+ DolphinSortFilterProxyModel* proxyModel = static_cast<DolphinSortFilterProxyModel*>(view->model());
+ m_toolTipManager = new ToolTipManager(view, proxyModel);
+
+ connect(dolphinViewController, SIGNAL(hideToolTip()),
+ m_toolTipManager, SLOT(hideTip()));
+ }
+
+ // initialize preview generator
+ Q_ASSERT(view->iconSize().isValid());
+ m_previewGenerator = new KFilePreviewGenerator(view);
+ m_previewGenerator->setPreviewShown(dolphinViewController->view()->showPreview());
+ connect(viewModeController, SIGNAL(zoomLevelChanged(int)),
+ this, SLOT(slotZoomLevelChanged()));
+ connect(viewModeController, SIGNAL(cancelPreviews()),
+ this, SLOT(cancelPreviews()));
+ connect(dolphinViewController->view(), SIGNAL(showPreviewChanged()),
+ this, SLOT(slotShowPreviewChanged()));
+
+ // initialize selection manager
+ if (settings->showSelectionToggle()) {
+ m_selectionManager = new SelectionManager(view);
+ connect(m_selectionManager, SIGNAL(selectionChanged()),
+ this, SLOT(requestActivation()));
+ connect(viewModeController, SIGNAL(urlChanged(const KUrl&)),
+ m_selectionManager, SLOT(reset()));
+ }
+
+ // initialize auto scroller
+ m_autoScroller = new DolphinViewAutoScroller(view);
+
+ // initialize file item delegate
+ m_fileItemDelegate = new DolphinFileItemDelegate(view);
+ m_fileItemDelegate->setShowToolTipWhenElided(false);
+ view->setItemDelegate(m_fileItemDelegate);
+
+ // initialize version control observer
+ const DolphinView* dolphinView = dolphinViewController->view();
+ m_versionControlObserver = new VersionControlObserver(view);
+ connect(m_versionControlObserver, SIGNAL(infoMessage(const QString&)),
+ dolphinView, SIGNAL(infoMessage(const QString&)));
+ connect(m_versionControlObserver, SIGNAL(errorMessage(const QString&)),
+ dolphinView, SIGNAL(errorMessage(const QString&)));
+ connect(m_versionControlObserver, SIGNAL(operationCompletedMessage(const QString&)),
+ dolphinView, SIGNAL(operationCompletedMessage(const QString&)));
+ connect(dolphinViewController, SIGNAL(requestVersionControlActions(const KFileItemList&)),
+ this, SLOT(slotRequestVersionControlActions(const KFileItemList&)));
+
+ // react on view property changes
+ connect(dolphinView, SIGNAL(showHiddenFilesChanged()),
+ this, SLOT(slotShowHiddenFilesChanged()));
+ connect(dolphinView, SIGNAL(sortingChanged(DolphinView::Sorting)),
+ this, SLOT(slotSortingChanged(DolphinView::Sorting)));
+ connect(dolphinView, SIGNAL(sortOrderChanged(Qt::SortOrder)),
+ this, SLOT(slotSortOrderChanged(Qt::SortOrder)));
+ connect(dolphinView, SIGNAL(sortFoldersFirstChanged(bool)),
+ this, SLOT(slotSortFoldersFirstChanged(bool)));
+
+ // Give the view the ability to auto-expand its directories on hovering
+ // (the column view takes care about this itself). If the details view
+ // uses expandable folders, the auto-expanding should be used always.
+ m_folderExpander = new FolderExpander(view, proxyModel());
+ m_folderExpander->setEnabled(settings->autoExpandFolders());
+ connect(m_folderExpander, SIGNAL(enterDir(const QModelIndex&)),
+ dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
+
+ // react on namefilter changes
+ connect(viewModeController, SIGNAL(nameFilterChanged(const QString&)),
+ this, SLOT(slotNameFilterChanged(const QString&)));
+
+ view->viewport()->installEventFilter(this);
+}
+
+ViewExtensionsFactory::~ViewExtensionsFactory()
+{
+}
+
+void ViewExtensionsFactory::handleCurrentIndexChange(const QModelIndex& current, const QModelIndex& previous)
+{
+ m_autoScroller->handleCurrentIndexChange(current, previous);
+}
+
+DolphinFileItemDelegate* ViewExtensionsFactory::fileItemDelegate() const
+{
+ return m_fileItemDelegate;
+}
+
+void ViewExtensionsFactory::setAutoFolderExpandingEnabled(bool enabled)
+{
+ m_folderExpander->setEnabled(enabled);
+}
+
+bool ViewExtensionsFactory::autoFolderExpandingEnabled() const
+{
+ return m_folderExpander->enabled();
+}
+
+bool ViewExtensionsFactory::eventFilter(QObject* watched, QEvent* event)
+{
+ Q_UNUSED(watched);
+ if ((event->type() == QEvent::Wheel) && (m_selectionManager != 0)) {
+ m_selectionManager->reset();
+ }
+ return false;
+}
+
+void ViewExtensionsFactory::slotZoomLevelChanged()
+{
+ m_previewGenerator->updateIcons();
+ if (m_selectionManager != 0) {
+ m_selectionManager->reset();
+ }
+}
+
+void ViewExtensionsFactory::cancelPreviews()
+{
+ m_previewGenerator->cancelPreviews();
+}
+
+void ViewExtensionsFactory::slotShowPreviewChanged()
+{
+ const bool show = m_dolphinViewController->view()->showPreview();
+ m_previewGenerator->setPreviewShown(show);
+}
+
+void ViewExtensionsFactory::slotShowHiddenFilesChanged()
+{
+ KDirModel* dirModel = static_cast<KDirModel*>(proxyModel()->sourceModel());
+ KDirLister* dirLister = dirModel->dirLister();
+
+ dirLister->stop();
+
+ const bool show = m_dolphinViewController->view()->showHiddenFiles();
+ dirLister->setShowingDotFiles(show);
+
+ const KUrl url = dirLister->url();
+ if (url.isValid()) {
+ dirLister->openUrl(url, KDirLister::NoFlags);
+ }
+}
+
+void ViewExtensionsFactory::slotSortingChanged(DolphinView::Sorting sorting)
+{
+ proxyModel()->setSorting(sorting);
+}
+
+void ViewExtensionsFactory::slotSortOrderChanged(Qt::SortOrder order)
+{
+ proxyModel()->setSortOrder(order);
+}
+
+void ViewExtensionsFactory::slotSortFoldersFirstChanged(bool foldersFirst)
+{
+ proxyModel()->setSortFoldersFirst(foldersFirst);
+}
+
+void ViewExtensionsFactory::slotNameFilterChanged(const QString& nameFilter)
+{
+ proxyModel()->setFilterFixedString(nameFilter);
+}
+
+void ViewExtensionsFactory::slotRequestVersionControlActions(const KFileItemList& items)
+{
+ QList<QAction*> actions;
+ if (items.isEmpty()) {
+ const KDirModel* dirModel = static_cast<const KDirModel*>(proxyModel()->sourceModel());
+ const KUrl url = dirModel->dirLister()->url();
+ actions = m_versionControlObserver->contextMenuActions(url.path(KUrl::AddTrailingSlash));
+ } else {
+ actions = m_versionControlObserver->contextMenuActions(items);
+ }
+ m_dolphinViewController->setVersionControlActions(actions);
+}
+
+void ViewExtensionsFactory::requestActivation()
+{
+ m_dolphinViewController->requestActivation();
+}
+
+DolphinSortFilterProxyModel* ViewExtensionsFactory::proxyModel() const
+{
+ return static_cast<DolphinSortFilterProxyModel*>(m_view->model());
+}
+
+#include "viewextensionsfactory.moc"
+
diff --git a/src/views/viewextensionsfactory.h b/src/views/viewextensionsfactory.h
new file mode 100644
index 000000000..9324932ac
--- /dev/null
+++ b/src/views/viewextensionsfactory.h
@@ -0,0 +1,105 @@
+/***************************************************************************
+ * Copyright (C) 2009 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef VIEWEXTENSIONSFACTORY_H
+#define VIEWEXTENSIONSFACTORY_H
+
+#include <QObject>
+
+#include "dolphinview.h"
+
+class DolphinFileItemDelegate;
+class DolphinSortFilterProxyModel;
+class DolphinViewAutoScroller;
+class KFilePreviewGenerator;
+class FolderExpander;
+class QModelIndex;
+class SelectionManager;
+class ToolTipManager;
+class QAbstractItemView;
+class VersionControlObserver;
+class ViewModeController;
+
+/**
+ * @brief Responsible for creating extensions like tooltips and previews
+ * that are available in all view implementations.
+ *
+ * Each view implementation (iconsview, detailsview, columnview) must
+ * instantiate an instance of this class to assure having
+ * a common behavior that is independent from the custom functionality of
+ * a view implementation.
+ */
+class ViewExtensionsFactory : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit ViewExtensionsFactory(QAbstractItemView* view,
+ DolphinViewController* dolphinViewController,
+ const ViewModeController* viewModeController);
+ virtual ~ViewExtensionsFactory();
+
+ /**
+ * Must be invoked by the item view, when QAbstractItemView::currentChanged()
+ * has been called. Assures that the current item stays visible when it has been
+ * changed by the keyboard.
+ */
+ void handleCurrentIndexChange(const QModelIndex& current, const QModelIndex& previous);
+
+ DolphinFileItemDelegate* fileItemDelegate() const;
+
+ /**
+ * Enables the automatically expanding of a folder when dragging
+ * items above the folder.
+ */
+ void setAutoFolderExpandingEnabled(bool enabled);
+ bool autoFolderExpandingEnabled() const;
+
+protected:
+ virtual bool eventFilter(QObject* watched, QEvent* event);
+
+private slots:
+ void slotZoomLevelChanged();
+ void cancelPreviews();
+ void slotShowPreviewChanged();
+ void slotShowHiddenFilesChanged();
+ void slotSortingChanged(DolphinView::Sorting sorting);
+ void slotSortOrderChanged(Qt::SortOrder order);
+ void slotSortFoldersFirstChanged(bool foldersFirst);
+ void slotNameFilterChanged(const QString& nameFilter);
+ void slotRequestVersionControlActions(const KFileItemList& items);
+ void requestActivation();
+
+private:
+ DolphinSortFilterProxyModel* proxyModel() const;
+
+private:
+ QAbstractItemView* m_view;
+ DolphinViewController* m_dolphinViewController;
+ ToolTipManager* m_toolTipManager;
+ KFilePreviewGenerator* m_previewGenerator;
+ SelectionManager* m_selectionManager;
+ DolphinViewAutoScroller* m_autoScroller;
+ DolphinFileItemDelegate* m_fileItemDelegate;
+ VersionControlObserver* m_versionControlObserver;
+ FolderExpander* m_folderExpander;
+};
+
+#endif
+
diff --git a/src/views/viewmodecontroller.cpp b/src/views/viewmodecontroller.cpp
new file mode 100644
index 000000000..17d0ba61f
--- /dev/null
+++ b/src/views/viewmodecontroller.cpp
@@ -0,0 +1,88 @@
+/***************************************************************************
+ * Copyright (C) 2010 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "viewmodecontroller.h"
+
+#include "zoomlevelinfo.h"
+
+ViewModeController::ViewModeController(QObject* parent) :
+ QObject(parent),
+ m_zoomLevel(0),
+ m_nameFilter(),
+ m_url()
+{
+}
+
+ViewModeController::~ViewModeController()
+{
+}
+
+KUrl ViewModeController::url() const
+{
+ return m_url;
+}
+
+void ViewModeController::redirectToUrl(const KUrl& url)
+{
+ m_url = url;
+}
+
+void ViewModeController::indicateActivationChange(bool active)
+{
+ emit activationChanged(active);
+}
+
+void ViewModeController::setNameFilter(const QString& nameFilter)
+{
+ if (nameFilter != m_nameFilter) {
+ m_nameFilter = nameFilter;
+ emit nameFilterChanged(nameFilter);
+ }
+}
+
+QString ViewModeController::nameFilter() const
+{
+ return m_nameFilter;
+}
+
+void ViewModeController::setZoomLevel(int level)
+{
+ Q_ASSERT(level >= ZoomLevelInfo::minimumLevel());
+ Q_ASSERT(level <= ZoomLevelInfo::maximumLevel());
+ if (level != m_zoomLevel) {
+ m_zoomLevel = level;
+ emit zoomLevelChanged(m_zoomLevel);
+ }
+}
+
+int ViewModeController::zoomLevel() const
+{
+ return m_zoomLevel;
+}
+
+void ViewModeController::setUrl(const KUrl& url)
+{
+ if (m_url != url) {
+ m_url = url;
+ emit cancelPreviews();
+ emit urlChanged(url);
+ }
+}
+
+#include "viewmodecontroller.moc"
diff --git a/src/views/viewmodecontroller.h b/src/views/viewmodecontroller.h
new file mode 100644
index 000000000..c7378d59a
--- /dev/null
+++ b/src/views/viewmodecontroller.h
@@ -0,0 +1,124 @@
+/***************************************************************************
+ * Copyright (C) 2010 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef VIEWMODECONTROLLER_H
+#define VIEWMODECONTROLLER_H
+
+#include <kurl.h>
+#include <QObject>
+#include <libdolphin_export.h>
+#include <views/dolphinview.h>
+
+/**
+ * @brief Allows the DolphinView to control the view implementations for the
+ * different view modes.
+ *
+ * The view implementations (DolphinIconsView, DolphinDetailsView, DolphinColumnView)
+ * connect to signals of the ViewModeController to react on changes. The view
+ * implementations get only read-access to the ViewModeController.
+ */
+class LIBDOLPHINPRIVATE_EXPORT ViewModeController : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit ViewModeController(QObject* parent = 0);
+ virtual ~ViewModeController();
+
+ /**
+ * @return URL that is shown by the view mode implementation.
+ */
+ KUrl url() const;
+
+ /**
+ * Sets the URL to \a url and does nothing else. Called when
+ * a redirection happens. See ViewModeController::setUrl()
+ */
+ void redirectToUrl(const KUrl& url);
+
+ /**
+ * Informs the view mode implementation about a change of the activation
+ * state by emitting the signal activationChanged().
+ */
+ void indicateActivationChange(bool active);
+
+ /**
+ * Sets the zoom level to \a level and emits the signal zoomLevelChanged().
+ * It must be assured that the used level is inside the range
+ * ViewModeController::zoomLevelMinimum() and
+ * ViewModeController::zoomLevelMaximum().
+ */
+ void setZoomLevel(int level);
+ int zoomLevel() const;
+
+ /**
+ * Sets the name filter to \a and emits the signal nameFilterChanged().
+ */
+ void setNameFilter(const QString& nameFilter);
+ QString nameFilter() const;
+
+ /**
+ * Requests the view mode implementation to hide tooltips.
+ */
+ void requestToolTipHiding();
+
+public slots:
+ /**
+ * Sets the URL to \a url and emits the signals cancelPreviews() and
+ * urlChanged() if \a url is different for the current URL.
+ */
+ void setUrl(const KUrl& url);
+
+signals:
+ /**
+ * Is emitted if the URL has been changed by ViewModeController::setUrl().
+ */
+ void urlChanged(const KUrl& url);
+
+ /**
+ * Is emitted, if ViewModeController::indicateActivationChange() has been
+ * invoked. The view mode implementation may update its visual state
+ * to represent the activation state.
+ */
+ void activationChanged(bool active);
+
+ /**
+ * Is emitted if the name filter has been changed by
+ * ViewModeController::setNameFilter().
+ */
+ void nameFilterChanged(const QString& nameFilter);
+
+ /**
+ * Is emitted if the zoom level has been changed by
+ * ViewModeController::setZoomLevel().
+ */
+ void zoomLevelChanged(int level);
+
+ /**
+ * Is emitted if pending previews should be canceled (e. g. because of an URL change).
+ */
+ void cancelPreviews();
+
+private:
+ int m_zoomLevel;
+ QString m_nameFilter;
+ KUrl m_url;
+};
+
+#endif
diff --git a/src/views/zoomlevelinfo.cpp b/src/views/zoomlevelinfo.cpp
new file mode 100644
index 000000000..08e95e3ca
--- /dev/null
+++ b/src/views/zoomlevelinfo.cpp
@@ -0,0 +1,60 @@
+/***************************************************************************
+ * Copyright (C) 2008 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "zoomlevelinfo.h"
+#include <kiconloader.h>
+#include <QSize>
+
+int ZoomLevelInfo::minimumLevel()
+{
+ return 0;
+}
+
+int ZoomLevelInfo::maximumLevel()
+{
+ return 16;
+}
+
+int ZoomLevelInfo::iconSizeForZoomLevel(int level)
+{
+ int size = KIconLoader::SizeMedium;
+ switch (level) {
+ case 0: size = KIconLoader::SizeSmall; break;
+ case 1: size = KIconLoader::SizeSmallMedium; break;
+ case 2: size = KIconLoader::SizeMedium; break;
+ case 3: size = KIconLoader::SizeLarge; break;
+ case 4: size = KIconLoader::SizeHuge; break;
+ default: size = KIconLoader::SizeHuge + ((level - 4) << 4);
+ }
+ return size;
+}
+
+int ZoomLevelInfo::zoomLevelForIconSize(const QSize& size)
+{
+ int level = 0;
+ switch (size.height()) {
+ case KIconLoader::SizeSmall: level = 0; break;
+ case KIconLoader::SizeSmallMedium: level = 1; break;
+ case KIconLoader::SizeMedium: level = 2; break;
+ case KIconLoader::SizeLarge: level = 3; break;
+ case KIconLoader::SizeHuge: level = 4; break;
+ default: level = 4 + ((size.height() - KIconLoader::SizeHuge) >> 4);
+ }
+ return level;
+}
diff --git a/src/views/zoomlevelinfo.h b/src/views/zoomlevelinfo.h
new file mode 100644
index 000000000..a6e92e653
--- /dev/null
+++ b/src/views/zoomlevelinfo.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * Copyright (C) 2008 by Peter Penz <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef ZOOMLEVELINFO_H
+#define ZOOMLEVELINFO_H
+
+class QSize;
+
+/**
+ * @short Helper class for getting information about the zooming
+ * capabilities.
+ */
+class ZoomLevelInfo {
+public:
+ static int minimumLevel();
+ static int maximumLevel();
+
+ /**
+ * Helper method for the view implementation to get
+ * the icon size for the zoom level \a level that
+ * is between the range ZoomLevelInfo::minimumLevel() and
+ * ZoomLevelInfo::maximumLevel().
+ */
+ static int iconSizeForZoomLevel(int level);
+
+ /**
+ * Helper method for the view implementation to get
+ * the zoom level for the icon size \a size that
+ * is between the range ZoomLevelInfo::minimumLevel() and
+ * ZoomLevelInfo::maximumLevel().
+ */
+ static int zoomLevelForIconSize(const QSize& size);
+};
+
+#endif