/*************************************************************************** * Copyright (C) 2007-2009 by Peter Penz * * * * 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 #include #include #include #include #include #include #include #include #include #include #include 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"