From ae4d11d918938fd9087f2035dac247969c1f2313 Mon Sep 17 00:00:00 2001 From: Peter Penz Date: Sat, 21 Apr 2012 21:28:16 +0200 Subject: [PATCH] Prepare view-engine for non-KFileItem usecase Up to now the view-engine only provided a model-implementation that supports file-items. The view-engine always had been designed to be able to work with any kind of model, so now a KStandardItemModel is available. The plan is to convert the places panel to the new view-engine. It should be no problem to fix this until the feature freeze - in the worst case the places-panel code could be reverted while still keeping the KStandardItemModel changes. --- src/CMakeLists.txt | 6 +- src/dolphinmainwindow.cpp | 10 +- src/kitemviews/kfileitemlistview.cpp | 142 +- src/kitemviews/kfileitemlistview.h | 23 +- src/kitemviews/kfileitemlistwidget.cpp | 1235 +---------------- src/kitemviews/kfileitemlistwidget.h | 184 +-- src/kitemviews/kfileitemmodel.cpp | 14 +- src/kitemviews/kfileitemmodel.h | 2 +- src/kitemviews/kitemlistcontainer.cpp | 84 +- src/kitemviews/kitemlistcontainer.h | 14 +- src/kitemviews/kitemlistcontroller.cpp | 29 +- src/kitemviews/kitemlistcontroller.h | 14 +- src/kitemviews/kitemliststyleoption.cpp | 8 +- src/kitemviews/kitemlistview.cpp | 299 ++-- src/kitemviews/kitemlistview.h | 91 +- src/kitemviews/kitemlistwidget.cpp | 11 +- src/kitemviews/kitemlistwidget.h | 33 +- src/kitemviews/kstandarditem.cpp | 100 ++ src/kitemviews/kstandarditem.h | 72 + src/kitemviews/kstandarditemlistview.cpp | 191 +++ src/kitemviews/kstandarditemlistview.h | 81 ++ src/kitemviews/kstandarditemlistwidget.cpp | 1234 ++++++++++++++++ src/kitemviews/kstandarditemlistwidget.h | 218 +++ src/kitemviews/kstandarditemmodel.cpp | 128 ++ src/kitemviews/kstandarditemmodel.h | 68 + src/panels/folders/folderspanel.cpp | 84 +- src/panels/folders/folderspanel.h | 3 +- src/panels/places/placespanel.cpp | 66 +- src/panels/places/placespanel.h | 19 +- src/settings/viewpropertiesdialog.cpp | 2 +- src/tests/kfileitemmodeltest.cpp | 24 +- src/tests/kitemlistcontrollertest.cpp | 27 +- src/views/dolphinfileitemlistwidget.cpp | 5 +- src/views/dolphinfileitemlistwidget.h | 2 +- ...tcontainer.cpp => dolphinitemlistview.cpp} | 207 +-- ...mlistcontainer.h => dolphinitemlistview.h} | 49 +- src/views/dolphinview.cpp | 271 ++-- src/views/dolphinview.h | 15 +- src/views/dolphinviewactionhandler.cpp | 4 +- src/views/viewproperties.cpp | 31 +- src/views/viewproperties.h | 9 +- 41 files changed, 2968 insertions(+), 2141 deletions(-) create mode 100644 src/kitemviews/kstandarditem.cpp create mode 100644 src/kitemviews/kstandarditem.h create mode 100644 src/kitemviews/kstandarditemlistview.cpp create mode 100644 src/kitemviews/kstandarditemlistview.h create mode 100644 src/kitemviews/kstandarditemlistwidget.cpp create mode 100644 src/kitemviews/kstandarditemlistwidget.h create mode 100644 src/kitemviews/kstandarditemmodel.cpp create mode 100644 src/kitemviews/kstandarditemmodel.h rename src/views/{dolphinitemlistcontainer.cpp => dolphinitemlistview.cpp} (57%) rename src/views/{dolphinitemlistcontainer.h => dolphinitemlistview.h} (59%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 97a307b5d..868ff6c60 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,10 @@ set(dolphinprivate_LIB_SRCS kitemviews/kitemlistview.cpp kitemviews/kitemlistwidget.cpp kitemviews/kitemmodelbase.cpp + kitemviews/kstandarditem.cpp + kitemviews/kstandarditemlistview.cpp + kitemviews/kstandarditemlistwidget.cpp + kitemviews/kstandarditemmodel.cpp kitemviews/private/kfileitemclipboard.cpp kitemviews/private/kfileitemmodeldirlister.cpp kitemviews/private/kfileitemmodelsortalgorithm.cpp @@ -52,7 +56,7 @@ set(dolphinprivate_LIB_SRCS settings/viewpropertiesdialog.cpp settings/viewpropsprogressinfo.cpp views/dolphinfileitemlistwidget.cpp - views/dolphinitemlistcontainer.cpp + views/dolphinitemlistview.cpp views/dolphinnewfilemenuobserver.cpp views/dolphinplacesmodel.cpp views/dolphinremoteencoding.cpp diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 6800daefb..a990e2a17 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -1847,17 +1847,17 @@ void DolphinMainWindow::setupDockWidgets() QList placesActions; placesActions.append(separator); placesActions.append(lockLayoutAction); - placesPanel->addActions(placesActions); - placesPanel->setModel(DolphinPlacesModel::instance()); - placesPanel->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + //placesPanel->addActions(placesActions); + //placesPanel->setModel(DolphinPlacesModel::instance()); + //placesPanel->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); placesDock->setWidget(placesPanel); QAction* placesAction = placesDock->toggleViewAction(); createPanelAction(KIcon("bookmarks"), Qt::Key_F9, placesAction, "show_places_panel"); addDockWidget(Qt::LeftDockWidgetArea, placesDock); - connect(placesPanel, SIGNAL(urlChanged(KUrl,Qt::MouseButtons)), - this, SLOT(handlePlacesClick(KUrl,Qt::MouseButtons))); + //connect(placesPanel, SIGNAL(urlChanged(KUrl,Qt::MouseButtons)), + // this, SLOT(handlePlacesClick(KUrl,Qt::MouseButtons))); connect(this, SIGNAL(urlChanged(KUrl)), placesPanel, SLOT(setUrl(KUrl))); connect(placesDock, SIGNAL(visibilityChanged(bool)), diff --git a/src/kitemviews/kfileitemlistview.cpp b/src/kitemviews/kfileitemlistview.cpp index c5c444875..7bcc781fa 100644 --- a/src/kitemviews/kfileitemlistview.cpp +++ b/src/kitemviews/kfileitemlistview.cpp @@ -43,8 +43,7 @@ namespace { } KFileItemListView::KFileItemListView(QGraphicsWidget* parent) : - KItemListView(parent), - m_itemLayout(IconsLayout), + KStandardItemListView(parent), m_modelRolesUpdater(0), m_updateVisibleIndexRangeTimer(0), m_updateIconSizeTimer(0) @@ -52,8 +51,6 @@ KFileItemListView::KFileItemListView(QGraphicsWidget* parent) : setAcceptDrops(true); setScrollOrientation(Qt::Vertical); - setWidgetCreator(new KItemListWidgetCreator()); - setGroupHeaderCreator(new KItemListGroupHeaderCreator()); m_updateVisibleIndexRangeTimer = new QTimer(this); m_updateVisibleIndexRangeTimer->setSingleShot(true); @@ -65,25 +62,26 @@ KFileItemListView::KFileItemListView(QGraphicsWidget* parent) : m_updateIconSizeTimer->setInterval(ShortInterval); connect(m_updateIconSizeTimer, SIGNAL(timeout()), this, SLOT(updateIconSize())); - setVisibleRoles(QList() << "name"); + setVisibleRoles(QList() << "text"); } KFileItemListView::~KFileItemListView() { - // The group headers are children of the widgets created by - // widgetCreator(). So it is mandatory to delete the group headers - // first. - delete groupHeaderCreator(); - delete widgetCreator(); - delete m_modelRolesUpdater; m_modelRolesUpdater = 0; } void KFileItemListView::setPreviewsShown(bool show) { - if (m_modelRolesUpdater) { + if (!m_modelRolesUpdater) { + return; + } + + if (m_modelRolesUpdater->previewsShown() != show) { + beginTransaction(); m_modelRolesUpdater->setPreviewsShown(show); + onPreviewsShownChanged(show); + endTransaction(); } } @@ -104,26 +102,6 @@ bool KFileItemListView::enlargeSmallPreviews() const return m_modelRolesUpdater ? m_modelRolesUpdater->enlargeSmallPreviews() : false; } -void KFileItemListView::setItemLayout(Layout layout) -{ - if (m_itemLayout != layout) { - const bool updateRoles = (m_itemLayout == DetailsLayout || layout == DetailsLayout); - m_itemLayout = layout; - if (updateRoles) { - // The details-layout requires some invisible roles that - // must be added to the model if the new layout is "details". - // If the old layout was "details" the roles will get removed. - applyRolesToModel(); - } - updateLayoutOfVisibleItems(); - } -} - -KFileItemListView::Layout KFileItemListView::itemLayout() const -{ - return m_itemLayout; -} - void KFileItemListView::setEnabledPlugins(const QStringList& list) { if (m_modelRolesUpdater) { @@ -207,52 +185,53 @@ QPixmap KFileItemListView::createDragPixmap(const QSet& indexes) const return dragPixmap; } -void KFileItemListView::initializeItemListWidget(KItemListWidget* item) +KItemListWidgetCreatorBase* KFileItemListView::defaultWidgetCreator() const { - KFileItemListWidget* fileItemListWidget = static_cast(item); - - switch (m_itemLayout) { - case IconsLayout: fileItemListWidget->setLayout(KFileItemListWidget::IconsLayout); break; - case CompactLayout: fileItemListWidget->setLayout(KFileItemListWidget::CompactLayout); break; - case DetailsLayout: fileItemListWidget->setLayout(KFileItemListWidget::DetailsLayout); break; - default: Q_ASSERT(false); break; - } + return new KItemListWidgetCreator(); +} - fileItemListWidget->setSupportsItemExpanding(supportsItemExpanding()); +KItemListGroupHeaderCreatorBase* KFileItemListView::defaultGroupHeaderCreator() const +{ + return new KItemListGroupHeaderCreator(); } -bool KFileItemListView::itemSizeHintUpdateRequired(const QSet& changedRoles) const +void KFileItemListView::onPreviewsShownChanged(bool shown) { - // Even if the icons have a different size they are always aligned within - // the area defined by KItemStyleOption.iconSize and hence result in no - // change of the item-size. - const bool containsIconName = changedRoles.contains("iconName"); - const bool containsIconPixmap = changedRoles.contains("iconPixmap"); - const int count = changedRoles.count(); + Q_UNUSED(shown); +} - const bool iconChanged = (containsIconName && containsIconPixmap && count == 2) || - (containsIconName && count == 1) || - (containsIconPixmap && count == 1); - return !iconChanged; +void KFileItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous) +{ + if (previous == DetailsLayout || current == DetailsLayout) { + // The details-layout requires some invisible roles that + // must be added to the model if the new layout is "details". + // If the old layout was "details" the roles will get removed. + applyRolesToModel(); + } + KStandardItemListView::onItemLayoutChanged(current, previous); + triggerVisibleIndexRangeUpdate(); } void KFileItemListView::onModelChanged(KItemModelBase* current, KItemModelBase* previous) { - Q_UNUSED(previous); Q_ASSERT(qobject_cast(current)); + KStandardItemListView::onModelChanged(current, previous); delete m_modelRolesUpdater; - m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast(current), this); - m_modelRolesUpdater->setIconSize(availableIconSize()); + m_modelRolesUpdater = 0; - applyRolesToModel(); + if (current) { + m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast(current), this); + m_modelRolesUpdater->setIconSize(availableIconSize()); + + applyRolesToModel(); + } } void KFileItemListView::onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous) { - Q_UNUSED(current); - Q_UNUSED(previous); - updateLayoutOfVisibleItems(); + KStandardItemListView::onScrollOrientationChanged(current, previous); + triggerVisibleIndexRangeUpdate(); } void KFileItemListView::onItemSizeChanged(const QSizeF& current, const QSizeF& previous) @@ -264,39 +243,42 @@ void KFileItemListView::onItemSizeChanged(const QSizeF& current, const QSizeF& p void KFileItemListView::onScrollOffsetChanged(qreal current, qreal previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + KStandardItemListView::onScrollOffsetChanged(current, previous); triggerVisibleIndexRangeUpdate(); } void KFileItemListView::onVisibleRolesChanged(const QList& current, const QList& previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + KStandardItemListView::onVisibleRolesChanged(current, previous); applyRolesToModel(); } void KFileItemListView::onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + KStandardItemListView::onStyleOptionChanged(current, previous); triggerIconSizeUpdate(); } void KFileItemListView::onSupportsItemExpandingChanged(bool supportsExpanding) { - Q_UNUSED(supportsExpanding); applyRolesToModel(); - updateLayoutOfVisibleItems(); + KStandardItemListView::onSupportsItemExpandingChanged(supportsExpanding); + triggerVisibleIndexRangeUpdate(); } void KFileItemListView::onTransactionBegin() { - m_modelRolesUpdater->setPaused(true); + if (m_modelRolesUpdater) { + m_modelRolesUpdater->setPaused(true); + } } void KFileItemListView::onTransactionEnd() { + if (!m_modelRolesUpdater) { + return; + } + // Only unpause the model-roles-updater if no timer is active. If one // timer is still active the model-roles-updater will be unpaused later as // soon as the timer has been exceeded. @@ -309,13 +291,13 @@ void KFileItemListView::onTransactionEnd() void KFileItemListView::resizeEvent(QGraphicsSceneResizeEvent* event) { - KItemListView::resizeEvent(event); + KStandardItemListView::resizeEvent(event); triggerVisibleIndexRangeUpdate(); } void KFileItemListView::slotItemsRemoved(const KItemRangeList& itemRanges) { - KItemListView::slotItemsRemoved(itemRanges); + KStandardItemListView::slotItemsRemoved(itemRanges); updateTimersInterval(); } @@ -326,7 +308,7 @@ void KFileItemListView::slotSortRoleChanged(const QByteArray& current, const QBy applyRolesToModel(); } - KItemListView::slotSortRoleChanged(current, previous); + KStandardItemListView::slotSortRoleChanged(current, previous); } void KFileItemListView::triggerVisibleIndexRangeUpdate() @@ -391,18 +373,6 @@ void KFileItemListView::updateIconSize() updateTimersInterval(); } -void KFileItemListView::updateLayoutOfVisibleItems() -{ - if (!model()) { - return; - } - - foreach (KItemListWidget* widget, visibleItemListWidgets()) { - initializeItemListWidget(widget); - } - triggerVisibleIndexRangeUpdate(); -} - void KFileItemListView::updateTimersInterval() { if (!model()) { @@ -434,7 +404,7 @@ void KFileItemListView::applyRolesToModel() QSet roles = visibleRoles().toSet(); roles.insert("iconPixmap"); roles.insert("iconName"); - roles.insert("name"); + roles.insert("text"); roles.insert("isDir"); if (supportsItemExpanding()) { roles.insert("isExpanded"); @@ -453,7 +423,7 @@ QSize KFileItemListView::availableIconSize() const { const KItemListStyleOption& option = styleOption(); const int iconSize = option.iconSize; - if (m_itemLayout == IconsLayout) { + if (itemLayout() == IconsLayout) { const int maxIconWidth = itemSize().width() - 2 * option.padding; return QSize(maxIconWidth, iconSize); } diff --git a/src/kitemviews/kfileitemlistview.h b/src/kitemviews/kfileitemlistview.h index 8f7ca9063..c8a3385fd 100644 --- a/src/kitemviews/kfileitemlistview.h +++ b/src/kitemviews/kfileitemlistview.h @@ -22,7 +22,7 @@ #include -#include +#include class KFileItemModelRolesUpdater; class QTimer; @@ -36,18 +36,11 @@ class QTimer; * KItemListView::setWidgetCreator() and KItemListView::setGroupHeaderCreator() * to apply customized generators. */ -class LIBDOLPHINPRIVATE_EXPORT KFileItemListView : public KItemListView +class LIBDOLPHINPRIVATE_EXPORT KFileItemListView : public KStandardItemListView { Q_OBJECT public: - enum Layout - { - IconsLayout, - CompactLayout, - DetailsLayout - }; - KFileItemListView(QGraphicsWidget* parent = 0); virtual ~KFileItemListView(); @@ -62,9 +55,6 @@ public: void setEnlargeSmallPreviews(bool enlarge); bool enlargeSmallPreviews() const; - void setItemLayout(Layout layout); - Layout itemLayout() const; - /** * Sets the list of enabled thumbnail plugins that are used for previews. * Per default all plugins enabled in the KConfigGroup "PreviewSettings" @@ -86,8 +76,10 @@ public: virtual QPixmap createDragPixmap(const QSet& indexes) const; protected: - virtual void initializeItemListWidget(KItemListWidget* item); - virtual bool itemSizeHintUpdateRequired(const QSet& changedRoles) const; + virtual KItemListWidgetCreatorBase* defaultWidgetCreator() const; + virtual KItemListGroupHeaderCreatorBase* defaultGroupHeaderCreator() const; + virtual void onPreviewsShownChanged(bool shown); + virtual void onItemLayoutChanged(ItemLayout current, ItemLayout previous); virtual void onModelChanged(KItemModelBase* current, KItemModelBase* previous); virtual void onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous); virtual void onItemSizeChanged(const QSizeF& current, const QSizeF& previous); @@ -111,7 +103,6 @@ private slots: void updateIconSize(); private: - void updateLayoutOfVisibleItems(); void updateTimersInterval(); /** @@ -130,8 +121,6 @@ private: QSize availableIconSize() const; private: - Layout m_itemLayout; - KFileItemModelRolesUpdater* m_modelRolesUpdater; QTimer* m_updateVisibleIndexRangeTimer; QTimer* m_updateIconSizeTimer; diff --git a/src/kitemviews/kfileitemlistwidget.cpp b/src/kitemviews/kfileitemlistwidget.cpp index 83df9da70..a5a4f9c0b 100644 --- a/src/kitemviews/kfileitemlistwidget.cpp +++ b/src/kitemviews/kfileitemlistwidget.cpp @@ -19,1202 +19,31 @@ #include "kfileitemlistwidget.h" -#include "kfileitemlistview.h" -#include "kfileitemmodel.h" - -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include -#include "private/kfileitemclipboard.h" -#include "private/kitemlistroleeditor.h" -#include "private/kpixmapmodifier.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -// #define KFILEITEMLISTWIDGET_DEBUG - -KFileItemListWidget::KFileItemListWidget(QGraphicsItem* parent) : - KItemListWidget(parent), - m_isCut(false), - m_isHidden(false), - m_isExpandable(false), - m_supportsItemExpanding(false), - m_dirtyLayout(true), - m_dirtyContent(true), - m_dirtyContentRoles(), - m_layout(IconsLayout), - m_pixmapPos(), - m_pixmap(), - m_scaledPixmapSize(), - m_iconRect(), - m_hoverPixmap(), - m_textInfo(), - m_textRect(), - m_sortedVisibleRoles(), - m_expansionArea(), - m_customTextColor(), - m_additionalInfoTextColor(), - m_overlay(), - m_rating(), - m_roleEditor(0) -{ -} - -KFileItemListWidget::~KFileItemListWidget() -{ - qDeleteAll(m_textInfo); - m_textInfo.clear(); - - delete m_roleEditor; -} - -void KFileItemListWidget::setLayout(Layout layout) -{ - if (m_layout != layout) { - m_layout = layout; - m_dirtyLayout = true; - updateAdditionalInfoTextColor(); - update(); - } -} - -KFileItemListWidget::Layout KFileItemListWidget::layout() const -{ - return m_layout; -} - -void KFileItemListWidget::setSupportsItemExpanding(bool supportsItemExpanding) -{ - if (m_supportsItemExpanding != supportsItemExpanding) { - m_supportsItemExpanding = supportsItemExpanding; - m_dirtyLayout = true; - update(); - } -} - -bool KFileItemListWidget::supportsItemExpanding() const -{ - return m_supportsItemExpanding; -} - -void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) -{ - const_cast(this)->triggerCacheRefreshing(); - - KItemListWidget::paint(painter, option, widget); - - if (!m_expansionArea.isEmpty()) { - drawSiblingsInformation(painter); - } - - const KItemListStyleOption& itemListStyleOption = styleOption(); - if (isHovered()) { - // Blend the unhovered and hovered pixmap if the hovering - // animation is ongoing - if (hoverOpacity() < 1.0) { - drawPixmap(painter, m_pixmap); - } - - const qreal opacity = painter->opacity(); - painter->setOpacity(hoverOpacity() * opacity); - drawPixmap(painter, m_hoverPixmap); - painter->setOpacity(opacity); - } else { - drawPixmap(painter, m_pixmap); - } - - painter->setFont(itemListStyleOption.font); - painter->setPen(textColor()); - const TextInfo* textInfo = m_textInfo.value("name"); - painter->drawStaticText(textInfo->pos, textInfo->staticText); - - bool clipAdditionalInfoBounds = false; - if (m_supportsItemExpanding) { - // Prevent a possible overlapping of the additional-information texts - // with the icon. This can happen if the user has minimized the width - // of the name-column to a very small value. - const qreal minX = m_pixmapPos.x() + m_pixmap.width() + 4 * itemListStyleOption.padding; - if (textInfo->pos.x() + columnWidth("name") > minX) { - clipAdditionalInfoBounds = true; - painter->save(); - painter->setClipRect(minX, 0, size().width() - minX, size().height(), Qt::IntersectClip); - } - } - - painter->setPen(m_additionalInfoTextColor); - painter->setFont(itemListStyleOption.font); - - for (int i = 1; i < m_sortedVisibleRoles.count(); ++i) { - const TextInfo* textInfo = m_textInfo.value(m_sortedVisibleRoles[i]); - painter->drawStaticText(textInfo->pos, textInfo->staticText); - } - - if (!m_rating.isNull()) { - const TextInfo* ratingTextInfo = m_textInfo.value("rating"); - QPointF pos = ratingTextInfo->pos; - const Qt::Alignment align = ratingTextInfo->staticText.textOption().alignment(); - if (align & Qt::AlignHCenter) { - pos.rx() += (size().width() - m_rating.width()) / 2; - } - painter->drawPixmap(pos, m_rating); - } - - if (clipAdditionalInfoBounds) { - painter->restore(); - } - -#ifdef KFILEITEMLISTWIDGET_DEBUG - painter->setBrush(Qt::NoBrush); - painter->setPen(Qt::green); - painter->drawRect(m_iconRect); - - painter->setPen(Qt::red); - painter->drawText(QPointF(0, itemListStyleOption.fontMetrics.height()), QString::number(index())); - painter->drawRect(rect()); -#endif -} - -QRectF KFileItemListWidget::iconRect() const -{ - const_cast(this)->triggerCacheRefreshing(); - return m_iconRect; -} - -QRectF KFileItemListWidget::textRect() const -{ - const_cast(this)->triggerCacheRefreshing(); - return m_textRect; -} - -QRectF KFileItemListWidget::textFocusRect() const -{ - // In the compact- and details-layout a larger textRect() is returned to be aligned - // with the iconRect(). This is useful to have a larger selection/hover-area - // when having a quite large icon size but only one line of text. Still the - // focus rectangle should be shown as narrow as possible around the text. - - const_cast(this)->triggerCacheRefreshing(); - - switch (m_layout) { - case CompactLayout: { - QRectF rect = m_textRect; - const TextInfo* topText = m_textInfo.value(m_sortedVisibleRoles.first()); - const TextInfo* bottomText = m_textInfo.value(m_sortedVisibleRoles.last()); - rect.setTop(topText->pos.y()); - rect.setBottom(bottomText->pos.y() + bottomText->staticText.size().height()); - return rect; - } - - case DetailsLayout: { - QRectF rect = m_textRect; - const TextInfo* textInfo = m_textInfo.value(m_sortedVisibleRoles.first()); - rect.setTop(textInfo->pos.y()); - rect.setBottom(textInfo->pos.y() + textInfo->staticText.size().height()); - return rect; - } - - default: - break; - } - - return m_textRect; -} - -QRectF KFileItemListWidget::expansionToggleRect() const -{ - const_cast(this)->triggerCacheRefreshing(); - return m_isExpandable ? m_expansionArea : QRectF(); -} - -QRectF KFileItemListWidget::selectionToggleRect() const -{ - const_cast(this)->triggerCacheRefreshing(); - - const int iconHeight = styleOption().iconSize; - - int toggleSize = KIconLoader::SizeSmall; - if (iconHeight >= KIconLoader::SizeEnormous) { - toggleSize = KIconLoader::SizeMedium; - } else if (iconHeight >= KIconLoader::SizeLarge) { - toggleSize = KIconLoader::SizeSmallMedium; - } - - QPointF pos = iconRect().topLeft(); - - // If the selection toggle has a very small distance to the - // widget borders, the size of the selection toggle will get - // increased to prevent an accidental clicking of the item - // when trying to hit the toggle. - const int widgetHeight = size().height(); - const int widgetWidth = size().width(); - const int minMargin = 2; - - if (toggleSize + minMargin * 2 >= widgetHeight) { - pos.rx() -= (widgetHeight - toggleSize) / 2; - toggleSize = widgetHeight; - pos.setY(0); - } - if (toggleSize + minMargin * 2 >= widgetWidth) { - pos.ry() -= (widgetWidth - toggleSize) / 2; - toggleSize = widgetWidth; - pos.setX(0); - } - - return QRectF(pos, QSizeF(toggleSize, toggleSize)); -} - -QSizeF KFileItemListWidget::itemSizeHint(int index, const KItemListView* view) -{ - const QHash values = view->model()->data(index); - const KItemListStyleOption& option = view->styleOption(); - const int additionalRolesCount = qMax(view->visibleRoles().count() - 1, 0); - - switch (static_cast(view)->itemLayout()) { - case IconsLayout: { - const QString text = KStringHandler::preProcessWrap(values["name"].toString()); - - const qreal itemWidth = view->itemSize().width(); - const qreal maxWidth = itemWidth - 2 * option.padding; - QTextLine line; - - // Calculate the number of lines required for wrapping the name - QTextOption textOption(Qt::AlignHCenter); - textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - - qreal textHeight = 0; - QTextLayout layout(text, option.font); - layout.setTextOption(textOption); - layout.beginLayout(); - while ((line = layout.createLine()).isValid()) { - line.setLineWidth(maxWidth); - line.naturalTextWidth(); - textHeight += line.height(); - } - layout.endLayout(); - - // Add one line for each additional information - textHeight += additionalRolesCount * option.fontMetrics.lineSpacing(); - - const qreal maxTextHeight = option.maxTextSize.height(); - if (maxTextHeight > 0 && textHeight > maxTextHeight) { - textHeight = maxTextHeight; - } - - return QSizeF(itemWidth, textHeight + option.iconSize + option.padding * 3); - } - - case CompactLayout: { - // For each row exactly one role is shown. Calculate the maximum required width that is necessary - // to show all roles without horizontal clipping. - qreal maximumRequiredWidth = 0.0; - - foreach (const QByteArray& role, view->visibleRoles()) { - const QString text = KFileItemListWidget::roleText(role, values); - const qreal requiredWidth = option.fontMetrics.width(text); - maximumRequiredWidth = qMax(maximumRequiredWidth, requiredWidth); - } - - qreal width = option.padding * 4 + option.iconSize + maximumRequiredWidth; - const qreal maxWidth = option.maxTextSize.width(); - if (maxWidth > 0 && width > maxWidth) { - width = maxWidth; - } - const qreal height = option.padding * 2 + qMax(option.iconSize, (1 + additionalRolesCount) * option.fontMetrics.lineSpacing()); - return QSizeF(width, height); - } - - case DetailsLayout: { - const qreal height = option.padding * 2 + qMax(option.iconSize, option.fontMetrics.height()); - return QSizeF(-1, height); - } - - default: - Q_ASSERT(false); - break; - } - - return QSize(); -} - -qreal KFileItemListWidget::preferredRoleColumnWidth(const QByteArray& role, - int index, - const KItemListView* view) -{ - - const QHash values = view->model()->data(index); - const KItemListStyleOption& option = view->styleOption(); - - const QString text = KFileItemListWidget::roleText(role, values); - qreal width = columnPadding(option); - - if (role == "rating") { - width += preferredRatingSize(option).width(); - } else { - width += option.fontMetrics.width(text); - - if (role == "name") { - // Increase the width by the expansion-toggle and the current expansion level - const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt(); - width += option.padding + (expandedParentsCount + 1) * view->itemSize().height() + KIconLoader::SizeSmall; - - // Increase the width by the required space for the icon - width += option.padding * 2 + option.iconSize; - } - } - - return width; -} - -void KFileItemListWidget::invalidateCache() -{ - m_dirtyLayout = true; - m_dirtyContent = true; -} - -void KFileItemListWidget::refreshCache() -{ -} - -void KFileItemListWidget::setTextColor(const QColor& color) -{ - if (color != m_customTextColor) { - m_customTextColor = color; - updateAdditionalInfoTextColor(); - update(); - } -} - -QColor KFileItemListWidget::textColor() const -{ - if (m_customTextColor.isValid() && !isSelected()) { - return m_customTextColor; - } - - const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive; - const QPalette::ColorRole role = isSelected() ? QPalette::HighlightedText : QPalette::Text; - return styleOption().palette.brush(group, role).color(); -} - -void KFileItemListWidget::setOverlay(const QPixmap& overlay) -{ - m_overlay = overlay; - m_dirtyContent = true; - update(); -} - -QPixmap KFileItemListWidget::overlay() const -{ - return m_overlay; -} - -void KFileItemListWidget::dataChanged(const QHash& current, - const QSet& roles) -{ - Q_UNUSED(current); - - m_dirtyContent = true; - - QSet dirtyRoles; - if (roles.isEmpty()) { - dirtyRoles = visibleRoles().toSet(); - dirtyRoles.insert("iconPixmap"); - dirtyRoles.insert("iconName"); - } else { - dirtyRoles = roles; - } - - QSetIterator it(dirtyRoles); - while (it.hasNext()) { - const QByteArray& role = it.next(); - m_dirtyContentRoles.insert(role); - } -} - -void KFileItemListWidget::visibleRolesChanged(const QList& current, - const QList& previous) -{ - Q_UNUSED(previous); - m_sortedVisibleRoles = current; - m_dirtyLayout = true; -} - -void KFileItemListWidget::columnWidthChanged(const QByteArray& role, - qreal current, - qreal previous) -{ - Q_UNUSED(role); - Q_UNUSED(current); - Q_UNUSED(previous); - m_dirtyLayout = true; -} - -void KFileItemListWidget::styleOptionChanged(const KItemListStyleOption& current, - const KItemListStyleOption& previous) -{ - Q_UNUSED(current); - Q_UNUSED(previous); - updateAdditionalInfoTextColor(); - m_dirtyLayout = true; -} - -void KFileItemListWidget::hoveredChanged(bool hovered) -{ - Q_UNUSED(hovered); - m_dirtyLayout = true; -} - -void KFileItemListWidget::selectedChanged(bool selected) -{ - Q_UNUSED(selected); - updateAdditionalInfoTextColor(); -} - -void KFileItemListWidget::siblingsInformationChanged(const QBitArray& current, const QBitArray& previous) -{ - Q_UNUSED(current); - Q_UNUSED(previous); - m_dirtyLayout = true; -} - -void KFileItemListWidget::editedRoleChanged(const QByteArray& current, const QByteArray& previous) -{ - Q_UNUSED(previous); - - QGraphicsView* parent = scene()->views()[0]; - if (current.isEmpty() || !parent || current != "name") { - if (m_roleEditor) { - emit roleEditingCanceled(index(), current, data().value(current)); - m_roleEditor->deleteLater(); - m_roleEditor = 0; - } - return; - } - - Q_ASSERT(!m_roleEditor); - - const TextInfo* textInfo = m_textInfo.value("name"); - - m_roleEditor = new KItemListRoleEditor(parent); - m_roleEditor->setIndex(index()); - m_roleEditor->setRole(current); - - const QString text = data().value(current).toString(); - m_roleEditor->setPlainText(text); - - QTextOption textOption = textInfo->staticText.textOption(); - m_roleEditor->document()->setDefaultTextOption(textOption); - - // Select the text without MIME-type extension - int selectionLength = text.length(); - - const QString extension = KMimeType::extractKnownExtension(text); - if (!extension.isEmpty()) { - selectionLength -= extension.length() + 1; - } - - if (selectionLength > 0) { - QTextCursor cursor = m_roleEditor->textCursor(); - cursor.movePosition(QTextCursor::StartOfBlock); - cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, selectionLength); - m_roleEditor->setTextCursor(cursor); - } - - connect(m_roleEditor, SIGNAL(roleEditingCanceled(int,QByteArray,QVariant)), - this, SLOT(slotRoleEditingCanceled(int,QByteArray,QVariant))); - connect(m_roleEditor, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), - this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant))); - - // Adjust the geometry of the editor - QRectF rect = roleEditingRect(current); - const int frameWidth = m_roleEditor->frameWidth(); - rect.adjust(-frameWidth, -frameWidth, frameWidth, frameWidth); - rect.translate(pos()); - if (rect.right() > parent->width()) { - rect.setWidth(parent->width() - rect.left()); - } - if (rect.bottom() > parent->height()) { - rect.setHeight(parent->height() - rect.top()); - } - m_roleEditor->setGeometry(rect.toRect()); - m_roleEditor->show(); - m_roleEditor->setFocus(); -} - -void KFileItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event) -{ - if (m_roleEditor) { - setEditedRole(QByteArray()); - Q_ASSERT(!m_roleEditor); - } - - KItemListWidget::resizeEvent(event); - - m_dirtyLayout = true; -} - -void KFileItemListWidget::showEvent(QShowEvent* event) -{ - KItemListWidget::showEvent(event); - - // Listen to changes of the clipboard to mark the item as cut/uncut - KFileItemClipboard* clipboard = KFileItemClipboard::instance(); - - const KUrl itemUrl = data().value("url").value(); - m_isCut = clipboard->isCut(itemUrl); - - connect(clipboard, SIGNAL(cutItemsChanged()), - this, SLOT(slotCutItemsChanged())); -} - -void KFileItemListWidget::hideEvent(QHideEvent* event) -{ - disconnect(KFileItemClipboard::instance(), SIGNAL(cutItemsChanged()), - this, SLOT(slotCutItemsChanged())); - - KItemListWidget::hideEvent(event); -} - -void KFileItemListWidget::slotCutItemsChanged() -{ - const KUrl itemUrl = data().value("url").value(); - const bool isCut = KFileItemClipboard::instance()->isCut(itemUrl); - if (m_isCut != isCut) { - m_isCut = isCut; - m_pixmap = QPixmap(); - m_dirtyContent = true; - update(); - } -} - -void KFileItemListWidget::slotRoleEditingCanceled(int index, - const QByteArray& role, - const QVariant& value) -{ - m_roleEditor->deleteLater(); - m_roleEditor = 0; - emit roleEditingCanceled(index, role, value); - setEditedRole(QByteArray()); -} - -void KFileItemListWidget::slotRoleEditingFinished(int index, - const QByteArray& role, - const QVariant& value) -{ - m_roleEditor->deleteLater(); - m_roleEditor = 0; - emit roleEditingFinished(index, role, value); - setEditedRole(QByteArray()); -} - -void KFileItemListWidget::triggerCacheRefreshing() -{ - if ((!m_dirtyContent && !m_dirtyLayout) || index() < 0) { - return; - } - - refreshCache(); - - const QHash values = data(); - m_isExpandable = m_supportsItemExpanding && values["isExpandable"].toBool(); - m_isHidden = values["name"].toString().startsWith(QLatin1Char('.')); - - updateExpansionArea(); - updateTextsCache(); - updatePixmapCache(); - - m_dirtyLayout = false; - m_dirtyContent = false; - m_dirtyContentRoles.clear(); -} - -void KFileItemListWidget::updateExpansionArea() -{ - if (m_supportsItemExpanding) { - const QHash values = data(); - Q_ASSERT(values.contains("expandedParentsCount")); - const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt(); - if (expandedParentsCount >= 0) { - const qreal widgetHeight = size().height(); - const qreal inc = (widgetHeight - KIconLoader::SizeSmall) / 2; - const qreal x = expandedParentsCount * widgetHeight + inc; - const qreal y = inc; - m_expansionArea = QRectF(x, y, KIconLoader::SizeSmall, KIconLoader::SizeSmall); - return; - } - } - - m_expansionArea = QRectF(); -} - -void KFileItemListWidget::updatePixmapCache() -{ - // Precondition: Requires already updated m_textPos values to calculate - // the remaining height when the alignment is vertical. - - const QSizeF widgetSize = size(); - const bool iconOnTop = (m_layout == IconsLayout); - const KItemListStyleOption& option = styleOption(); - const qreal padding = option.padding; - - const int maxIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : option.iconSize; - const int maxIconHeight = option.iconSize; - - const QHash values = data(); - - bool updatePixmap = (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight); - if (!updatePixmap && m_dirtyContent) { - updatePixmap = m_dirtyContentRoles.isEmpty() - || m_dirtyContentRoles.contains("iconPixmap") - || m_dirtyContentRoles.contains("iconName") - || m_dirtyContentRoles.contains("iconOverlays"); - } - - if (updatePixmap) { - m_pixmap = values["iconPixmap"].value(); - if (m_pixmap.isNull()) { - // Use the icon that fits to the MIME-type - QString iconName = values["iconName"].toString(); - if (iconName.isEmpty()) { - // The icon-name has not been not resolved by KFileItemModelRolesUpdater, - // use a generic icon as fallback - iconName = QLatin1String("unknown"); - } - m_pixmap = pixmapForIcon(iconName, maxIconHeight); - } else if (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight) { - // A custom pixmap has been applied. Assure that the pixmap - // is scaled to the maximum available size. - KPixmapModifier::scale(m_pixmap, QSize(maxIconWidth, maxIconHeight)); - } - - const QStringList overlays = values["iconOverlays"].toStringList(); - - // Strangely KFileItem::overlays() returns empty string-values, so - // we need to check first whether an overlay must be drawn at all. - // It is more efficient to do it here, as KIconLoader::drawOverlays() - // assumes that an overlay will be drawn and has some additional - // setup time. - foreach (const QString& overlay, overlays) { - if (!overlay.isEmpty()) { - // There is at least one overlay, draw all overlays above m_pixmap - // and cancel the check - KIconLoader::global()->drawOverlays(overlays, m_pixmap, KIconLoader::Desktop); - break; - } - } - - if (m_isCut) { - applyCutEffect(m_pixmap); - } - - if (m_isHidden) { - applyHiddenEffect(m_pixmap); - } - } - - if (!m_overlay.isNull()) { - QPainter painter(&m_pixmap); - painter.drawPixmap(0, m_pixmap.height() - m_overlay.height(), m_overlay); - } - - int scaledIconSize = 0; - if (iconOnTop) { - const TextInfo* textInfo = m_textInfo.value("name"); - scaledIconSize = static_cast(textInfo->pos.y() - 2 * padding); - } else { - const int textRowsCount = (m_layout == CompactLayout) ? visibleRoles().count() : 1; - const qreal requiredTextHeight = textRowsCount * option.fontMetrics.height(); - scaledIconSize = (requiredTextHeight < maxIconHeight) ? - widgetSize.height() - 2 * padding : maxIconHeight; - } - - const int maxScaledIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : scaledIconSize; - const int maxScaledIconHeight = scaledIconSize; - - m_scaledPixmapSize = m_pixmap.size(); - m_scaledPixmapSize.scale(maxScaledIconWidth, maxScaledIconHeight, Qt::KeepAspectRatio); - - if (iconOnTop) { - // Center horizontally and align on bottom within the icon-area - m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2); - m_pixmapPos.setY(padding + scaledIconSize - m_scaledPixmapSize.height()); - } else { - // Center horizontally and vertically within the icon-area - const TextInfo* textInfo = m_textInfo.value("name"); - m_pixmapPos.setX(textInfo->pos.x() - 2 * padding - - (scaledIconSize + m_scaledPixmapSize.width()) / 2); - m_pixmapPos.setY(padding - + (scaledIconSize - m_scaledPixmapSize.height()) / 2); - } - - m_iconRect = QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize)); - - // Prepare the pixmap that is used when the item gets hovered - if (isHovered()) { - m_hoverPixmap = m_pixmap; - KIconEffect* effect = KIconLoader::global()->iconEffect(); - // In the KIconLoader terminology, active = hover. - if (effect->hasEffect(KIconLoader::Desktop, KIconLoader::ActiveState)) { - m_hoverPixmap = effect->apply(m_pixmap, KIconLoader::Desktop, KIconLoader::ActiveState); - } else { - m_hoverPixmap = m_pixmap; - } - } else if (hoverOpacity() <= 0.0) { - // No hover animation is ongoing. Clear m_hoverPixmap to save memory. - m_hoverPixmap = QPixmap(); - } -} - -void KFileItemListWidget::updateTextsCache() -{ - QTextOption textOption; - switch (m_layout) { - case IconsLayout: - textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - textOption.setAlignment(Qt::AlignHCenter); - break; - case CompactLayout: - case DetailsLayout: - textOption.setAlignment(Qt::AlignLeft); - textOption.setWrapMode(QTextOption::NoWrap); - break; - default: - Q_ASSERT(false); - break; - } - - qDeleteAll(m_textInfo); - m_textInfo.clear(); - for (int i = 0; i < m_sortedVisibleRoles.count(); ++i) { - TextInfo* textInfo = new TextInfo(); - textInfo->staticText.setTextFormat(Qt::PlainText); - textInfo->staticText.setPerformanceHint(QStaticText::AggressiveCaching); - textInfo->staticText.setTextOption(textOption); - m_textInfo.insert(m_sortedVisibleRoles[i], textInfo); - } - - switch (m_layout) { - case IconsLayout: updateIconsLayoutTextCache(); break; - case CompactLayout: updateCompactLayoutTextCache(); break; - case DetailsLayout: updateDetailsLayoutTextCache(); break; - default: Q_ASSERT(false); break; - } - - const TextInfo* ratingTextInfo = m_textInfo.value("rating"); - if (ratingTextInfo) { - // The text of the rating-role has been set to empty to get - // replaced by a rating-image showing the rating as stars. - const KItemListStyleOption& option = styleOption(); - QSizeF ratingSize = preferredRatingSize(option); - - const qreal availableWidth = (m_layout == DetailsLayout) - ? columnWidth("rating") - columnPadding(option) - : m_textRect.width(); - if (ratingSize.width() > availableWidth) { - ratingSize.rwidth() = availableWidth; - } - m_rating = QPixmap(ratingSize.toSize()); - m_rating.fill(Qt::transparent); - - QPainter painter(&m_rating); - const QRect rect(0, 0, m_rating.width(), m_rating.height()); - const int rating = data().value("rating").toInt(); - KRatingPainter::paintRating(&painter, rect, Qt::AlignJustify | Qt::AlignVCenter, rating); - } else if (!m_rating.isNull()) { - m_rating = QPixmap(); - } -} - -void KFileItemListWidget::updateIconsLayoutTextCache() -{ - // +------+ - // | Icon | - // +------+ - // - // Name role that - // might get wrapped above - // several lines. - // Additional role 1 - // Additional role 2 - - const QHash values = data(); - - const KItemListStyleOption& option = styleOption(); - const qreal padding = option.padding; - const qreal maxWidth = size().width() - 2 * padding; - const qreal widgetHeight = size().height(); - const qreal lineSpacing = option.fontMetrics.lineSpacing(); - - // Initialize properties for the "name" role. It will be used as anchor - // for initializing the position of the other roles. - TextInfo* nameTextInfo = m_textInfo.value("name"); - const QString nameText = KStringHandler::preProcessWrap(values["name"].toString()); - nameTextInfo->staticText.setText(nameText); - - // Calculate the number of lines required for the name and the required width - qreal nameWidth = 0; - qreal nameHeight = 0; - QTextLine line; - - const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0); - const int maxNameLines = (option.maxTextSize.height() / int(lineSpacing)) - additionalRolesCount; - - QTextLayout layout(nameTextInfo->staticText.text(), option.font); - layout.setTextOption(nameTextInfo->staticText.textOption()); - layout.beginLayout(); - int nameLineIndex = 0; - while ((line = layout.createLine()).isValid()) { - line.setLineWidth(maxWidth); - nameWidth = qMax(nameWidth, line.naturalTextWidth()); - nameHeight += line.height(); - - ++nameLineIndex; - if (nameLineIndex == maxNameLines) { - // The maximum number of textlines has been reached. If this is - // the case provide an elided text if necessary. - const int textLength = line.textStart() + line.textLength(); - if (textLength < nameText.length()) { - // Elide the last line of the text - QString lastTextLine = nameText.mid(line.textStart(), line.textLength()); - lastTextLine = option.fontMetrics.elidedText(lastTextLine, - Qt::ElideRight, - line.naturalTextWidth() - 1); - const QString elidedText = nameText.left(line.textStart()) + lastTextLine; - nameTextInfo->staticText.setText(elidedText); - } - break; - } - } - layout.endLayout(); - - // Use one line for each additional information - nameTextInfo->staticText.setTextWidth(maxWidth); - nameTextInfo->pos = QPointF(padding, widgetHeight - - nameHeight - - additionalRolesCount * lineSpacing - - padding); - m_textRect = QRectF(padding + (maxWidth - nameWidth) / 2, - nameTextInfo->pos.y(), - nameWidth, - nameHeight); - - // Calculate the position for each additional information - qreal y = nameTextInfo->pos.y() + nameHeight; - foreach (const QByteArray& role, m_sortedVisibleRoles) { - if (role == "name") { - continue; - } - - const QString text = roleText(role, values); - TextInfo* textInfo = m_textInfo.value(role); - textInfo->staticText.setText(text); - - qreal requiredWidth = 0; - - QTextLayout layout(text, option.font); - QTextOption textOption; - textOption.setWrapMode(QTextOption::NoWrap); - layout.setTextOption(textOption); - - layout.beginLayout(); - QTextLine textLine = layout.createLine(); - if (textLine.isValid()) { - textLine.setLineWidth(maxWidth); - requiredWidth = textLine.naturalTextWidth(); - if (requiredWidth > maxWidth) { - const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth); - textInfo->staticText.setText(elidedText); - requiredWidth = option.fontMetrics.width(elidedText); - } - } - layout.endLayout(); - - textInfo->pos = QPointF(padding, y); - textInfo->staticText.setTextWidth(maxWidth); - - const QRectF textRect(padding + (maxWidth - requiredWidth) / 2, y, requiredWidth, lineSpacing); - m_textRect |= textRect; - - y += lineSpacing; - } - - // Add a padding to the text rectangle - m_textRect.adjust(-padding, -padding, padding, padding); -} - -void KFileItemListWidget::updateCompactLayoutTextCache() -{ - // +------+ Name role - // | Icon | Additional role 1 - // +------+ Additional role 2 - - const QHash values = data(); - - const KItemListStyleOption& option = styleOption(); - const qreal widgetHeight = size().height(); - const qreal lineSpacing = option.fontMetrics.lineSpacing(); - const qreal textLinesHeight = qMax(visibleRoles().count(), 1) * lineSpacing; - const int scaledIconSize = (textLinesHeight < option.iconSize) ? widgetHeight - 2 * option.padding : option.iconSize; - - qreal maximumRequiredTextWidth = 0; - const qreal x = option.padding * 3 + scaledIconSize; - qreal y = qRound((widgetHeight - textLinesHeight) / 2); - const qreal maxWidth = size().width() - x - option.padding; - foreach (const QByteArray& role, m_sortedVisibleRoles) { - const QString text = roleText(role, values); - TextInfo* textInfo = m_textInfo.value(role); - textInfo->staticText.setText(text); - - qreal requiredWidth = option.fontMetrics.width(text); - if (requiredWidth > maxWidth) { - requiredWidth = maxWidth; - const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth); - textInfo->staticText.setText(elidedText); - } - - textInfo->pos = QPointF(x, y); - textInfo->staticText.setTextWidth(maxWidth); - - maximumRequiredTextWidth = qMax(maximumRequiredTextWidth, requiredWidth); - - y += lineSpacing; - } - - m_textRect = QRectF(x - option.padding, 0, maximumRequiredTextWidth + 2 * option.padding, widgetHeight); -} - -void KFileItemListWidget::updateDetailsLayoutTextCache() -{ - // Precondition: Requires already updated m_expansionArea - // to determine the left position. - - // +------+ - // | Icon | Name role Additional role 1 Additional role 2 - // +------+ - m_textRect = QRectF(); - - const KItemListStyleOption& option = styleOption(); - const QHash values = data(); - - const qreal widgetHeight = size().height(); - const int scaledIconSize = widgetHeight - 2 * option.padding; - const int fontHeight = option.fontMetrics.height(); - - const qreal columnWidthInc = columnPadding(option); - qreal firstColumnInc = scaledIconSize; - if (m_supportsItemExpanding) { - firstColumnInc += (m_expansionArea.left() + m_expansionArea.right() + widgetHeight) / 2; - } else { - firstColumnInc += option.padding; - } - - qreal x = firstColumnInc; - const qreal y = qMax(qreal(option.padding), (widgetHeight - fontHeight) / 2); - - foreach (const QByteArray& role, m_sortedVisibleRoles) { - const RoleType type = roleType(role); - - QString text = roleText(role, values); - - // Elide the text in case it does not fit into the available column-width - qreal requiredWidth = option.fontMetrics.width(text); - const qreal roleWidth = columnWidth(role); - qreal availableTextWidth = roleWidth - columnWidthInc; - if (type == Name) { - availableTextWidth -= firstColumnInc; - } - - if (requiredWidth > availableTextWidth) { - text = option.fontMetrics.elidedText(text, Qt::ElideRight, availableTextWidth); - requiredWidth = option.fontMetrics.width(text); - } - - TextInfo* textInfo = m_textInfo.value(role); - textInfo->staticText.setText(text); - textInfo->pos = QPointF(x + columnWidthInc / 2, y); - x += roleWidth; - - switch (type) { - case Name: { - const qreal textWidth = option.extendedSelectionRegion - ? size().width() - textInfo->pos.x() - : requiredWidth + 2 * option.padding; - m_textRect = QRectF(textInfo->pos.x() - option.padding, 0, - textWidth, size().height()); - - // The column after the name should always be aligned on the same x-position independent - // from the expansion-level shown in the name column - x -= firstColumnInc; - break; - } - case Size: - // The values for the size should be right aligned - textInfo->pos.rx() += roleWidth - requiredWidth - columnWidthInc; - break; - - default: - break; - } - } -} - -void KFileItemListWidget::updateAdditionalInfoTextColor() -{ - QColor c1; - if (m_customTextColor.isValid()) { - c1 = m_customTextColor; - } else if (isSelected() && m_layout != DetailsLayout) { - c1 = styleOption().palette.highlightedText().color(); - } else { - c1 = styleOption().palette.text().color(); - } - - // For the color of the additional info the inactive text color - // is not used as this might lead to unreadable text for some color schemes. Instead - // the text color c1 is slightly mixed with the background color. - const QColor c2 = styleOption().palette.base().color(); - const int p1 = 70; - const int p2 = 100 - p1; - m_additionalInfoTextColor = QColor((c1.red() * p1 + c2.red() * p2) / 100, - (c1.green() * p1 + c2.green() * p2) / 100, - (c1.blue() * p1 + c2.blue() * p2) / 100); -} - -void KFileItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap) -{ - if (m_scaledPixmapSize != pixmap.size()) { - QPixmap scaledPixmap = pixmap; - KPixmapModifier::scale(scaledPixmap, m_scaledPixmapSize); - painter->drawPixmap(m_pixmapPos, scaledPixmap); - -#ifdef KFILEITEMLISTWIDGET_DEBUG - painter->setPen(Qt::blue); - painter->drawRect(QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize))); -#endif - } else { - painter->drawPixmap(m_pixmapPos, pixmap); - } -} - -void KFileItemListWidget::drawSiblingsInformation(QPainter* painter) -{ - const int siblingSize = size().height(); - const int x = (m_expansionArea.left() + m_expansionArea.right() - siblingSize) / 2; - QRect siblingRect(x, 0, siblingSize, siblingSize); - - QStyleOption option; - bool isItemSibling = true; - - const QBitArray siblings = siblingsInformation(); - for (int i = siblings.count() - 1; i >= 0; --i) { - option.rect = siblingRect; - option.state = siblings.at(i) ? QStyle::State_Sibling : QStyle::State_None; - - if (isItemSibling) { - option.state |= QStyle::State_Item; - if (m_isExpandable) { - option.state |= QStyle::State_Children; - } - if (data()["isExpanded"].toBool()) { - option.state |= QStyle::State_Open; - } - isItemSibling = false; - } - - style()->drawPrimitive(QStyle::PE_IndicatorBranch, &option, painter); - - siblingRect.translate(-siblingRect.width(), 0); - } -} - -QRectF KFileItemListWidget::roleEditingRect(const QByteArray& role) const -{ - const TextInfo* textInfo = m_textInfo.value(role); - if (!textInfo) { - return QRectF(); - } - - QRectF rect(textInfo->pos, textInfo->staticText.size()); - if (m_layout == DetailsLayout) { - rect.setWidth(columnWidth(role) - rect.x()); - } - - return rect; -} - -QPixmap KFileItemListWidget::pixmapForIcon(const QString& name, int size) -{ - const KIcon icon(name); - - int requestedSize; - if (size <= KIconLoader::SizeSmall) { - requestedSize = KIconLoader::SizeSmall; - } else if (size <= KIconLoader::SizeSmallMedium) { - requestedSize = KIconLoader::SizeSmallMedium; - } else if (size <= KIconLoader::SizeMedium) { - requestedSize = KIconLoader::SizeMedium; - } else if (size <= KIconLoader::SizeLarge) { - requestedSize = KIconLoader::SizeLarge; - } else if (size <= KIconLoader::SizeHuge) { - requestedSize = KIconLoader::SizeHuge; - } else if (size <= KIconLoader::SizeEnormous) { - requestedSize = KIconLoader::SizeEnormous; - } else if (size <= KIconLoader::SizeEnormous * 2) { - requestedSize = KIconLoader::SizeEnormous * 2; - } else { - requestedSize = size; - } - - QPixmap pixmap = icon.pixmap(requestedSize, requestedSize); - if (requestedSize != size) { - KPixmapModifier::scale(pixmap, QSize(size, size)); - } - - return pixmap; -} - -void KFileItemListWidget::applyCutEffect(QPixmap& pixmap) -{ - KIconEffect* effect = KIconLoader::global()->iconEffect(); - pixmap = effect->apply(pixmap, KIconLoader::Desktop, KIconLoader::DisabledState); -} - -void KFileItemListWidget::applyHiddenEffect(QPixmap& pixmap) +KFileItemListWidgetInformant::KFileItemListWidgetInformant() : + KStandardItemListWidgetInformant() { - KIconEffect::semiTransparent(pixmap); } -KFileItemListWidget::RoleType KFileItemListWidget::roleType(const QByteArray& role) +KFileItemListWidgetInformant::~KFileItemListWidgetInformant() { - static QHash rolesHash; - if (rolesHash.isEmpty()) { - rolesHash.insert("name", Name); - rolesHash.insert("size", Size); - rolesHash.insert("date", Date); - rolesHash.insert("rating", Rating); - } - - return rolesHash.value(role, Generic); } -QString KFileItemListWidget::roleText(const QByteArray& role, const QHash& values) +QString KFileItemListWidgetInformant::roleText(const QByteArray& role, + const QHash& values) const { QString text; const QVariant roleValue = values.value(role); - switch (roleType(role)) { - case Size: { + // Implementation note: In case if more roles require a custom handling + // use a hash + switch for a linear runtime. + + if (role == "size") { if (values.value("isDir").toBool()) { // The item represents a directory. Show the number of sub directories // instead of the file size of the directory. @@ -1230,41 +59,33 @@ QString KFileItemListWidget::roleText(const QByteArray& role, const QHash(); text = KGlobal::locale()->formatByteSize(size); } - break; - } - - case Date: { + } else if (role == "date") { const QDateTime dateTime = roleValue.toDateTime(); text = KGlobal::locale()->formatDateTime(dateTime); - break; + } else { + text = KStandardItemListWidgetInformant::roleText(role, values); } - case Rating: - // Always use an empty text, as the rating is shown by the image m_rating. - break; - - case Name: - case Generic: - text = roleValue.toString(); - break; + return text; +} - default: - Q_ASSERT(false); - break; - } +KFileItemListWidget::KFileItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) : + KStandardItemListWidget(informant, parent) +{ +} - return text; +KFileItemListWidget::~KFileItemListWidget() +{ } -QSizeF KFileItemListWidget::preferredRatingSize(const KItemListStyleOption& option) +KItemListWidgetInformant* KFileItemListWidget::createInformant() { - const qreal height = option.fontMetrics.ascent(); - return QSizeF(height * 5, height); + return new KFileItemListWidgetInformant(); } -qreal KFileItemListWidget::columnPadding(const KItemListStyleOption& option) +bool KFileItemListWidget::isRoleRightAligned(const QByteArray& role) const { - return option.padding * 6; + return role == "size"; } #include "kfileitemlistwidget.moc" diff --git a/src/kitemviews/kfileitemlistwidget.h b/src/kitemviews/kfileitemlistwidget.h index 33d348bab..4bd375481 100644 --- a/src/kitemviews/kfileitemlistwidget.h +++ b/src/kitemviews/kfileitemlistwidget.h @@ -22,188 +22,30 @@ #include -#include +#include -#include -#include -#include +class LIBDOLPHINPRIVATE_EXPORT KFileItemListWidgetInformant : public KStandardItemListWidgetInformant +{ +public: + KFileItemListWidgetInformant(); + virtual ~KFileItemListWidgetInformant(); -class KItemListRoleEditor; -class KItemListStyleOption; -class KItemListView; +protected: + virtual QString roleText(const QByteArray& role, const QHash& values) const; +}; -class LIBDOLPHINPRIVATE_EXPORT KFileItemListWidget : public KItemListWidget +class LIBDOLPHINPRIVATE_EXPORT KFileItemListWidget : public KStandardItemListWidget { Q_OBJECT public: - enum Layout - { - IconsLayout, - CompactLayout, - DetailsLayout - }; - - KFileItemListWidget(QGraphicsItem* parent); + KFileItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent); virtual ~KFileItemListWidget(); - void setLayout(Layout layout); - Layout layout() const; - - void setSupportsItemExpanding(bool supportsItemExpanding); - bool supportsItemExpanding() const; - - virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); - - virtual QRectF iconRect() const; - virtual QRectF textRect() const; - virtual QRectF textFocusRect() const; - virtual QRectF expansionToggleRect() const; - virtual QRectF selectionToggleRect() const; - - /** - * Implementation of KItemListWidgetCreatorBase::itemSizeHint() when - * using the KItemListWidgetCreator-template. - * - * @see KItemListView - */ - static QSizeF itemSizeHint(int index, const KItemListView* view); - - /** - * Implementation of KItemListWidgetCreatorBase::preferredRoleColumnWidth() when - * using the KItemListWidgetCreator-template. - * - * @see KItemListView - */ - static qreal preferredRoleColumnWidth(const QByteArray& role, int index, const KItemListView* view); + static KItemListWidgetInformant* createInformant(); protected: - /** - * Invalidates the cache which results in calling KFileItemListWidget::refreshCache() as - * soon as the item need to gets repainted. - */ - void invalidateCache(); - - /** - * Is called if the cache got invalidated by KFileItemListWidget::invalidateCache(). - * The default implementation is empty. - */ - virtual void refreshCache(); - - void setTextColor(const QColor& color); - QColor textColor() const; - - void setOverlay(const QPixmap& overlay); - QPixmap overlay() const; - - virtual void dataChanged(const QHash& current, const QSet& roles = QSet()); - virtual void visibleRolesChanged(const QList& current, const QList& previous); - virtual void columnWidthChanged(const QByteArray& role, qreal current, qreal previous); - virtual void styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous); - virtual void hoveredChanged(bool hovered); - virtual void selectedChanged(bool selected); - virtual void siblingsInformationChanged(const QBitArray& current, const QBitArray& previous); - virtual void editedRoleChanged(const QByteArray& current, const QByteArray& previous); - virtual void resizeEvent(QGraphicsSceneResizeEvent* event); - virtual void showEvent(QShowEvent* event); - virtual void hideEvent(QHideEvent* event); - -private slots: - void slotCutItemsChanged(); - void slotRoleEditingCanceled(int index, const QByteArray& role, const QVariant& value); - void slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value); - -private: - /** - * Typedefinitions for roles that require a special handling - * and must be accessible in a fast way. The mapping of a - * QByteArray role to the type is done by KFileItemListWidget::roleType(). - */ - enum RoleType { - Name, - Size, - Date, - Rating, - Generic // Mandatory last entry - }; - - void triggerCacheRefreshing(); - void updateExpansionArea(); - void updatePixmapCache(); - - void updateTextsCache(); - void updateIconsLayoutTextCache(); - void updateCompactLayoutTextCache(); - void updateDetailsLayoutTextCache(); - - void updateAdditionalInfoTextColor(); - - void drawPixmap(QPainter* painter, const QPixmap& pixmap); - void drawSiblingsInformation(QPainter* painter); - - QRectF roleEditingRect(const QByteArray &role) const; - - static QPixmap pixmapForIcon(const QString& name, int size); - static void applyCutEffect(QPixmap& pixmap); - static void applyHiddenEffect(QPixmap& pixmap); - static RoleType roleType(const QByteArray& role); - - /** - * @return Preferred size of the rating-image based on the given - * style-option. The height of the font is taken as - * reference. - */ - static QSizeF preferredRatingSize(const KItemListStyleOption& option); - - /** - * @return Horizontal padding in pixels that is added to the required width of - * a column to display the content. - */ - static qreal columnPadding(const KItemListStyleOption& option); - - /** - * @return Shown string for the role \p role of the item with the values \p values. - */ - static QString roleText(const QByteArray& role, const QHash& values); - -private: - bool m_isCut; - bool m_isHidden; - bool m_isExpandable; - bool m_supportsItemExpanding; - - bool m_dirtyLayout; - bool m_dirtyContent; - QSet m_dirtyContentRoles; - - Layout m_layout; - QPointF m_pixmapPos; - QPixmap m_pixmap; - QSize m_scaledPixmapSize; - - QRectF m_iconRect; // Cache for KItemListWidget::iconRect() - QPixmap m_hoverPixmap; // Cache for modified m_pixmap when hovering the item - - struct TextInfo - { - QPointF pos; - QStaticText staticText; - }; - QHash m_textInfo; - - QRectF m_textRect; - - QList m_sortedVisibleRoles; - - QRectF m_expansionArea; - - QColor m_customTextColor; - QColor m_additionalInfoTextColor; - - QPixmap m_overlay; - QPixmap m_rating; - - KItemListRoleEditor* m_roleEditor; + virtual bool isRoleRightAligned(const QByteArray& role) const; }; #endif diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 8710f4440..ac8fc3bef 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -35,7 +35,7 @@ // #define KFILEITEMMODEL_DEBUG KFileItemModel::KFileItemModel(QObject* parent) : - KItemModelBase("name", parent), + KItemModelBase("text", parent), m_dirLister(0), m_naturalSorting(KGlobalSettings::naturalSorting()), m_sortDirsFirst(true), @@ -77,7 +77,7 @@ KFileItemModel::KFileItemModel(QObject* parent) : resetRoles(); m_requestRole[NameRole] = true; m_requestRole[IsDirRole] = true; - m_roles.insert("name"); + m_roles.insert("text"); m_roles.insert("isDir"); // For slow KIO-slaves like used for searching it makes sense to show results periodically even @@ -253,12 +253,12 @@ int KFileItemModel::indexForKeyboardSearch(const QString& text, int startFromInd { startFromIndex = qMax(0, startFromIndex); for (int i = startFromIndex; i < count(); ++i) { - if (data(i)["name"].toString().startsWith(text, Qt::CaseInsensitive)) { + if (data(i)["text"].toString().startsWith(text, Qt::CaseInsensitive)) { return i; } } for (int i = 0; i < startFromIndex; ++i) { - if (data(i)["name"].toString().startsWith(text, Qt::CaseInsensitive)) { + if (data(i)["text"].toString().startsWith(text, Qt::CaseInsensitive)) { return i; } } @@ -1165,7 +1165,7 @@ QHash KFileItemModel::retrieveData(const KFileItem& item) } if (m_requestRole[NameRole]) { - data.insert("name", item.text()); + data.insert("text", item.text()); } if (m_requestRole[SizeRole]) { @@ -1523,7 +1523,7 @@ QList > KFileItemModel::nameRoleGroups() const continue; } - const QString name = m_itemData.at(i)->values.value("name").toString(); + const QString name = m_itemData.at(i)->values.value("text").toString(); // Use the first character of the name as group indication QChar newFirstChar = name.at(0).toUpper(); @@ -1873,7 +1873,7 @@ const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count) static const RoleInfoMap rolesInfoMap[] = { // | role | roleType | role translation | group translation | requires Nepomuk | requires indexer { 0, NoRole, 0, 0, 0, 0, false, false }, - { "name", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), 0, 0, false, false }, + { "text", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), 0, 0, false, false }, { "size", SizeRole, I18N_NOOP2_NOSTRIP("@label", "Size"), 0, 0, false, false }, { "date", DateRole, I18N_NOOP2_NOSTRIP("@label", "Date"), 0, 0, false, false }, { "type", TypeRole, I18N_NOOP2_NOSTRIP("@label", "Type"), 0, 0, false, false }, diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index b29c76c92..2683336e1 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -108,7 +108,7 @@ public: virtual bool supportsDropping(int index) const; /** @reimp */ - virtual QString roleDescription(const QByteArray& typeForRole) const; + virtual QString roleDescription(const QByteArray& role) const; /** @reimp */ virtual QList > groups() const; diff --git a/src/kitemviews/kitemlistcontainer.cpp b/src/kitemviews/kitemlistcontainer.cpp index 5a485b62c..5500851c8 100644 --- a/src/kitemviews/kitemlistcontainer.cpp +++ b/src/kitemviews/kitemlistcontainer.cpp @@ -67,8 +67,6 @@ void KItemListContainerViewport::wheelEvent(QWheelEvent* event) event->ignore(); } - - KItemListContainer::KItemListContainer(KItemListController* controller, QWidget* parent) : QAbstractScrollArea(parent), m_controller(controller), @@ -77,20 +75,32 @@ KItemListContainer::KItemListContainer(KItemListController* controller, QWidget* { Q_ASSERT(controller); controller->setParent(this); - initialize(); -} -KItemListContainer::KItemListContainer(QWidget* parent) : - QAbstractScrollArea(parent), - m_controller(0), - m_horizontalSmoothScroller(0), - m_verticalSmoothScroller(0) -{ - initialize(); + QGraphicsView* graphicsView = new KItemListContainerViewport(new QGraphicsScene(this), this); + setViewport(graphicsView); + + m_horizontalSmoothScroller = new KItemListSmoothScroller(horizontalScrollBar(), this); + m_verticalSmoothScroller = new KItemListSmoothScroller(verticalScrollBar(), this); + + if (controller->model()) { + slotModelChanged(controller->model(), 0); + } + if (controller->view()) { + slotViewChanged(controller->view(), 0); + } + + connect(controller, SIGNAL(modelChanged(KItemModelBase*,KItemModelBase*)), + this, SLOT(slotModelChanged(KItemModelBase*,KItemModelBase*))); + connect(controller, SIGNAL(viewChanged(KItemListView*,KItemListView*)), + this, SLOT(slotViewChanged(KItemListView*,KItemListView*))); } KItemListContainer::~KItemListContainer() { + // Don't rely on the QObject-order to delete the controller, otherwise + // the QGraphicsScene might get deleted before the view. + delete m_controller; + m_controller = 0; } KItemListController* KItemListContainer::controller() const @@ -98,6 +108,33 @@ KItemListController* KItemListContainer::controller() const return m_controller; } +void KItemListContainer::setEnabledFrame(bool enable) +{ + QGraphicsView* graphicsView = qobject_cast(viewport()); + if (enable) { + setFrameShape(QFrame::StyledPanel); + graphicsView->setPalette(palette()); + graphicsView->viewport()->setAutoFillBackground(true); + } else { + setFrameShape(QFrame::NoFrame); + // Make the background of the container transparent and apply the window-text color + // to the text color, so that enough contrast is given for all color + // schemes + QPalette p = graphicsView->palette(); + p.setColor(QPalette::Active, QPalette::Text, p.color(QPalette::Active, QPalette::WindowText)); + p.setColor(QPalette::Inactive, QPalette::Text, p.color(QPalette::Inactive, QPalette::WindowText)); + p.setColor(QPalette::Disabled, QPalette::Text, p.color(QPalette::Disabled, QPalette::WindowText)); + graphicsView->setPalette(p); + graphicsView->viewport()->setAutoFillBackground(false); + } +} + +bool KItemListContainer::enabledFrame() const +{ + const QGraphicsView* graphicsView = qobject_cast(viewport()); + return graphicsView->autoFillBackground(); +} + void KItemListContainer::keyPressEvent(QKeyEvent* event) { // TODO: We should find a better way to handle the key press events in the view. @@ -355,29 +392,4 @@ void KItemListContainer::updateScrollOffsetScrollBarPolicy() } } -void KItemListContainer::initialize() -{ - if (m_controller) { - if (m_controller->model()) { - slotModelChanged(m_controller->model(), 0); - } - if (m_controller->view()) { - slotViewChanged(m_controller->view(), 0); - } - } else { - m_controller = new KItemListController(this); - } - - connect(m_controller, SIGNAL(modelChanged(KItemModelBase*,KItemModelBase*)), - this, SLOT(slotModelChanged(KItemModelBase*,KItemModelBase*))); - connect(m_controller, SIGNAL(viewChanged(KItemListView*,KItemListView*)), - this, SLOT(slotViewChanged(KItemListView*,KItemListView*))); - - QGraphicsView* graphicsView = new KItemListContainerViewport(new QGraphicsScene(this), this); - setViewport(graphicsView); - - m_horizontalSmoothScroller = new KItemListSmoothScroller(horizontalScrollBar(), this); - m_verticalSmoothScroller = new KItemListSmoothScroller(verticalScrollBar(), this); -} - #include "kitemlistcontainer.moc" diff --git a/src/kitemviews/kitemlistcontainer.h b/src/kitemviews/kitemlistcontainer.h index b41f48a7f..474a9ecc0 100644 --- a/src/kitemviews/kitemlistcontainer.h +++ b/src/kitemviews/kitemlistcontainer.h @@ -37,6 +37,8 @@ class QPropertyAnimation; /** * @brief Provides a QWidget based scrolling view for a KItemListController. * + * The model and view are maintained by the KItemListController. + * * @see KItemListController */ class LIBDOLPHINPRIVATE_EXPORT KItemListContainer : public QAbstractScrollArea @@ -44,12 +46,19 @@ class LIBDOLPHINPRIVATE_EXPORT KItemListContainer : public QAbstractScrollArea Q_OBJECT public: + /** + * @param controller Controller that maintains the model and the view. + * The KItemListContainer takes ownership of the controller + * (the parent will be set to the KItemListContainer). + * @param parent Optional parent widget. + */ explicit KItemListContainer(KItemListController* controller, QWidget* parent = 0); - KItemListContainer(QWidget* parent = 0); virtual ~KItemListContainer(); - KItemListController* controller() const; + void setEnabledFrame(bool enable); + bool enabledFrame() const; + protected: virtual void keyPressEvent(QKeyEvent* event); virtual void showEvent(QShowEvent* event); @@ -66,7 +75,6 @@ private slots: void updateItemOffsetScrollBar(); private: - void initialize(); void updateGeometries(); void updateSmoothScrollers(Qt::Orientation orientation); diff --git a/src/kitemviews/kitemlistcontroller.cpp b/src/kitemviews/kitemlistcontroller.cpp index c0f565b4d..011a3b57d 100644 --- a/src/kitemviews/kitemlistcontroller.cpp +++ b/src/kitemviews/kitemlistcontroller.cpp @@ -41,7 +41,7 @@ #include #include -KItemListController::KItemListController(QObject* parent) : +KItemListController::KItemListController(KItemModelBase* model, KItemListView* view, QObject* parent) : QObject(parent), m_singleClickActivation(KGlobalSettings::singleClick()), m_selectionTogglePressed(false), @@ -65,10 +65,20 @@ KItemListController::KItemListController(QObject* parent) : m_autoActivationTimer->setSingleShot(true); m_autoActivationTimer->setInterval(-1); connect(m_autoActivationTimer, SIGNAL(timeout()), this, SLOT(slotAutoActivationTimeout())); + + setModel(model); + setView(view); } KItemListController::~KItemListController() { + setView(0); + delete m_view; + m_view = 0; + + setModel(0); + delete m_model; + m_model = 0; } void KItemListController::setModel(KItemModelBase* model) @@ -79,6 +89,9 @@ void KItemListController::setModel(KItemModelBase* model) KItemModelBase* oldModel = m_model; m_model = model; + if (m_model) { + m_model->setParent(this); + } if (m_view) { m_view->setModel(m_model); @@ -116,6 +129,7 @@ void KItemListController::setView(KItemListView* view) m_view->setController(this); m_view->setModel(m_model); connect(m_view, SIGNAL(scrollOffsetChanged(qreal,qreal)), this, SLOT(slotViewScrollOffsetChanged(qreal,qreal))); + updateExtendedSelectionRegion(); } emit viewChanged(m_view, oldView); @@ -129,6 +143,7 @@ KItemListView* KItemListController::view() const void KItemListController::setSelectionBehavior(SelectionBehavior behavior) { m_selectionBehavior = behavior; + updateExtendedSelectionRegion(); } KItemListController::SelectionBehavior KItemListController::selectionBehavior() const @@ -1159,4 +1174,16 @@ qreal KItemListController::keyboardAnchorPos(int index) const return 0; } +void KItemListController::updateExtendedSelectionRegion() +{ + if (m_view) { + const bool extend = (m_selectionBehavior != MultiSelection); + KItemListStyleOption option = m_view->styleOption(); + if (option.extendedSelectionRegion != extend) { + option.extendedSelectionRegion = extend; + m_view->setStyleOption(option); + } + } +} + #include "kitemlistcontroller.moc" diff --git a/src/kitemviews/kitemlistcontroller.h b/src/kitemviews/kitemlistcontroller.h index b44fcca3c..db31d50c3 100644 --- a/src/kitemviews/kitemlistcontroller.h +++ b/src/kitemviews/kitemlistcontroller.h @@ -72,7 +72,12 @@ public: MultiSelection }; - KItemListController(QObject* parent = 0); + /** + * @param model Model of the controller. The ownership is passed to the controller. + * @param view View of the controller. The ownership is passed to the controller. + * @param parent Optional parent object. + */ + KItemListController(KItemModelBase* model, KItemListView* view, QObject* parent = 0); virtual ~KItemListController(); void setModel(KItemModelBase* model); @@ -261,6 +266,13 @@ private: */ qreal keyboardAnchorPos(int index) const; + /** + * Dependent on the selection-behavior the extendedSelectionRegion-property + * of the KItemListStyleOption from the view should be adjusted: If no + * rubberband selection is used the property should be enabled. + */ + void updateExtendedSelectionRegion(); + private: bool m_singleClickActivation; bool m_selectionTogglePressed; diff --git a/src/kitemviews/kitemliststyleoption.cpp b/src/kitemviews/kitemliststyleoption.cpp index 36cfeb088..ac2587962 100644 --- a/src/kitemviews/kitemliststyleoption.cpp +++ b/src/kitemviews/kitemliststyleoption.cpp @@ -26,10 +26,10 @@ KItemListStyleOption::KItemListStyleOption() : font(), fontMetrics(QFont()), palette(), - padding(0), - horizontalMargin(0), - verticalMargin(0), - iconSize(KIconLoader::SizeMedium), + padding(-1), + horizontalMargin(-1), + verticalMargin(-1), + iconSize(-1), extendedSelectionRegion(false), maxTextSize() { diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index d53c24589..934edf050 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -22,6 +22,7 @@ #include "kitemlistview.h" +#include #include "kitemlistcontroller.h" #include "kitemlistheader.h" #include "kitemlistselectionmanager.h" @@ -33,8 +34,6 @@ #include "private/kitemlistviewlayouter.h" #include "private/kitemlistviewanimation.h" -#include - #include #include #include @@ -113,91 +112,17 @@ KItemListView::KItemListView(QGraphicsWidget* parent) : KItemListView::~KItemListView() { - delete m_sizeHintResolver; - m_sizeHintResolver = 0; -} - -void KItemListView::setScrollOrientation(Qt::Orientation orientation) -{ - const Qt::Orientation previousOrientation = m_layouter->scrollOrientation(); - if (orientation == previousOrientation) { - return; - } - - m_layouter->setScrollOrientation(orientation); - m_animation->setScrollOrientation(orientation); - m_sizeHintResolver->clearCache(); - - if (m_grouped) { - QMutableHashIterator it (m_visibleGroups); - while (it.hasNext()) { - it.next(); - it.value()->setScrollOrientation(orientation); - } - updateGroupHeaderHeight(); - - } - - doLayout(NoAnimation); - - onScrollOrientationChanged(orientation, previousOrientation); - emit scrollOrientationChanged(orientation, previousOrientation); -} - -Qt::Orientation KItemListView::scrollOrientation() const -{ - return m_layouter->scrollOrientation(); -} + // The group headers are children of the widgets created by + // widgetCreator(). So it is mandatory to delete the group headers + // first. + delete m_groupHeaderCreator; + m_groupHeaderCreator = 0; -void KItemListView::setItemSize(const QSizeF& size) -{ - const QSizeF previousSize = m_itemSize; - if (size == previousSize) { - return; - } + delete m_widgetCreator; + m_widgetCreator = 0; - // Skip animations when the number of rows or columns - // are changed in the grid layout. Although the animation - // engine can handle this usecase, it looks obtrusive. - const bool animate = !changesItemGridLayout(m_layouter->size(), - size, - m_layouter->itemMargin()); - - const bool alternateBackgroundsChanged = (m_visibleRoles.count() > 1) && - (( m_itemSize.isEmpty() && !size.isEmpty()) || - (!m_itemSize.isEmpty() && size.isEmpty())); - - m_itemSize = size; - - if (alternateBackgroundsChanged) { - // For an empty item size alternate backgrounds are drawn if more than - // one role is shown. Assure that the backgrounds for visible items are - // updated when changing the size in this context. - updateAlternateBackgrounds(); - } - - if (size.isEmpty()) { - if (m_headerWidget->automaticColumnResizing()) { - updatePreferredColumnWidths(); - } else { - // Only apply the changed height and respect the header widths - // set by the user - const qreal currentWidth = m_layouter->itemSize().width(); - const QSizeF newSize(currentWidth, size.height()); - m_layouter->setItemSize(newSize); - } - } else { - m_layouter->setItemSize(size); - } - - m_sizeHintResolver->clearCache(); - doLayout(animate ? Animation : NoAnimation); - onItemSizeChanged(size, previousSize); -} - -QSizeF KItemListView::itemSize() const -{ - return m_itemSize; + delete m_sizeHintResolver; + m_sizeHintResolver = 0; } void KItemListView::setScrollOffset(qreal offset) @@ -354,62 +279,39 @@ KItemModelBase* KItemListView::model() const void KItemListView::setWidgetCreator(KItemListWidgetCreatorBase* widgetCreator) { + if (m_widgetCreator) { + delete m_widgetCreator; + } m_widgetCreator = widgetCreator; } KItemListWidgetCreatorBase* KItemListView::widgetCreator() const { + if (!m_widgetCreator) { + m_widgetCreator = defaultWidgetCreator(); + } return m_widgetCreator; } void KItemListView::setGroupHeaderCreator(KItemListGroupHeaderCreatorBase* groupHeaderCreator) { + if (m_groupHeaderCreator) { + delete m_groupHeaderCreator; + } m_groupHeaderCreator = groupHeaderCreator; } KItemListGroupHeaderCreatorBase* KItemListView::groupHeaderCreator() const { + if (!m_groupHeaderCreator) { + m_groupHeaderCreator = defaultGroupHeaderCreator(); + } return m_groupHeaderCreator; } -void KItemListView::setStyleOption(const KItemListStyleOption& option) +QSizeF KItemListView::itemSize() const { - const KItemListStyleOption previousOption = m_styleOption; - m_styleOption = option; - - bool animate = true; - const QSizeF margin(option.horizontalMargin, option.verticalMargin); - if (margin != m_layouter->itemMargin()) { - // Skip animations when the number of rows or columns - // are changed in the grid layout. Although the animation - // engine can handle this usecase, it looks obtrusive. - animate = !changesItemGridLayout(m_layouter->size(), - m_layouter->itemSize(), - margin); - m_layouter->setItemMargin(margin); - } - - if (m_grouped) { - updateGroupHeaderHeight(); - } - - if (animate && previousOption.maxTextSize != option.maxTextSize) { - // Animating a change of the maximum text size just results in expensive - // temporary eliding and clipping operations and does not look good visually. - animate = false; - } - - QHashIterator it(m_visibleItems); - while (it.hasNext()) { - it.next(); - it.value()->setStyleOption(option); - } - - m_sizeHintResolver->clearCache(); - m_layouter->markAsDirty(); - doLayout(animate ? Animation : NoAnimation); - - onStyleOptionChanged(option, previousOption); + return m_itemSize; } const KItemListStyleOption& KItemListView::styleOption() const @@ -519,7 +421,7 @@ int KItemListView::lastVisibleIndex() const QSizeF KItemListView::itemSizeHint(int index) const { - return m_widgetCreator->itemSizeHint(index, this); + return widgetCreator()->itemSizeHint(index, this); } void KItemListView::setSupportsItemExpanding(bool supportsExpanding) @@ -709,6 +611,134 @@ void KItemListView::paint(QPainter* painter, const QStyleOptionGraphicsItem* opt } } +void KItemListView::setItemSize(const QSizeF& size) +{ + const QSizeF previousSize = m_itemSize; + if (size == previousSize) { + return; + } + + // Skip animations when the number of rows or columns + // are changed in the grid layout. Although the animation + // engine can handle this usecase, it looks obtrusive. + const bool animate = !changesItemGridLayout(m_layouter->size(), + size, + m_layouter->itemMargin()); + + const bool alternateBackgroundsChanged = (m_visibleRoles.count() > 1) && + (( m_itemSize.isEmpty() && !size.isEmpty()) || + (!m_itemSize.isEmpty() && size.isEmpty())); + + m_itemSize = size; + + if (alternateBackgroundsChanged) { + // For an empty item size alternate backgrounds are drawn if more than + // one role is shown. Assure that the backgrounds for visible items are + // updated when changing the size in this context. + updateAlternateBackgrounds(); + } + + if (size.isEmpty()) { + if (m_headerWidget->automaticColumnResizing()) { + updatePreferredColumnWidths(); + } else { + // Only apply the changed height and respect the header widths + // set by the user + const qreal currentWidth = m_layouter->itemSize().width(); + const QSizeF newSize(currentWidth, size.height()); + m_layouter->setItemSize(newSize); + } + } else { + m_layouter->setItemSize(size); + } + + m_sizeHintResolver->clearCache(); + doLayout(animate ? Animation : NoAnimation); + onItemSizeChanged(size, previousSize); +} + +void KItemListView::setStyleOption(const KItemListStyleOption& option) +{ + const KItemListStyleOption previousOption = m_styleOption; + m_styleOption = option; + + bool animate = true; + const QSizeF margin(option.horizontalMargin, option.verticalMargin); + if (margin != m_layouter->itemMargin()) { + // Skip animations when the number of rows or columns + // are changed in the grid layout. Although the animation + // engine can handle this usecase, it looks obtrusive. + animate = !changesItemGridLayout(m_layouter->size(), + m_layouter->itemSize(), + margin); + m_layouter->setItemMargin(margin); + } + + if (m_grouped) { + updateGroupHeaderHeight(); + } + + if (animate && previousOption.maxTextSize != option.maxTextSize) { + // Animating a change of the maximum text size just results in expensive + // temporary eliding and clipping operations and does not look good visually. + animate = false; + } + + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + it.value()->setStyleOption(option); + } + + m_sizeHintResolver->clearCache(); + m_layouter->markAsDirty(); + doLayout(animate ? Animation : NoAnimation); + + onStyleOptionChanged(option, previousOption); +} + +void KItemListView::setScrollOrientation(Qt::Orientation orientation) +{ + const Qt::Orientation previousOrientation = m_layouter->scrollOrientation(); + if (orientation == previousOrientation) { + return; + } + + m_layouter->setScrollOrientation(orientation); + m_animation->setScrollOrientation(orientation); + m_sizeHintResolver->clearCache(); + + if (m_grouped) { + QMutableHashIterator it (m_visibleGroups); + while (it.hasNext()) { + it.next(); + it.value()->setScrollOrientation(orientation); + } + updateGroupHeaderHeight(); + + } + + doLayout(NoAnimation); + + onScrollOrientationChanged(orientation, previousOrientation); + emit scrollOrientationChanged(orientation, previousOrientation); +} + +Qt::Orientation KItemListView::scrollOrientation() const +{ + return m_layouter->scrollOrientation(); +} + +KItemListWidgetCreatorBase* KItemListView::defaultWidgetCreator() const +{ + return 0; +} + +KItemListGroupHeaderCreatorBase* KItemListView::defaultGroupHeaderCreator() const +{ + return 0; +} + void KItemListView::initializeItemListWidget(KItemListWidget* item) { Q_UNUSED(item); @@ -728,8 +758,13 @@ void KItemListView::onControllerChanged(KItemListController* current, KItemListC void KItemListView::onModelChanged(KItemModelBase* current, KItemModelBase* previous) { - Q_UNUSED(current); Q_UNUSED(previous); + + m_sizeHintResolver->clearCache(); + const int itemCount = current->count(); + if (itemCount > 0) { + m_sizeHintResolver->itemsInserted(0, itemCount); + } } void KItemListView::onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous) @@ -1205,7 +1240,7 @@ void KItemListView::slotAnimationFinished(QGraphicsWidget* widget, // by m_visibleWidgets and must be deleted manually after the animation has // been finished. recycleGroupHeaderForWidget(itemListWidget); - m_widgetCreator->recycle(itemListWidget); + widgetCreator()->recycle(itemListWidget); break; } @@ -1713,7 +1748,7 @@ void KItemListView::emitOffsetChanges() KItemListWidget* KItemListView::createWidget(int index) { - KItemListWidget* widget = m_widgetCreator->create(this); + KItemListWidget* widget = widgetCreator()->create(this); widget->setFlag(QGraphicsItem::ItemStacksBehindParent); m_visibleItems.insert(index, widget); @@ -1733,7 +1768,7 @@ void KItemListView::recycleWidget(KItemListWidget* widget) m_visibleItems.remove(index); m_visibleCells.remove(index); - m_widgetCreator->recycle(widget); + widgetCreator()->recycle(widget); } void KItemListView::setWidgetIndex(KItemListWidget* widget, int index) @@ -1813,7 +1848,7 @@ void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget) KItemListGroupHeader* groupHeader = m_visibleGroups.value(widget); if (!groupHeader) { - groupHeader = m_groupHeaderCreator->create(this); + groupHeader = groupHeaderCreator()->create(this); groupHeader->setParentItem(widget); m_visibleGroups.insert(widget, groupHeader); connect(widget, SIGNAL(geometryChanged()), this, SLOT(slotGeometryOfGroupHeaderParentChanged())); @@ -1859,7 +1894,7 @@ void KItemListView::recycleGroupHeaderForWidget(KItemListWidget* widget) KItemListGroupHeader* header = m_visibleGroups.value(widget); if (header) { header->setParentItem(0); - m_groupHeaderCreator->recycle(header); + groupHeaderCreator()->recycle(header); m_visibleGroups.remove(widget); disconnect(widget, SIGNAL(geometryChanged()), this, SLOT(slotGeometryOfGroupHeaderParentChanged())); } @@ -1960,6 +1995,7 @@ QHash KItemListView::preferredColumnWidths(const KItemRangeLi // Calculate the preferred column withs for each item and ignore values // smaller than the width for showing the headline unclipped. + const KItemListWidgetCreatorBase* creator = widgetCreator(); int calculatedItemCount = 0; bool maxTimeExceeded = false; foreach (const KItemRange& itemRange, itemRanges) { @@ -1969,7 +2005,7 @@ QHash KItemListView::preferredColumnWidths(const KItemRangeLi for (int i = startIndex; i <= endIndex; ++i) { foreach (const QByteArray& visibleRole, visibleRoles()) { qreal maxWidth = widths.value(visibleRole, 0); - const qreal width = m_widgetCreator->preferredRoleColumnWidth(visibleRole, i, this); + const qreal width = creator->preferredRoleColumnWidth(visibleRole, i, this); maxWidth = qMax(width, maxWidth); widths.insert(visibleRole, maxWidth); } @@ -2071,6 +2107,9 @@ void KItemListView::applyAutomaticColumnWidths() { Q_ASSERT(m_itemSize.isEmpty()); Q_ASSERT(m_headerWidget->automaticColumnResizing()); + if (m_visibleRoles.isEmpty()) { + return; + } // Calculate the maximum size of an item by considering the // visible role sizes and apply them to the layouter. If the diff --git a/src/kitemviews/kitemlistview.h b/src/kitemviews/kitemlistview.h index 13f62f89b..3c47e95c6 100644 --- a/src/kitemviews/kitemlistview.h +++ b/src/kitemviews/kitemlistview.h @@ -42,6 +42,7 @@ class KItemListRubberBand; class KItemListViewAnimation; class KItemListViewLayouter; class KItemListWidget; +class KItemListWidgetInformant; class KItemListWidgetCreatorBase; class KItemListViewCreatorBase; class QTimer; @@ -53,11 +54,8 @@ class QTimer; * a GraphicsItem. Each visible item is represented by a KItemListWidget. * * The created view must be applied to the KItemListController with - * KItemListController::setView(). For showing a custom model it is not - * mandatory to derive from KItemListView, all that is necessary is - * to set a widget-creator that is capable to create KItemListWidgets - * showing the model items. A widget-creator can be set with - * KItemListView::setWidgetCreator(). + * KItemListController::setView() or with the constructor of + * KItemListController. * * @see KItemListWidget * @see KItemModelBase @@ -73,17 +71,6 @@ public: KItemListView(QGraphicsWidget* parent = 0); virtual ~KItemListView(); - /** - * If the scroll-orientation is vertical, the items are ordered - * from top to bottom (= default setting). If the scroll-orientation - * is horizontal, the items are ordered from left to right. - */ - void setScrollOrientation(Qt::Orientation orientation); - Qt::Orientation scrollOrientation() const; - - void setItemSize(const QSizeF& size); - QSizeF itemSize() const; - /** * Offset of the scrollbar that represents the scroll-orientation * (see setScrollOrientation()). @@ -145,17 +132,27 @@ public: * * itemListView->setWidgetCreator(new KItemListWidgetCreator()); * - * Note that the ownership of the widget creator is not transferred to - * the item-list view: One instance of a widget creator might get shared - * by several item-list view instances. + * The ownership of the widget creator is transferred to + * the item-list view. **/ void setWidgetCreator(KItemListWidgetCreatorBase* widgetCreator); KItemListWidgetCreatorBase* widgetCreator() const; + /** + * Sets the creator that creates a group header. Usually it is sufficient + * to implement a custom header widget X derived from KItemListGroupHeader and + * set the creator by: + * + * itemListView->setGroupHeaderCreator(new KItemListGroupHeaderCreator()); + * + * The ownership of the gropup header creator is transferred to + * the item-list view. + **/ void setGroupHeaderCreator(KItemListGroupHeaderCreatorBase* groupHeaderCreator); KItemListGroupHeaderCreatorBase* groupHeaderCreator() const; - void setStyleOption(const KItemListStyleOption& option); + QSizeF itemSize() const; + const KItemListStyleOption& styleOption() const; /** @reimp */ @@ -313,6 +310,33 @@ signals: void roleEditingFinished(int index, const QByteArray& role, const QVariant& value); protected: + void setItemSize(const QSizeF& size); + void setStyleOption(const KItemListStyleOption& option); + + /** + * If the scroll-orientation is vertical, the items are ordered + * from top to bottom (= default setting). If the scroll-orientation + * is horizontal, the items are ordered from left to right. + */ + void setScrollOrientation(Qt::Orientation orientation); + Qt::Orientation scrollOrientation() const; + + /** + * Factory method for creating a default widget-creator. The method will be used + * in case if setWidgetCreator() has not been set by the application. + * @return New instance of the widget-creator that should be used per + * default. + */ + virtual KItemListWidgetCreatorBase* defaultWidgetCreator() const; + + /** + * Factory method for creating a default group-header-creator. The method will be used + * in case if setGroupHeaderCreator() has not been set by the application. + * @return New instance of the group-header-creator that should be used per + * default. + */ + virtual KItemListGroupHeaderCreatorBase* defaultGroupHeaderCreator() const; + /** * Is called when creating a new KItemListWidget instance and allows derived * classes to do a custom initialization. @@ -656,8 +680,8 @@ private: KItemListController* m_controller; KItemModelBase* m_model; QList m_visibleRoles; - KItemListWidgetCreatorBase* m_widgetCreator; - KItemListGroupHeaderCreatorBase* m_groupHeaderCreator; + mutable KItemListWidgetCreatorBase* m_widgetCreator; + mutable KItemListGroupHeaderCreatorBase* m_groupHeaderCreator; KItemListStyleOption m_styleOption; QHash m_visibleItems; @@ -747,18 +771,12 @@ public: /** * @brief Template class for creating KItemListWidgets. - * - * The template class must provide the following two static methods: - * - QSizeF itemSizeHint(int index, const KItemListView* view) - * - preferredRoleColumnWidth(const QByteArray& role, int index, const KItemListView* view) - * Those static methods are used as implementation for - * KItemListWidgetCreatorBase::itemSizeHint() and - * KItemListWidgetCreatorBase::preferedRoleColumnWidth(). */ template class KItemListWidgetCreator : public KItemListWidgetCreatorBase { public: + KItemListWidgetCreator(); virtual ~KItemListWidgetCreator(); virtual KItemListWidget* create(KItemListView* view); @@ -768,11 +786,20 @@ public: virtual qreal preferredRoleColumnWidth(const QByteArray& role, int index, const KItemListView* view) const; +private: + KItemListWidgetInformant* m_informant; }; +template +KItemListWidgetCreator::KItemListWidgetCreator() : + m_informant(T::createInformant()) +{ +} + template KItemListWidgetCreator::~KItemListWidgetCreator() { + delete m_informant; } template @@ -780,7 +807,7 @@ KItemListWidget* KItemListWidgetCreator::create(KItemListView* view) { KItemListWidget* widget = static_cast(popRecycleableWidget()); if (!widget) { - widget = new T(view); + widget = new T(m_informant, view); addCreatedWidget(widget); } return widget; @@ -789,7 +816,7 @@ KItemListWidget* KItemListWidgetCreator::create(KItemListView* view) template QSizeF KItemListWidgetCreator::itemSizeHint(int index, const KItemListView* view) const { - return T::itemSizeHint(index, view); + return m_informant->itemSizeHint(index, view); } template @@ -797,7 +824,7 @@ qreal KItemListWidgetCreator::preferredRoleColumnWidth(const QByteArray& role int index, const KItemListView* view) const { - return T::preferredRoleColumnWidth(role, index, view); + return m_informant->preferredRoleColumnWidth(role, index, view); } /** diff --git a/src/kitemviews/kitemlistwidget.cpp b/src/kitemviews/kitemlistwidget.cpp index 542b781f1..f304aa41d 100644 --- a/src/kitemviews/kitemlistwidget.cpp +++ b/src/kitemviews/kitemlistwidget.cpp @@ -35,8 +35,17 @@ #include #include -KItemListWidget::KItemListWidget(QGraphicsItem* parent) : +KItemListWidgetInformant::KItemListWidgetInformant() +{ +} + +KItemListWidgetInformant::~KItemListWidgetInformant() +{ +} + +KItemListWidget::KItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) : QGraphicsWidget(parent, 0), + m_informant(informant), m_index(-1), m_selected(false), m_current(false), diff --git a/src/kitemviews/kitemlistwidget.h b/src/kitemviews/kitemlistwidget.h index 8a28913b6..fdb07d62e 100644 --- a/src/kitemviews/kitemlistwidget.h +++ b/src/kitemviews/kitemlistwidget.h @@ -32,8 +32,30 @@ #include class KItemListSelectionToggle; +class KItemListView; class QPropertyAnimation; +/** + * @brief Provides information for creating an instance of KItemListWidget. + * + * KItemListView only creates KItemListWidget instances for the visible + * area. For calculating the required size of all items the expected + * size for the invisible items must be accessible. KItemListWidgetInformant + * provides this information. + */ +class LIBDOLPHINPRIVATE_EXPORT KItemListWidgetInformant +{ +public: + KItemListWidgetInformant(); + virtual ~KItemListWidgetInformant(); + + virtual QSizeF itemSizeHint(int index, const KItemListView* view) const = 0; + + virtual qreal preferredRoleColumnWidth(const QByteArray& role, + int index, + const KItemListView* view) const = 0; +}; + /** * @brief Widget that shows a visible item from the model. * @@ -46,7 +68,7 @@ class LIBDOLPHINPRIVATE_EXPORT KItemListWidget : public QGraphicsWidget Q_OBJECT public: - KItemListWidget(QGraphicsItem* parent); + KItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent); virtual ~KItemListWidget(); void setIndex(int index); @@ -176,6 +198,8 @@ protected: */ qreal hoverOpacity() const; + const KItemListWidgetInformant* informant() const; + private slots: void slotHoverAnimationFinished(); @@ -188,6 +212,7 @@ private: private: Q_PROPERTY(qreal hoverOpacity READ hoverOpacity WRITE setHoverOpacity) + KItemListWidgetInformant* m_informant; int m_index; bool m_selected; bool m_current; @@ -208,6 +233,12 @@ private: QByteArray m_editedRole; }; + +inline const KItemListWidgetInformant* KItemListWidget::informant() const +{ + return m_informant; +} + #endif diff --git a/src/kitemviews/kstandarditem.cpp b/src/kitemviews/kstandarditem.cpp new file mode 100644 index 000000000..090746df1 --- /dev/null +++ b/src/kitemviews/kstandarditem.cpp @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2012 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 "kstandarditem.h" + +KStandardItem::KStandardItem(KStandardItem* parent) : + m_text(), + m_icon(), + m_group(), + m_parent(parent), + m_children(), + m_model(0) +{ +} + +KStandardItem::KStandardItem(const QString& text, KStandardItem* parent) : + m_text(text), + m_icon(), + m_group(), + m_parent(parent), + m_children(), + m_model(0) +{ +} + +KStandardItem::KStandardItem(const QIcon& icon, const QString& text, KStandardItem* parent) : + m_text(text), + m_icon(icon), + m_group(), + m_parent(parent), + m_children(), + m_model(0) +{ +} + +KStandardItem::~KStandardItem() +{ +} + +void KStandardItem::setText(const QString& text) +{ + m_text = text; +} + +QString KStandardItem::text() const +{ + return m_text; +} + +void KStandardItem::setIcon(const QIcon& icon) +{ + m_icon = icon; +} + +QIcon KStandardItem::icon() const +{ + return m_icon; +} + +void KStandardItem::setGroup(const QString& group) +{ + m_group = group; +} + +QString KStandardItem::group() const +{ + return m_group; +} + +void KStandardItem::setParent(KStandardItem* parent) +{ + // TODO: not implemented yet + m_parent = parent; +} + +KStandardItem* KStandardItem::parent() const +{ + return m_parent; +} + +QList KStandardItem::children() const +{ + return m_children; +} diff --git a/src/kitemviews/kstandarditem.h b/src/kitemviews/kstandarditem.h new file mode 100644 index 000000000..0315f4bf6 --- /dev/null +++ b/src/kitemviews/kstandarditem.h @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (C) 2012 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 * + ***************************************************************************/ + +#ifndef KSTANDARDITEM_H +#define KSTANDARDITEM_H + +#include + +#include +#include + +class KStandardItemModel; + +/** + * @brief Represents and item of KStandardItemModel. + * + * Provides setter- and getter-methods for most commonly + * used properties. + */ +class LIBDOLPHINPRIVATE_EXPORT KStandardItem +{ + +public: + explicit KStandardItem(KStandardItem* parent = 0); + explicit KStandardItem(const QString& text, KStandardItem* parent = 0); + KStandardItem(const QIcon& icon, const QString& text, KStandardItem* parent = 0); + virtual ~KStandardItem(); + + void setText(const QString& text); + QString text() const; + + void setIcon(const QIcon& icon); + QIcon icon() const; + + void setGroup(const QString& group); + QString group() const; + + void setParent(KStandardItem* parent); + KStandardItem* parent() const; + + QList children() const; + +private: + QString m_text; + QIcon m_icon; + QString m_group; + KStandardItem* m_parent; + QList m_children; + KStandardItemModel* m_model; + + friend class KStandardItemModel; +}; + +#endif + + diff --git a/src/kitemviews/kstandarditemlistview.cpp b/src/kitemviews/kstandarditemlistview.cpp new file mode 100644 index 000000000..bd5da9eb0 --- /dev/null +++ b/src/kitemviews/kstandarditemlistview.cpp @@ -0,0 +1,191 @@ +/*************************************************************************** + * Copyright (C) 2012 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 "kstandarditemlistview.h" + +#include +#include +#include "kstandarditemlistwidget.h" + +KStandardItemListView::KStandardItemListView(QGraphicsWidget* parent) : + KItemListView(parent), + m_itemLayout(DetailsLayout) +{ + setAcceptDrops(true); + setScrollOrientation(Qt::Vertical); + setVisibleRoles(QList() << "text"); +} + +KStandardItemListView::~KStandardItemListView() +{ +} + +void KStandardItemListView::setItemLayout(ItemLayout layout) +{ + if (m_itemLayout == layout) { + return; + } + + beginTransaction(); + + const ItemLayout previous = m_itemLayout; + m_itemLayout = layout; + + switch (layout) { + case IconsLayout: + setScrollOrientation(Qt::Vertical); + setSupportsItemExpanding(false); + break; + case DetailsLayout: + setScrollOrientation(Qt::Vertical); + setSupportsItemExpanding(true); + break; + case CompactLayout: + setScrollOrientation(Qt::Horizontal); + setSupportsItemExpanding(false); + break; + default: + Q_ASSERT(false); + break; + } + + onItemLayoutChanged(layout, previous); + + endTransaction(); +} + +KStandardItemListView::ItemLayout KStandardItemListView::itemLayout() const +{ + return m_itemLayout; +} + +KItemListWidgetCreatorBase* KStandardItemListView::defaultWidgetCreator() const +{ + return new KItemListWidgetCreator(); +} + +KItemListGroupHeaderCreatorBase* KStandardItemListView::defaultGroupHeaderCreator() const +{ + return 0; // TODO: new KItemListGroupHeaderCreator() +} + +void KStandardItemListView::initializeItemListWidget(KItemListWidget* item) +{ + KStandardItemListWidget* standardItemListWidget = qobject_cast(item); + Q_ASSERT(standardItemListWidget); + + switch (itemLayout()) { + case IconsLayout: standardItemListWidget->setLayout(KStandardItemListWidget::IconsLayout); break; + case CompactLayout: standardItemListWidget->setLayout(KStandardItemListWidget::CompactLayout); break; + case DetailsLayout: standardItemListWidget->setLayout(KStandardItemListWidget::DetailsLayout); break; + default: Q_ASSERT(false); break; + } + + standardItemListWidget->setSupportsItemExpanding(supportsItemExpanding()); +} + + +bool KStandardItemListView::itemSizeHintUpdateRequired(const QSet& changedRoles) const +{ + // Even if the icons have a different size they are always aligned within + // the area defined by KItemStyleOption.iconSize and hence result in no + // change of the item-size. + const bool containsIconName = changedRoles.contains("iconName"); + const bool containsIconPixmap = changedRoles.contains("iconPixmap"); + const int count = changedRoles.count(); + + const bool iconChanged = (containsIconName && containsIconPixmap && count == 2) || + (containsIconName && count == 1) || + (containsIconPixmap && count == 1); + return !iconChanged; +} + +void KStandardItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + updateLayoutOfVisibleItems(); +} + +void KStandardItemListView::onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + updateLayoutOfVisibleItems(); +} + +void KStandardItemListView::onSupportsItemExpandingChanged(bool supportsExpanding) +{ + Q_UNUSED(supportsExpanding); + updateLayoutOfVisibleItems(); +} + + +void KStandardItemListView::polishEvent() +{ + switch (m_itemLayout) { + case IconsLayout: applyDefaultStyleOption(KIconLoader::SizeMedium, 2, 4, 8); break; + case CompactLayout: applyDefaultStyleOption(KIconLoader::SizeSmall, 2, 8, 0); break; + case DetailsLayout: applyDefaultStyleOption(KIconLoader::SizeSmall, 2, 0, 0); break; + default: Q_ASSERT(false); break; + } + + QGraphicsWidget::polishEvent(); +} + +void KStandardItemListView::applyDefaultStyleOption(int iconSize, + int padding, + int horizontalMargin, + int verticalMargin) +{ + KItemListStyleOption option = styleOption(); + + bool changed = false; + if (option.iconSize < 0) { + option.iconSize = iconSize; + changed = true; + } + if (option.padding < 0) { + option.padding = padding; + changed = true; + } + if (option.horizontalMargin < 0) { + option.horizontalMargin = horizontalMargin; + changed = true; + } + if (option.verticalMargin < 0) { + option.verticalMargin = verticalMargin; + changed = true; + } + + if (changed) { + setStyleOption(option); + } +} + +void KStandardItemListView::updateLayoutOfVisibleItems() +{ + if (model()) { + foreach (KItemListWidget* widget, visibleItemListWidgets()) { + initializeItemListWidget(widget); + } + } +} + +#include "kstandarditemlistview.moc" diff --git a/src/kitemviews/kstandarditemlistview.h b/src/kitemviews/kstandarditemlistview.h new file mode 100644 index 000000000..fd4fa861c --- /dev/null +++ b/src/kitemviews/kstandarditemlistview.h @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (C) 2012 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 * + ***************************************************************************/ + +#ifndef KSTANDARDITEMLISTVIEW_H +#define KSTANDARDITEMLISTVIEW_H + +#include + +#include + +/** + * @brief Provides layouts for icons-, compact- and details-view. + * + * Together with the KStandardItemModel lists for standard usecases + * can be created in a straight forward way. + * + * Example code: + * + * KStandardItemListView* view = new KStandardItemListView(); + * KStandardItemModel* model = new KStandardItemModel(); + * model->appendItem(new KStandardItem("Item 1")); + * model->appendItem(new KStandardItem("Item 2")); + * KItemListController* controller = new KItemListController(model, view); + * KItemListContainer* container = new KItemListContainer(controller, parentWidget); + * + */ +class LIBDOLPHINPRIVATE_EXPORT KStandardItemListView : public KItemListView +{ + Q_OBJECT + +public: + enum ItemLayout + { + IconsLayout, + CompactLayout, + DetailsLayout + }; + + KStandardItemListView(QGraphicsWidget* parent = 0); + virtual ~KStandardItemListView(); + + void setItemLayout(ItemLayout layout); + ItemLayout itemLayout() const; + +protected: + virtual KItemListWidgetCreatorBase* defaultWidgetCreator() const; + virtual KItemListGroupHeaderCreatorBase* defaultGroupHeaderCreator() const; + virtual void initializeItemListWidget(KItemListWidget* item); + virtual bool itemSizeHintUpdateRequired(const QSet& changedRoles) const; + virtual void onItemLayoutChanged(ItemLayout current, ItemLayout previous); + virtual void onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous); + virtual void onSupportsItemExpandingChanged(bool supportsExpanding); + virtual void polishEvent(); + +private: + void applyDefaultStyleOption(int iconSize, int padding, int horizontalMargin, int verticalMargin); + void updateLayoutOfVisibleItems(); + +private: + ItemLayout m_itemLayout; +}; + +#endif + + diff --git a/src/kitemviews/kstandarditemlistwidget.cpp b/src/kitemviews/kstandarditemlistwidget.cpp new file mode 100644 index 000000000..c9d9b4271 --- /dev/null +++ b/src/kitemviews/kstandarditemlistwidget.cpp @@ -0,0 +1,1234 @@ +/*************************************************************************** + * Copyright (C) 2012 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 "kstandarditemlistwidget.h" + +#include "kfileitemlistview.h" +#include "kfileitemmodel.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "private/kfileitemclipboard.h" +#include "private/kitemlistroleeditor.h" +#include "private/kpixmapmodifier.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +// #define KFILEITEMLISTWIDGET_DEBUG + +KStandardItemListWidgetInformant::KStandardItemListWidgetInformant() : + KItemListWidgetInformant() +{ +} + +KStandardItemListWidgetInformant::~KStandardItemListWidgetInformant() +{ +} + +QSizeF KStandardItemListWidgetInformant::itemSizeHint(int index, const KItemListView* view) const +{ + const QHash values = view->model()->data(index); + const KItemListStyleOption& option = view->styleOption(); + const int additionalRolesCount = qMax(view->visibleRoles().count() - 1, 0); + + switch (static_cast(view)->itemLayout()) { + case KStandardItemListWidget::IconsLayout: { + const QString text = KStringHandler::preProcessWrap(values["text"].toString()); + + const qreal itemWidth = view->itemSize().width(); + const qreal maxWidth = itemWidth - 2 * option.padding; + QTextLine line; + + // Calculate the number of lines required for wrapping the name + QTextOption textOption(Qt::AlignHCenter); + textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + + qreal textHeight = 0; + QTextLayout layout(text, option.font); + layout.setTextOption(textOption); + layout.beginLayout(); + while ((line = layout.createLine()).isValid()) { + line.setLineWidth(maxWidth); + line.naturalTextWidth(); + textHeight += line.height(); + } + layout.endLayout(); + + // Add one line for each additional information + textHeight += additionalRolesCount * option.fontMetrics.lineSpacing(); + + const qreal maxTextHeight = option.maxTextSize.height(); + if (maxTextHeight > 0 && textHeight > maxTextHeight) { + textHeight = maxTextHeight; + } + + return QSizeF(itemWidth, textHeight + option.iconSize + option.padding * 3); + } + + case KStandardItemListWidget::CompactLayout: { + // For each row exactly one role is shown. Calculate the maximum required width that is necessary + // to show all roles without horizontal clipping. + qreal maximumRequiredWidth = 0.0; + + foreach (const QByteArray& role, view->visibleRoles()) { + const QString text = roleText(role, values); + const qreal requiredWidth = option.fontMetrics.width(text); + maximumRequiredWidth = qMax(maximumRequiredWidth, requiredWidth); + } + + qreal width = option.padding * 4 + option.iconSize + maximumRequiredWidth; + const qreal maxWidth = option.maxTextSize.width(); + if (maxWidth > 0 && width > maxWidth) { + width = maxWidth; + } + const qreal height = option.padding * 2 + qMax(option.iconSize, (1 + additionalRolesCount) * option.fontMetrics.lineSpacing()); + return QSizeF(width, height); + } + + case KStandardItemListWidget::DetailsLayout: { + const qreal height = option.padding * 2 + qMax(option.iconSize, option.fontMetrics.height()); + return QSizeF(-1, height); + } + + default: + Q_ASSERT(false); + break; + } + + return QSize(); +} + +qreal KStandardItemListWidgetInformant::preferredRoleColumnWidth(const QByteArray& role, + int index, + const KItemListView* view) const +{ + const QHash values = view->model()->data(index); + const KItemListStyleOption& option = view->styleOption(); + + const QString text = roleText(role, values); + qreal width = KStandardItemListWidget::columnPadding(option); + + if (role == "rating") { + width += KStandardItemListWidget::preferredRatingSize(option).width(); + } else { + width += option.fontMetrics.width(text); + + if (role == "text") { + // Increase the width by the expansion-toggle and the current expansion level + const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt(); + width += option.padding + (expandedParentsCount + 1) * view->itemSize().height() + KIconLoader::SizeSmall; + + // Increase the width by the required space for the icon + width += option.padding * 2 + option.iconSize; + } + } + + return width; +} + +QString KStandardItemListWidgetInformant::roleText(const QByteArray& role, + const QHash& values) const +{ + if (role == "rating") { + // Always use an empty text, as the rating is shown by the image m_rating. + return QString(); + } + return values.value(role).toString(); +} + +KStandardItemListWidget::KStandardItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) : + KItemListWidget(informant, parent), + m_isCut(false), + m_isHidden(false), + m_isExpandable(false), + m_supportsItemExpanding(false), + m_dirtyLayout(true), + m_dirtyContent(true), + m_dirtyContentRoles(), + m_layout(IconsLayout), + m_pixmapPos(), + m_pixmap(), + m_scaledPixmapSize(), + m_iconRect(), + m_hoverPixmap(), + m_textInfo(), + m_textRect(), + m_sortedVisibleRoles(), + m_expansionArea(), + m_customTextColor(), + m_additionalInfoTextColor(), + m_overlay(), + m_rating(), + m_roleEditor(0) +{ +} + +KStandardItemListWidget::~KStandardItemListWidget() +{ + qDeleteAll(m_textInfo); + m_textInfo.clear(); + + delete m_roleEditor; +} + +void KStandardItemListWidget::setLayout(Layout layout) +{ + if (m_layout != layout) { + m_layout = layout; + m_dirtyLayout = true; + updateAdditionalInfoTextColor(); + update(); + } +} + +KStandardItemListWidget::Layout KStandardItemListWidget::layout() const +{ + return m_layout; +} + +void KStandardItemListWidget::setSupportsItemExpanding(bool supportsItemExpanding) +{ + if (m_supportsItemExpanding != supportsItemExpanding) { + m_supportsItemExpanding = supportsItemExpanding; + m_dirtyLayout = true; + update(); + } +} + +bool KStandardItemListWidget::supportsItemExpanding() const +{ + return m_supportsItemExpanding; +} + +void KStandardItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + const_cast(this)->triggerCacheRefreshing(); + + KItemListWidget::paint(painter, option, widget); + + if (!m_expansionArea.isEmpty()) { + drawSiblingsInformation(painter); + } + + const KItemListStyleOption& itemListStyleOption = styleOption(); + if (isHovered()) { + // Blend the unhovered and hovered pixmap if the hovering + // animation is ongoing + if (hoverOpacity() < 1.0) { + drawPixmap(painter, m_pixmap); + } + + const qreal opacity = painter->opacity(); + painter->setOpacity(hoverOpacity() * opacity); + drawPixmap(painter, m_hoverPixmap); + painter->setOpacity(opacity); + } else { + drawPixmap(painter, m_pixmap); + } + + painter->setFont(itemListStyleOption.font); + painter->setPen(textColor()); + const TextInfo* textInfo = m_textInfo.value("text"); + painter->drawStaticText(textInfo->pos, textInfo->staticText); + + bool clipAdditionalInfoBounds = false; + if (m_supportsItemExpanding) { + // Prevent a possible overlapping of the additional-information texts + // with the icon. This can happen if the user has minimized the width + // of the name-column to a very small value. + const qreal minX = m_pixmapPos.x() + m_pixmap.width() + 4 * itemListStyleOption.padding; + if (textInfo->pos.x() + columnWidth("text") > minX) { + clipAdditionalInfoBounds = true; + painter->save(); + painter->setClipRect(minX, 0, size().width() - minX, size().height(), Qt::IntersectClip); + } + } + + painter->setPen(m_additionalInfoTextColor); + painter->setFont(itemListStyleOption.font); + + for (int i = 1; i < m_sortedVisibleRoles.count(); ++i) { + const TextInfo* textInfo = m_textInfo.value(m_sortedVisibleRoles[i]); + painter->drawStaticText(textInfo->pos, textInfo->staticText); + } + + if (!m_rating.isNull()) { + const TextInfo* ratingTextInfo = m_textInfo.value("rating"); + QPointF pos = ratingTextInfo->pos; + const Qt::Alignment align = ratingTextInfo->staticText.textOption().alignment(); + if (align & Qt::AlignHCenter) { + pos.rx() += (size().width() - m_rating.width()) / 2; + } + painter->drawPixmap(pos, m_rating); + } + + if (clipAdditionalInfoBounds) { + painter->restore(); + } + +#ifdef KFILEITEMLISTWIDGET_DEBUG + painter->setBrush(Qt::NoBrush); + painter->setPen(Qt::green); + painter->drawRect(m_iconRect); + + painter->setPen(Qt::red); + painter->drawText(QPointF(0, itemListStyleOption.fontMetrics.height()), QString::number(index())); + painter->drawRect(rect()); +#endif +} + +QRectF KStandardItemListWidget::iconRect() const +{ + const_cast(this)->triggerCacheRefreshing(); + return m_iconRect; +} + +QRectF KStandardItemListWidget::textRect() const +{ + const_cast(this)->triggerCacheRefreshing(); + return m_textRect; +} + +QRectF KStandardItemListWidget::textFocusRect() const +{ + // In the compact- and details-layout a larger textRect() is returned to be aligned + // with the iconRect(). This is useful to have a larger selection/hover-area + // when having a quite large icon size but only one line of text. Still the + // focus rectangle should be shown as narrow as possible around the text. + + const_cast(this)->triggerCacheRefreshing(); + + switch (m_layout) { + case CompactLayout: { + QRectF rect = m_textRect; + const TextInfo* topText = m_textInfo.value(m_sortedVisibleRoles.first()); + const TextInfo* bottomText = m_textInfo.value(m_sortedVisibleRoles.last()); + rect.setTop(topText->pos.y()); + rect.setBottom(bottomText->pos.y() + bottomText->staticText.size().height()); + return rect; + } + + case DetailsLayout: { + QRectF rect = m_textRect; + const TextInfo* textInfo = m_textInfo.value(m_sortedVisibleRoles.first()); + rect.setTop(textInfo->pos.y()); + rect.setBottom(textInfo->pos.y() + textInfo->staticText.size().height()); + return rect; + } + + default: + break; + } + + return m_textRect; +} + +QRectF KStandardItemListWidget::expansionToggleRect() const +{ + const_cast(this)->triggerCacheRefreshing(); + return m_isExpandable ? m_expansionArea : QRectF(); +} + +QRectF KStandardItemListWidget::selectionToggleRect() const +{ + const_cast(this)->triggerCacheRefreshing(); + + const int iconHeight = styleOption().iconSize; + + int toggleSize = KIconLoader::SizeSmall; + if (iconHeight >= KIconLoader::SizeEnormous) { + toggleSize = KIconLoader::SizeMedium; + } else if (iconHeight >= KIconLoader::SizeLarge) { + toggleSize = KIconLoader::SizeSmallMedium; + } + + QPointF pos = iconRect().topLeft(); + + // If the selection toggle has a very small distance to the + // widget borders, the size of the selection toggle will get + // increased to prevent an accidental clicking of the item + // when trying to hit the toggle. + const int widgetHeight = size().height(); + const int widgetWidth = size().width(); + const int minMargin = 2; + + if (toggleSize + minMargin * 2 >= widgetHeight) { + pos.rx() -= (widgetHeight - toggleSize) / 2; + toggleSize = widgetHeight; + pos.setY(0); + } + if (toggleSize + minMargin * 2 >= widgetWidth) { + pos.ry() -= (widgetWidth - toggleSize) / 2; + toggleSize = widgetWidth; + pos.setX(0); + } + + return QRectF(pos, QSizeF(toggleSize, toggleSize)); +} + +KItemListWidgetInformant* KStandardItemListWidget::createInformant() +{ + return new KStandardItemListWidgetInformant(); +} + +void KStandardItemListWidget::invalidateCache() +{ + m_dirtyLayout = true; + m_dirtyContent = true; +} + +void KStandardItemListWidget::refreshCache() +{ +} + +bool KStandardItemListWidget::isRoleRightAligned(const QByteArray& role) const +{ + Q_UNUSED(role); + return false; +} + +void KStandardItemListWidget::setTextColor(const QColor& color) +{ + if (color != m_customTextColor) { + m_customTextColor = color; + updateAdditionalInfoTextColor(); + update(); + } +} + +QColor KStandardItemListWidget::textColor() const +{ + if (m_customTextColor.isValid() && !isSelected()) { + return m_customTextColor; + } + + const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive; + const QPalette::ColorRole role = isSelected() ? QPalette::HighlightedText : QPalette::Text; + return styleOption().palette.brush(group, role).color(); +} + +void KStandardItemListWidget::setOverlay(const QPixmap& overlay) +{ + m_overlay = overlay; + m_dirtyContent = true; + update(); +} + +QPixmap KStandardItemListWidget::overlay() const +{ + return m_overlay; +} + + +QString KStandardItemListWidget::roleText(const QByteArray& role, + const QHash& values) const +{ + return static_cast(informant())->roleText(role, values); +} + +void KStandardItemListWidget::dataChanged(const QHash& current, + const QSet& roles) +{ + Q_UNUSED(current); + + m_dirtyContent = true; + + QSet dirtyRoles; + if (roles.isEmpty()) { + dirtyRoles = visibleRoles().toSet(); + dirtyRoles.insert("iconPixmap"); + dirtyRoles.insert("iconName"); + } else { + dirtyRoles = roles; + } + + QSetIterator it(dirtyRoles); + while (it.hasNext()) { + const QByteArray& role = it.next(); + m_dirtyContentRoles.insert(role); + } +} + +void KStandardItemListWidget::visibleRolesChanged(const QList& current, + const QList& previous) +{ + Q_UNUSED(previous); + m_sortedVisibleRoles = current; + m_dirtyLayout = true; +} + +void KStandardItemListWidget::columnWidthChanged(const QByteArray& role, + qreal current, + qreal previous) +{ + Q_UNUSED(role); + Q_UNUSED(current); + Q_UNUSED(previous); + m_dirtyLayout = true; +} + +void KStandardItemListWidget::styleOptionChanged(const KItemListStyleOption& current, + const KItemListStyleOption& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + updateAdditionalInfoTextColor(); + m_dirtyLayout = true; +} + +void KStandardItemListWidget::hoveredChanged(bool hovered) +{ + Q_UNUSED(hovered); + m_dirtyLayout = true; +} + +void KStandardItemListWidget::selectedChanged(bool selected) +{ + Q_UNUSED(selected); + updateAdditionalInfoTextColor(); +} + +void KStandardItemListWidget::siblingsInformationChanged(const QBitArray& current, const QBitArray& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + m_dirtyLayout = true; +} + +void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const QByteArray& previous) +{ + Q_UNUSED(previous); + + QGraphicsView* parent = scene()->views()[0]; + if (current.isEmpty() || !parent || current != "text") { + if (m_roleEditor) { + emit roleEditingCanceled(index(), current, data().value(current)); + m_roleEditor->deleteLater(); + m_roleEditor = 0; + } + return; + } + + Q_ASSERT(!m_roleEditor); + + const TextInfo* textInfo = m_textInfo.value("text"); + + m_roleEditor = new KItemListRoleEditor(parent); + m_roleEditor->setIndex(index()); + m_roleEditor->setRole(current); + + const QString text = data().value(current).toString(); + m_roleEditor->setPlainText(text); + + QTextOption textOption = textInfo->staticText.textOption(); + m_roleEditor->document()->setDefaultTextOption(textOption); + + // Select the text without MIME-type extension + int selectionLength = text.length(); + + const QString extension = KMimeType::extractKnownExtension(text); + if (!extension.isEmpty()) { + selectionLength -= extension.length() + 1; + } + + if (selectionLength > 0) { + QTextCursor cursor = m_roleEditor->textCursor(); + cursor.movePosition(QTextCursor::StartOfBlock); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, selectionLength); + m_roleEditor->setTextCursor(cursor); + } + + connect(m_roleEditor, SIGNAL(roleEditingCanceled(int,QByteArray,QVariant)), + this, SLOT(slotRoleEditingCanceled(int,QByteArray,QVariant))); + connect(m_roleEditor, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant))); + + // Adjust the geometry of the editor + QRectF rect = roleEditingRect(current); + const int frameWidth = m_roleEditor->frameWidth(); + rect.adjust(-frameWidth, -frameWidth, frameWidth, frameWidth); + rect.translate(pos()); + if (rect.right() > parent->width()) { + rect.setWidth(parent->width() - rect.left()); + } + m_roleEditor->setGeometry(rect.toRect()); + m_roleEditor->show(); + m_roleEditor->setFocus(); +} + +void KStandardItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event) +{ + if (m_roleEditor) { + setEditedRole(QByteArray()); + Q_ASSERT(!m_roleEditor); + } + + KItemListWidget::resizeEvent(event); + + m_dirtyLayout = true; +} + +void KStandardItemListWidget::showEvent(QShowEvent* event) +{ + KItemListWidget::showEvent(event); + + // Listen to changes of the clipboard to mark the item as cut/uncut + KFileItemClipboard* clipboard = KFileItemClipboard::instance(); + + const KUrl itemUrl = data().value("url").value(); + m_isCut = clipboard->isCut(itemUrl); + + connect(clipboard, SIGNAL(cutItemsChanged()), + this, SLOT(slotCutItemsChanged())); +} + +void KStandardItemListWidget::hideEvent(QHideEvent* event) +{ + disconnect(KFileItemClipboard::instance(), SIGNAL(cutItemsChanged()), + this, SLOT(slotCutItemsChanged())); + + KItemListWidget::hideEvent(event); +} + +void KStandardItemListWidget::slotCutItemsChanged() +{ + const KUrl itemUrl = data().value("url").value(); + const bool isCut = KFileItemClipboard::instance()->isCut(itemUrl); + if (m_isCut != isCut) { + m_isCut = isCut; + m_pixmap = QPixmap(); + m_dirtyContent = true; + update(); + } +} + +void KStandardItemListWidget::slotRoleEditingCanceled(int index, + const QByteArray& role, + const QVariant& value) +{ + m_roleEditor->deleteLater(); + m_roleEditor = 0; + emit roleEditingCanceled(index, role, value); + setEditedRole(QByteArray()); +} + +void KStandardItemListWidget::slotRoleEditingFinished(int index, + const QByteArray& role, + const QVariant& value) +{ + m_roleEditor->deleteLater(); + m_roleEditor = 0; + emit roleEditingFinished(index, role, value); + setEditedRole(QByteArray()); +} + +void KStandardItemListWidget::triggerCacheRefreshing() +{ + if ((!m_dirtyContent && !m_dirtyLayout) || index() < 0) { + return; + } + + refreshCache(); + + const QHash values = data(); + m_isExpandable = m_supportsItemExpanding && values["isExpandable"].toBool(); + m_isHidden = values["text"].toString().startsWith(QLatin1Char('.')); + + updateExpansionArea(); + updateTextsCache(); + updatePixmapCache(); + + m_dirtyLayout = false; + m_dirtyContent = false; + m_dirtyContentRoles.clear(); +} + +void KStandardItemListWidget::updateExpansionArea() +{ + if (m_supportsItemExpanding) { + const QHash values = data(); + Q_ASSERT(values.contains("expandedParentsCount")); + const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt(); + if (expandedParentsCount >= 0) { + const qreal widgetHeight = size().height(); + const qreal inc = (widgetHeight - KIconLoader::SizeSmall) / 2; + const qreal x = expandedParentsCount * widgetHeight + inc; + const qreal y = inc; + m_expansionArea = QRectF(x, y, KIconLoader::SizeSmall, KIconLoader::SizeSmall); + return; + } + } + + m_expansionArea = QRectF(); +} + +void KStandardItemListWidget::updatePixmapCache() +{ + // Precondition: Requires already updated m_textPos values to calculate + // the remaining height when the alignment is vertical. + + const QSizeF widgetSize = size(); + const bool iconOnTop = (m_layout == IconsLayout); + const KItemListStyleOption& option = styleOption(); + const qreal padding = option.padding; + + const int maxIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : option.iconSize; + const int maxIconHeight = option.iconSize; + + const QHash values = data(); + + bool updatePixmap = (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight); + if (!updatePixmap && m_dirtyContent) { + updatePixmap = m_dirtyContentRoles.isEmpty() + || m_dirtyContentRoles.contains("iconPixmap") + || m_dirtyContentRoles.contains("iconName") + || m_dirtyContentRoles.contains("iconOverlays"); + } + + if (updatePixmap) { + m_pixmap = values["iconPixmap"].value(); + if (m_pixmap.isNull()) { + // Use the icon that fits to the MIME-type + QString iconName = values["iconName"].toString(); + if (iconName.isEmpty()) { + // The icon-name has not been not resolved by KFileItemModelRolesUpdater, + // use a generic icon as fallback + iconName = QLatin1String("unknown"); + } + m_pixmap = pixmapForIcon(iconName, maxIconHeight); + } else if (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight) { + // A custom pixmap has been applied. Assure that the pixmap + // is scaled to the maximum available size. + KPixmapModifier::scale(m_pixmap, QSize(maxIconWidth, maxIconHeight)); + } + + const QStringList overlays = values["iconOverlays"].toStringList(); + + // Strangely KFileItem::overlays() returns empty string-values, so + // we need to check first whether an overlay must be drawn at all. + // It is more efficient to do it here, as KIconLoader::drawOverlays() + // assumes that an overlay will be drawn and has some additional + // setup time. + foreach (const QString& overlay, overlays) { + if (!overlay.isEmpty()) { + // There is at least one overlay, draw all overlays above m_pixmap + // and cancel the check + KIconLoader::global()->drawOverlays(overlays, m_pixmap, KIconLoader::Desktop); + break; + } + } + + if (m_isCut) { + applyCutEffect(m_pixmap); + } + + if (m_isHidden) { + applyHiddenEffect(m_pixmap); + } + } + + if (!m_overlay.isNull()) { + QPainter painter(&m_pixmap); + painter.drawPixmap(0, m_pixmap.height() - m_overlay.height(), m_overlay); + } + + int scaledIconSize = 0; + if (iconOnTop) { + const TextInfo* textInfo = m_textInfo.value("text"); + scaledIconSize = static_cast(textInfo->pos.y() - 2 * padding); + } else { + const int textRowsCount = (m_layout == CompactLayout) ? visibleRoles().count() : 1; + const qreal requiredTextHeight = textRowsCount * option.fontMetrics.height(); + scaledIconSize = (requiredTextHeight < maxIconHeight) ? + widgetSize.height() - 2 * padding : maxIconHeight; + } + + const int maxScaledIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : scaledIconSize; + const int maxScaledIconHeight = scaledIconSize; + + m_scaledPixmapSize = m_pixmap.size(); + m_scaledPixmapSize.scale(maxScaledIconWidth, maxScaledIconHeight, Qt::KeepAspectRatio); + + if (iconOnTop) { + // Center horizontally and align on bottom within the icon-area + m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2); + m_pixmapPos.setY(padding + scaledIconSize - m_scaledPixmapSize.height()); + } else { + // Center horizontally and vertically within the icon-area + const TextInfo* textInfo = m_textInfo.value("text"); + m_pixmapPos.setX(textInfo->pos.x() - 2 * padding + - (scaledIconSize + m_scaledPixmapSize.width()) / 2); + m_pixmapPos.setY(padding + + (scaledIconSize - m_scaledPixmapSize.height()) / 2); + } + + m_iconRect = QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize)); + + // Prepare the pixmap that is used when the item gets hovered + if (isHovered()) { + m_hoverPixmap = m_pixmap; + KIconEffect* effect = KIconLoader::global()->iconEffect(); + // In the KIconLoader terminology, active = hover. + if (effect->hasEffect(KIconLoader::Desktop, KIconLoader::ActiveState)) { + m_hoverPixmap = effect->apply(m_pixmap, KIconLoader::Desktop, KIconLoader::ActiveState); + } else { + m_hoverPixmap = m_pixmap; + } + } else if (hoverOpacity() <= 0.0) { + // No hover animation is ongoing. Clear m_hoverPixmap to save memory. + m_hoverPixmap = QPixmap(); + } +} + +void KStandardItemListWidget::updateTextsCache() +{ + QTextOption textOption; + switch (m_layout) { + case IconsLayout: + textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + textOption.setAlignment(Qt::AlignHCenter); + break; + case CompactLayout: + case DetailsLayout: + textOption.setAlignment(Qt::AlignLeft); + textOption.setWrapMode(QTextOption::NoWrap); + break; + default: + Q_ASSERT(false); + break; + } + + qDeleteAll(m_textInfo); + m_textInfo.clear(); + for (int i = 0; i < m_sortedVisibleRoles.count(); ++i) { + TextInfo* textInfo = new TextInfo(); + textInfo->staticText.setTextFormat(Qt::PlainText); + textInfo->staticText.setPerformanceHint(QStaticText::AggressiveCaching); + textInfo->staticText.setTextOption(textOption); + m_textInfo.insert(m_sortedVisibleRoles[i], textInfo); + } + + switch (m_layout) { + case IconsLayout: updateIconsLayoutTextCache(); break; + case CompactLayout: updateCompactLayoutTextCache(); break; + case DetailsLayout: updateDetailsLayoutTextCache(); break; + default: Q_ASSERT(false); break; + } + + const TextInfo* ratingTextInfo = m_textInfo.value("rating"); + if (ratingTextInfo) { + // The text of the rating-role has been set to empty to get + // replaced by a rating-image showing the rating as stars. + const KItemListStyleOption& option = styleOption(); + QSizeF ratingSize = preferredRatingSize(option); + + const qreal availableWidth = (m_layout == DetailsLayout) + ? columnWidth("rating") - columnPadding(option) + : m_textRect.width(); + if (ratingSize.width() > availableWidth) { + ratingSize.rwidth() = availableWidth; + } + m_rating = QPixmap(ratingSize.toSize()); + m_rating.fill(Qt::transparent); + + QPainter painter(&m_rating); + const QRect rect(0, 0, m_rating.width(), m_rating.height()); + const int rating = data().value("rating").toInt(); + KRatingPainter::paintRating(&painter, rect, Qt::AlignJustify | Qt::AlignVCenter, rating); + } else if (!m_rating.isNull()) { + m_rating = QPixmap(); + } +} + +void KStandardItemListWidget::updateIconsLayoutTextCache() +{ + // +------+ + // | Icon | + // +------+ + // + // Name role that + // might get wrapped above + // several lines. + // Additional role 1 + // Additional role 2 + + const QHash values = data(); + + const KItemListStyleOption& option = styleOption(); + const qreal padding = option.padding; + const qreal maxWidth = size().width() - 2 * padding; + const qreal widgetHeight = size().height(); + const qreal lineSpacing = option.fontMetrics.lineSpacing(); + + // Initialize properties for the "text" role. It will be used as anchor + // for initializing the position of the other roles. + TextInfo* nameTextInfo = m_textInfo.value("text"); + const QString nameText = KStringHandler::preProcessWrap(values["text"].toString()); + nameTextInfo->staticText.setText(nameText); + + // Calculate the number of lines required for the name and the required width + qreal nameWidth = 0; + qreal nameHeight = 0; + QTextLine line; + + const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0); + const int maxNameLines = (option.maxTextSize.height() / int(lineSpacing)) - additionalRolesCount; + + QTextLayout layout(nameTextInfo->staticText.text(), option.font); + layout.setTextOption(nameTextInfo->staticText.textOption()); + layout.beginLayout(); + int nameLineIndex = 0; + while ((line = layout.createLine()).isValid()) { + line.setLineWidth(maxWidth); + nameWidth = qMax(nameWidth, line.naturalTextWidth()); + nameHeight += line.height(); + + ++nameLineIndex; + if (nameLineIndex == maxNameLines) { + // The maximum number of textlines has been reached. If this is + // the case provide an elided text if necessary. + const int textLength = line.textStart() + line.textLength(); + if (textLength < nameText.length()) { + // Elide the last line of the text + QString lastTextLine = nameText.mid(line.textStart(), line.textLength()); + lastTextLine = option.fontMetrics.elidedText(lastTextLine, + Qt::ElideRight, + line.naturalTextWidth() - 1); + const QString elidedText = nameText.left(line.textStart()) + lastTextLine; + nameTextInfo->staticText.setText(elidedText); + } + break; + } + } + layout.endLayout(); + + // Use one line for each additional information + nameTextInfo->staticText.setTextWidth(maxWidth); + nameTextInfo->pos = QPointF(padding, widgetHeight - + nameHeight - + additionalRolesCount * lineSpacing - + padding); + m_textRect = QRectF(padding + (maxWidth - nameWidth) / 2, + nameTextInfo->pos.y(), + nameWidth, + nameHeight); + + // Calculate the position for each additional information + qreal y = nameTextInfo->pos.y() + nameHeight; + foreach (const QByteArray& role, m_sortedVisibleRoles) { + if (role == "text") { + continue; + } + + const QString text = roleText(role, values); + TextInfo* textInfo = m_textInfo.value(role); + textInfo->staticText.setText(text); + + qreal requiredWidth = 0; + + QTextLayout layout(text, option.font); + QTextOption textOption; + textOption.setWrapMode(QTextOption::NoWrap); + layout.setTextOption(textOption); + + layout.beginLayout(); + QTextLine textLine = layout.createLine(); + if (textLine.isValid()) { + textLine.setLineWidth(maxWidth); + requiredWidth = textLine.naturalTextWidth(); + if (requiredWidth > maxWidth) { + const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth); + textInfo->staticText.setText(elidedText); + requiredWidth = option.fontMetrics.width(elidedText); + } + } + layout.endLayout(); + + textInfo->pos = QPointF(padding, y); + textInfo->staticText.setTextWidth(maxWidth); + + const QRectF textRect(padding + (maxWidth - requiredWidth) / 2, y, requiredWidth, lineSpacing); + m_textRect |= textRect; + + y += lineSpacing; + } + + // Add a padding to the text rectangle + m_textRect.adjust(-padding, -padding, padding, padding); +} + +void KStandardItemListWidget::updateCompactLayoutTextCache() +{ + // +------+ Name role + // | Icon | Additional role 1 + // +------+ Additional role 2 + + const QHash values = data(); + + const KItemListStyleOption& option = styleOption(); + const qreal widgetHeight = size().height(); + const qreal lineSpacing = option.fontMetrics.lineSpacing(); + const qreal textLinesHeight = qMax(visibleRoles().count(), 1) * lineSpacing; + const int scaledIconSize = (textLinesHeight < option.iconSize) ? widgetHeight - 2 * option.padding : option.iconSize; + + qreal maximumRequiredTextWidth = 0; + const qreal x = option.padding * 3 + scaledIconSize; + qreal y = qRound((widgetHeight - textLinesHeight) / 2); + const qreal maxWidth = size().width() - x - option.padding; + foreach (const QByteArray& role, m_sortedVisibleRoles) { + const QString text = roleText(role, values); + TextInfo* textInfo = m_textInfo.value(role); + textInfo->staticText.setText(text); + + qreal requiredWidth = option.fontMetrics.width(text); + if (requiredWidth > maxWidth) { + requiredWidth = maxWidth; + const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth); + textInfo->staticText.setText(elidedText); + } + + textInfo->pos = QPointF(x, y); + textInfo->staticText.setTextWidth(maxWidth); + + maximumRequiredTextWidth = qMax(maximumRequiredTextWidth, requiredWidth); + + y += lineSpacing; + } + + m_textRect = QRectF(x - option.padding, 0, maximumRequiredTextWidth + 2 * option.padding, widgetHeight); +} + +void KStandardItemListWidget::updateDetailsLayoutTextCache() +{ + // Precondition: Requires already updated m_expansionArea + // to determine the left position. + + // +------+ + // | Icon | Name role Additional role 1 Additional role 2 + // +------+ + m_textRect = QRectF(); + + const KItemListStyleOption& option = styleOption(); + const QHash values = data(); + + const qreal widgetHeight = size().height(); + const int scaledIconSize = widgetHeight - 2 * option.padding; + const int fontHeight = option.fontMetrics.height(); + + const qreal columnWidthInc = columnPadding(option); + qreal firstColumnInc = scaledIconSize; + if (m_supportsItemExpanding) { + firstColumnInc += (m_expansionArea.left() + m_expansionArea.right() + widgetHeight) / 2; + } else { + firstColumnInc += option.padding; + } + + qreal x = firstColumnInc; + const qreal y = qMax(qreal(option.padding), (widgetHeight - fontHeight) / 2); + + foreach (const QByteArray& role, m_sortedVisibleRoles) { + QString text = roleText(role, values); + + // Elide the text in case it does not fit into the available column-width + qreal requiredWidth = option.fontMetrics.width(text); + const qreal roleWidth = columnWidth(role); + qreal availableTextWidth = roleWidth - columnWidthInc; + + const bool isTextRole = (role == "text"); + if (isTextRole) { + availableTextWidth -= firstColumnInc; + } + + if (requiredWidth > availableTextWidth) { + text = option.fontMetrics.elidedText(text, Qt::ElideRight, availableTextWidth); + requiredWidth = option.fontMetrics.width(text); + } + + TextInfo* textInfo = m_textInfo.value(role); + textInfo->staticText.setText(text); + textInfo->pos = QPointF(x + columnWidthInc / 2, y); + x += roleWidth; + + if (isTextRole) { + const qreal textWidth = option.extendedSelectionRegion + ? size().width() - textInfo->pos.x() + : requiredWidth + 2 * option.padding; + m_textRect = QRectF(textInfo->pos.x() - option.padding, 0, + textWidth, size().height()); + + // The column after the name should always be aligned on the same x-position independent + // from the expansion-level shown in the name column + x -= firstColumnInc; + } else if (isRoleRightAligned(role)) { + textInfo->pos.rx() += roleWidth - requiredWidth - columnWidthInc; + } + } +} + +void KStandardItemListWidget::updateAdditionalInfoTextColor() +{ + QColor c1; + if (m_customTextColor.isValid()) { + c1 = m_customTextColor; + } else if (isSelected() && m_layout != DetailsLayout) { + c1 = styleOption().palette.highlightedText().color(); + } else { + c1 = styleOption().palette.text().color(); + } + + // For the color of the additional info the inactive text color + // is not used as this might lead to unreadable text for some color schemes. Instead + // the text color c1 is slightly mixed with the background color. + const QColor c2 = styleOption().palette.base().color(); + const int p1 = 70; + const int p2 = 100 - p1; + m_additionalInfoTextColor = QColor((c1.red() * p1 + c2.red() * p2) / 100, + (c1.green() * p1 + c2.green() * p2) / 100, + (c1.blue() * p1 + c2.blue() * p2) / 100); +} + +void KStandardItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap) +{ + if (m_scaledPixmapSize != pixmap.size()) { + QPixmap scaledPixmap = pixmap; + KPixmapModifier::scale(scaledPixmap, m_scaledPixmapSize); + painter->drawPixmap(m_pixmapPos, scaledPixmap); + +#ifdef KFILEITEMLISTWIDGET_DEBUG + painter->setPen(Qt::blue); + painter->drawRect(QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize))); +#endif + } else { + painter->drawPixmap(m_pixmapPos, pixmap); + } +} + +void KStandardItemListWidget::drawSiblingsInformation(QPainter* painter) +{ + const int siblingSize = size().height(); + const int x = (m_expansionArea.left() + m_expansionArea.right() - siblingSize) / 2; + QRect siblingRect(x, 0, siblingSize, siblingSize); + + QStyleOption option; + bool isItemSibling = true; + + const QBitArray siblings = siblingsInformation(); + for (int i = siblings.count() - 1; i >= 0; --i) { + option.rect = siblingRect; + option.state = siblings.at(i) ? QStyle::State_Sibling : QStyle::State_None; + + if (isItemSibling) { + option.state |= QStyle::State_Item; + if (m_isExpandable) { + option.state |= QStyle::State_Children; + } + if (data()["isExpanded"].toBool()) { + option.state |= QStyle::State_Open; + } + isItemSibling = false; + } + + style()->drawPrimitive(QStyle::PE_IndicatorBranch, &option, painter); + + siblingRect.translate(-siblingRect.width(), 0); + } +} + +QRectF KStandardItemListWidget::roleEditingRect(const QByteArray& role) const +{ + const TextInfo* textInfo = m_textInfo.value(role); + if (!textInfo) { + return QRectF(); + } + + QRectF rect(textInfo->pos, textInfo->staticText.size()); + if (m_layout == DetailsLayout) { + rect.setWidth(columnWidth(role) - rect.x()); + } + + return rect; +} + +QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, int size) +{ + const KIcon icon(name); + + int requestedSize; + if (size <= KIconLoader::SizeSmall) { + requestedSize = KIconLoader::SizeSmall; + } else if (size <= KIconLoader::SizeSmallMedium) { + requestedSize = KIconLoader::SizeSmallMedium; + } else if (size <= KIconLoader::SizeMedium) { + requestedSize = KIconLoader::SizeMedium; + } else if (size <= KIconLoader::SizeLarge) { + requestedSize = KIconLoader::SizeLarge; + } else if (size <= KIconLoader::SizeHuge) { + requestedSize = KIconLoader::SizeHuge; + } else if (size <= KIconLoader::SizeEnormous) { + requestedSize = KIconLoader::SizeEnormous; + } else if (size <= KIconLoader::SizeEnormous * 2) { + requestedSize = KIconLoader::SizeEnormous * 2; + } else { + requestedSize = size; + } + + QPixmap pixmap = icon.pixmap(requestedSize, requestedSize); + if (requestedSize != size) { + KPixmapModifier::scale(pixmap, QSize(size, size)); + } + + return pixmap; +} + +void KStandardItemListWidget::applyCutEffect(QPixmap& pixmap) +{ + KIconEffect* effect = KIconLoader::global()->iconEffect(); + pixmap = effect->apply(pixmap, KIconLoader::Desktop, KIconLoader::DisabledState); +} + +void KStandardItemListWidget::applyHiddenEffect(QPixmap& pixmap) +{ + KIconEffect::semiTransparent(pixmap); +} + +QSizeF KStandardItemListWidget::preferredRatingSize(const KItemListStyleOption& option) +{ + const qreal height = option.fontMetrics.ascent(); + return QSizeF(height * 5, height); +} + +qreal KStandardItemListWidget::columnPadding(const KItemListStyleOption& option) +{ + return option.padding * 6; +} + +#include "kstandarditemlistwidget.moc" diff --git a/src/kitemviews/kstandarditemlistwidget.h b/src/kitemviews/kstandarditemlistwidget.h new file mode 100644 index 000000000..1bd44e2f6 --- /dev/null +++ b/src/kitemviews/kstandarditemlistwidget.h @@ -0,0 +1,218 @@ +/*************************************************************************** + * Copyright (C) 2012 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 * + ***************************************************************************/ + +#ifndef KSTANDARDITEMLISTWIDGET_H +#define KSTANDARDITEMLISTWIDGET_H + +#include + +#include + +#include +#include +#include + +class KItemListRoleEditor; +class KItemListStyleOption; +class KItemListView; + +class LIBDOLPHINPRIVATE_EXPORT KStandardItemListWidgetInformant : public KItemListWidgetInformant +{ +public: + KStandardItemListWidgetInformant(); + virtual ~KStandardItemListWidgetInformant(); + + virtual QSizeF itemSizeHint(int index, const KItemListView* view) const; + + virtual qreal preferredRoleColumnWidth(const QByteArray& role, + int index, + const KItemListView* view) const; +protected: + /** + * @return String representation of the role \a role. The representation of + * a role might depend on other roles, so the values of all roles + * are passed as parameter. + */ + virtual QString roleText(const QByteArray& role, + const QHash& values) const; + + friend class KStandardItemListWidget; // Accesses roleText() +}; + +/** + * @brief Itemlist widget implementation for KStandardItemView and KStandardItemModel. + */ +class LIBDOLPHINPRIVATE_EXPORT KStandardItemListWidget : public KItemListWidget +{ + Q_OBJECT + +public: + enum Layout + { + IconsLayout, + CompactLayout, + DetailsLayout + }; + + KStandardItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent); + virtual ~KStandardItemListWidget(); + + void setLayout(Layout layout); + Layout layout() const; + + void setSupportsItemExpanding(bool supportsItemExpanding); + bool supportsItemExpanding() const; + + virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); + + virtual QRectF iconRect() const; + virtual QRectF textRect() const; + virtual QRectF textFocusRect() const; + virtual QRectF expansionToggleRect() const; + virtual QRectF selectionToggleRect() const; + + static KItemListWidgetInformant* createInformant(); + +protected: + /** + * Invalidates the cache which results in calling KStandardItemListWidget::refreshCache() as + * soon as the item need to gets repainted. + */ + void invalidateCache(); + + /** + * Is called if the cache got invalidated by KStandardItemListWidget::invalidateCache(). + * The default implementation is empty. + */ + virtual void refreshCache(); + + /** + * @return True if the give role should be right aligned when showing it inside a column. + * Per default false is returned. + */ + virtual bool isRoleRightAligned(const QByteArray& role) const; + + void setTextColor(const QColor& color); + QColor textColor() const; + + void setOverlay(const QPixmap& overlay); + QPixmap overlay() const; + + /** + * @see KStandardItemListWidgetInformant::roleText(). + */ + QString roleText(const QByteArray& role, const QHash& values) const; + + virtual void dataChanged(const QHash& current, const QSet& roles = QSet()); + virtual void visibleRolesChanged(const QList& current, const QList& previous); + virtual void columnWidthChanged(const QByteArray& role, qreal current, qreal previous); + virtual void styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous); + virtual void hoveredChanged(bool hovered); + virtual void selectedChanged(bool selected); + virtual void siblingsInformationChanged(const QBitArray& current, const QBitArray& previous); + virtual void editedRoleChanged(const QByteArray& current, const QByteArray& previous); + virtual void resizeEvent(QGraphicsSceneResizeEvent* event); + virtual void showEvent(QShowEvent* event); + virtual void hideEvent(QHideEvent* event); + +private slots: + void slotCutItemsChanged(); + void slotRoleEditingCanceled(int index, const QByteArray& role, const QVariant& value); + void slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value); + +private: + void triggerCacheRefreshing(); + void updateExpansionArea(); + void updatePixmapCache(); + + void updateTextsCache(); + void updateIconsLayoutTextCache(); + void updateCompactLayoutTextCache(); + void updateDetailsLayoutTextCache(); + + void updateAdditionalInfoTextColor(); + + void drawPixmap(QPainter* painter, const QPixmap& pixmap); + void drawSiblingsInformation(QPainter* painter); + + QRectF roleEditingRect(const QByteArray &role) const; + + static QPixmap pixmapForIcon(const QString& name, int size); + static void applyCutEffect(QPixmap& pixmap); + static void applyHiddenEffect(QPixmap& pixmap); + + /** + * @return Preferred size of the rating-image based on the given + * style-option. The height of the font is taken as + * reference. + */ + static QSizeF preferredRatingSize(const KItemListStyleOption& option); + + /** + * @return Horizontal padding in pixels that is added to the required width of + * a column to display the content. + */ + static qreal columnPadding(const KItemListStyleOption& option); + +private: + bool m_isCut; + bool m_isHidden; + bool m_isExpandable; + bool m_supportsItemExpanding; + + bool m_dirtyLayout; + bool m_dirtyContent; + QSet m_dirtyContentRoles; + + Layout m_layout; + QPointF m_pixmapPos; + QPixmap m_pixmap; + QSize m_scaledPixmapSize; + + QRectF m_iconRect; // Cache for KItemListWidget::iconRect() + QPixmap m_hoverPixmap; // Cache for modified m_pixmap when hovering the item + + struct TextInfo + { + QPointF pos; + QStaticText staticText; + }; + QHash m_textInfo; + + QRectF m_textRect; + + QList m_sortedVisibleRoles; + + QRectF m_expansionArea; + + QColor m_customTextColor; + QColor m_additionalInfoTextColor; + + QPixmap m_overlay; + QPixmap m_rating; + + KItemListRoleEditor* m_roleEditor; + + friend class KStandardItemListWidgetInformant; // Accesses private static methods to be able to + // share a common layout calculation +}; + +#endif + + diff --git a/src/kitemviews/kstandarditemmodel.cpp b/src/kitemviews/kstandarditemmodel.cpp new file mode 100644 index 000000000..76b2ad09a --- /dev/null +++ b/src/kitemviews/kstandarditemmodel.cpp @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright (C) 2012 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 "kstandarditemmodel.h" +#include "kstandarditem.h" + +KStandardItemModel::KStandardItemModel(QObject* parent) : + KItemModelBase(parent), + m_items(), + m_indexesForItems() +{ +} + +KStandardItemModel::~KStandardItemModel() +{ +} + +void KStandardItemModel::insertItem(int index, KStandardItem* item) +{ + if (!m_indexesForItems.contains(item) && !item->m_model) { + m_items.insert(index, item); + m_indexesForItems.insert(item, index); + item->m_model = this; + // TODO: no hierarchical items are handled yet + } +} + +void KStandardItemModel::appendItem(KStandardItem *item) +{ + insertItem(m_items.count(), item); +} + +void KStandardItemModel::removeItem(KStandardItem* item) +{ + const int index = m_indexesForItems.value(item, -1); + if (index >= 0) { + m_items.removeAt(index); + m_indexesForItems.remove(item); + delete item; + // TODO: no hierarchical items are handled yet + } +} + +KStandardItem* KStandardItemModel::item(int index) const +{ + if (index < 0 || index >= m_items.count()) { + return 0; + } + return m_items[index]; +} + +int KStandardItemModel::index(const KStandardItem* item) const +{ + return m_indexesForItems.value(item, -1); +} + +int KStandardItemModel::count() const +{ + return m_items.count(); +} + +QHash KStandardItemModel::data(int index) const +{ + // TODO: Ugly hack + QHash values; + const KStandardItem* item = m_items[index]; + values.insert("text", item->text()); + values.insert("iconName", item->icon().name()); + return values; +} + +bool KStandardItemModel::setData(int index, const QHash& values) +{ + Q_UNUSED(values); + if (index < 0 || index >= count()) { + return false; + } + + return true; +} + +QMimeData* KStandardItemModel::createMimeData(const QSet& indexes) const +{ + Q_UNUSED(indexes); + return 0; +} + +int KStandardItemModel::indexForKeyboardSearch(const QString& text, int startFromIndex) const +{ + Q_UNUSED(text); + Q_UNUSED(startFromIndex); + return -1; +} + +bool KStandardItemModel::supportsDropping(int index) const +{ + Q_UNUSED(index); + return false; +} + +QString KStandardItemModel::roleDescription(const QByteArray& role) const +{ + Q_UNUSED(role); + return QString(); +} + +QList > KStandardItemModel::groups() const +{ + return QList >(); +} + +#include "kstandarditemmodel.moc" diff --git a/src/kitemviews/kstandarditemmodel.h b/src/kitemviews/kstandarditemmodel.h new file mode 100644 index 000000000..01d2d4d76 --- /dev/null +++ b/src/kitemviews/kstandarditemmodel.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2012 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 * + ***************************************************************************/ + +#ifndef KSTANDARDITEMMODEL_H +#define KSTANDARDITEMMODEL_H + +#include +#include +#include +#include + +class KStandardItem; + +/** + * @brief Model counterpart for KStandardItemView. + * + * Allows to add items to the model in an easy way by the + * class KStandardItem. + * + * @see KStandardItem + */ +class LIBDOLPHINPRIVATE_EXPORT KStandardItemModel : public KItemModelBase +{ + Q_OBJECT + +public: + explicit KStandardItemModel(QObject* parent = 0); + virtual ~KStandardItemModel(); + + void insertItem(int index, KStandardItem* item); + void appendItem(KStandardItem* item); + void removeItem(KStandardItem* item); + KStandardItem* item(int index) const; + int index(const KStandardItem* item) const; + + virtual int count() const; + virtual QHash data(int index) const; + virtual bool setData(int index, const QHash& values); + virtual QMimeData* createMimeData(const QSet& indexes) const; + virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const; + virtual bool supportsDropping(int index) const; + virtual QString roleDescription(const QByteArray& role) const; + virtual QList > groups() const; + +private: + QList m_items; + QHash m_indexesForItems; +}; + +#endif + + diff --git a/src/panels/folders/folderspanel.cpp b/src/panels/folders/folderspanel.cpp index 40e5eca8d..5e8286cdc 100644 --- a/src/panels/folders/folderspanel.cpp +++ b/src/panels/folders/folderspanel.cpp @@ -48,7 +48,8 @@ FoldersPanel::FoldersPanel(QWidget* parent) : Panel(parent), m_updateCurrentItem(false), - m_controller(0) + m_controller(0), + m_model(0) { setLayoutDirection(Qt::LeftToRight); } @@ -67,7 +68,7 @@ FoldersPanel::~FoldersPanel() void FoldersPanel::setShowHiddenFiles(bool show) { FoldersPanelSettings::setHiddenFilesShown(show); - fileItemModel()->setShowHiddenFiles(show); + m_model->setShowHiddenFiles(show); } bool FoldersPanel::showHiddenFiles() const @@ -88,8 +89,8 @@ bool FoldersPanel::autoScrolling() const void FoldersPanel::rename(const KFileItem& item) { - const int index = fileItemModel()->index(item); - m_controller->view()->editRole(index, "name"); + const int index = m_model->index(item); + m_controller->view()->editRole(index, "text"); } bool FoldersPanel::urlChanged() @@ -115,21 +116,10 @@ void FoldersPanel::showEvent(QShowEvent* event) } if (!m_controller) { - // Postpone the creating of the dir lister to the first show event. - // This assures that no performance and memory overhead is given when the TreeView is not - // used at all (see FoldersPanel::setUrl()). + // Postpone the creating of the controller to the first show event. + // This assures that no performance and memory overhead is given when the folders panel is not + // used at all and stays invisible. KFileItemListView* view = new KFileItemListView(); - view->setWidgetCreator(new KItemListWidgetCreator()); - - KItemListStyleOption styleOption = view->styleOption(); - styleOption.padding = 2; - styleOption.iconSize = KIconLoader::SizeSmall; - styleOption.extendedSelectionRegion = true; - view->setStyleOption(styleOption); - - const qreal itemHeight = qMax(int(KIconLoader::SizeSmall), styleOption.fontMetrics.height()); - view->setItemSize(QSizeF(-1, itemHeight + 2 * styleOption.padding)); - view->setItemLayout(KFileItemListView::DetailsLayout); view->setSupportsItemExpanding(true); // Set the opacity to 0 initially. The opacity will be increased after the loading of the initial tree // has been finished in slotLoadingCompleted(). This prevents an unnecessary animation-mess when @@ -139,17 +129,14 @@ void FoldersPanel::showEvent(QShowEvent* event) connect(view, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant))); - KFileItemModel* model = new KFileItemModel(this); - model->setShowDirectoriesOnly(true); - model->setShowHiddenFiles(FoldersPanelSettings::hiddenFilesShown()); + m_model = new KFileItemModel(this); + m_model->setShowDirectoriesOnly(true); + m_model->setShowHiddenFiles(FoldersPanelSettings::hiddenFilesShown()); // Use a QueuedConnection to give the view the possibility to react first on the // finished loading. - connect(model, SIGNAL(directoryLoadingCompleted()), this, SLOT(slotLoadingCompleted()), Qt::QueuedConnection); + connect(m_model, SIGNAL(directoryLoadingCompleted()), this, SLOT(slotLoadingCompleted()), Qt::QueuedConnection); - KItemListContainer* container = new KItemListContainer(this); - m_controller = container->controller(); - m_controller->setView(view); - m_controller->setModel(model); + m_controller = new KItemListController(m_model, view, this); m_controller->setSelectionBehavior(KItemListController::SingleSelection); m_controller->setAutoActivationDelay(750); m_controller->setSingleClickActivation(true); @@ -160,21 +147,8 @@ void FoldersPanel::showEvent(QShowEvent* event) connect(m_controller, SIGNAL(viewContextMenuRequested(QPointF)), this, SLOT(slotViewContextMenuRequested(QPointF))); connect(m_controller, SIGNAL(itemDropEvent(int,QGraphicsSceneDragDropEvent*)), this, SLOT(slotItemDropEvent(int,QGraphicsSceneDragDropEvent*))); - // TODO: Check whether it makes sense to make an explicit API for KItemListContainer - // to make the background transparent. - container->setFrameShape(QFrame::NoFrame); - QGraphicsView* graphicsView = qobject_cast(container->viewport()); - if (graphicsView) { - // Make the background of the container transparent and apply the window-text color - // to the text color, so that enough contrast is given for all color - // schemes - QPalette p = graphicsView->palette(); - p.setColor(QPalette::Active, QPalette::Text, p.color(QPalette::Active, QPalette::WindowText)); - p.setColor(QPalette::Inactive, QPalette::Text, p.color(QPalette::Inactive, QPalette::WindowText)); - p.setColor(QPalette::Disabled, QPalette::Text, p.color(QPalette::Disabled, QPalette::WindowText)); - graphicsView->setPalette(p); - graphicsView->viewport()->setAutoFillBackground(false); - } + KItemListContainer* container = new KItemListContainer(m_controller, this); + container->setEnabledFrame(false); QVBoxLayout* layout = new QVBoxLayout(this); layout->setMargin(0); @@ -197,7 +171,7 @@ void FoldersPanel::keyPressEvent(QKeyEvent* event) void FoldersPanel::slotItemActivated(int index) { - const KFileItem item = fileItemModel()->fileItem(index); + const KFileItem item = m_model->fileItem(index); if (!item.isNull()) { emit changeUrl(item.url(), Qt::LeftButton); } @@ -205,7 +179,7 @@ void FoldersPanel::slotItemActivated(int index) void FoldersPanel::slotItemMiddleClicked(int index) { - const KFileItem item = fileItemModel()->fileItem(index); + const KFileItem item = m_model->fileItem(index); if (!item.isNull()) { emit changeUrl(item.url(), Qt::MiddleButton); } @@ -215,7 +189,7 @@ void FoldersPanel::slotItemContextMenuRequested(int index, const QPointF& pos) { Q_UNUSED(pos); - const KFileItem fileItem = fileItemModel()->fileItem(index); + const KFileItem fileItem = m_model->fileItem(index); QWeakPointer contextMenu = new TreeViewContextMenu(this, fileItem); contextMenu.data()->open(); @@ -238,7 +212,7 @@ void FoldersPanel::slotViewContextMenuRequested(const QPointF& pos) void FoldersPanel::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event) { if (index >= 0) { - KFileItem destItem = fileItemModel()->fileItem(index); + KFileItem destItem = m_model->fileItem(index); if (destItem.isNull()) { return; } @@ -255,8 +229,8 @@ void FoldersPanel::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* eve void FoldersPanel::slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value) { - if (role == "name") { - const KFileItem item = fileItemModel()->fileItem(index); + if (role == "text") { + const KFileItem item = m_model->fileItem(index); const QString newName = value.toString(); if (!newName.isEmpty() && newName != item.text() && newName != QLatin1String(".") && newName != QLatin1String("..")) { KonqOperations::rename(this, item.url(), newName); @@ -280,7 +254,7 @@ void FoldersPanel::slotLoadingCompleted() return; } - const int index = fileItemModel()->index(url()); + const int index = m_model->index(url()); updateCurrentItem(index); m_updateCurrentItem = false; } @@ -311,18 +285,17 @@ void FoldersPanel::loadTree(const KUrl& url) baseUrl.setPath(QString('/')); } - KFileItemModel* model = fileItemModel(); - if (model->directory() != baseUrl) { + if (m_model->directory() != baseUrl) { m_updateCurrentItem = true; - model->refreshDirectory(baseUrl); + m_model->refreshDirectory(baseUrl); } - const int index = model->index(url); + const int index = m_model->index(url); if (index >= 0) { updateCurrentItem(index); } else { m_updateCurrentItem = true; - model->expandParentDirectories(url); + m_model->expandParentDirectories(url); // slotLoadingCompleted() will be invoked after the model has // expanded the url } @@ -338,9 +311,4 @@ void FoldersPanel::updateCurrentItem(int index) m_controller->view()->scrollToItem(index); } -KFileItemModel* FoldersPanel::fileItemModel() const -{ - return static_cast(m_controller->model()); -} - #include "folderspanel.moc" diff --git a/src/panels/folders/folderspanel.h b/src/panels/folders/folderspanel.h index 10a30d1f7..b21ecabad 100644 --- a/src/panels/folders/folderspanel.h +++ b/src/panels/folders/folderspanel.h @@ -96,11 +96,10 @@ private: */ void updateCurrentItem(int index); - KFileItemModel* fileItemModel() const; - private: bool m_updateCurrentItem; KItemListController* m_controller; + KFileItemModel* m_model; }; #endif // FOLDERSPANEL_H diff --git a/src/panels/places/placespanel.cpp b/src/panels/places/placespanel.cpp index 902c436cf..4ac822760 100644 --- a/src/panels/places/placespanel.cpp +++ b/src/panels/places/placespanel.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008 by Peter Penz * + * Copyright (C) 2008-2012 by Peter Penz * * Copyright (C) 2010 by Christian Muehlhaeuser * * * * This program is free software; you can redistribute it and/or modify * @@ -20,40 +20,70 @@ #include "placespanel.h" -#include -#include +#include +#include +#include +#include +#include +#include #include +#include +#include PlacesPanel::PlacesPanel(QWidget* parent) : - KFilePlacesView(parent), - m_mouseButtons(Qt::NoButton) + Panel(parent), + m_controller(0), + m_model(0) { - setDropOnPlaceEnabled(true); - connect(this, SIGNAL(urlsDropped(KUrl,QDropEvent*,QWidget*)), - this, SLOT(slotUrlsDropped(KUrl,QDropEvent*,QWidget*))); - connect(this, SIGNAL(urlChanged(KUrl)), - this, SLOT(emitExtendedUrlChangedSignal(KUrl))); } PlacesPanel::~PlacesPanel() { } -void PlacesPanel::mousePressEvent(QMouseEvent* event) +bool PlacesPanel::urlChanged() { - m_mouseButtons = event->buttons(); - KFilePlacesView::mousePressEvent(event); + return true; } -void PlacesPanel::slotUrlsDropped(const KUrl& dest, QDropEvent* event, QWidget* parent) +void PlacesPanel::showEvent(QShowEvent* event) { - Q_UNUSED(parent); - DragAndDropHelper::dropUrls(KFileItem(), dest, event); + if (event->spontaneous()) { + Panel::showEvent(event); + return; + } + + if (!m_controller) { + // Postpone the creating of the controller to the first show event. + // This assures that no performance and memory overhead is given when the folders panel is not + // used at all and stays invisible. + KStandardItemListView* view = new KStandardItemListView(); + m_model = new KStandardItemModel(this); + m_model->appendItem(new KStandardItem("Temporary")); + m_model->appendItem(new KStandardItem("out of")); + m_model->appendItem(new KStandardItem("order. Press")); + m_model->appendItem(new KStandardItem("F9 and use")); + m_model->appendItem(new KStandardItem("the left icon")); + m_model->appendItem(new KStandardItem("of the location")); + m_model->appendItem(new KStandardItem("bar instead.")); + + m_controller = new KItemListController(m_model, view, this); + + KItemListContainer* container = new KItemListContainer(m_controller, this); + container->setEnabledFrame(false); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->addWidget(container); + } + + Panel::showEvent(event); } -void PlacesPanel::emitExtendedUrlChangedSignal(const KUrl& url) +void PlacesPanel::slotUrlsDropped(const KUrl& dest, QDropEvent* event, QWidget* parent) { - emit urlChanged(url, m_mouseButtons); + Q_UNUSED(parent); + DragAndDropHelper::dropUrls(KFileItem(), dest, event); } #include "placespanel.moc" diff --git a/src/panels/places/placespanel.h b/src/panels/places/placespanel.h index 903f2cadb..1c46cd57d 100644 --- a/src/panels/places/placespanel.h +++ b/src/panels/places/placespanel.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008 by Peter Penz * + * Copyright (C) 2008-2012 by Peter Penz * * Copyright (C) 2010 by Christian Muehlhaeuser * * * * This program is free software; you can redistribute it and/or modify * @@ -21,12 +21,15 @@ #ifndef PLACESPANEL_H #define PLACESPANEL_H -#include +#include + +class KItemListController; +class KStandardItemModel; /** * @brief Combines bookmarks and mounted devices as list. */ -class PlacesPanel : public KFilePlacesView +class PlacesPanel : public Panel { Q_OBJECT @@ -34,18 +37,16 @@ public: PlacesPanel(QWidget* parent); virtual ~PlacesPanel(); -signals: - void urlChanged(const KUrl& url, Qt::MouseButtons buttons); - protected: - virtual void mousePressEvent(QMouseEvent* event); + virtual bool urlChanged(); + virtual void showEvent(QShowEvent* event); private slots: void slotUrlsDropped(const KUrl& dest, QDropEvent* event, QWidget* parent); - void emitExtendedUrlChangedSignal(const KUrl& url); private: - Qt::MouseButtons m_mouseButtons; + KItemListController* m_controller; + KStandardItemModel* m_model; }; #endif // PLACESPANEL_H diff --git a/src/settings/viewpropertiesdialog.cpp b/src/settings/viewpropertiesdialog.cpp index 20461a394..a3af275ea 100644 --- a/src/settings/viewpropertiesdialog.cpp +++ b/src/settings/viewpropertiesdialog.cpp @@ -297,7 +297,7 @@ void ViewPropertiesDialog::configureAdditionalInfo() // makes no sense and leads to a usability problem as no viewport area is available // anymore. Hence as fallback provide at least a size and date column. visibleRoles.clear(); - visibleRoles.append("name"); + visibleRoles.append("text"); visibleRoles.append("size"); visibleRoles.append("date"); m_viewProps->setVisibleRoles(visibleRoles); diff --git a/src/tests/kfileitemmodeltest.cpp b/src/tests/kfileitemmodeltest.cpp index 9eee50d44..7b56957d4 100644 --- a/src/tests/kfileitemmodeltest.cpp +++ b/src/tests/kfileitemmodeltest.cpp @@ -114,13 +114,13 @@ void KFileItemModelTest::testDefaultRoles() { const QSet roles = m_model->roles(); QCOMPARE(roles.count(), 2); - QVERIFY(roles.contains("name")); + QVERIFY(roles.contains("text")); QVERIFY(roles.contains("isDir")); } void KFileItemModelTest::testDefaultSortRole() { - QCOMPARE(m_model->sortRole(), QByteArray("name")); + QCOMPARE(m_model->sortRole(), QByteArray("text")); QStringList files; files << "c.txt" << "a.txt" << "b.txt"; @@ -131,9 +131,9 @@ void KFileItemModelTest::testDefaultSortRole() QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); QCOMPARE(m_model->count(), 3); - QCOMPARE(m_model->data(0)["name"].toString(), QString("a.txt")); - QCOMPARE(m_model->data(1)["name"].toString(), QString("b.txt")); - QCOMPARE(m_model->data(2)["name"].toString(), QString("c.txt")); + QCOMPARE(m_model->data(0)["text"].toString(), QString("a.txt")); + QCOMPARE(m_model->data(1)["text"].toString(), QString("b.txt")); + QCOMPARE(m_model->data(2)["text"].toString(), QString("c.txt")); } void KFileItemModelTest::testDefaultGroupedSorting() @@ -269,7 +269,7 @@ void KFileItemModelTest::testSetDataWithModifiedSortRole() // Changing the value of a sort-role must result in // a reordering of the items. - QCOMPARE(m_model->sortRole(), QByteArray("name")); + QCOMPARE(m_model->sortRole(), QByteArray("text")); QStringList files; files << "a.txt" << "b.txt" << "c.txt"; @@ -572,7 +572,7 @@ void KFileItemModelTest::testSorting() QDateTime now = QDateTime::currentDateTime(); QSet roles; - roles.insert("name"); + roles.insert("text"); roles.insert("isExpanded"); roles.insert("isExpandable"); roles.insert("expandedParentsCount"); @@ -601,7 +601,7 @@ void KFileItemModelTest::testSorting() QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); // Default: Sort by Name, ascending - QCOMPARE(m_model->sortRole(), QByteArray("name")); + QCOMPARE(m_model->sortRole(), QByteArray("text")); QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); QVERIFY(m_model->sortDirectoriesFirst()); QVERIFY(!m_model->showHiddenFiles()); @@ -611,7 +611,7 @@ void KFileItemModelTest::testSorting() // Sort by Name, ascending, 'Sort Folders First' disabled m_model->setSortDirectoriesFirst(false); - QCOMPARE(m_model->sortRole(), QByteArray("name")); + QCOMPARE(m_model->sortRole(), QByteArray("text")); QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c" << "c-1" << "c-2" << "c-3" << "d" << "e"); QCOMPARE(spyItemsMoved.count(), 1); @@ -620,7 +620,7 @@ void KFileItemModelTest::testSorting() // Sort by Name, descending m_model->setSortDirectoriesFirst(true); m_model->setSortOrder(Qt::DescendingOrder); - QCOMPARE(m_model->sortRole(), QByteArray("name")); + QCOMPARE(m_model->sortRole(), QByteArray("text")); QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder); QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "e" << "d" << "b" << "a"); QCOMPARE(spyItemsMoved.count(), 2); @@ -654,7 +654,7 @@ void KFileItemModelTest::testSorting() QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 2 << 4 << 5 << 3 << 0 << 1 << 6 << 7); // Sort by Name, ascending, 'Sort Folders First' disabled - m_model->setSortRole("name"); + m_model->setSortRole("text"); QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); QVERIFY(!m_model->sortDirectoriesFirst()); QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c" << "c-1" << "c-2" << "c-3" << "d" << "e"); @@ -800,7 +800,7 @@ QStringList KFileItemModelTest::itemsInModel() const { QStringList items; for (int i = 0; i < m_model->count(); i++) { - items << m_model->data(i).value("name").toString(); + items << m_model->data(i).value("text").toString(); } return items; } diff --git a/src/tests/kitemlistcontrollertest.cpp b/src/tests/kitemlistcontrollertest.cpp index 3b67e7b50..47b2b8b75 100644 --- a/src/tests/kitemlistcontrollertest.cpp +++ b/src/tests/kitemlistcontrollertest.cpp @@ -33,7 +33,7 @@ namespace { const int DefaultTimeout = 2000; }; -Q_DECLARE_METATYPE(KFileItemListView::Layout); +Q_DECLARE_METATYPE(KFileItemListView::ItemLayout); Q_DECLARE_METATYPE(Qt::Orientation); Q_DECLARE_METATYPE(KItemListController::SelectionBehavior); Q_DECLARE_METATYPE(QSet); @@ -79,15 +79,13 @@ void KItemListControllerTest::initTestCase() m_testDir = new TestDir(); m_model = new KFileItemModel(); - m_container = new KItemListContainer(); + m_view = new KFileItemListView(); + m_controller = new KItemListController(m_model, m_view, this); + m_container = new KItemListContainer(m_controller); m_controller = m_container->controller(); m_controller->setSelectionBehavior(KItemListController::MultiSelection); m_selectionManager = m_controller->selectionManager(); - m_view = new KFileItemListView(); - m_controller->setView(m_view); - m_controller->setModel(m_model); - QStringList files; files << "a1" << "a2" << "a3" @@ -106,15 +104,8 @@ void KItemListControllerTest::initTestCase() void KItemListControllerTest::cleanupTestCase() { - delete m_view; - m_view = 0; - delete m_container; m_container = 0; - m_controller = 0; - - delete m_model; - m_model = 0; delete m_testDir; m_testDir = 0; @@ -187,15 +178,15 @@ Q_DECLARE_METATYPE(QList); */ void KItemListControllerTest::testKeyboardNavigation_data() { - QTest::addColumn("layout"); + QTest::addColumn("layout"); QTest::addColumn("scrollOrientation"); QTest::addColumn("columnCount"); QTest::addColumn("selectionBehavior"); QTest::addColumn("groupingEnabled"); QTest::addColumn > >("testList"); - QList layoutList; - QHash layoutNames; + QList layoutList; + QHash layoutNames; layoutList.append(KFileItemListView::IconsLayout); layoutNames[KFileItemListView::IconsLayout] = "Icons"; layoutList.append(KFileItemListView::CompactLayout); @@ -219,7 +210,7 @@ void KItemListControllerTest::testKeyboardNavigation_data() groupingEnabledList.append(true); groupingEnabledNames[true] = "grouping enabled"; - foreach (KFileItemListView::Layout layout, layoutList) { + foreach (KFileItemListView::ItemLayout layout, layoutList) { // The following settings depend on the layout. // Note that 'columns' are actually 'rows' in // Compact layout. @@ -443,7 +434,7 @@ void KItemListControllerTest::testKeyboardNavigation_data() */ void KItemListControllerTest::testKeyboardNavigation() { - QFETCH(KFileItemListView::Layout, layout); + QFETCH(KFileItemListView::ItemLayout, layout); QFETCH(Qt::Orientation, scrollOrientation); QFETCH(int, columnCount); QFETCH(KItemListController::SelectionBehavior, selectionBehavior); diff --git a/src/views/dolphinfileitemlistwidget.cpp b/src/views/dolphinfileitemlistwidget.cpp index e730c47b8..43030c634 100644 --- a/src/views/dolphinfileitemlistwidget.cpp +++ b/src/views/dolphinfileitemlistwidget.cpp @@ -26,8 +26,9 @@ #include -DolphinFileItemListWidget::DolphinFileItemListWidget(QGraphicsItem* parent) : - KFileItemListWidget(parent) +DolphinFileItemListWidget::DolphinFileItemListWidget(KItemListWidgetInformant* informant, + QGraphicsItem* parent) : + KFileItemListWidget(informant, parent) { } diff --git a/src/views/dolphinfileitemlistwidget.h b/src/views/dolphinfileitemlistwidget.h index 87ed0333b..b9de6fb19 100644 --- a/src/views/dolphinfileitemlistwidget.h +++ b/src/views/dolphinfileitemlistwidget.h @@ -36,7 +36,7 @@ class LIBDOLPHINPRIVATE_EXPORT DolphinFileItemListWidget : public KFileItemListW Q_OBJECT public: - DolphinFileItemListWidget(QGraphicsItem* parent); + DolphinFileItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent); virtual ~DolphinFileItemListWidget(); protected: diff --git a/src/views/dolphinitemlistcontainer.cpp b/src/views/dolphinitemlistview.cpp similarity index 57% rename from src/views/dolphinitemlistcontainer.cpp rename to src/views/dolphinitemlistview.cpp index 88e10d8a3..156ca204f 100644 --- a/src/views/dolphinitemlistcontainer.cpp +++ b/src/views/dolphinitemlistview.cpp @@ -17,13 +17,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ -#include "dolphinitemlistcontainer.h" +#include "dolphinitemlistview.h" #include "dolphin_generalsettings.h" #include "dolphin_iconsmodesettings.h" #include "dolphin_detailsmodesettings.h" #include "dolphin_compactmodesettings.h" - #include "dolphinfileitemlistwidget.h" #include @@ -38,59 +37,20 @@ #include "zoomlevelinfo.h" -DolphinItemListContainer::DolphinItemListContainer(QWidget* parent) : - KItemListContainer(parent), - m_zoomLevel(0), - m_fileItemListView(0) +DolphinItemListView::DolphinItemListView(QGraphicsWidget* parent) : + KFileItemListView(parent), + m_zoomLevel(0) { - controller()->setModel(new KFileItemModel(this)); - - m_fileItemListView = new KFileItemListView(); - controller()->setView(m_fileItemListView); - - m_fileItemListView->setWidgetCreator(new KItemListWidgetCreator()); - m_fileItemListView->setEnabledSelectionToggles(GeneralSettings::showSelectionToggle()); - m_fileItemListView->setEnlargeSmallPreviews(GeneralSettings::enlargeSmallPreviews()); - - updateAutoActivationDelay(); updateFont(); updateGridSize(); } -DolphinItemListContainer::~DolphinItemListContainer() +DolphinItemListView::~DolphinItemListView() { writeSettings(); - - controller()->setView(0); - delete m_fileItemListView; - m_fileItemListView = 0; -} - -void DolphinItemListContainer::setPreviewsShown(bool show) -{ - beginTransaction(); - m_fileItemListView->setPreviewsShown(show); - updateGridSize(); - endTransaction(); -} - -bool DolphinItemListContainer::previewsShown() const -{ - return m_fileItemListView->previewsShown(); -} - -void DolphinItemListContainer::setVisibleRoles(const QList& roles) -{ - m_fileItemListView->setVisibleRoles(roles); - updateGridSize(); -} - -QList DolphinItemListContainer::visibleRoles() const -{ - return m_fileItemListView->visibleRoles(); } -void DolphinItemListContainer::setZoomLevel(int level) +void DolphinItemListView::setZoomLevel(int level) { if (level < ZoomLevelInfo::minimumLevel()) { level = ZoomLevelInfo::minimumLevel(); @@ -116,76 +76,24 @@ void DolphinItemListContainer::setZoomLevel(int level) updateGridSize(); } -int DolphinItemListContainer::zoomLevel() const +int DolphinItemListView::zoomLevel() const { return m_zoomLevel; } -void DolphinItemListContainer::setItemLayout(KFileItemListView::Layout layout) -{ - if (layout == itemLayout()) { - return; - } - - beginTransaction(); - m_fileItemListView->setItemLayout(layout); - - switch (layout) { - case KFileItemListView::IconsLayout: - m_fileItemListView->setScrollOrientation(Qt::Vertical); - m_fileItemListView->setHeaderVisible(false); - m_fileItemListView->setSupportsItemExpanding(false); - break; - case KFileItemListView::DetailsLayout: - m_fileItemListView->setScrollOrientation(Qt::Vertical); - m_fileItemListView->setHeaderVisible(true); - m_fileItemListView->setSupportsItemExpanding(DetailsModeSettings::expandableFolders()); - - break; - case KFileItemListView::CompactLayout: - m_fileItemListView->setScrollOrientation(Qt::Horizontal); - m_fileItemListView->setHeaderVisible(false); - m_fileItemListView->setSupportsItemExpanding(false); - break; - default: - Q_ASSERT(false); - break; - } - - updateFont(); - updateGridSize(); - endTransaction(); -} - -KFileItemListView::Layout DolphinItemListContainer::itemLayout() const -{ - return m_fileItemListView->itemLayout(); -} - -void DolphinItemListContainer::beginTransaction() -{ - m_fileItemListView->beginTransaction(); -} - -void DolphinItemListContainer::endTransaction() -{ - m_fileItemListView->endTransaction(); -} - -void DolphinItemListContainer::readSettings() +void DolphinItemListView::readSettings() { ViewModeSettings settings(viewMode()); settings.readConfig(); beginTransaction(); - m_fileItemListView->setEnabledSelectionToggles(GeneralSettings::showSelectionToggle()); + setEnabledSelectionToggles(GeneralSettings::showSelectionToggle()); const bool expandableFolders = (itemLayout() && KFileItemListView::DetailsLayout) && DetailsModeSettings::expandableFolders(); - m_fileItemListView->setSupportsItemExpanding(expandableFolders); + setSupportsItemExpanding(expandableFolders); - updateAutoActivationDelay(); updateFont(); updateGridSize(); @@ -194,26 +102,59 @@ void DolphinItemListContainer::readSettings() << "directorythumbnail" << "imagethumbnail" << "jpegthumbnail"); - m_fileItemListView->setEnabledPlugins(plugins); + setEnabledPlugins(plugins); endTransaction(); } -void DolphinItemListContainer::writeSettings() +void DolphinItemListView::writeSettings() { IconsModeSettings::self()->writeConfig(); CompactModeSettings::self()->writeConfig(); DetailsModeSettings::self()->writeConfig(); } -void DolphinItemListContainer::updateGridSize() +KItemListWidgetCreatorBase* DolphinItemListView::defaultWidgetCreator() const +{ + return new KItemListWidgetCreator(); +} + +void DolphinItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous) +{ + Q_UNUSED(previous); + + if (current == DetailsLayout) { + setSupportsItemExpanding(DetailsModeSettings::expandableFolders()); + setHeaderVisible(true); + } else { + setHeaderVisible(false); + } + + updateFont(); + updateGridSize(); +} + +void DolphinItemListView::onPreviewsShownChanged(bool shown) +{ + Q_UNUSED(shown); + updateGridSize(); +} + +void DolphinItemListView::onVisibleRolesChanged(const QList& current, + const QList& previous) +{ + KFileItemListView::onVisibleRolesChanged(current, previous); + updateGridSize(); +} + +void DolphinItemListView::updateGridSize() { const ViewModeSettings settings(viewMode()); // Calculate the size of the icon const int iconSize = previewsShown() ? settings.previewSize() : settings.iconSize(); m_zoomLevel = ZoomLevelInfo::zoomLevelForIconSize(QSize(iconSize, iconSize)); - KItemListStyleOption styleOption = m_fileItemListView->styleOption(); + KItemListStyleOption option = styleOption(); const int padding = 2; int horizontalMargin = 0; @@ -241,13 +182,13 @@ void DolphinItemListContainer::updateGridSize() itemWidth = iconSize + padding * 2; } - itemHeight = padding * 3 + iconSize + styleOption.fontMetrics.lineSpacing(); + itemHeight = padding * 3 + iconSize + option.fontMetrics.lineSpacing(); if (IconsModeSettings::maximumTextLines() > 0) { // A restriction is given for the maximum number of textlines (0 means // having no restriction) - const int additionalInfoCount = m_fileItemListView->visibleRoles().count() - 1; + const int additionalInfoCount = visibleRoles().count() - 1; const int maxAdditionalLines = additionalInfoCount + IconsModeSettings::maximumTextLines(); - maxTextSize.rheight() = styleOption.fontMetrics.lineSpacing() * maxAdditionalLines; + maxTextSize.rheight() = option.fontMetrics.lineSpacing() * maxAdditionalLines; } horizontalMargin = 4; @@ -255,14 +196,14 @@ void DolphinItemListContainer::updateGridSize() break; } case KFileItemListView::CompactLayout: { - itemWidth = padding * 4 + iconSize + styleOption.fontMetrics.height() * 5; - const int textLinesCount = m_fileItemListView->visibleRoles().count(); - itemHeight = padding * 2 + qMax(iconSize, textLinesCount * styleOption.fontMetrics.lineSpacing()); + itemWidth = padding * 4 + iconSize + option.fontMetrics.height() * 5; + const int textLinesCount = visibleRoles().count(); + itemHeight = padding * 2 + qMax(iconSize, textLinesCount * option.fontMetrics.lineSpacing()); if (CompactModeSettings::maximumTextWidthIndex() > 0) { // A restriction is given for the maximum width of the text (0 means // having no restriction) - maxTextSize.rwidth() = styleOption.fontMetrics.height() * 10 * + maxTextSize.rwidth() = option.fontMetrics.height() * 10 * CompactModeSettings::maximumTextWidthIndex(); } @@ -271,7 +212,7 @@ void DolphinItemListContainer::updateGridSize() } case KFileItemListView::DetailsLayout: { itemWidth = -1; - itemHeight = padding * 2 + qMax(iconSize, styleOption.fontMetrics.lineSpacing()); + itemHeight = padding * 2 + qMax(iconSize, option.fontMetrics.lineSpacing()); break; } default: @@ -282,20 +223,20 @@ void DolphinItemListContainer::updateGridSize() } // Apply the calculated values - styleOption.padding = padding; - styleOption.horizontalMargin = horizontalMargin; - styleOption.verticalMargin = verticalMargin; - styleOption.iconSize = iconSize; - styleOption.maxTextSize = maxTextSize; - m_fileItemListView->beginTransaction(); - m_fileItemListView->setStyleOption(styleOption); - m_fileItemListView->setItemSize(QSizeF(itemWidth, itemHeight)); - m_fileItemListView->endTransaction(); + option.padding = padding; + option.horizontalMargin = horizontalMargin; + option.verticalMargin = verticalMargin; + option.iconSize = iconSize; + option.maxTextSize = maxTextSize; + beginTransaction(); + setStyleOption(option); + setItemSize(QSizeF(itemWidth, itemHeight)); + endTransaction(); } -void DolphinItemListContainer::updateFont() +void DolphinItemListView::updateFont() { - KItemListStyleOption styleOption = m_fileItemListView->styleOption(); + KItemListStyleOption option = styleOption(); const ViewModeSettings settings(viewMode()); @@ -304,19 +245,13 @@ void DolphinItemListContainer::updateFont() font.setWeight(settings.fontWeight()); font.setPointSizeF(settings.fontSize()); - styleOption.font = font; - styleOption.fontMetrics = QFontMetrics(font); - - m_fileItemListView->setStyleOption(styleOption); -} + option.font = font; + option.fontMetrics = QFontMetrics(font); -void DolphinItemListContainer::updateAutoActivationDelay() -{ - const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1; - controller()->setAutoActivationDelay(delay); + setStyleOption(option); } -ViewModeSettings::ViewMode DolphinItemListContainer::viewMode() const +ViewModeSettings::ViewMode DolphinItemListView::viewMode() const { ViewModeSettings::ViewMode mode; @@ -332,4 +267,4 @@ ViewModeSettings::ViewMode DolphinItemListContainer::viewMode() const return mode; } -#include "dolphinitemlistcontainer.moc" +#include "dolphinitemlistview.moc" diff --git a/src/views/dolphinitemlistcontainer.h b/src/views/dolphinitemlistview.h similarity index 59% rename from src/views/dolphinitemlistcontainer.h rename to src/views/dolphinitemlistview.h index f3505b663..c2d86cc5e 100644 --- a/src/views/dolphinitemlistcontainer.h +++ b/src/views/dolphinitemlistview.h @@ -17,11 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ -#ifndef DOLPHINITEMLISTCONTAINER_H -#define DOLPHINITEMLISTCONTAINER_H +#ifndef DOLPHINITEMLISTVIEW_H +#define DOLPHINITEMLISTVIEW_H #include -#include #include #include @@ -29,57 +28,41 @@ class KFileItemListView; /** - * @brief Extends KItemListContainer by Dolphin specific properties. + * @brief Dolphin specific view-implementation. * - * The view and model for KFileItems are created automatically when - * instantating KItemListContainer. - * - * The Dolphin settings of the icons-, compact- and details-view are - * converted internally to properties that can be used to configure e.g. - * the item-size and visible roles of the KItemListView. + * Offers zoom-level support and takes care for translating + * the view-properties into the corresponding KItemListView + * properties. */ -class LIBDOLPHINPRIVATE_EXPORT DolphinItemListContainer : public KItemListContainer +class LIBDOLPHINPRIVATE_EXPORT DolphinItemListView : public KFileItemListView { Q_OBJECT public: - explicit DolphinItemListContainer(QWidget* parent = 0); - - virtual ~DolphinItemListContainer(); - - void setPreviewsShown(bool show); - bool previewsShown() const; - - void setVisibleRoles(const QList& roles); - QList visibleRoles() const; + explicit DolphinItemListView(QGraphicsWidget* parent = 0); + virtual ~DolphinItemListView(); void setZoomLevel(int level); int zoomLevel() const; - void setItemLayout(KFileItemListView::Layout layout); - KFileItemListView::Layout itemLayout() const; - - void beginTransaction(); - void endTransaction(); - void readSettings(); void writeSettings(); +protected: + virtual KItemListWidgetCreatorBase* defaultWidgetCreator() const; + virtual void onItemLayoutChanged(ItemLayout current, ItemLayout previous); + virtual void onPreviewsShownChanged(bool shown); + virtual void onVisibleRolesChanged(const QList& current, + const QList& previous); + private: void updateGridSize(); void updateFont(); - /** - * Updates the auto activation delay of the itemlist controller - * dependent on the 'autoExpand' setting from the general settings. - */ - void updateAutoActivationDelay(); - ViewModeSettings::ViewMode viewMode() const; private: int m_zoomLevel; - KFileItemListView* m_fileItemListView; }; #endif diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index 7494ac5e6..12932c709 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -62,7 +63,7 @@ #include "dolphinnewfilemenuobserver.h" #include "dolphin_detailsmodesettings.h" #include "dolphin_generalsettings.h" -#include "dolphinitemlistcontainer.h" +#include "dolphinitemlistview.h" #include "draganddrophelper.h" #include "renamedialog.h" #include "versioncontrol/versioncontrolobserver.h" @@ -90,6 +91,8 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) : m_mode(DolphinView::IconsView), m_visibleRoles(), m_topLayout(0), + m_model(0), + m_view(0), m_container(0), m_toolTipManager(0), m_selectionChangedTimer(0), @@ -115,14 +118,23 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) : connect(m_selectionChangedTimer, SIGNAL(timeout()), this, SLOT(emitSelectionChangedSignal())); - m_container = new DolphinItemListContainer(this); - m_container->setVisibleRoles(QList() << "name"); + m_model = new KFileItemModel(this); + m_view = new DolphinItemListView(); + m_view->setEnabledSelectionToggles(GeneralSettings::showSelectionToggle()); + m_view->setEnlargeSmallPreviews(GeneralSettings::enlargeSmallPreviews()); + m_view->setVisibleRoles(QList() << "text"); + applyModeToView(); + + KItemListController* controller = new KItemListController(m_model, m_view, this); + const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1; + controller->setAutoActivationDelay(delay); + + m_container = new KItemListContainer(controller, this); m_container->installEventFilter(this); setFocusProxy(m_container); connect(m_container->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(hideToolTip())); connect(m_container->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(hideToolTip())); - KItemListController* controller = m_container->controller(); controller->setSelectionBehavior(KItemListController::MultiSelection); connect(controller, SIGNAL(itemActivated(int)), this, SLOT(slotItemActivated(int))); connect(controller, SIGNAL(itemsActivated(QSet)), this, SLOT(slotItemsActivated(QSet))); @@ -136,30 +148,28 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) : connect(controller, SIGNAL(itemDropEvent(int,QGraphicsSceneDragDropEvent*)), this, SLOT(slotItemDropEvent(int,QGraphicsSceneDragDropEvent*))); connect(controller, SIGNAL(modelChanged(KItemModelBase*,KItemModelBase*)), this, SLOT(slotModelChanged(KItemModelBase*,KItemModelBase*))); - KFileItemModel* model = fileItemModel(); - connect(model, SIGNAL(directoryLoadingStarted()), this, SLOT(slotDirectoryLoadingStarted())); - connect(model, SIGNAL(directoryLoadingCompleted()), this, SLOT(slotDirectoryLoadingCompleted())); - connect(model, SIGNAL(directoryLoadingProgress(int)), this, SIGNAL(directoryLoadingProgress(int))); - connect(model, SIGNAL(directorySortingProgress(int)), this, SIGNAL(directorySortingProgress(int))); - connect(model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + connect(m_model, SIGNAL(directoryLoadingStarted()), this, SLOT(slotDirectoryLoadingStarted())); + connect(m_model, SIGNAL(directoryLoadingCompleted()), this, SLOT(slotDirectoryLoadingCompleted())); + connect(m_model, SIGNAL(directoryLoadingProgress(int)), this, SIGNAL(directoryLoadingProgress(int))); + connect(m_model, SIGNAL(directorySortingProgress(int)), this, SIGNAL(directorySortingProgress(int))); + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), this, SLOT(slotItemsChanged())); - connect(model, SIGNAL(itemsRemoved(KItemRangeList)), this, SIGNAL(itemCountChanged())); - connect(model, SIGNAL(itemsInserted(KItemRangeList)), this, SIGNAL(itemCountChanged())); - connect(model, SIGNAL(infoMessage(QString)), this, SIGNAL(infoMessage(QString))); - connect(model, SIGNAL(errorMessage(QString)), this, SIGNAL(errorMessage(QString))); - connect(model, SIGNAL(directoryRedirection(KUrl,KUrl)), this, SLOT(slotDirectoryRedirection(KUrl,KUrl))); - - KItemListView* view = controller->view(); - view->installEventFilter(this); - connect(view, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), + connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)), this, SIGNAL(itemCountChanged())); + connect(m_model, SIGNAL(itemsInserted(KItemRangeList)), this, SIGNAL(itemCountChanged())); + connect(m_model, SIGNAL(infoMessage(QString)), this, SIGNAL(infoMessage(QString))); + connect(m_model, SIGNAL(errorMessage(QString)), this, SIGNAL(errorMessage(QString))); + connect(m_model, SIGNAL(directoryRedirection(KUrl,KUrl)), this, SLOT(slotDirectoryRedirection(KUrl,KUrl))); + + m_view->installEventFilter(this); + connect(m_view, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), this, SLOT(slotSortOrderChangedByHeader(Qt::SortOrder,Qt::SortOrder))); - connect(view, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), + connect(m_view, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), this, SLOT(slotSortRoleChangedByHeader(QByteArray,QByteArray))); - connect(view, SIGNAL(visibleRolesChanged(QList,QList)), + connect(m_view, SIGNAL(visibleRolesChanged(QList,QList)), this, SLOT(slotVisibleRolesChangedByHeader(QList,QList))); - connect(view, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), + connect(m_view, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant))); - connect(view->header(), SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)), + connect(m_view->header(), SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)), this, SLOT(slotHeaderColumnWidthChanged(QByteArray,qreal,qreal))); KItemListSelectionManager* selectionManager = controller->selectionManager(); @@ -169,7 +179,7 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) : m_toolTipManager = new ToolTipManager(this); m_versionControlObserver = new VersionControlObserver(this); - m_versionControlObserver->setModel(model); + m_versionControlObserver->setModel(m_model); connect(m_versionControlObserver, SIGNAL(infoMessage(QString)), this, SIGNAL(infoMessage(QString))); connect(m_versionControlObserver, SIGNAL(errorMessage(QString)), this, SIGNAL(errorMessage(QString))); connect(m_versionControlObserver, SIGNAL(operationCompletedMessage(QString)), this, SIGNAL(operationCompletedMessage(QString))); @@ -248,19 +258,18 @@ void DolphinView::setPreviewsShown(bool show) ViewProperties props(url()); props.setPreviewsShown(show); - m_container->setPreviewsShown(show); + m_view->setPreviewsShown(show); emit previewsShownChanged(show); } bool DolphinView::previewsShown() const { - return m_container->previewsShown(); + return m_view->previewsShown(); } void DolphinView::setHiddenFilesShown(bool show) { - KFileItemModel* model = fileItemModel(); - if (model->showHiddenFiles() == show) { + if (m_model->showHiddenFiles() == show) { return; } @@ -271,13 +280,13 @@ void DolphinView::setHiddenFilesShown(bool show) ViewProperties props(url()); props.setHiddenFilesShown(show); - model->setShowHiddenFiles(show); + m_model->setShowHiddenFiles(show); emit hiddenFilesShownChanged(show); } bool DolphinView::hiddenFilesShown() const { - return fileItemModel()->showHiddenFiles(); + return m_model->showHiddenFiles(); } void DolphinView::setGroupedSorting(bool grouped) @@ -297,18 +306,17 @@ void DolphinView::setGroupedSorting(bool grouped) bool DolphinView::groupedSorting() const { - return fileItemModel()->groupedSorting(); + return m_model->groupedSorting(); } KFileItemList DolphinView::items() const { KFileItemList list; - const KFileItemModel* model = fileItemModel(); - const int itemCount = model->count(); + const int itemCount = m_model->count(); list.reserve(itemCount); for (int i = 0; i < itemCount; ++i) { - list.append(model->fileItem(i)); + list.append(m_model->fileItem(i)); } return list; @@ -316,12 +324,11 @@ KFileItemList DolphinView::items() const int DolphinView::itemsCount() const { - return fileItemModel()->count(); + return m_model->count(); } KFileItemList DolphinView::selectedItems() const { - const KFileItemModel* model = fileItemModel(); const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); const QSet selectedIndexes = selectionManager->selectedItems(); @@ -329,7 +336,7 @@ KFileItemList DolphinView::selectedItems() const QSetIterator it(selectedIndexes); while (it.hasNext()) { const int index = it.next(); - selectedItems.append(model->fileItem(index)); + selectedItems.append(m_model->fileItem(index)); } return selectedItems; } @@ -355,11 +362,10 @@ void DolphinView::selectItems(const QRegExp& pattern, bool enabled) const KItemListSelectionManager::SelectionMode mode = enabled ? KItemListSelectionManager::Select : KItemListSelectionManager::Deselect; - const KFileItemModel* model = fileItemModel(); KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); - for (int index = 0; index < model->count(); index++) { - const KFileItem item = model->fileItem(index); + for (int index = 0; index < m_model->count(); index++) { + const KFileItem item = m_model->fileItem(index); if (pattern.exactMatch(item.text())) { // An alternative approach would be to store the matching items in a QSet and // select them in one go after the loop, but we'd need a new function @@ -373,7 +379,7 @@ void DolphinView::selectItems(const QRegExp& pattern, bool enabled) void DolphinView::setZoomLevel(int level) { const int oldZoomLevel = zoomLevel(); - m_container->setZoomLevel(level); + m_view->setZoomLevel(level); if (zoomLevel() != oldZoomLevel) { emit zoomLevelChanged(zoomLevel(), oldZoomLevel); } @@ -381,7 +387,7 @@ void DolphinView::setZoomLevel(int level) int DolphinView::zoomLevel() const { - return m_container->zoomLevel(); + return m_view->zoomLevel(); } void DolphinView::setSortRole(const QByteArray& role) @@ -406,8 +412,7 @@ void DolphinView::setSortOrder(Qt::SortOrder order) Qt::SortOrder DolphinView::sortOrder() const { - KItemModelBase* model = fileItemModel(); - return model->sortOrder(); + return m_model->sortOrder(); } void DolphinView::setSortFoldersFirst(bool foldersFirst) @@ -419,8 +424,7 @@ void DolphinView::setSortFoldersFirst(bool foldersFirst) bool DolphinView::sortFoldersFirst() const { - KFileItemModel* model = fileItemModel(); - return model->sortDirectoriesFirst(); + return m_model->sortDirectoriesFirst(); } void DolphinView::setVisibleRoles(const QList& roles) @@ -431,7 +435,7 @@ void DolphinView::setVisibleRoles(const QList& roles) props.setVisibleRoles(roles); m_visibleRoles = roles; - m_container->setVisibleRoles(roles); + m_view->setVisibleRoles(roles); emit visibleRolesChanged(m_visibleRoles, previousRoles); } @@ -460,18 +464,21 @@ void DolphinView::reload() void DolphinView::stopLoading() { - fileItemModel()->cancelDirectoryLoading(); + m_model->cancelDirectoryLoading(); } void DolphinView::readSettings() { - const int oldZoomLevel = m_container->zoomLevel(); + const int oldZoomLevel = m_view->zoomLevel(); GeneralSettings::self()->readConfig(); - m_container->readSettings(); + m_view->readSettings(); applyViewProperties(); - const int newZoomLevel = m_container->zoomLevel(); + const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1; + m_container->controller()->setAutoActivationDelay(delay); + + const int newZoomLevel = m_view->zoomLevel(); if (newZoomLevel != oldZoomLevel) { emit zoomLevelChanged(newZoomLevel, oldZoomLevel); } @@ -480,17 +487,17 @@ void DolphinView::readSettings() void DolphinView::writeSettings() { GeneralSettings::self()->writeConfig(); - m_container->writeSettings(); + m_view->writeSettings(); } void DolphinView::setNameFilter(const QString& nameFilter) { - fileItemModel()->setNameFilter(nameFilter); + m_model->setNameFilter(nameFilter); } QString DolphinView::nameFilter() const { - return fileItemModel()->nameFilter(); + return m_model->nameFilter(); } QString DolphinView::statusBarText() const @@ -551,7 +558,7 @@ QList DolphinView::versionControlActions(const KFileItemList& items) c QList actions; if (items.isEmpty()) { - const KFileItem item = fileItemModel()->rootItem(); + const KFileItem item = m_model->rootItem(); actions = m_versionControlObserver->actions(KFileItemList() << item); } else { actions = m_versionControlObserver->actions(items); @@ -575,7 +582,7 @@ void DolphinView::setUrl(const KUrl& url) // applying the view properties, otherwise expensive operations // might be done on the existing items although they get cleared // anyhow afterwards by loadDirectory(). - fileItemModel()->clear(); + m_model->clear(); applyViewProperties(); loadDirectory(url); @@ -585,13 +592,13 @@ void DolphinView::setUrl(const KUrl& url) void DolphinView::selectAll() { KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); - selectionManager->setSelected(0, fileItemModel()->count()); + selectionManager->setSelected(0, m_model->count()); } void DolphinView::invertSelection() { KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); - selectionManager->setSelected(0, fileItemModel()->count(), KItemListSelectionManager::Toggle); + selectionManager->setSelected(0, m_model->count(), KItemListSelectionManager::Toggle); } void DolphinView::clearSelection() @@ -607,8 +614,8 @@ void DolphinView::renameSelectedItems() } if (items.count() == 1) { - const int index = fileItemModel()->index(items.first()); - m_container->controller()->view()->editRole(index, "name"); + const int index = m_model->index(items.first()); + m_container->controller()->view()->editRole(index, "text"); } else { RenameDialog* dialog = new RenameDialog(this, items); dialog->setAttribute(Qt::WA_DeleteOnClose); @@ -683,19 +690,19 @@ bool DolphinView::eventFilter(QObject* watched, QEvent* event) break; case QEvent::GraphicsSceneDragEnter: - if (watched == m_container->controller()->view()) { + if (watched == m_view) { m_dragging = true; } break; case QEvent::GraphicsSceneDragLeave: - if (watched == m_container->controller()->view()) { + if (watched == m_view) { m_dragging = false; } break; case QEvent::GraphicsSceneDrop: - if (watched == m_container->controller()->view()) { + if (watched == m_view) { m_dragging = false; } default: @@ -731,7 +738,7 @@ void DolphinView::activate() void DolphinView::slotItemActivated(int index) { - const KFileItem item = fileItemModel()->fileItem(index); + const KFileItem item = m_model->fileItem(index); if (!item.isNull()) { emit itemActivated(item); } @@ -743,11 +750,10 @@ void DolphinView::slotItemsActivated(const QSet& indexes) KFileItemList items; - KFileItemModel* model = fileItemModel(); QSetIterator it(indexes); while (it.hasNext()) { const int index = it.next(); - items.append(model->fileItem(index)); + items.append(m_model->fileItem(index)); } foreach (const KFileItem& item, items) { @@ -761,7 +767,7 @@ void DolphinView::slotItemsActivated(const QSet& indexes) void DolphinView::slotItemMiddleClicked(int index) { - const KFileItem item = fileItemModel()->fileItem(index); + const KFileItem item = m_model->fileItem(index); if (item.isDir() || isTabsForFilesEnabled()) { emit tabRequested(item.url()); } @@ -769,7 +775,7 @@ void DolphinView::slotItemMiddleClicked(int index) void DolphinView::slotItemContextMenuRequested(int index, const QPointF& pos) { - const KFileItem item = fileItemModel()->fileItem(index); + const KFileItem item = m_model->fileItem(index); emit requestContextMenu(pos.toPoint(), item, url(), QList()); } @@ -803,12 +809,12 @@ void DolphinView::slotHeaderContextMenuRequested(const QPointF& pos) // Add all roles to the menu that can be shown or hidden by the user const QList rolesInfo = KFileItemModel::rolesInformation(); foreach (const KFileItemModel::RoleInfo& info, rolesInfo) { - if (info.role == "name") { - // It should not be possible to hide the "name" role + if (info.role == "text") { + // It should not be possible to hide the "text" role continue; } - const QString text = fileItemModel()->roleDescription(info.role); + const QString text = m_model->roleDescription(info.role); QAction* action = 0; if (info.group.isEmpty()) { action = menu->addAction(text); @@ -895,14 +901,14 @@ void DolphinView::slotHeaderColumnWidthChanged(const QByteArray& role, qreal cur { Q_UNUSED(previous); - const QList visibleRoles = m_container->visibleRoles(); + const QList visibleRoles = m_view->visibleRoles(); ViewProperties props(url()); QList columnWidths = props.headerColumnWidths(); if (columnWidths.count() != visibleRoles.count()) { columnWidths.clear(); columnWidths.reserve(visibleRoles.count()); - const KItemListHeader* header = m_container->controller()->view()->header(); + const KItemListHeader* header = m_view->header(); foreach (const QByteArray& role, visibleRoles) { const int width = header->columnWidth(role); columnWidths.append(width); @@ -918,7 +924,7 @@ void DolphinView::slotHeaderColumnWidthChanged(const QByteArray& role, qreal cur void DolphinView::slotItemHovered(int index) { - const KFileItem item = fileItemModel()->fileItem(index); + const KFileItem item = m_model->fileItem(index); if (GeneralSettings::showToolTips() && !m_dragging) { QRectF itemRect = m_container->controller()->view()->itemContextRect(index); @@ -941,11 +947,11 @@ void DolphinView::slotItemUnhovered(int index) void DolphinView::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event) { KUrl destUrl; - KFileItem destItem = fileItemModel()->fileItem(index); + KFileItem destItem = m_model->fileItem(index); if (destItem.isNull() || (!destItem.isDir() && !destItem.isDesktopFile())) { // Use the URL of the view as drop target if the item is no directory // or desktop-file - destItem = fileItemModel()->rootItem(); + destItem = m_model->rootItem(); destUrl = url(); } else { // The item represents a directory or desktop-file @@ -973,13 +979,16 @@ void DolphinView::slotModelChanged(KItemModelBase* current, KItemModelBase* prev { if (previous != 0) { disconnect(previous, SIGNAL(directoryLoadingCompleted()), this, SLOT(slotDirectoryLoadingCompleted())); + m_versionControlObserver->setModel(0); } - Q_ASSERT(qobject_cast(current)); - connect(current, SIGNAL(loadingCompleted()), this, SLOT(slotDirectoryLoadingCompleted())); + if (current) { + Q_ASSERT(qobject_cast(current)); + connect(current, SIGNAL(loadingCompleted()), this, SLOT(slotDirectoryLoadingCompleted())); - KFileItemModel* fileItemModel = static_cast(current); - m_versionControlObserver->setModel(fileItemModel); + KFileItemModel* fileItemModel = static_cast(current); + m_versionControlObserver->setModel(fileItemModel); + } } void DolphinView::slotMouseButtonPressed(int itemIndex, Qt::MouseButtons buttons) @@ -1034,8 +1043,7 @@ void DolphinView::updateSortOrder(Qt::SortOrder order) ViewProperties props(url()); props.setSortOrder(order); - KItemModelBase* model = fileItemModel(); - model->setSortOrder(order); + m_model->setSortOrder(order); emit sortOrderChanged(order); } @@ -1045,8 +1053,7 @@ void DolphinView::updateSortFoldersFirst(bool foldersFirst) ViewProperties props(url()); props.setSortFoldersFirst(foldersFirst); - KFileItemModel* model = fileItemModel(); - model->setSortDirectoriesFirst(foldersFirst); + m_model->setSortDirectoriesFirst(foldersFirst); emit sortFoldersFirstChanged(foldersFirst); } @@ -1082,7 +1089,7 @@ void DolphinView::restoreState(QDataStream& stream) // Restore expanded folders (only relevant for the details view - will be ignored by the view in other view modes) QSet urls; stream >> urls; - fileItemModel()->restoreExpandedDirectories(urls); + m_model->restoreExpandedDirectories(urls); } void DolphinView::saveState(QDataStream& stream) @@ -1090,7 +1097,7 @@ void DolphinView::saveState(QDataStream& stream) // Save the current item that has the keyboard focus const int currentIndex = m_container->controller()->selectionManager()->currentItem(); if (currentIndex != -1) { - KFileItem item = fileItemModel()->fileItem(currentIndex); + KFileItem item = m_model->fileItem(currentIndex); Q_ASSERT(!item.isNull()); // If the current index is valid a item must exist KUrl currentItemUrl = item.url(); stream << currentItemUrl; @@ -1104,12 +1111,12 @@ void DolphinView::saveState(QDataStream& stream) stream << QPoint(x, y); // Save expanded folders (only relevant for the details view - the set will be empty in other view modes) - stream << fileItemModel()->expandedDirectories(); + stream << m_model->expandedDirectories(); } KFileItem DolphinView::rootItem() const { - return fileItemModel()->rootItem(); + return m_model->rootItem(); } void DolphinView::observeCreatedItem(const KUrl& url) @@ -1147,7 +1154,7 @@ void DolphinView::updateViewState() { if (m_currentItemUrl != KUrl()) { KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); - const int currentIndex = fileItemModel()->index(m_currentItemUrl); + const int currentIndex = m_model->index(m_currentItemUrl); if (currentIndex != -1) { selectionManager->setCurrentItem(currentIndex); } else { @@ -1170,10 +1177,9 @@ void DolphinView::updateViewState() KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); QSet selectedItems = selectionManager->selectedItems(); - const KFileItemModel* model = fileItemModel(); foreach (const KUrl& url, m_selectedUrls) { - const int index = model->index(url); + const int index = m_model->index(url); if (index >= 0) { selectedItems.insert(index); } @@ -1195,10 +1201,9 @@ void DolphinView::calculateItemCount(int& fileCount, int& folderCount, KIO::filesize_t& totalFileSize) const { - const KFileItemModel* model = fileItemModel(); - const int itemCount = model->count(); + const int itemCount = m_model->count(); for (int i = 0; i < itemCount; ++i) { - const KFileItem item = model->fileItem(i); + const KFileItem item = m_model->fileItem(i); if (item.isDir()) { ++folderCount; } else { @@ -1258,7 +1263,7 @@ void DolphinView::slotItemsChanged() void DolphinView::slotSortOrderChangedByHeader(Qt::SortOrder current, Qt::SortOrder previous) { Q_UNUSED(previous); - Q_ASSERT(fileItemModel()->sortOrder() == current); + Q_ASSERT(m_model->sortOrder() == current); ViewProperties props(url()); props.setSortOrder(current); @@ -1269,7 +1274,7 @@ void DolphinView::slotSortOrderChangedByHeader(Qt::SortOrder current, Qt::SortOr void DolphinView::slotSortRoleChangedByHeader(const QByteArray& current, const QByteArray& previous) { Q_UNUSED(previous); - Q_ASSERT(fileItemModel()->sortRole() == current); + Q_ASSERT(m_model->sortRole() == current); ViewProperties props(url()); props.setSortRole(current); @@ -1295,8 +1300,8 @@ void DolphinView::slotVisibleRolesChangedByHeader(const QList& curre void DolphinView::slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value) { - if (role == "name") { - const KFileItem item = fileItemModel()->fileItem(index); + if (role == "text") { + const KFileItem item = m_model->fileItem(index); const QString newName = value.toString(); if (!newName.isEmpty() && newName != item.text() && newName != QLatin1String(".") && newName != QLatin1String("..")) { KonqOperations::rename(this, item.url(), newName); @@ -1304,11 +1309,6 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray& role, con } } -KFileItemModel* DolphinView::fileItemModel() const -{ - return static_cast(m_container->controller()->model()); -} - void DolphinView::loadDirectory(const KUrl& url, bool reload) { if (!url.isValid()) { @@ -1321,20 +1321,18 @@ void DolphinView::loadDirectory(const KUrl& url, bool reload) return; } - KFileItemModel* model = fileItemModel(); if (reload) { - model->refreshDirectory(url); + m_model->refreshDirectory(url); } else { - model->loadDirectory(url); + m_model->loadDirectory(url); } } void DolphinView::applyViewProperties() { - m_container->beginTransaction(); + m_view->beginTransaction(); const ViewProperties props(url()); - KFileItemModel* model = fileItemModel(); const Mode mode = props.viewMode(); if (m_mode != mode) { @@ -1344,49 +1342,43 @@ void DolphinView::applyViewProperties() // Changing the mode might result in changing // the zoom level. Remember the old zoom level so // that zoomLevelChanged() can get emitted. - const int oldZoomLevel = m_container->zoomLevel(); - - switch (m_mode) { - case IconsView: m_container->setItemLayout(KFileItemListView::IconsLayout); break; - case CompactView: m_container->setItemLayout(KFileItemListView::CompactLayout); break; - case DetailsView: m_container->setItemLayout(KFileItemListView::DetailsLayout); break; - default: Q_ASSERT(false); break; - } + const int oldZoomLevel = m_view->zoomLevel(); + applyModeToView(); emit modeChanged(m_mode, previousMode); - if (m_container->zoomLevel() != oldZoomLevel) { - emit zoomLevelChanged(m_container->zoomLevel(), oldZoomLevel); + if (m_view->zoomLevel() != oldZoomLevel) { + emit zoomLevelChanged(m_view->zoomLevel(), oldZoomLevel); } } const bool hiddenFilesShown = props.hiddenFilesShown(); - if (hiddenFilesShown != model->showHiddenFiles()) { - model->setShowHiddenFiles(hiddenFilesShown); + if (hiddenFilesShown != m_model->showHiddenFiles()) { + m_model->setShowHiddenFiles(hiddenFilesShown); emit hiddenFilesShownChanged(hiddenFilesShown); } const bool groupedSorting = props.groupedSorting(); - if (groupedSorting != model->groupedSorting()) { - model->setGroupedSorting(groupedSorting); + if (groupedSorting != m_model->groupedSorting()) { + m_model->setGroupedSorting(groupedSorting); emit groupedSortingChanged(groupedSorting); } const QByteArray sortRole = props.sortRole(); - if (sortRole != model->sortRole()) { - model->setSortRole(sortRole); + if (sortRole != m_model->sortRole()) { + m_model->setSortRole(sortRole); emit sortRoleChanged(sortRole); } const Qt::SortOrder sortOrder = props.sortOrder(); - if (sortOrder != model->sortOrder()) { - model->setSortOrder(sortOrder); + if (sortOrder != m_model->sortOrder()) { + m_model->setSortOrder(sortOrder); emit sortOrderChanged(sortOrder); } const bool sortFoldersFirst = props.sortFoldersFirst(); - if (sortFoldersFirst != model->sortDirectoriesFirst()) { - model->setSortDirectoriesFirst(sortFoldersFirst); + if (sortFoldersFirst != m_model->sortDirectoriesFirst()) { + m_model->setSortDirectoriesFirst(sortFoldersFirst); emit sortFoldersFirstChanged(sortFoldersFirst); } @@ -1394,15 +1386,15 @@ void DolphinView::applyViewProperties() if (visibleRoles != m_visibleRoles) { const QList previousVisibleRoles = m_visibleRoles; m_visibleRoles = visibleRoles; - m_container->setVisibleRoles(visibleRoles); + m_view->setVisibleRoles(visibleRoles); emit visibleRolesChanged(m_visibleRoles, previousVisibleRoles); } const bool previewsShown = props.previewsShown(); - if (previewsShown != m_container->previewsShown()) { + if (previewsShown != m_view->previewsShown()) { const int oldZoomLevel = zoomLevel(); - m_container->setPreviewsShown(previewsShown); + m_view->setPreviewsShown(previewsShown); emit previewsShownChanged(previewsShown); // Changing the preview-state might result in a changed zoom-level @@ -1429,7 +1421,17 @@ void DolphinView::applyViewProperties() } } - m_container->endTransaction(); + m_view->endTransaction(); +} + +void DolphinView::applyModeToView() +{ + switch (m_mode) { + case IconsView: m_view->setItemLayout(KFileItemListView::IconsLayout); break; + case CompactView: m_view->setItemLayout(KFileItemListView::CompactLayout); break; + case DetailsView: m_view->setItemLayout(KFileItemListView::DetailsLayout); break; + default: Q_ASSERT(false); break; + } } void DolphinView::pasteToUrl(const KUrl& url) @@ -1457,11 +1459,10 @@ KUrl::List DolphinView::simplifiedSelectedUrls() const QMimeData* DolphinView::selectionMimeData() const { - const KFileItemModel* model = fileItemModel(); const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); const QSet selectedIndexes = selectionManager->selectedItems(); - return model->createMimeData(selectedIndexes); + return m_model->createMimeData(selectedIndexes); } void DolphinView::markPastedUrlsAsSelected(const QMimeData* mimeData) @@ -1480,7 +1481,7 @@ void DolphinView::updateWritableState() const bool wasFolderWritable = m_isFolderWritable; m_isFolderWritable = true; - const KFileItem item = fileItemModel()->rootItem(); + const KFileItem item = m_model->rootItem(); if (!item.isNull()) { KFileItemListProperties capabilities(KFileItemList() << item); m_isFolderWritable = capabilities.supportsWriting(); diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index a9cf0ade4..bfac88502 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -39,10 +39,11 @@ typedef KIO::FileUndoManager::CommandType CommandType; -class DolphinItemListContainer; +class DolphinItemListView; class KAction; class KActionCollection; class KFileItemModel; +class KItemListContainer; class KItemModelBase; class KUrl; class ToolTipManager; @@ -659,8 +660,6 @@ private slots: void calculateItemCount(int& fileCount, int& folderCount, KIO::filesize_t& totalFileSize) const; private: - KFileItemModel* fileItemModel() const; - void loadDirectory(const KUrl& url, bool reload = false); /** @@ -669,6 +668,12 @@ private: */ void applyViewProperties(); + /** + * Applies the m_mode property to the corresponding + * itemlayout-property of the KItemListView. + */ + void applyModeToView(); + /** * Helper method for DolphinView::paste() and DolphinView::pasteIntoFolder(). * Pastes the clipboard data into the URL \a url. @@ -716,7 +721,9 @@ private: QVBoxLayout* m_topLayout; - DolphinItemListContainer* m_container; + KFileItemModel* m_model; + DolphinItemListView* m_view; + KItemListContainer* m_container; ToolTipManager* m_toolTipManager; diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp index e22936740..6f69a6420 100644 --- a/src/views/dolphinviewactionhandler.cpp +++ b/src/views/dolphinviewactionhandler.cpp @@ -246,8 +246,8 @@ QActionGroup* DolphinViewActionHandler::createFileItemRolesActionGroup(const QSt const QList rolesInfo = KFileItemModel::rolesInformation(); foreach (const KFileItemModel::RoleInfo& info, rolesInfo) { - if (!isSortGroup && info.role == "name") { - // It should not be possible to hide the "name" role + if (!isSortGroup && info.role == "text") { + // It should not be possible to hide the "text" role continue; } diff --git a/src/views/viewproperties.cpp b/src/views/viewproperties.cpp index 83958ad00..d19b3bfda 100644 --- a/src/views/viewproperties.cpp +++ b/src/views/viewproperties.cpp @@ -33,8 +33,9 @@ #include namespace { - const int CurrentViewPropertiesVersion = 2; const int AdditionalInfoViewPropertiesVersion = 1; + const int NameRolePropertiesVersion = 2; + const int CurrentViewPropertiesVersion = 3; // String representation to mark the additional properties of // the details view as customized by the user. See @@ -256,7 +257,7 @@ QList ViewProperties::visibleRoles() const // is accepted. QList roles; - roles.append("name"); + roles.append("text"); // Iterate through all stored keys and append all roles that match to // the curren view mode. @@ -264,16 +265,19 @@ QList ViewProperties::visibleRoles() const const int prefixLength = prefix.length(); QStringList visibleRoles = m_node->visibleRoles(); - if (visibleRoles.isEmpty() && m_node->version() <= AdditionalInfoViewPropertiesVersion) { + const int version = m_node->version(); + if (visibleRoles.isEmpty() && version <= AdditionalInfoViewPropertiesVersion) { // Convert the obsolete additionalInfo-property from older versions into the // visibleRoles-property visibleRoles = const_cast(this)->convertAdditionalInfo(); + } else if (version <= NameRolePropertiesVersion) { + visibleRoles = const_cast(this)->convertNameRole(); } foreach (const QString& visibleRole, visibleRoles) { if (visibleRole.startsWith(prefix)) { const QByteArray role = visibleRole.right(visibleRole.length() - prefixLength).toLatin1(); - if (role != "name") { + if (role != "text") { roles.append(role); } } @@ -281,7 +285,7 @@ QList ViewProperties::visibleRoles() const // For the details view the size and date should be shown per default // until the additional information has been explicitly changed by the user - const bool useDefaultValues = roles.count() == 1 // "name" + const bool useDefaultValues = roles.count() == 1 // "text" && (m_node->viewMode() == DolphinView::DetailsView) && !visibleRoles.contains(CustomizedDetailsString); if (useDefaultValues) { @@ -403,6 +407,23 @@ QStringList ViewProperties::convertAdditionalInfo() return visibleRoles; } +QStringList ViewProperties::convertNameRole() +{ + QStringList visibleRoles = m_node->visibleRoles(); + for (int i = 0; i < visibleRoles.count(); ++i) { + if (visibleRoles[i].endsWith("_name")) { + const int leftLength = visibleRoles[i].length() - 5; + visibleRoles[i] = visibleRoles[i].left(leftLength) + "_text"; + } + } + + m_node->setVisibleRoles(visibleRoles); + update(); + + return visibleRoles; +} + + bool ViewProperties::isPartOfHome(const QString& filePath) { // For performance reasons cache the path in a static QString diff --git a/src/views/viewproperties.h b/src/views/viewproperties.h index 303c04227..29f7c282c 100644 --- a/src/views/viewproperties.h +++ b/src/views/viewproperties.h @@ -142,11 +142,18 @@ private: /** * Provides backward compatibility with .directory files created with - * Dolphin < 2.1: Converts the old additionalInfo-property into + * Dolphin < 2.0: Converts the old additionalInfo-property into * the visibleRoles-property and clears the additionalInfo-property. */ QStringList convertAdditionalInfo(); + /** + * Provides backward compatibility with .directory files created with + * Dolphin < 2.1: Converts the old name-role "name" to the generic + * role "text". + */ + QStringList convertNameRole(); + /** * Returns true, if \a filePath is part of the home-path (see QDir::homePath()). */ -- 2.47.3