/***************************************************************************
- * Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at) *
+ * Copyright (C) 2007 by Peter Penz <peter.penz@gmx.at> *
* *
* 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 *
#include "dolphincolumnview.h"
+#include "dolphincolumnwidget.h"
#include "dolphincontroller.h"
#include "dolphinsettings.h"
#include "dolphin_columnmodesettings.h"
-#include <QtGui/QAbstractProxyModel>
-#include <QtCore/QPoint>
+#include <QPoint>
+#include <QScrollBar>
+#include <QTimeLine>
DolphinColumnView::DolphinColumnView(QWidget* parent, DolphinController* controller) :
- QColumnView(parent),
- m_controller(controller)
+ QAbstractItemView(parent),
+ m_controller(controller),
+ m_active(false),
+ m_index(-1),
+ m_contentX(0),
+ m_columns(),
+ m_emptyViewport(0),
+ m_animation(0),
+ m_nameFilter()
{
Q_ASSERT(controller != 0);
setAcceptDrops(true);
- setSelectionBehavior(SelectItems);
setDragDropMode(QAbstractItemView::DragDrop);
setDropIndicatorShown(false);
+ setSelectionMode(ExtendedSelection);
+ setFocusPolicy(Qt::NoFocus);
+ setFrameShape(QFrame::NoFrame);
- setMouseTracking(true);
- viewport()->setAttribute(Qt::WA_Hover);
-
- if (KGlobalSettings::singleClick()) {
- connect(this, SIGNAL(clicked(const QModelIndex&)),
- controller, SLOT(triggerItem(const QModelIndex&)));
- } else {
- connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
- controller, SLOT(triggerItem(const QModelIndex&)));
- }
- connect(this, SIGNAL(activated(const QModelIndex&)),
- controller, SLOT(triggerItem(const QModelIndex&)));
- connect(this, SIGNAL(entered(const QModelIndex&)),
- controller, SLOT(emitItemEntered(const QModelIndex&)));
connect(this, SIGNAL(viewportEntered()),
controller, SLOT(emitViewportEntered()));
connect(controller, SIGNAL(zoomIn()),
this, SLOT(zoomIn()));
connect(controller, SIGNAL(zoomOut()),
this, SLOT(zoomOut()));
+ connect(controller, SIGNAL(activationChanged(bool)),
+ this, SLOT(updateColumnsBackground(bool)));
+
+ const DolphinView* view = controller->dolphinView();
+ connect(view, SIGNAL(showHiddenFilesChanged()),
+ this, SLOT(slotShowHiddenFilesChanged()));
+ connect(view, SIGNAL(showPreviewChanged()),
+ this, SLOT(slotShowPreviewChanged()));
+
+ connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(moveContentHorizontally(int)));
- // apply the column mode settings to the widget
- const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
- Q_ASSERT(settings != 0);
+ m_animation = new QTimeLine(500, this);
+ connect(m_animation, SIGNAL(frameChanged(int)), horizontalScrollBar(), SLOT(setValue(int)));
- m_viewOptions = QColumnView::viewOptions();
+ DolphinColumnWidget* column = new DolphinColumnWidget(viewport(), this, m_controller->url());
+ m_columns.append(column);
+ setActiveColumnIndex(0);
- QFont font(settings->fontFamily(), settings->fontSize());
- font.setItalic(settings->italicFont());
- font.setBold(settings->boldFont());
- m_viewOptions.font = font;
+ m_emptyViewport = new QFrame(viewport());
+ m_emptyViewport->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
updateDecorationSize();
+ updateColumnsBackground(true);
}
DolphinColumnView::~DolphinColumnView()
{
}
-QStyleOptionViewItem DolphinColumnView::viewOptions() const
+QModelIndex DolphinColumnView::indexAt(const QPoint& point) const
+{
+ foreach (DolphinColumnWidget* column, m_columns) {
+ const QPoint topLeft = column->frameGeometry().topLeft();
+ const QPoint adjustedPoint(point.x() - topLeft.x(), point.y() - topLeft.y());
+ const QModelIndex index = column->indexAt(adjustedPoint);
+ if (index.isValid()) {
+ return index;
+ }
+ }
+
+ return QModelIndex();
+}
+
+void DolphinColumnView::scrollTo(const QModelIndex& index, ScrollHint hint)
+{
+ activeColumn()->scrollTo(index, hint);
+}
+
+QRect DolphinColumnView::visualRect(const QModelIndex& index) const
+{
+ return activeColumn()->visualRect(index);
+}
+
+void DolphinColumnView::invertSelection()
{
- return m_viewOptions;
+ QItemSelectionModel* selectionModel = activeColumn()->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 DolphinColumnView::contextMenuEvent(QContextMenuEvent* event)
+void DolphinColumnView::reload()
{
- QColumnView::contextMenuEvent(event);
- m_controller->triggerContextMenuRequest(event->pos());
+ foreach (DolphinColumnWidget* column, m_columns) {
+ column->reload();
+ }
}
-void DolphinColumnView::mouseReleaseEvent(QMouseEvent* event)
+void DolphinColumnView::setRootUrl(const KUrl& url)
{
- QColumnView::mouseReleaseEvent(event);
- m_controller->triggerActivation();
+ removeAllColumns();
+ m_columns[0]->setUrl(url);
}
-void DolphinColumnView::dragEnterEvent(QDragEnterEvent* event)
+void DolphinColumnView::setNameFilter(const QString& nameFilter)
{
- if (event->mimeData()->hasUrls()) {
- event->acceptProposedAction();
+ if (nameFilter != m_nameFilter) {
+ m_nameFilter = nameFilter;
+ foreach (DolphinColumnWidget* column, m_columns) {
+ column->setNameFilter(nameFilter);
+ }
}
}
-void DolphinColumnView::dropEvent(QDropEvent* event)
+QString DolphinColumnView::nameFilter() const
+{
+ return m_nameFilter;
+}
+
+KUrl DolphinColumnView::rootUrl() const
+{
+ return m_columns[0]->url();
+}
+
+void DolphinColumnView::showColumn(const KUrl& url)
{
- const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
- if (!urls.isEmpty()) {
- m_controller->indicateDroppedUrls(urls,
- indexAt(event->pos()),
- event->source());
- event->acceptProposedAction();
+ if (!rootUrl().isParentOf(url)) {
+ setRootUrl(url);
+ return;
+ }
+
+ int columnIndex = 0;
+ foreach (DolphinColumnWidget* 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<DolphinColumnWidget*>::iterator start = m_columns.begin() + columnIndex;
+ QList<DolphinColumnWidget*>::iterator end = m_columns.end();
+ for (QList<DolphinColumnWidget*>::iterator it = start; it != end; ++it) {
+ (*it)->deleteLater();
+ }
+ 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;
}
- QColumnView::dropEvent(event);
+
+ // 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++;
+
+ DolphinColumnWidget* column = new DolphinColumnWidget(viewport(), this, childUrl);
+ const QString filter = nameFilter();
+ if (!filter.isEmpty()) {
+ column->setNameFilter(filter);
+ }
+ column->setActive(false);
+
+ 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();
+ }
+ }
+
+ // set the last column as active column without modifying the controller
+ // and hence the history
+ activeColumn()->setActive(false);
+ m_index = columnIndex;
+ activeColumn()->setActive(true);
+ assureVisibleActiveColumn();
+}
+
+void DolphinColumnView::selectAll()
+{
+ activeColumn()->selectAll();
+}
+
+bool DolphinColumnView::isIndexHidden(const QModelIndex& index) const
+{
+ Q_UNUSED(index);
+ return false;//activeColumn()->isIndexHidden(index);
+}
+
+QModelIndex DolphinColumnView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
+{
+ // Parts of this code have been taken from QColumnView::moveCursor().
+ // Copyright (C) 1992-2007 Trolltech ASA.
+
+ Q_UNUSED(modifiers);
+ if (model() == 0) {
+ return QModelIndex();
+ }
+
+ const QModelIndex current = currentIndex();
+ if (isRightToLeft()) {
+ if (cursorAction == MoveLeft) {
+ cursorAction = MoveRight;
+ } else if (cursorAction == MoveRight) {
+ cursorAction = MoveLeft;
+ }
+ }
+
+ switch (cursorAction) {
+ case MoveLeft:
+ if (m_index > 0) {
+ setActiveColumnIndex(m_index - 1);
+ m_controller->triggerUrlChangeRequest(activeColumn()->url());
+ }
+ break;
+
+ case MoveRight:
+ if (m_index < m_columns.count() - 1) {
+ setActiveColumnIndex(m_index + 1);
+ m_controller->triggerUrlChangeRequest(m_columns[m_index]->url());
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return QModelIndex();
+}
+
+void DolphinColumnView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags)
+{
+ Q_UNUSED(rect);
+ Q_UNUSED(flags);
+ //activeColumn()->setSelection(rect, flags);
+}
+
+QRegion DolphinColumnView::visualRegionForSelection(const QItemSelection& selection) const
+{
+ Q_UNUSED(selection);
+ return QRegion(); //activeColumn()->visualRegionForSelection(selection);
+}
+
+int DolphinColumnView::horizontalOffset() const
+{
+ return -m_contentX;
+}
+
+int DolphinColumnView::verticalOffset() const
+{
+ return 0;
+}
+
+void DolphinColumnView::mousePressEvent(QMouseEvent* event)
+{
+ m_controller->requestActivation();
+ QAbstractItemView::mousePressEvent(event);
+}
+
+void DolphinColumnView::resizeEvent(QResizeEvent* event)
+{
+ QAbstractItemView::resizeEvent(event);
+ layoutColumns();
+ updateScrollBar();
+ assureVisibleActiveColumn();
}
void DolphinColumnView::zoomIn()
{
if (isZoomInPossible()) {
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
- // TODO: get rid of K3Icon sizes
switch (settings->iconSize()) {
- case K3Icon::SizeSmall: settings->setIconSize(K3Icon::SizeMedium); break;
- case K3Icon::SizeMedium: settings->setIconSize(K3Icon::SizeLarge); break;
+ case KIconLoader::SizeSmall: settings->setIconSize(KIconLoader::SizeMedium); break;
+ case KIconLoader::SizeMedium: settings->setIconSize(KIconLoader::SizeLarge); break;
default: Q_ASSERT(false); break;
}
updateDecorationSize();
{
if (isZoomOutPossible()) {
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
- // TODO: get rid of K3Icon sizes
switch (settings->iconSize()) {
- case K3Icon::SizeLarge: settings->setIconSize(K3Icon::SizeMedium); break;
- case K3Icon::SizeMedium: settings->setIconSize(K3Icon::SizeSmall); break;
+ case KIconLoader::SizeLarge: settings->setIconSize(KIconLoader::SizeMedium); break;
+ case KIconLoader::SizeMedium: settings->setIconSize(KIconLoader::SizeSmall); break;
default: Q_ASSERT(false); break;
}
updateDecorationSize();
}
}
+void DolphinColumnView::moveContentHorizontally(int x)
+{
+ m_contentX = isRightToLeft() ? +x : -x;
+ layoutColumns();
+}
+
+void DolphinColumnView::updateDecorationSize()
+{
+ ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
+ const int iconSize = settings->iconSize();
+
+ foreach (QObject* object, viewport()->children()) {
+ if (object->inherits("QListView")) {
+ DolphinColumnWidget* widget = static_cast<DolphinColumnWidget*>(object);
+ widget->setDecorationSize(QSize(iconSize, iconSize));
+ }
+ }
+
+ m_controller->setZoomInPossible(isZoomInPossible());
+ m_controller->setZoomOutPossible(isZoomOutPossible());
+
+ doItemsLayout();
+}
+
+void DolphinColumnView::updateColumnsBackground(bool active)
+{
+ if (active == m_active) {
+ return;
+ }
+
+ m_active = active;
+
+ // dim the background of the viewport
+ QPalette palette;
+ palette.setColor(viewport()->backgroundRole(), QColor(0, 0, 0, 0));
+ viewport()->setPalette(palette);
+
+ foreach (DolphinColumnWidget* column, m_columns) {
+ column->updateBackground();
+ }
+}
+
+void DolphinColumnView::slotShowHiddenFilesChanged()
+{
+ const bool show = m_controller->dolphinView()->showHiddenFiles();
+ foreach (DolphinColumnWidget* column, m_columns) {
+ column->setShowHiddenFiles(show);
+ }
+}
+
+void DolphinColumnView::slotShowPreviewChanged()
+{
+ const bool show = m_controller->dolphinView()->showPreview();
+ foreach (DolphinColumnWidget* column, m_columns) {
+ column->setShowPreview(show);
+ }
+}
+
bool DolphinColumnView::isZoomInPossible() const
{
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
- return settings->iconSize() < K3Icon::SizeLarge;
+ return settings->iconSize() < KIconLoader::SizeLarge;
}
bool DolphinColumnView::isZoomOutPossible() const
{
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
- return settings->iconSize() > K3Icon::SizeSmall;
+ return settings->iconSize() > KIconLoader::SizeSmall;
}
-void DolphinColumnView::updateDecorationSize()
+void DolphinColumnView::setActiveColumnIndex(int index)
{
+ if (m_index == index) {
+ return;
+ }
+
+ const bool hasActiveColumn = (m_index >= 0);
+ if (hasActiveColumn) {
+ m_columns[m_index]->setActive(false);
+ }
+
+ m_index = index;
+ m_columns[m_index]->setActive(true);
+
+ assureVisibleActiveColumn();
+}
+
+void DolphinColumnView::layoutColumns()
+{
+ const int gap = 4;
+
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
- const int iconSize = settings->iconSize();
- m_viewOptions.decorationSize = QSize(iconSize, iconSize);
+ const int columnWidth = settings->columnWidth();
- m_controller->setZoomInPossible(isZoomInPossible());
- m_controller->setZoomOutPossible(isZoomOutPossible());
+ QRect emptyViewportRect;
+ if (isRightToLeft()) {
+ int x = viewport()->width() - columnWidth + m_contentX;
+ foreach (DolphinColumnWidget* 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 (DolphinColumnWidget* column, m_columns) {
+ column->setGeometry(QRect(x, 0, columnWidth - gap, viewport()->height()));
+ x += columnWidth;
+ }
+ emptyViewportRect = QRect(x, 0, viewport()->width() - x - gap, viewport()->height());
+ }
- doItemsLayout();
+ if (emptyViewportRect.isValid()) {
+ m_emptyViewport->show();
+ m_emptyViewport->setGeometry(emptyViewportRect);
+ } else {
+ m_emptyViewport->hide();
+ }
+}
+
+void DolphinColumnView::updateScrollBar()
+{
+ ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
+ const int contentWidth = m_columns.count() * settings->columnWidth();
+
+ horizontalScrollBar()->setPageStep(contentWidth);
+ horizontalScrollBar()->setRange(0, contentWidth - viewport()->width());
+}
+
+void DolphinColumnView::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);
+ }
+ 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);
+ }
+ m_animation->start();
+ }
+}
+
+void DolphinColumnView::requestActivation(DolphinColumnWidget* column)
+{
+ if (column->isActive()) {
+ assureVisibleActiveColumn();
+ } else {
+ int index = 0;
+ foreach (DolphinColumnWidget* currColumn, m_columns) {
+ if (currColumn == column) {
+ setActiveColumnIndex(index);
+ return;
+ }
+ ++index;
+ }
+ }
+}
+
+void DolphinColumnView::removeAllColumns()
+{
+ QList<DolphinColumnWidget*>::iterator start = m_columns.begin() + 1;
+ QList<DolphinColumnWidget*>::iterator end = m_columns.end();
+ for (QList<DolphinColumnWidget*>::iterator it = start; it != end; ++it) {
+ (*it)->deleteLater();
+ }
+ m_columns.erase(start, end);
+ m_index = 0;
+ m_columns[0]->setActive(true);
+ assureVisibleActiveColumn();
}
#include "dolphincolumnview.moc"