#include "dolphincolumnview.h"
+#include "dolphinmodel.h"
#include "dolphincontroller.h"
#include "dolphinsettings.h"
#include <kcolorutils.h>
#include <kcolorscheme.h>
#include <kdirlister.h>
-#include <kdirmodel.h>
#include <QAbstractProxyModel>
#include <QApplication>
#include <QPoint>
-
-/*
- * General implementation notes
- * ----------------------------
- *
- * In Qt4.3 the QColumnView widget has a default behavior regarding the
- * active column and the selection handling, which leads to some usability
- * problems within Dolphin:
- *
- * - No matter which mouse button has been clicked: If the mouse is above
- * a folder, the folder content will be loaded in the next column. The problem
- * is that this column also will marked as 'active column' within QColumnView,
- * hence it is not possible to select more than one folder within a column.
- *
- * - The currently opened folder is not always marked in the left column when
- * doing drag & drop and selections inside other columns.
- *
- * - The currently active column is visually not recognizable.
- *
- * - It is not possible for derived classes to remove inactive columns.
- *
- * DolphinView tries to bypass those points, but this required some workarounds:
- *
- * - QColumnView internally maps the selection model from the ColumnView to the
- * active column. As the active column from the Dolphin perspective is different
- * as the active column from QColumnView, the selection model is adjusted on
- * each interaction by the methods QColumnWidget::obtainSelectionModel(),
- * QColumnWidget::releaseSelectionModel() and QColumnView::requestSelectionModel().
- * QColumnView offers no hook to adjust this behavior, so those methods have to
- * be invoked throughout the code...
- *
- * - Some copy/paste code from QColumnView is part of DolphinColumnView::createColumn(), but Qt 4.4
- * will offer a solution for this.
- *
- * - The mousePressEvent() has been customized to prevent that folders are loaded on each
- * mouse click.
- *
- * We'll try to give some input for Trolltech if the Dolphin solution is stable enough, so hopefully
- * some workarounds can be removed when switching to Qt 4.4 or later.
- */
+#include <QScrollBar>
+#include <QTimer>
+#include <QTimeLine>
/**
* Represents one column inside the DolphinColumnView and has been
* drawn in a lighter color. All operations are applied to this column.
*/
void setActive(bool active);
- inline bool isActive() const;
-
- inline const KUrl& url() const;
+ bool isActive() const;
/**
- * Obtains the selection model from the column view. This assures that
- * selections of the column view will always applied to the active column.
+ * 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 obtainSelectionModel();
+ void setChildUrl(const KUrl& url);
+ const KUrl& childUrl() const;
- /**
- * Releases the selection model from the column view and replaces it by
- * a custom selection model.
- */
- void releaseSelectionModel();
+ /** 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. */
+ const KUrl& url() const;
protected:
virtual QStyleOptionViewItem viewOptions() const;
virtual void dragLeaveEvent(QDragLeaveEvent* event);
virtual void dragMoveEvent(QDragMoveEvent* event);
virtual void dropEvent(QDropEvent* event);
- virtual void mousePressEvent(QMouseEvent* event);
- virtual void mouseMoveEvent(QMouseEvent* event);
- virtual void mouseReleaseEvent(QMouseEvent* event);
virtual void paintEvent(QPaintEvent* event);
+ virtual void mousePressEvent(QMouseEvent* event);
+ virtual void keyPressEvent(QKeyEvent* event);
virtual void contextMenuEvent(QContextMenuEvent* event);
-
-protected slots:
virtual void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
private:
private:
bool m_active;
- bool m_swallowMouseMoveEvents;
DolphinColumnView* m_view;
- KUrl m_url;
+ KUrl m_url; // URL of the directory that is shown
KUrl m_childUrl; // URL of the next column that is shown
QStyleOptionViewItem m_viewOptions;
const KUrl& url) :
QListView(parent),
m_active(true),
- m_swallowMouseMoveEvents(false),
m_view(columnView),
m_url(url),
m_childUrl(),
{
setMouseTracking(true);
viewport()->setAttribute(Qt::WA_Hover);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+ setSelectionBehavior(SelectItems);
+ setSelectionMode(QAbstractItemView::ExtendedSelection);
+ setDragDropMode(QAbstractItemView::DragDrop);
+ setDropIndicatorShown(false);
+ setFocusPolicy(Qt::NoFocus);
// apply the column mode settings to the widget
const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
const int iconSize = settings->iconSize();
m_viewOptions.decorationSize = QSize(iconSize, iconSize);
+ m_viewOptions.showDecorationSelected = true;
+
+ KFileItemDelegate* delegate = new KFileItemDelegate(this);
+ setItemDelegate(delegate);
+
activate();
+
+ connect(this, SIGNAL(entered(const QModelIndex&)),
+ m_view->m_controller, SLOT(emitItemEntered(const QModelIndex&)));
+ connect(this, SIGNAL(viewportEntered()),
+ m_view->m_controller, SLOT(emitViewportEntered()));
}
ColumnWidget::~ColumnWidget()
void ColumnWidget::setActive(bool active)
{
- if (active) {
- obtainSelectionModel();
- } else {
- releaseSelectionModel();
- }
-
if (m_active == active) {
return;
}
return m_active;
}
-const KUrl& ColumnWidget::url() const
+inline void ColumnWidget::setChildUrl(const KUrl& url)
{
- return m_url;
+ m_childUrl = url;
}
-void ColumnWidget::obtainSelectionModel()
+inline const KUrl& ColumnWidget::childUrl() const
{
- if (selectionModel() != m_view->selectionModel()) {
- selectionModel()->deleteLater();
- setSelectionModel(m_view->selectionModel());
- clearSelection();
- }
+ return m_childUrl;
}
-void ColumnWidget::releaseSelectionModel()
+inline void ColumnWidget::setUrl(const KUrl& url)
{
- if (selectionModel() == m_view->selectionModel()) {
- QItemSelectionModel* replacementModel = new QItemSelectionModel(model());
- setSelectionModel(replacementModel);
- }
+ m_url = url;
+}
+
+inline const KUrl& ColumnWidget::url() const
+{
+ return m_url;
}
-QStyleOptionViewItem ColumnWidget::viewOptions() const
+inline QStyleOptionViewItem ColumnWidget::viewOptions() const
{
return m_viewOptions;
}
if (!urls.isEmpty()) {
event->acceptProposedAction();
m_view->m_controller->indicateDroppedUrls(urls,
+ url(),
indexAt(event->pos()),
event->source());
}
m_dragging = false;
}
-void ColumnWidget::mousePressEvent(QMouseEvent* event)
-{
- // On each mouse press event QColumnView triggers the loading of the
- // current folder in the next column. This is not wanted for Dolphin when
- // opening a context menu or when the CTRL modifier is pressed. Beside usability
- // aspects the loading of the folder also implies losing the current selection,
- // which makes it impossible to select folders from the current column. To bypass
- // this behavior QListView::mousePressEvent() is not invoked in those cases, which
- // is not a nice solution. Maybe another solution can be found in future versions
- // of QColumnView.
-
- m_view->requestSelectionModel(this);
-
- bool swallowMousePressEvent = false;
- const QModelIndex index = indexAt(event->pos());
- if (index.isValid()) {
- // a click on an item has been done
- const QAbstractProxyModel* proxyModel = static_cast<const QAbstractProxyModel*>(m_view->model());
- const KDirModel* dirModel = static_cast<const KDirModel*>(proxyModel->sourceModel());
- const QModelIndex dirIndex = proxyModel->mapToSource(index);
- KFileItem item = dirModel->itemForIndex(dirIndex);
- if (!item.isNull()) {
- QItemSelectionModel* selModel = selectionModel();
-
- bool activate = true;
- const Qt::KeyboardModifiers modifier = QApplication::keyboardModifiers();
- if (modifier & Qt::ControlModifier) {
- m_view->requestActivation(this);
- if (!selModel->hasSelection()) {
- // Assure to set the current index, so that a selection by the SHIFT key
- // will work. TODO: If the index specifies a folder, the loading of the folder will
- // be triggered by QColumnView although this is not wanted by Dolphin.
- selModel->setCurrentIndex(index, QItemSelectionModel::Select);
- }
- selModel->select(index, QItemSelectionModel::Toggle);
- swallowMousePressEvent = true;
- } else if (item.isDir()) {
- m_childUrl = item.url();
- viewport()->update();
-
- // Only request the activation if not the left button is pressed.
- // The left button on a directory opens a new column, hence requesting
- // an activation is useless as the new column will request the activation
- // afterwards.
- if (event->button() == Qt::LeftButton) {
- activate = false;
- }
- }
-
- if (activate) {
- m_view->requestActivation(this);
- }
-
- // TODO: is the assumption OK that Qt::RightButton always represents the context menu button?
- if (event->button() == Qt::RightButton) {
- swallowMousePressEvent = true;
- if (!selModel->isSelected(index)) {
- clearSelection();
- }
- selModel->select(index, QItemSelectionModel::Select);
- }
- }
- } else {
- // a click on the viewport has been done
- m_view->requestActivation(this);
-
- // Swallow mouse move events if a click is done on the viewport. Otherwise the QColumnView
- // triggers an unwanted loading of directories on hovering folder items.
- m_swallowMouseMoveEvents = true;
- clearSelection();
- }
-
- if (!swallowMousePressEvent) {
- QListView::mousePressEvent(event);
- }
-}
-
-void ColumnWidget::mouseMoveEvent(QMouseEvent* event)
-{
- // see description in ColumnView::mousePressEvent()
- if (!m_swallowMouseMoveEvents) {
- QListView::mouseMoveEvent(event);
- }
-}
-
-void ColumnWidget::mouseReleaseEvent(QMouseEvent* event)
-{
- QListView::mouseReleaseEvent(event);
- m_swallowMouseMoveEvents = false;
-}
-
-
void ColumnWidget::paintEvent(QPaintEvent* event)
{
if (!m_childUrl.isEmpty()) {
// indicate the shown URL of the next column by highlighting the shown folder item
- const QAbstractProxyModel* proxyModel = static_cast<const QAbstractProxyModel*>(m_view->model());
- const KDirModel* dirModel = static_cast<const KDirModel*>(proxyModel->sourceModel());
- const QModelIndex dirIndex = dirModel->indexForUrl(m_childUrl);
- const QModelIndex proxyIndex = proxyModel->mapFromSource(dirIndex);
+ const QModelIndex dirIndex = m_view->m_dolphinModel->indexForUrl(m_childUrl);
+ const QModelIndex proxyIndex = m_view->m_proxyModel->mapFromSource(dirIndex);
if (proxyIndex.isValid() && !selectionModel()->isSelected(proxyIndex)) {
const QRect itemRect = visualRect(proxyIndex);
QPainter painter(viewport());
}
}
+void ColumnWidget::mousePressEvent(QMouseEvent* event)
+{
+ if (!m_active) {
+ m_view->requestActivation(this);
+ }
+
+ QListView::mousePressEvent(event);
+}
+
+void ColumnWidget::keyPressEvent(QKeyEvent* event)
+{
+ QListView::keyPressEvent(event);
+
+ const QItemSelectionModel* selModel = selectionModel();
+ const QModelIndex currentIndex = selModel->currentIndex();
+ const bool triggerItem = currentIndex.isValid()
+ && (event->key() == Qt::Key_Return)
+ && (selModel->selectedIndexes().count() <= 1);
+ if (triggerItem) {
+ m_view->m_controller->triggerItem(currentIndex);
+ }
+}
+
void ColumnWidget::contextMenuEvent(QContextMenuEvent* event)
{
if (!m_active) {
void ColumnWidget::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
- // inactive views should not have any selection
- if (!m_active) {
- clearSelection();
- }
QListView::selectionChanged(selected, deselected);
+
+ QItemSelectionModel* selModel = m_view->selectionModel();
+ selModel->select(selected, QItemSelectionModel::Select);
+ selModel->select(deselected, QItemSelectionModel::Deselect);
}
void ColumnWidget::activate()
{
+ if (m_view->hasFocus()) {
+ setFocus(Qt::OtherFocusReason);
+ }
+ m_view->setFocusProxy(this);
+
+ // TODO: Connecting to the signal 'activated()' is not possible, as kstyle
+ // does not forward the single vs. doubleclick to it yet (KDE 4.1?). Hence it is
+ // necessary connecting the signal 'singleClick()' or 'doubleClick'.
+ if (KGlobalSettings::singleClick()) {
+ connect(this, SIGNAL(clicked(const QModelIndex&)),
+ m_view->m_controller, SLOT(triggerItem(const QModelIndex&)));
+ } else {
+ connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
+ m_view->m_controller, SLOT(triggerItem(const QModelIndex&)));
+ }
+
const QColor bgColor = KColorScheme(QPalette::Active, KColorScheme::View).background().color();
QPalette palette = viewport()->palette();
palette.setColor(viewport()->backgroundRole(), bgColor);
viewport()->setPalette(palette);
+ if (!m_childUrl.isEmpty()) {
+ // assure that the current index is set on the index that represents
+ // the child URL
+ const QModelIndex dirIndex = m_view->m_dolphinModel->indexForUrl(m_childUrl);
+ const QModelIndex proxyIndex = m_view->m_proxyModel->mapFromSource(dirIndex);
+ selectionModel()->setCurrentIndex(proxyIndex, QItemSelectionModel::Current);
+ }
+
update();
}
void ColumnWidget::deactivate()
{
- QColor bgColor = KColorScheme(QPalette::Active, KColorScheme::View).background().color();
- const QColor fgColor = KColorScheme(QPalette::Active, KColorScheme::View).foreground().color();
- bgColor = KColorUtils::mix(bgColor, fgColor, 0.04);
+ // TODO: Connecting to the signal 'activated()' is not possible, as kstyle
+ // does not forward the single vs. doubleclick to it yet (KDE 4.1?). Hence it is
+ // necessary connecting the signal 'singleClick()' or 'doubleClick'.
+ if (KGlobalSettings::singleClick()) {
+ disconnect(this, SIGNAL(clicked(const QModelIndex&)),
+ m_view->m_controller, SLOT(triggerItem(const QModelIndex&)));
+ } else {
+ disconnect(this, SIGNAL(doubleClicked(const QModelIndex&)),
+ m_view->m_controller, SLOT(triggerItem(const QModelIndex&)));
+ }
- QPalette palette = viewport()->palette();
- palette.setColor(viewport()->backgroundRole(), bgColor);
+ const QPalette palette = m_view->viewport()->palette();
viewport()->setPalette(palette);
+ selectionModel()->clear();
update();
}
// ---
DolphinColumnView::DolphinColumnView(QWidget* parent, DolphinController* controller) :
- QColumnView(parent),
- m_controller(controller)
+ QAbstractItemView(parent),
+ m_controller(controller),
+ m_restoreActiveColumnFocus(false),
+ m_dirListerCompleted(false),
+ m_index(-1),
+ m_contentX(0),
+ m_columns(),
+ m_animation(0),
+ m_dolphinModel(0),
+ m_proxyModel(0)
{
Q_ASSERT(controller != 0);
setDropIndicatorShown(false);
setSelectionMode(ExtendedSelection);
- // TODO: Connecting to the signal 'activated()' is not possible, as kstyle
- // does not forward the single vs. doubleclick to it yet (KDE 4.1?). Hence it is
- // necessary connecting the signal 'singleClick()' or 'doubleClick'.
- if (KGlobalSettings::singleClick()) {
- connect(this, SIGNAL(clicked(const QModelIndex&)),
- this, SLOT(triggerItem(const QModelIndex&)));
- } else {
- connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
- this, SLOT(triggerItem(const QModelIndex&)));
- }
connect(this, SIGNAL(entered(const QModelIndex&)),
controller, SLOT(emitItemEntered(const QModelIndex&)));
connect(this, SIGNAL(viewportEntered()),
connect(controller, SIGNAL(zoomOut()),
this, SLOT(zoomOut()));
connect(controller, SIGNAL(urlChanged(const KUrl&)),
- this, SLOT(updateColumnsState(const KUrl&)));
+ this, SLOT(showColumn(const KUrl&)));
+
+ 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)));
+
+ ColumnWidget* column = new ColumnWidget(viewport(), this, m_controller->url());
+ m_columns.append(column);
+ setActiveColumnIndex(0);
updateDecorationSize();
+
+ // dim the background of the viewport
+ QColor bgColor = KColorScheme(QPalette::Active, KColorScheme::View).background().color();
+ const QColor fgColor = KColorScheme(QPalette::Active, KColorScheme::View).foreground().color();
+ bgColor = KColorUtils::mix(bgColor, fgColor, 0.04);
+
+ QPalette palette = viewport()->palette();
+ palette.setColor(viewport()->backgroundRole(), bgColor);
+ viewport()->setPalette(palette);
}
DolphinColumnView::~DolphinColumnView()
{
}
-void DolphinColumnView::invertSelection()
+QModelIndex DolphinColumnView::indexAt(const QPoint& point) const
{
- selectActiveColumn(QItemSelectionModel::Toggle);
+ foreach (ColumnWidget* 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::selectAll()
+void DolphinColumnView::scrollTo(const QModelIndex& index, ScrollHint hint)
{
- selectActiveColumn(QItemSelectionModel::Select);
+ activeColumn()->scrollTo(index, hint);
}
-QAbstractItemView* DolphinColumnView::createColumn(const QModelIndex& index)
+QRect DolphinColumnView::visualRect(const QModelIndex& index) const
{
- // let the column widget be aware about its URL...
- KUrl columnUrl;
- if (viewport()->children().count() == 0) {
- // For the first column widget the directory lister has not been started
- // yet, hence use the URL from the controller instead.
- columnUrl = m_controller->url();
- } else {
- const QAbstractProxyModel* proxyModel = static_cast<const QAbstractProxyModel*>(model());
- const KDirModel* dirModel = static_cast<const KDirModel*>(proxyModel->sourceModel());
+ return activeColumn()->visualRect(index);
+}
- const QModelIndex dirModelIndex = proxyModel->mapToSource(index);
- KFileItem fileItem = dirModel->itemForIndex(dirModelIndex);
- if (!fileItem.isNull()) {
- columnUrl = fileItem.url();
- }
+void DolphinColumnView::setModel(QAbstractItemModel* model)
+{
+ if (m_dolphinModel != 0) {
+ m_dolphinModel->disconnect(this);
}
- ColumnWidget* view = new ColumnWidget(viewport(), this, columnUrl);
+ m_proxyModel = static_cast<QAbstractProxyModel*>(model);
+ m_dolphinModel = static_cast<DolphinModel*>(m_proxyModel->sourceModel());
+ connect(m_dolphinModel, SIGNAL(expand(const QModelIndex&)),
+ this, SLOT(triggerReloadColumns(const QModelIndex&)));
- // The following code has been copied 1:1 from QColumnView::createColumn().
- // Copyright (C) 1992-2007 Trolltech ASA. In Qt 4.4 the new method
- // QColumnView::initializeColumn() will be available for this.
+ KDirLister* dirLister = m_dolphinModel->dirLister();
+ connect(dirLister, SIGNAL(started(const KUrl&)),
+ this, SLOT(slotDirListerStarted(const KUrl&)));
+ connect(dirLister, SIGNAL(completed()),
+ this, SLOT(slotDirListerCompleted()));
- view->setFrameShape(QFrame::NoFrame);
- view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
- view->setMinimumWidth(100);
- view->setAttribute(Qt::WA_MacShowFocusRect, false);
+ activeColumn()->setModel(model);
+ QAbstractItemView::setModel(model);
+}
- // copy the 'view' behavior
- view->setDragDropMode(dragDropMode());
- view->setDragDropOverwriteMode(dragDropOverwriteMode());
- view->setDropIndicatorShown(showDropIndicator());
- view->setAlternatingRowColors(alternatingRowColors());
- view->setAutoScroll(hasAutoScroll());
- view->setEditTriggers(editTriggers());
- view->setHorizontalScrollMode(horizontalScrollMode());
- view->setIconSize(iconSize());
- view->setSelectionBehavior(selectionBehavior());
- view->setSelectionMode(selectionMode());
- view->setTabKeyNavigation(tabKeyNavigation());
- view->setTextElideMode(textElideMode());
- view->setVerticalScrollMode(verticalScrollMode());
+void DolphinColumnView::invertSelection()
+{
+ // TODO: this approach of inverting the selection is quite slow. It should
+ // be possible to speedup the implementation by using QItemSelection, but
+ // all adempts have failed yet...
+
+ ColumnWidget* column = activeColumn();
+ QItemSelectionModel* selModel = column->selectionModel();
+
+ KDirLister* dirLister = m_dolphinModel->dirLister();
+ const KFileItemList list = dirLister->itemsForDir(column->url());
+ foreach (const KFileItem item, list) {
+ const QModelIndex index = m_dolphinModel->indexForUrl(item.url());
+ selModel->select(m_proxyModel->mapFromSource(index), QItemSelectionModel::Toggle);
+ }
+}
+
+void DolphinColumnView::reload()
+{
+ // Due to the reloading of the model all columns will be reset to show
+ // the same content as the first column. As this is not wanted, all columns
+ // except of the first column are temporary hidden until the root index can
+ // be updated again.
+ m_restoreActiveColumnFocus = false;
+ QList<ColumnWidget*>::iterator start = m_columns.begin() + 1;
+ QList<ColumnWidget*>::iterator end = m_columns.end();
+ for (QList<ColumnWidget*>::iterator it = start; it != end; ++it) {
+ ColumnWidget* column = (*it);
+ if (column->isActive() && column->hasFocus()) {
+ // because of hiding the column, it will lose the focus
+ // -> remember that the focus should be restored after reloading
+ m_restoreActiveColumnFocus = true;
+ }
+ column->hide();
+ }
+
+ // all columns are hidden, now reload the directory lister
+ KDirLister* dirLister = m_dolphinModel->dirLister();
+ const KUrl& rootUrl = m_columns[0]->url();
+ dirLister->openUrl(rootUrl, false, true);
+ updateColumns();
+}
+
+void DolphinColumnView::showColumn(const KUrl& url)
+{
+ const KUrl& rootUrl = m_columns[0]->url();
+ if (!rootUrl.isParentOf(url)) {
+ // the URL is no child URL of the column view, hence clear all columns
+ // and reset the root column
+ QList<ColumnWidget*>::iterator start = m_columns.begin() + 1;
+ QList<ColumnWidget*>::iterator end = m_columns.end();
+ for (QList<ColumnWidget*>::iterator it = start; it != end; ++it) {
+ (*it)->deleteLater();
+ }
+ m_columns.erase(start, end);
+ m_index = 0;
+ m_columns[0]->setActive(true);
+ m_columns[0]->setUrl(url);
+ assureVisibleActiveColumn();
+ return;
+ }
- view->setModel(model());
+ KDirLister* dirLister = m_dolphinModel->dirLister();
+ const KUrl dirListerUrl = dirLister->url();
+ if (dirListerUrl != rootUrl) {
+ // It is possible that root URL of the directory lister is adjusted
+ // after creating the column widget (e. g. when restoring the history
+ // having a different root URL than the controller indicates).
+ m_columns[0]->setUrl(dirListerUrl);
+ }
- // set the delegate to be the columnview delegate
- QAbstractItemDelegate* delegate = view->itemDelegate();
- view->setItemDelegate(itemDelegate());
- delete delegate;
+ int columnIndex = 0;
+ foreach (ColumnWidget* column, m_columns) {
+ if (column->url() == url) {
+ // the column represents already the requested URL, hence activate it
+ requestActivation(column);
+ 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<ColumnWidget*>::iterator start = m_columns.begin() + columnIndex;
+ QList<ColumnWidget*>::iterator end = m_columns.end();
+ for (QList<ColumnWidget*>::iterator it = start; it != end; ++it) {
+ (*it)->deleteLater();
+ }
+ m_columns.erase(start, end);
- view->setRootIndex(index);
+ const int maxIndex = m_columns.count() - 1;
+ Q_ASSERT(maxIndex >= 0);
+ if (m_index > maxIndex) {
+ m_index = maxIndex;
+ }
+ break;
+ }
+ }
+ ++columnIndex;
+ }
- if (model()->canFetchMore(index)) {
- model()->fetchMore(index);
+ // 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);
+ const QModelIndex dirIndex = m_dolphinModel->indexForUrl(KUrl(path));
+ const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
+
+ m_columns[columnIndex]->setChildUrl(childUrl);
+ columnIndex++;
+
+ ColumnWidget* column = new ColumnWidget(viewport(), this, childUrl);
+ column->setModel(model());
+ column->setRootIndex(proxyIndex);
+ 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();
+
+ // the layout is finished, now let the column be invisible until it
+ // gets a valid root index due to expandToActiveUrl()
+ column->hide();
+ }
}
- return view;
+ // 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::mousePressEvent(QMouseEvent* event)
+void DolphinColumnView::selectAll()
{
- m_controller->triggerActivation();
- QColumnView::mousePressEvent(event);
+ activeColumn()->selectAll();
}
-void DolphinColumnView::dragEnterEvent(QDragEnterEvent* event)
+bool DolphinColumnView::isIndexHidden(const QModelIndex& index) const
{
- if (event->mimeData()->hasUrls()) {
- event->acceptProposedAction();
- }
+ Q_UNUSED(index);
+ return false;//activeColumn()->isIndexHidden(index);
}
-void DolphinColumnView::dropEvent(QDropEvent* event)
+QModelIndex DolphinColumnView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{
- const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
- if (!urls.isEmpty()) {
- m_controller->indicateDroppedUrls(urls,
- indexAt(event->pos()),
- event->source());
- event->acceptProposedAction();
+ // 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);
+ }
+ break;
+
+ case MoveRight:
+ if (m_index < m_columns.count() - 1) {
+ setActiveColumnIndex(m_index + 1);
+ }
+ break;
+
+ default:
+ break;
}
- QColumnView::dropEvent(event);
+
+ 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->triggerActivation();
+ QAbstractItemView::mousePressEvent(event);
+}
+
+void DolphinColumnView::resizeEvent(QResizeEvent* event)
+{
+ QAbstractItemView::resizeEvent(event);
+ layoutColumns();
+ updateScrollBar();
}
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::triggerItem(const QModelIndex& index)
+void DolphinColumnView::moveContentHorizontally(int x)
{
- m_controller->triggerItem(index);
- updateColumnsState(m_controller->url());
+ m_contentX = isRightToLeft() ? +x : -x;
+ layoutColumns();
}
-void DolphinColumnView::updateColumnsState(const KUrl& url)
-{
- foreach (QObject* object, viewport()->children()) {
- if (object->inherits("QListView")) {
- ColumnWidget* widget = static_cast<ColumnWidget*>(object);
- widget->setActive(widget->url() == url);
- }
- }
-}
-
-
void DolphinColumnView::updateDecorationSize()
{
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
doItemsLayout();
}
+void DolphinColumnView::expandToActiveUrl()
+{
+ const int lastIndex = m_columns.count() - 1;
+ Q_ASSERT(lastIndex >= 0);
+ const KUrl& activeUrl = m_columns[lastIndex]->url();
+ const KUrl rootUrl = m_dolphinModel->dirLister()->url();
+ const bool expand = m_dirListerCompleted
+ && rootUrl.isParentOf(activeUrl)
+ && !rootUrl.equals(activeUrl, KUrl::CompareWithoutTrailingSlash);
+ if (expand) {
+ m_dolphinModel->expandToUrl(activeUrl);
+ }
+ updateColumns();
+}
+
+void DolphinColumnView::triggerUpdateColumns(const QModelIndex& index)
+{
+ Q_UNUSED(index);
+ // the updating of the columns may not be done in the context of this slot
+ QMetaObject::invokeMethod(this, "updateColumns", Qt::QueuedConnection);
+}
+
+void DolphinColumnView::updateColumns()
+{
+ const int end = m_columns.count() - 2; // next to last column
+ for (int i = 0; i <= end; ++i) {
+ ColumnWidget* nextColumn = m_columns[i + 1];
+ const QModelIndex rootIndex = nextColumn->rootIndex();
+ if (rootIndex.isValid()) {
+ nextColumn->show();
+ } else {
+ const QModelIndex dirIndex = m_dolphinModel->indexForUrl(m_columns[i]->childUrl());
+ const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
+ if (proxyIndex.isValid()) {
+ nextColumn->setRootIndex(proxyIndex);
+ nextColumn->show();
+ if (nextColumn->isActive() && m_restoreActiveColumnFocus) {
+ nextColumn->setFocus();
+ m_restoreActiveColumnFocus = false;
+ }
+ }
+ }
+ }
+}
+
+void DolphinColumnView::slotDirListerStarted(const KUrl& url)
+{
+ Q_UNUSED(url);
+ m_dirListerCompleted = false;
+}
+
+void DolphinColumnView::slotDirListerCompleted()
+{
+ m_dirListerCompleted = true;
+ QMetaObject::invokeMethod(this, "expandToActiveUrl", Qt::QueuedConnection);
+}
+
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::requestActivation(QWidget* column)
+void DolphinColumnView::setActiveColumnIndex(int index)
{
- foreach (QObject* object, viewport()->children()) {
- if (object->inherits("QListView")) {
- ColumnWidget* widget = static_cast<ColumnWidget*>(object);
- const bool isActive = (widget == column);
- widget->setActive(isActive);
- if (isActive) {
- m_controller->setUrl(widget->url());
- }
- }
+ 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);
+
+ m_controller->setUrl(m_columns[m_index]->url());
+
+ assureVisibleActiveColumn();
}
-void DolphinColumnView::requestSelectionModel(QAbstractItemView* view)
+void DolphinColumnView::layoutColumns()
{
- foreach (QObject* object, viewport()->children()) {
- if (object->inherits("QListView")) {
- ColumnWidget* widget = static_cast<ColumnWidget*>(object);
- if (widget == view) {
- widget->obtainSelectionModel();
- } else {
- widget->releaseSelectionModel();
- }
+ ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
+ const int columnWidth = settings->columnWidth();
+ if (isRightToLeft()) {
+ int x = viewport()->width() - columnWidth + m_contentX;
+ foreach (ColumnWidget* column, m_columns) {
+ column->setGeometry(QRect(x, 0, columnWidth, viewport()->height()));
+ x -= columnWidth;
+ }
+ } else {
+ int x = m_contentX;
+ foreach (ColumnWidget* column, m_columns) {
+ column->setGeometry(QRect(x, 0, columnWidth, viewport()->height()));
+ x += columnWidth;
}
}
}
-void DolphinColumnView::selectActiveColumn(QItemSelectionModel::SelectionFlags flags)
+void DolphinColumnView::updateScrollBar()
{
- // TODO: this approach of selecting the active column is very slow. It should be
- // possible to speedup the implementation by using QItemSelection, but all adempts
- // have failed yet...
-
- // assure that the selection model of the active column is set properly, otherwise
- // no visual update of the selections is done
- const KUrl& activeUrl = m_controller->url();
- foreach (QObject* object, viewport()->children()) {
- if (object->inherits("QListView")) {
- ColumnWidget* widget = static_cast<ColumnWidget*>(object);
- if (widget->url() == activeUrl) {
- widget->obtainSelectionModel();
- } else {
- widget->releaseSelectionModel();
- }
- }
+ int contentWidth = 0;
+ foreach (ColumnWidget* column, m_columns) {
+ contentWidth += column->width();
}
- QItemSelectionModel* selModel = selectionModel();
+ horizontalScrollBar()->setPageStep(contentWidth);
+ horizontalScrollBar()->setRange(0, contentWidth - viewport()->width());
+}
- const QAbstractProxyModel* proxyModel = static_cast<const QAbstractProxyModel*>(model());
- const KDirModel* dirModel = static_cast<const KDirModel*>(proxyModel->sourceModel());
- KDirLister* dirLister = dirModel->dirLister();
+void DolphinColumnView::assureVisibleActiveColumn()
+{
+ const int viewportWidth = viewport()->width();
+ const int x = activeColumn()->x();
+ const int width = activeColumn()->width();
+ 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();
+ }
+}
- const KFileItemList list = dirLister->itemsForDir(activeUrl);
- foreach (KFileItem* item, list) {
- const QModelIndex index = dirModel->indexForUrl(item->url());
- selModel->select(proxyModel->mapFromSource(index), flags);
+void DolphinColumnView::requestActivation(ColumnWidget* column)
+{
+ if (column->isActive()) {
+ assureVisibleActiveColumn();
+ } else {
+ int index = 0;
+ foreach (ColumnWidget* currColumn, m_columns) {
+ if (currColumn == column) {
+ setActiveColumnIndex(index);
+ return;
+ }
+ ++index;
+ }
}
}