mirror of
https://invent.kde.org/system/dolphin
synced 2024-09-12 12:54:29 +00:00
623 lines
19 KiB
C++
623 lines
19 KiB
C++
/***************************************************************************
|
|
* 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 *
|
|
* 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 "dolphincolumnwidget.h"
|
|
#include "dolphincontroller.h"
|
|
#include "dolphinsettings.h"
|
|
|
|
#include "dolphin_columnmodesettings.h"
|
|
|
|
#include <QPoint>
|
|
#include <QScrollBar>
|
|
#include <QTimeLine>
|
|
|
|
DolphinColumnView::DolphinColumnView(QWidget* parent, DolphinController* 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);
|
|
setDragDropMode(QAbstractItemView::DragDrop);
|
|
setDropIndicatorShown(false);
|
|
setSelectionMode(ExtendedSelection);
|
|
setFocusPolicy(Qt::NoFocus);
|
|
setFrameShape(QFrame::NoFrame);
|
|
|
|
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(sortingChanged(DolphinView::Sorting)),
|
|
this, SLOT(slotSortingChanged(DolphinView::Sorting)));
|
|
connect(view, SIGNAL(sortOrderChanged(Qt::SortOrder)),
|
|
this, SLOT(slotSortOrderChanged(Qt::SortOrder)));
|
|
connect(view, SIGNAL(showHiddenFilesChanged()),
|
|
this, SLOT(slotShowHiddenFilesChanged()));
|
|
connect(view, SIGNAL(showPreviewChanged()),
|
|
this, SLOT(slotShowPreviewChanged()));
|
|
|
|
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)));
|
|
|
|
DolphinColumnWidget* column = new DolphinColumnWidget(viewport(), this, m_controller->url());
|
|
m_columns.append(column);
|
|
setActiveColumnIndex(0);
|
|
|
|
m_emptyViewport = new QFrame(viewport());
|
|
m_emptyViewport->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
|
|
|
|
updateDecorationSize();
|
|
updateColumnsBackground(true);
|
|
}
|
|
|
|
DolphinColumnView::~DolphinColumnView()
|
|
{
|
|
}
|
|
|
|
QModelIndex DolphinColumnView::indexAt(const QPoint& point) const
|
|
{
|
|
foreach (DolphinColumnWidget* column, m_columns) {
|
|
const QModelIndex index = column->indexAt(columnPosition(column, point));
|
|
if (index.isValid()) {
|
|
return index;
|
|
}
|
|
}
|
|
|
|
return QModelIndex();
|
|
}
|
|
|
|
KFileItem DolphinColumnView::itemAt(const QPoint& point) const
|
|
{
|
|
foreach (DolphinColumnWidget* column, m_columns) {
|
|
KFileItem item = column->itemAt(columnPosition(column, point));
|
|
if (!item.isNull()) {
|
|
return item;
|
|
}
|
|
}
|
|
|
|
return KFileItem();
|
|
}
|
|
|
|
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()
|
|
{
|
|
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::reload()
|
|
{
|
|
foreach (DolphinColumnWidget* column, m_columns) {
|
|
column->reload();
|
|
}
|
|
}
|
|
|
|
void DolphinColumnView::setRootUrl(const KUrl& url)
|
|
{
|
|
removeAllColumns();
|
|
m_columns[0]->setUrl(url);
|
|
}
|
|
|
|
void DolphinColumnView::setNameFilter(const QString& nameFilter)
|
|
{
|
|
if (nameFilter != m_nameFilter) {
|
|
m_nameFilter = nameFilter;
|
|
foreach (DolphinColumnWidget* column, m_columns) {
|
|
column->setNameFilter(nameFilter);
|
|
}
|
|
}
|
|
}
|
|
|
|
QString DolphinColumnView::nameFilter() const
|
|
{
|
|
return m_nameFilter;
|
|
}
|
|
|
|
KUrl DolphinColumnView::rootUrl() const
|
|
{
|
|
return m_columns[0]->url();
|
|
}
|
|
|
|
void DolphinColumnView::showColumn(const KUrl& url)
|
|
{
|
|
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) {
|
|
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++;
|
|
|
|
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::editItem(const KFileItem& item)
|
|
{
|
|
activeColumn()->editItem(item);
|
|
}
|
|
|
|
KFileItemList DolphinColumnView::selectedItems() const
|
|
{
|
|
return activeColumn()->selectedItems();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
QRegion DolphinColumnView::visualRegionForSelection(const QItemSelection& selection) const
|
|
{
|
|
Q_UNUSED(selection);
|
|
return QRegion();
|
|
}
|
|
|
|
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::wheelEvent(QWheelEvent* event)
|
|
{
|
|
// let Ctrl+wheel events propagate to the DolphinView for icon zooming
|
|
if ((event->modifiers() & Qt::ControlModifier) == Qt::ControlModifier) {
|
|
event->ignore();
|
|
return;
|
|
}
|
|
QAbstractItemView::wheelEvent(event);
|
|
}
|
|
|
|
void DolphinColumnView::zoomIn()
|
|
{
|
|
if (isZoomInPossible()) {
|
|
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
|
|
switch (settings->iconSize()) {
|
|
case KIconLoader::SizeSmall: settings->setIconSize(KIconLoader::SizeMedium); break;
|
|
case KIconLoader::SizeMedium: settings->setIconSize(KIconLoader::SizeLarge); break;
|
|
default: Q_ASSERT(false); break;
|
|
}
|
|
updateDecorationSize();
|
|
}
|
|
}
|
|
|
|
void DolphinColumnView::zoomOut()
|
|
{
|
|
if (isZoomOutPossible()) {
|
|
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
|
|
switch (settings->iconSize()) {
|
|
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();
|
|
const QSize size(iconSize, iconSize);
|
|
setIconSize(size);
|
|
|
|
foreach (QObject* object, viewport()->children()) {
|
|
if (object->inherits("QListView")) {
|
|
DolphinColumnWidget* widget = static_cast<DolphinColumnWidget*>(object);
|
|
widget->setDecorationSize(size);
|
|
}
|
|
}
|
|
|
|
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
|
|
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 (DolphinColumnWidget* column, m_columns) {
|
|
column->updateBackground();
|
|
}
|
|
}
|
|
|
|
void DolphinColumnView::slotSortingChanged(DolphinView::Sorting sorting)
|
|
{
|
|
foreach (DolphinColumnWidget* column, m_columns) {
|
|
column->setSorting(sorting);
|
|
}
|
|
}
|
|
|
|
void DolphinColumnView::slotSortOrderChanged(Qt::SortOrder order)
|
|
{
|
|
foreach (DolphinColumnWidget* column, m_columns) {
|
|
column->setSortOrder(order);
|
|
}
|
|
}
|
|
|
|
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() < KIconLoader::SizeLarge;
|
|
}
|
|
|
|
bool DolphinColumnView::isZoomOutPossible() const
|
|
{
|
|
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
|
|
return settings->iconSize() > KIconLoader::SizeSmall;
|
|
}
|
|
|
|
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 columnWidth = settings->columnWidth();
|
|
|
|
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());
|
|
}
|
|
|
|
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);
|
|
}
|
|
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 DolphinColumnView::requestActivation(DolphinColumnWidget* column)
|
|
{
|
|
m_controller->setItemView(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) {
|
|
deleteColumn(*it);
|
|
}
|
|
m_columns.erase(start, end);
|
|
m_index = 0;
|
|
m_columns[0]->setActive(true);
|
|
assureVisibleActiveColumn();
|
|
}
|
|
|
|
QPoint DolphinColumnView::columnPosition(DolphinColumnWidget* column, const QPoint& point) const
|
|
{
|
|
const QPoint topLeft = column->frameGeometry().topLeft();
|
|
return QPoint(point.x() - topLeft.x(), point.y() - topLeft.y());
|
|
}
|
|
|
|
void DolphinColumnView::deleteColumn(DolphinColumnWidget* column)
|
|
{
|
|
if (column != 0) {
|
|
if (m_controller->itemView() == column) {
|
|
m_controller->setItemView(0);
|
|
}
|
|
column->disconnect();
|
|
column->deleteLater();
|
|
}
|
|
}
|
|
|
|
#include "dolphincolumnview.moc"
|