#include <QApplication>
#include <QPoint>
#include <QScrollBar>
+#include <QTimer>
#include <QTimeLine>
/**
* drawn in a lighter color. All operations are applied to this column.
*/
void setActive(bool active);
- inline bool isActive() const;
+ 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.
*/
- inline void setChildUrl(const KUrl& url);
- inline const KUrl& childUrl() const;
+ void setChildUrl(const KUrl& url);
+ const KUrl& childUrl() const;
- /**
- * Returns the directory URL that is shown inside the column widget.
- */
- inline const KUrl& url() 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. */
+ const KUrl& url() const;
protected:
virtual QStyleOptionViewItem viewOptions() const;
viewport()->setAttribute(Qt::WA_Hover);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
setSelectionBehavior(SelectItems);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setDragDropMode(QAbstractItemView::DragDrop);
const int iconSize = settings->iconSize();
m_viewOptions.decorationSize = QSize(iconSize, iconSize);
+ m_viewOptions.showDecorationSelected = true;
+
KFileItemDelegate* delegate = new KFileItemDelegate(this);
setItemDelegate(delegate);
return m_childUrl;
}
-const KUrl& ColumnWidget::url() const
+inline void ColumnWidget::setUrl(const KUrl& url)
+{
+ m_url = url;
+}
+
+inline const KUrl& ColumnWidget::url() const
{
return m_url;
}
-QStyleOptionViewItem ColumnWidget::viewOptions() const
+inline QStyleOptionViewItem ColumnWidget::viewOptions() const
{
return m_viewOptions;
}
&& (event->key() == Qt::Key_Return)
&& (selModel->selectedIndexes().count() <= 1);
if (triggerItem) {
- m_view->triggerItem(currentIndex);
+ m_view->m_controller->triggerItem(currentIndex);
}
}
// necessary connecting the signal 'singleClick()' or 'doubleClick'.
if (KGlobalSettings::singleClick()) {
connect(this, SIGNAL(clicked(const QModelIndex&)),
- m_view, SLOT(triggerItem(const QModelIndex&)));
+ m_view->m_controller, SLOT(triggerItem(const QModelIndex&)));
} else {
connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
- m_view, SLOT(triggerItem(const QModelIndex&)));
+ m_view->m_controller, SLOT(triggerItem(const QModelIndex&)));
}
const QColor bgColor = KColorScheme(QPalette::Active, KColorScheme::View).background().color();
// necessary connecting the signal 'singleClick()' or 'doubleClick'.
if (KGlobalSettings::singleClick()) {
disconnect(this, SIGNAL(clicked(const QModelIndex&)),
- m_view, SLOT(triggerItem(const QModelIndex&)));
+ m_view->m_controller, SLOT(triggerItem(const QModelIndex&)));
} else {
disconnect(this, SIGNAL(doubleClicked(const QModelIndex&)),
- m_view, SLOT(triggerItem(const QModelIndex&)));
+ m_view->m_controller, SLOT(triggerItem(const QModelIndex&)));
}
- 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);
+ const QPalette palette = m_view->viewport()->palette();
viewport()->setPalette(palette);
selectionModel()->clear();
-
update();
}
DolphinColumnView::DolphinColumnView(QWidget* parent, DolphinController* controller) :
QAbstractItemView(parent),
m_controller(controller),
+ m_restoreActiveColumnFocus(false),
m_index(-1),
m_contentX(0),
m_columns(),
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();
- m_animation = new QTimeLine(500, this);
- connect(m_animation, SIGNAL(frameChanged(int)), horizontalScrollBar(), SLOT(setValue(int)));
+ // 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()
m_dolphinModel->disconnect(this);
}
- m_proxyModel = static_cast<const QAbstractProxyModel*>(model);
- m_dolphinModel = static_cast<const DolphinModel*>(m_proxyModel->sourceModel());
+ 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&)));
+ KDirLister* dirLister = m_dolphinModel->dirLister();
+ connect(dirLister, SIGNAL(completed()),
+ this, SLOT(triggerExpandToActiveUrl()));
+
activeColumn()->setModel(model);
QAbstractItemView::setModel(model);
}
-void DolphinColumnView::reload()
+void DolphinColumnView::invertSelection()
{
- deleteInactiveChildColumns();
+ // 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) {
- (*it)->hide();
- (*it)->setRootIndex(QModelIndex());
- }
+ 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();
- connect(dirLister, SIGNAL(completed()),
- this, SLOT(expandToActiveUrl()));
- const KUrl baseUrl = m_columns[0]->url();
- dirLister->openUrl(baseUrl, false, true);
+ 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;
+ }
+
+ 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);
+ }
+
+ 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);
+
+ 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);
+ 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();
+ }
+ }
+
+ // 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
{
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)
-{
- m_controller->triggerItem(index);
-
- const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
- if ((modifiers & Qt::ControlModifier) || (modifiers & Qt::ShiftModifier)) {
- return;
- }
-
- const KFileItem item = m_dolphinModel->itemForIndex(m_proxyModel->mapToSource(index));
- if ((item.url() != activeColumn()->url()) && item.isDir()) {
- deleteInactiveChildColumns();
-
- const KUrl& childUrl = m_controller->url();
- activeColumn()->setChildUrl(childUrl);
-
- ColumnWidget* column = new ColumnWidget(viewport(), this, childUrl);
- column->setModel(model());
- column->setRootIndex(index);
-
- m_columns.append(column);
-
- setActiveColumnIndex(m_index + 1);
-
- // Before invoking layoutColumns() the column must be shown. To prevent
- // a flickering the initial geometry is set to be invisible.
- column->setGeometry(QRect(-1, -1, 1, 1));
- column->show();
-
- layoutColumns();
- updateScrollBar();
- assureVisibleActiveColumn();
- }
-}
-
void DolphinColumnView::moveContentHorizontally(int x)
{
- m_contentX = -x;
+ m_contentX = isRightToLeft() ? +x : -x;
layoutColumns();
}
-void DolphinColumnView::showColumn(const KUrl& url)
-{
- if (!m_columns[0]->url().isParentOf(url)) {
- // the URL is no child URL of the column view, hence do nothing
- return;
- }
-
- 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 it must
- // be deleted and a new column must be loaded
- if (columnIndex > 0) {
- setActiveColumnIndex(columnIndex - 1);
- deleteInactiveChildColumns();
- }
-
- const QModelIndex dirIndex = m_dolphinModel->indexForUrl(url);
- if (dirIndex.isValid()) {
- triggerItem(m_proxyModel->mapFromSource(dirIndex));
- }
- return;
- }
- ++columnIndex;
- }
-
- // no existing column has been replaced and a new column must be created
- const QModelIndex dirIndex = m_dolphinModel->indexForUrl(url);
- if (dirIndex.isValid()) {
- triggerItem(m_proxyModel->mapFromSource(dirIndex));
- }
-}
-
void DolphinColumnView::updateDecorationSize()
{
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
void DolphinColumnView::expandToActiveUrl()
{
- const KUrl& activeUrl = m_controller->url();
- const KUrl baseUrl = m_dolphinModel->dirLister()->url();
- if (baseUrl.isParentOf(activeUrl) && (baseUrl != activeUrl)) {
+ 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 = rootUrl.isParentOf(activeUrl)
+ && !rootUrl.equals(activeUrl, KUrl::CompareWithoutTrailingSlash);
+ if (expand) {
m_dolphinModel->expandToUrl(activeUrl);
- reloadColumns();
}
+ updateColumns();
}
-void DolphinColumnView::triggerReloadColumns(const QModelIndex& index)
+void DolphinColumnView::triggerUpdateColumns(const QModelIndex& index)
{
Q_UNUSED(index);
- disconnect(m_dolphinModel, SIGNAL(expand(const QModelIndex&)),
- this, SLOT(triggerReloadColumns(const QModelIndex&)));
- // the reloading of the columns may not be done in the context of this slot
- QMetaObject::invokeMethod(this, "reloadColumns", Qt::QueuedConnection);
+ // the updating of the columns may not be done in the context of this slot
+ QMetaObject::invokeMethod(this, "updateColumns", Qt::QueuedConnection);
}
-void DolphinColumnView::reloadColumns()
+void DolphinColumnView::updateColumns()
{
- const int count = m_columns.count() - 1; // ignore the last column
- for (int i = 0; i < count; ++i) {
+ 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()) {
+ 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::triggerExpandToActiveUrl()
+{
+ 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::setActiveColumnIndex(int index)
m_columns[m_index]->setActive(true);
m_controller->setUrl(m_columns[m_index]->url());
+
+ assureVisibleActiveColumn();
}
void DolphinColumnView::layoutColumns()
{
- int x = m_contentX;
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
const int columnWidth = settings->columnWidth();
- foreach (ColumnWidget* column, m_columns) {
- column->setGeometry(QRect(x, 0, columnWidth, viewport()->height()));
- x += 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;
+ }
}
}
const int x = activeColumn()->x();
const int width = activeColumn()->width();
if (x + width > viewportWidth) {
- int newContentX = m_contentX - x - width + viewportWidth;
- if (newContentX > 0) {
- newContentX = 0;
+ 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->setFrameRange(-m_contentX, -newContentX);
m_animation->start();
} else if (x < 0) {
const int newContentX = m_contentX - x;
- m_animation->setFrameRange(-m_contentX, -newContentX);
+ if (isRightToLeft()) {
+ m_animation->setFrameRange(m_contentX, newContentX);
+ } else {
+ m_animation->setFrameRange(-m_contentX, -newContentX);
+ }
m_animation->start();
}
}
foreach (ColumnWidget* currColumn, m_columns) {
if (currColumn == column) {
setActiveColumnIndex(index);
- assureVisibleActiveColumn();
return;
}
++index;
}
}
-void DolphinColumnView::deleteInactiveChildColumns()
-{
- QList<ColumnWidget*>::iterator start = m_columns.begin() + m_index + 1;
- QList<ColumnWidget*>::iterator end = m_columns.end();
- for (QList<ColumnWidget*>::iterator it = start; it != end; ++it) {
- (*it)->deleteLater();
- }
- m_columns.erase(start, end);
-}
-
#include "dolphincolumnview.moc"