kitemviews/kitemlistcontroller.cpp
kitemviews/kitemlistgroupheader.cpp
kitemviews/kitemlistheader.cpp
+ kitemviews/kitemlistheaderwidget.cpp
kitemviews/kitemlistkeyboardsearchmanager.cpp
kitemviews/kitemlistrubberband.cpp
kitemviews/kitemlistselectionmanager.cpp
return QSize();
}
-QHash<QByteArray, QSizeF> KFileItemListView::visibleRolesSizes(const KItemRangeList& itemRanges) const
+QHash<QByteArray, qreal> KFileItemListView::columnWidths(const KItemRangeList& itemRanges) const
{
QElapsedTimer timer;
timer.start();
- QHash<QByteArray, QSizeF> sizes;
+ QHash<QByteArray, qreal> widths;
int calculatedItemCount = 0;
bool maxTimeExceeded = false;
for (int i = startIndex; i <= endIndex; ++i) {
foreach (const QByteArray& visibleRole, visibleRoles()) {
- QSizeF maxSize = sizes.value(visibleRole, QSizeF(0, 0));
+ qreal maxWidth = widths.value(visibleRole, 0);
const QSizeF itemSize = visibleRoleSizeHint(i, visibleRole);
- maxSize = maxSize.expandedTo(itemSize);
- sizes.insert(visibleRole, maxSize);
+ maxWidth = qMax(itemSize.width(), maxWidth);
+ widths.insert(visibleRole, maxWidth);
}
if (calculatedItemCount > 100 && timer.elapsed() > 200) {
}
kDebug() << "[TIME] Calculated dynamic item size for " << rangesItemCount << "items:" << timer.elapsed();
#endif
- return sizes;
+ return widths;
}
QPixmap KFileItemListView::createDragPixmap(const QSet<int>& indexes) const
virtual QSizeF itemSizeHint(int index) const;
/** @reimp */
- virtual QHash<QByteArray, QSizeF> visibleRolesSizes(const KItemRangeList& itemRanges) const;
+ virtual QHash<QByteArray, qreal> columnWidths(const KItemRangeList& itemRanges) const;
/** @reimp */
virtual QPixmap createDragPixmap(const QSet<int>& indexes) const;
// 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 (m_textPos[Name + 1].x() < minX) {
+ if (m_textPos[Name].x() + columnWidth("name") > minX) {
clipAdditionalInfoBounds = true;
painter->save();
painter->setClipRect(minX, 0, size().width() - minX, size().height(), Qt::IntersectClip);
m_dirtyLayout = true;
}
-void KFileItemListWidget::visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current,
- const QHash<QByteArray, QSizeF>& previous)
+void KFileItemListWidget::columnWidthChanged(const QByteArray& role,
+ qreal current,
+ qreal previous)
{
+ Q_UNUSED(role);
Q_UNUSED(current);
Q_UNUSED(previous);
m_dirtyLayout = true;
// Elide the text in case it does not fit into the available column-width
qreal requiredWidth = option.fontMetrics.width(text);
- const qreal columnWidth = visibleRolesSizes().value(role, QSizeF(0, 0)).width();
- qreal availableTextWidth = columnWidth - 2 * columnPadding;
+ const qreal roleWidth = columnWidth(role);
+ qreal availableTextWidth = roleWidth - 2 * columnPadding;
if (textId == Name) {
availableTextWidth -= firstColumnInc;
}
m_text[textId].setText(text);
m_textPos[textId] = QPointF(x + columnPadding, y);
- x += columnWidth;
+ x += roleWidth;
switch (textId) {
case Name: {
}
case Size:
// The values for the size should be right aligned
- m_textPos[textId].rx() += columnWidth - requiredWidth - 2 * columnPadding;
+ m_textPos[textId].rx() += roleWidth - requiredWidth - 2 * columnPadding;
break;
default:
virtual void dataChanged(const QHash<QByteArray, QVariant>& current, const QSet<QByteArray>& roles = QSet<QByteArray>());
virtual void visibleRolesChanged(const QList<QByteArray>& current, const QList<QByteArray>& previous);
- virtual void visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current, const QHash<QByteArray, QSizeF>& 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);
/***************************************************************************
- * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
+ * Copyright (C) 2012 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
-#include "kitemlistheader_p.h"
-
-#include <KAction>
-#include <KMenu>
-#include "kitemmodelbase.h"
-
-#include <QApplication>
-#include <QGraphicsSceneHoverEvent>
-#include <QPainter>
-#include <QStyleOptionHeader>
-
-#include <KDebug>
-
-KItemListHeader::KItemListHeader(QGraphicsWidget* parent) :
- QGraphicsWidget(parent),
- m_model(0),
- m_visibleRoles(),
- m_visibleRolesWidths(),
- m_hoveredRoleIndex(-1),
- m_pressedRoleIndex(-1),
- m_roleOperation(NoRoleOperation),
- m_pressedMousePos(),
- m_movingRole()
-{
- m_movingRole.x = 0;
- m_movingRole.xDec = 0;
- m_movingRole.index = -1;
-
- setAcceptHoverEvents(true);
-
- QStyleOptionHeader option;
- const QSize headerSize = style()->sizeFromContents(QStyle::CT_HeaderSection, &option, QSize());
- resize(0, headerSize.height());
-}
+#include "kitemlistheader.h"
+#include "kitemlistheaderwidget_p.h"
+#include "kitemlistview.h"
KItemListHeader::~KItemListHeader()
{
}
-void KItemListHeader::setModel(KItemModelBase* model)
-{
- if (m_model == model) {
- return;
- }
-
- if (m_model) {
- disconnect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
- this, SLOT(slotSortRoleChanged(QByteArray,QByteArray)));
- disconnect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
- this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder)));
- }
-
- m_model = model;
-
- if (m_model) {
- connect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
- this, SLOT(slotSortRoleChanged(QByteArray,QByteArray)));
- connect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
- this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder)));
- }
-}
-
-KItemModelBase* KItemListHeader::model() const
-{
- return m_model;
-}
-
-void KItemListHeader::setVisibleRoles(const QList<QByteArray>& roles)
-{
- m_visibleRoles = roles;
- update();
-}
-
-QList<QByteArray> KItemListHeader::visibleRoles() const
-{
- return m_visibleRoles;
-}
-
-void KItemListHeader::setVisibleRolesWidths(const QHash<QByteArray, qreal>& rolesWidths)
-{
- m_visibleRolesWidths = rolesWidths;
-
- // Assure that no width is smaller than the minimum allowed width
- const qreal minWidth = minimumRoleWidth();
- QMutableHashIterator<QByteArray, qreal> it(m_visibleRolesWidths);
- while (it.hasNext()) {
- it.next();
- if (it.value() < minWidth) {
- m_visibleRolesWidths.insert(it.key(), minWidth);
- }
- }
-
- update();
-}
-
-QHash<QByteArray, qreal> KItemListHeader::visibleRolesWidths() const
-{
- return m_visibleRolesWidths;
-}
-
-qreal KItemListHeader::minimumRoleWidth() const
-{
- QFontMetricsF fontMetrics(font());
- return fontMetrics.height() * 4;
-}
-
-void KItemListHeader::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
-{
- Q_UNUSED(option);
- Q_UNUSED(widget);
-
- if (!m_model) {
- return;
- }
-
- // Draw roles
- painter->setFont(font());
- painter->setPen(palette().text().color());
-
- qreal x = 0;
- int orderIndex = 0;
- foreach (const QByteArray& role, m_visibleRoles) {
- const qreal roleWidth = m_visibleRolesWidths.value(role);
- const QRectF rect(x, 0, roleWidth, size().height());
- paintRole(painter, role, rect, orderIndex, widget);
- x += roleWidth;
- ++orderIndex;
- }
-
- // Draw background without roles
- QStyleOption opt;
- opt.init(widget);
- opt.rect = QRect(x, 0, size().width() - x, size().height());
- opt.state |= QStyle::State_Horizontal;
- style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, painter);
-
- if (!m_movingRole.pixmap.isNull()) {
- Q_ASSERT(m_roleOperation == MoveRoleOperation);
- painter->drawPixmap(m_movingRole.x, 0, m_movingRole.pixmap);
- }
-}
-
-void KItemListHeader::mousePressEvent(QGraphicsSceneMouseEvent* event)
-{
- if (event->button() & Qt::LeftButton) {
- updatePressedRoleIndex(event->pos());
- m_pressedMousePos = event->pos();
- m_roleOperation = isAboveRoleGrip(m_pressedMousePos, m_pressedRoleIndex) ?
- ResizeRoleOperation : NoRoleOperation;
- event->accept();
- } else {
- event->ignore();
- }
-}
-
-void KItemListHeader::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
+void KItemListHeader::setAutomaticColumnResizing(bool automatic)
{
- QGraphicsWidget::mouseReleaseEvent(event);
-
- if (m_pressedRoleIndex == -1) {
- return;
- }
-
- switch (m_roleOperation) {
- case NoRoleOperation: {
- // Only a click has been done and no moving or resizing has been started
- const QByteArray sortRole = m_model->sortRole();
- const int sortRoleIndex = m_visibleRoles.indexOf(sortRole);
- if (m_pressedRoleIndex == sortRoleIndex) {
- // Toggle the sort order
- const Qt::SortOrder previous = m_model->sortOrder();
- const Qt::SortOrder current = (m_model->sortOrder() == Qt::AscendingOrder) ?
- Qt::DescendingOrder : Qt::AscendingOrder;
- m_model->setSortOrder(current);
- emit sortOrderChanged(current, previous);
- } else {
- // Change the sort role
- const QByteArray previous = m_model->sortRole();
- const QByteArray current = m_visibleRoles[m_pressedRoleIndex];
- m_model->setSortRole(current);
- emit sortRoleChanged(current, previous);
- }
- break;
- }
-
- case MoveRoleOperation:
- m_movingRole.pixmap = QPixmap();
- m_movingRole.x = 0;
- m_movingRole.xDec = 0;
- m_movingRole.index = -1;
- break;
-
- default:
- break;
- }
-
- m_pressedRoleIndex = -1;
- m_roleOperation = NoRoleOperation;
- update();
-
- QApplication::restoreOverrideCursor();
-}
-
-void KItemListHeader::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
-{
- QGraphicsWidget::mouseMoveEvent(event);
-
- switch (m_roleOperation) {
- case NoRoleOperation:
- if ((event->pos() - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
- // A role gets dragged by the user. Create a pixmap of the role that will get
- // synchronized on each furter mouse-move-event with the mouse-position.
- m_roleOperation = MoveRoleOperation;
- const int roleIndex = roleIndexAt(m_pressedMousePos);
- m_movingRole.index = roleIndex;
- if (roleIndex == 0) {
- // TODO: It should be configurable whether moving the first role is allowed.
- // In the context of Dolphin this is not required, however this should be
- // changed if KItemViews are used in a more generic way.
- QApplication::setOverrideCursor(QCursor(Qt::ForbiddenCursor));
- } else {
- m_movingRole.pixmap = createRolePixmap(roleIndex);
-
- qreal roleX = 0;
- for (int i = 0; i < roleIndex; ++i) {
- const QByteArray role = m_visibleRoles[i];
- roleX += m_visibleRolesWidths.value(role);
- }
-
- m_movingRole.xDec = event->pos().x() - roleX;
- m_movingRole.x = roleX;
- update();
- }
+ if (m_headerWidget->automaticColumnResizing() != automatic) {
+ m_headerWidget->setAutomaticColumnResizing(automatic);
+ if (automatic) {
+ m_view->updateColumnWidthsForHeader();
}
- break;
-
- case ResizeRoleOperation: {
- const QByteArray pressedRole = m_visibleRoles[m_pressedRoleIndex];
-
- qreal previousWidth = m_visibleRolesWidths.value(pressedRole);
- qreal currentWidth = previousWidth;
- currentWidth += event->pos().x() - event->lastPos().x();
- currentWidth = qMax(minimumRoleWidth(), currentWidth);
-
- m_visibleRolesWidths.insert(pressedRole, currentWidth);
- update();
-
- emit visibleRoleWidthChanged(pressedRole, currentWidth, previousWidth);
- break;
- }
-
- case MoveRoleOperation: {
- // TODO: It should be configurable whether moving the first role is allowed.
- // In the context of Dolphin this is not required, however this should be
- // changed if KItemViews are used in a more generic way.
- if (m_movingRole.index > 0) {
- m_movingRole.x = event->pos().x() - m_movingRole.xDec;
- update();
-
- const int targetIndex = targetOfMovingRole();
- if (targetIndex > 0 && targetIndex != m_movingRole.index) {
- const QByteArray role = m_visibleRoles[m_movingRole.index];
- const int previousIndex = m_movingRole.index;
- m_movingRole.index = targetIndex;
- emit visibleRoleMoved(role, targetIndex, previousIndex);
-
- m_movingRole.xDec = event->pos().x() - roleXPosition(role);
- }
- }
- break;
- }
-
- default:
- break;
- }
-}
-
-void KItemListHeader::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
-{
- QGraphicsWidget::hoverEnterEvent(event);
- updateHoveredRoleIndex(event->pos());
-}
-
-void KItemListHeader::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
-{
- QGraphicsWidget::hoverLeaveEvent(event);
- if (m_hoveredRoleIndex != -1) {
- m_hoveredRoleIndex = -1;
- update();
- }
-}
-
-void KItemListHeader::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
-{
- QGraphicsWidget::hoverMoveEvent(event);
-
- const QPointF& pos = event->pos();
- updateHoveredRoleIndex(pos);
- if (m_hoveredRoleIndex >= 0 && isAboveRoleGrip(pos, m_hoveredRoleIndex)) {
- setCursor(Qt::SplitHCursor);
- } else {
- unsetCursor();
- }
-}
-
-void KItemListHeader::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous)
-{
- Q_UNUSED(current);
- Q_UNUSED(previous);
- update();
-}
-
-void KItemListHeader::slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous)
-{
- Q_UNUSED(current);
- Q_UNUSED(previous);
- update();
-}
-
-void KItemListHeader::paintRole(QPainter* painter,
- const QByteArray& role,
- const QRectF& rect,
- int orderIndex,
- QWidget* widget) const
-{
- // The following code is based on the code from QHeaderView::paintSection().
- // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
- QStyleOptionHeader option;
- option.section = orderIndex;
- option.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal;
- if (isEnabled()) {
- option.state |= QStyle::State_Enabled;
- }
- if (window() && window()->isActiveWindow()) {
- option.state |= QStyle::State_Active;
- }
- if (m_hoveredRoleIndex == orderIndex) {
- option.state |= QStyle::State_MouseOver;
- }
- if (m_pressedRoleIndex == orderIndex) {
- option.state |= QStyle::State_Sunken;
- }
- if (m_model->sortRole() == role) {
- option.sortIndicator = (m_model->sortOrder() == Qt::AscendingOrder) ?
- QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
- }
- option.rect = rect.toRect();
-
- if (m_visibleRoles.count() == 1) {
- option.position = QStyleOptionHeader::OnlyOneSection;
- } else if (orderIndex == 0) {
- option.position = QStyleOptionHeader::Beginning;
- } else if (orderIndex == m_visibleRoles.count() - 1) {
- option.position = QStyleOptionHeader::End;
- } else {
- option.position = QStyleOptionHeader::Middle;
- }
-
- option.orientation = Qt::Horizontal;
- option.selectedPosition = QStyleOptionHeader::NotAdjacent;
- option.text = m_model->roleDescription(role);
-
- style()->drawControl(QStyle::CE_Header, &option, painter, widget);
-}
-
-void KItemListHeader::updatePressedRoleIndex(const QPointF& pos)
-{
- const int pressedIndex = roleIndexAt(pos);
- if (m_pressedRoleIndex != pressedIndex) {
- m_pressedRoleIndex = pressedIndex;
- update();
- }
-}
-
-void KItemListHeader::updateHoveredRoleIndex(const QPointF& pos)
-{
- const int hoverIndex = roleIndexAt(pos);
- if (m_hoveredRoleIndex != hoverIndex) {
- m_hoveredRoleIndex = hoverIndex;
- update();
}
}
-int KItemListHeader::roleIndexAt(const QPointF& pos) const
+bool KItemListHeader::automaticColumnResizing() const
{
- int index = -1;
-
- qreal x = 0;
- foreach (const QByteArray& role, m_visibleRoles) {
- ++index;
- x += m_visibleRolesWidths.value(role);
- if (pos.x() <= x) {
- break;
- }
- }
-
- return index;
+ return m_headerWidget->automaticColumnResizing();
}
-bool KItemListHeader::isAboveRoleGrip(const QPointF& pos, int roleIndex) const
+void KItemListHeader::setColumnWidth(const QByteArray& role, qreal width)
{
- qreal x = 0;
- for (int i = 0; i <= roleIndex; ++i) {
- const QByteArray role = m_visibleRoles[i];
- x += m_visibleRolesWidths.value(role);
+ if (!m_headerWidget->automaticColumnResizing()) {
+ m_headerWidget->setColumnWidth(role, width);
+ m_view->applyColumnWidthsFromHeader();
}
-
- const int grip = style()->pixelMetric(QStyle::PM_HeaderGripMargin);
- return pos.x() >= (x - grip) && pos.x() <= x;
}
-QPixmap KItemListHeader::createRolePixmap(int roleIndex) const
+qreal KItemListHeader::columnWidth(const QByteArray& role) const
{
- const QByteArray role = m_visibleRoles[roleIndex];
- const qreal roleWidth = m_visibleRolesWidths.value(role);
- const QRect rect(0, 0, roleWidth, size().height());
-
- QImage image(rect.size(), QImage::Format_ARGB32_Premultiplied);
-
- QPainter painter(&image);
- paintRole(&painter, role, rect, roleIndex);
-
- // Apply a highlighting-color
- const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive;
- QColor highlightColor = palette().color(group, QPalette::Highlight);
- highlightColor.setAlpha(64);
- painter.fillRect(rect, highlightColor);
-
- // Make the image transparent
- painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
- painter.fillRect(0, 0, image.width(), image.height(), QColor(0, 0, 0, 192));
-
- return QPixmap::fromImage(image);
+ return m_headerWidget->columnWidth(role);
}
-int KItemListHeader::targetOfMovingRole() const
+void KItemListHeader::setColumnWidths(const QHash<QByteArray, qreal>& columnWidths)
{
- const int movingWidth = m_movingRole.pixmap.width();
- const int movingLeft = m_movingRole.x;
- const int movingRight = movingLeft + movingWidth - 1;
-
- int targetIndex = 0;
- qreal targetLeft = 0;
- while (targetIndex < m_visibleRoles.count()) {
- const QByteArray role = m_visibleRoles[targetIndex];
- const qreal targetWidth = m_visibleRolesWidths.value(role);
- const qreal targetRight = targetLeft + targetWidth - 1;
-
- const bool isInTarget = (targetWidth >= movingWidth &&
- movingLeft >= targetLeft &&
- movingRight <= targetRight) ||
- (targetWidth < movingWidth &&
- movingLeft <= targetLeft &&
- movingRight >= targetRight);
-
- if (isInTarget) {
- return targetIndex;
+ if (!m_headerWidget->automaticColumnResizing()) {
+ foreach (const QByteArray& role, m_view->visibleRoles()) {
+ const qreal width = columnWidths.value(role);
+ m_headerWidget->setColumnWidth(role, width);
}
- targetLeft += targetWidth;
- ++targetIndex;
+ m_view->applyColumnWidthsFromHeader();
}
-
- return m_movingRole.index;
}
-qreal KItemListHeader::roleXPosition(const QByteArray& role) const
+KItemListHeader::KItemListHeader(KItemListView* listView) :
+ QObject(listView->parent()),
+ m_view(listView)
{
- qreal x = 0;
- foreach (const QByteArray& visibleRole, m_visibleRoles) {
- if (visibleRole == role) {
- return x;
- }
-
- x += m_visibleRolesWidths.value(visibleRole);
- }
+ m_headerWidget = m_view->m_headerWidget;
+ Q_ASSERT(m_headerWidget);
- return -1;
+ connect(m_headerWidget, SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)),
+ this, SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)));
}
-#include "kitemlistheader_p.moc"
+#include "kitemlistheader.moc"
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2012 by Peter Penz <peter.penz19@gmail.com> *
+ * *
+ * 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 KITEMLISTHEADER_H
+#define KITEMLISTHEADER_H
+
+#include <libdolphin_export.h>
+#include <QHash>
+#include <QObject>
+
+class KItemListHeaderWidget;
+class KItemListView;
+
+/**
+ * @brief Provides access to the header of a KItemListView.
+ *
+ * Each column of the header represents a visible role
+ * accessible by KItemListView::visibleRoles().
+ */
+class LIBDOLPHINPRIVATE_EXPORT KItemListHeader : public QObject
+{
+ Q_OBJECT
+
+public:
+ virtual ~KItemListHeader();
+
+ /**
+ * If set to true, KItemListView will automatically adjust the
+ * widths of the columns. If set to false, the size can be
+ * manually adjusted by KItemListHeader::setColumnWidth().
+ */
+ void setAutomaticColumnResizing(bool automatic);
+ bool automaticColumnResizing() const;
+
+ /**
+ * Sets the width of the column for the given role. Note that
+ * the width only gets applied if KItemListHeader::automaticColumnResizing()
+ * has been turned off.
+ */
+ void setColumnWidth(const QByteArray& role, qreal width);
+ qreal columnWidth(const QByteArray& role) const;
+
+ /**
+ * Sets the widths of the columns for all roles. From a performance point of
+ * view calling this method should be preferred over several setColumnWidth()
+ * calls in case if the width of more than one column should be changed.
+ * Note that the widths only get applied if KItemListHeader::automaticColumnResizing()
+ * has been turned off.
+ */
+ void setColumnWidths(const QHash<QByteArray, qreal>& columnWidths);
+
+signals:
+ /**
+ * Is emitted if the width of a column has been adjusted by the user with the mouse
+ * (no signal is emitted if KItemListHeader::setColumnWidth() is invoked).
+ */
+ void columnWidthChanged(const QByteArray& role,
+ qreal currentWidth,
+ qreal previousWidth);
+
+private:
+ KItemListHeader(KItemListView* listView);
+
+private:
+ KItemListView* m_view;
+ KItemListHeaderWidget* m_headerWidget;
+
+ friend class KItemListView; // Constructs the KItemListHeader instance
+};
+
+#endif
+
+
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
+ * *
+ * 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 "kitemlistheaderwidget_p.h"
+
+#include <KAction>
+#include <KMenu>
+#include "kitemmodelbase.h"
+
+#include <QApplication>
+#include <QGraphicsSceneHoverEvent>
+#include <QPainter>
+#include <QStyleOptionHeader>
+
+#include <KDebug>
+
+KItemListHeaderWidget::KItemListHeaderWidget(QGraphicsWidget* parent) :
+ QGraphicsWidget(parent),
+ m_automaticColumnResizing(false),
+ m_model(0),
+ m_columns(),
+ m_columnsWidths(),
+ m_hoveredRoleIndex(-1),
+ m_pressedRoleIndex(-1),
+ m_roleOperation(NoRoleOperation),
+ m_pressedMousePos(),
+ m_movingRole()
+{
+ m_movingRole.x = 0;
+ m_movingRole.xDec = 0;
+ m_movingRole.index = -1;
+
+ setAcceptHoverEvents(true);
+
+ QStyleOptionHeader option;
+ const QSize headerSize = style()->sizeFromContents(QStyle::CT_HeaderSection, &option, QSize());
+ resize(0, headerSize.height());
+}
+
+KItemListHeaderWidget::~KItemListHeaderWidget()
+{
+}
+
+void KItemListHeaderWidget::setModel(KItemModelBase* model)
+{
+ if (m_model == model) {
+ return;
+ }
+
+ if (m_model) {
+ disconnect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
+ this, SLOT(slotSortRoleChanged(QByteArray,QByteArray)));
+ disconnect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
+ this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder)));
+ }
+
+ m_model = model;
+
+ if (m_model) {
+ connect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
+ this, SLOT(slotSortRoleChanged(QByteArray,QByteArray)));
+ connect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
+ this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder)));
+ }
+}
+
+KItemModelBase* KItemListHeaderWidget::model() const
+{
+ return m_model;
+}
+
+void KItemListHeaderWidget::setAutomaticColumnResizing(bool automatic)
+{
+ m_automaticColumnResizing = automatic;
+}
+
+bool KItemListHeaderWidget::automaticColumnResizing() const
+{
+ return m_automaticColumnResizing;
+}
+
+void KItemListHeaderWidget::setColumns(const QList<QByteArray>& roles)
+{
+ m_columns = roles;
+ update();
+}
+
+QList<QByteArray> KItemListHeaderWidget::columns() const
+{
+ return m_columns;
+}
+
+void KItemListHeaderWidget::setColumnWidth(const QByteArray& role, qreal width)
+{
+ const qreal minWidth = minimumColumnWidth();
+ if (width < minWidth) {
+ width = minWidth;
+ }
+
+ if (m_columnsWidths.value(role) != width) {
+ m_columnsWidths.insert(role, width);
+ update();
+ }
+}
+
+qreal KItemListHeaderWidget::columnWidth(const QByteArray& role) const
+{
+ return m_columnsWidths.value(role);
+}
+
+qreal KItemListHeaderWidget::minimumColumnWidth() const
+{
+ QFontMetricsF fontMetrics(font());
+ return fontMetrics.height() * 4;
+}
+
+void KItemListHeaderWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
+{
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+
+ if (!m_model) {
+ return;
+ }
+
+ // Draw roles
+ painter->setFont(font());
+ painter->setPen(palette().text().color());
+
+ qreal x = 0;
+ int orderIndex = 0;
+ foreach (const QByteArray& role, m_columns) {
+ const qreal roleWidth = m_columnsWidths.value(role);
+ const QRectF rect(x, 0, roleWidth, size().height());
+ paintRole(painter, role, rect, orderIndex, widget);
+ x += roleWidth;
+ ++orderIndex;
+ }
+
+ // Draw background without roles
+ QStyleOption opt;
+ opt.init(widget);
+ opt.rect = QRect(x, 0, size().width() - x, size().height());
+ opt.state |= QStyle::State_Horizontal;
+ style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, painter);
+
+ if (!m_movingRole.pixmap.isNull()) {
+ Q_ASSERT(m_roleOperation == MoveRoleOperation);
+ painter->drawPixmap(m_movingRole.x, 0, m_movingRole.pixmap);
+ }
+}
+
+void KItemListHeaderWidget::mousePressEvent(QGraphicsSceneMouseEvent* event)
+{
+ if (event->button() & Qt::LeftButton) {
+ updatePressedRoleIndex(event->pos());
+ m_pressedMousePos = event->pos();
+ m_roleOperation = isAboveRoleGrip(m_pressedMousePos, m_pressedRoleIndex) ?
+ ResizeRoleOperation : NoRoleOperation;
+ event->accept();
+ } else {
+ event->ignore();
+ }
+}
+
+void KItemListHeaderWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
+{
+ QGraphicsWidget::mouseReleaseEvent(event);
+
+ if (m_pressedRoleIndex == -1) {
+ return;
+ }
+
+ switch (m_roleOperation) {
+ case NoRoleOperation: {
+ // Only a click has been done and no moving or resizing has been started
+ const QByteArray sortRole = m_model->sortRole();
+ const int sortRoleIndex = m_columns.indexOf(sortRole);
+ if (m_pressedRoleIndex == sortRoleIndex) {
+ // Toggle the sort order
+ const Qt::SortOrder previous = m_model->sortOrder();
+ const Qt::SortOrder current = (m_model->sortOrder() == Qt::AscendingOrder) ?
+ Qt::DescendingOrder : Qt::AscendingOrder;
+ m_model->setSortOrder(current);
+ emit sortOrderChanged(current, previous);
+ } else {
+ // Change the sort role
+ const QByteArray previous = m_model->sortRole();
+ const QByteArray current = m_columns[m_pressedRoleIndex];
+ m_model->setSortRole(current);
+ emit sortRoleChanged(current, previous);
+ }
+ break;
+ }
+
+ case MoveRoleOperation:
+ m_movingRole.pixmap = QPixmap();
+ m_movingRole.x = 0;
+ m_movingRole.xDec = 0;
+ m_movingRole.index = -1;
+ break;
+
+ default:
+ break;
+ }
+
+ m_pressedRoleIndex = -1;
+ m_roleOperation = NoRoleOperation;
+ update();
+
+ QApplication::restoreOverrideCursor();
+}
+
+void KItemListHeaderWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
+{
+ QGraphicsWidget::mouseMoveEvent(event);
+
+ switch (m_roleOperation) {
+ case NoRoleOperation:
+ if ((event->pos() - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
+ // A role gets dragged by the user. Create a pixmap of the role that will get
+ // synchronized on each furter mouse-move-event with the mouse-position.
+ m_roleOperation = MoveRoleOperation;
+ const int roleIndex = roleIndexAt(m_pressedMousePos);
+ m_movingRole.index = roleIndex;
+ if (roleIndex == 0) {
+ // TODO: It should be configurable whether moving the first role is allowed.
+ // In the context of Dolphin this is not required, however this should be
+ // changed if KItemViews are used in a more generic way.
+ QApplication::setOverrideCursor(QCursor(Qt::ForbiddenCursor));
+ } else {
+ m_movingRole.pixmap = createRolePixmap(roleIndex);
+
+ qreal roleX = 0;
+ for (int i = 0; i < roleIndex; ++i) {
+ const QByteArray role = m_columns[i];
+ roleX += m_columnsWidths.value(role);
+ }
+
+ m_movingRole.xDec = event->pos().x() - roleX;
+ m_movingRole.x = roleX;
+ update();
+ }
+ }
+ break;
+
+ case ResizeRoleOperation: {
+ const QByteArray pressedRole = m_columns[m_pressedRoleIndex];
+
+ qreal previousWidth = m_columnsWidths.value(pressedRole);
+ qreal currentWidth = previousWidth;
+ currentWidth += event->pos().x() - event->lastPos().x();
+ currentWidth = qMax(minimumColumnWidth(), currentWidth);
+
+ m_columnsWidths.insert(pressedRole, currentWidth);
+ update();
+
+ emit columnWidthChanged(pressedRole, currentWidth, previousWidth);
+ break;
+ }
+
+ case MoveRoleOperation: {
+ // TODO: It should be configurable whether moving the first role is allowed.
+ // In the context of Dolphin this is not required, however this should be
+ // changed if KItemViews are used in a more generic way.
+ if (m_movingRole.index > 0) {
+ m_movingRole.x = event->pos().x() - m_movingRole.xDec;
+ update();
+
+ const int targetIndex = targetOfMovingRole();
+ if (targetIndex > 0 && targetIndex != m_movingRole.index) {
+ const QByteArray role = m_columns[m_movingRole.index];
+ const int previousIndex = m_movingRole.index;
+ m_movingRole.index = targetIndex;
+ emit columnMoved(role, targetIndex, previousIndex);
+
+ m_movingRole.xDec = event->pos().x() - roleXPosition(role);
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void KItemListHeaderWidget::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
+{
+ QGraphicsWidget::hoverEnterEvent(event);
+ updateHoveredRoleIndex(event->pos());
+}
+
+void KItemListHeaderWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
+{
+ QGraphicsWidget::hoverLeaveEvent(event);
+ if (m_hoveredRoleIndex != -1) {
+ m_hoveredRoleIndex = -1;
+ update();
+ }
+}
+
+void KItemListHeaderWidget::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
+{
+ QGraphicsWidget::hoverMoveEvent(event);
+
+ const QPointF& pos = event->pos();
+ updateHoveredRoleIndex(pos);
+ if (m_hoveredRoleIndex >= 0 && isAboveRoleGrip(pos, m_hoveredRoleIndex)) {
+ setCursor(Qt::SplitHCursor);
+ } else {
+ unsetCursor();
+ }
+}
+
+void KItemListHeaderWidget::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous)
+{
+ Q_UNUSED(current);
+ Q_UNUSED(previous);
+ update();
+}
+
+void KItemListHeaderWidget::slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous)
+{
+ Q_UNUSED(current);
+ Q_UNUSED(previous);
+ update();
+}
+
+void KItemListHeaderWidget::paintRole(QPainter* painter,
+ const QByteArray& role,
+ const QRectF& rect,
+ int orderIndex,
+ QWidget* widget) const
+{
+ // The following code is based on the code from QHeaderView::paintSection().
+ // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ QStyleOptionHeader option;
+ option.section = orderIndex;
+ option.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal;
+ if (isEnabled()) {
+ option.state |= QStyle::State_Enabled;
+ }
+ if (window() && window()->isActiveWindow()) {
+ option.state |= QStyle::State_Active;
+ }
+ if (m_hoveredRoleIndex == orderIndex) {
+ option.state |= QStyle::State_MouseOver;
+ }
+ if (m_pressedRoleIndex == orderIndex) {
+ option.state |= QStyle::State_Sunken;
+ }
+ if (m_model->sortRole() == role) {
+ option.sortIndicator = (m_model->sortOrder() == Qt::AscendingOrder) ?
+ QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
+ }
+ option.rect = rect.toRect();
+
+ if (m_columns.count() == 1) {
+ option.position = QStyleOptionHeader::OnlyOneSection;
+ } else if (orderIndex == 0) {
+ option.position = QStyleOptionHeader::Beginning;
+ } else if (orderIndex == m_columns.count() - 1) {
+ option.position = QStyleOptionHeader::End;
+ } else {
+ option.position = QStyleOptionHeader::Middle;
+ }
+
+ option.orientation = Qt::Horizontal;
+ option.selectedPosition = QStyleOptionHeader::NotAdjacent;
+ option.text = m_model->roleDescription(role);
+
+ style()->drawControl(QStyle::CE_Header, &option, painter, widget);
+}
+
+void KItemListHeaderWidget::updatePressedRoleIndex(const QPointF& pos)
+{
+ const int pressedIndex = roleIndexAt(pos);
+ if (m_pressedRoleIndex != pressedIndex) {
+ m_pressedRoleIndex = pressedIndex;
+ update();
+ }
+}
+
+void KItemListHeaderWidget::updateHoveredRoleIndex(const QPointF& pos)
+{
+ const int hoverIndex = roleIndexAt(pos);
+ if (m_hoveredRoleIndex != hoverIndex) {
+ m_hoveredRoleIndex = hoverIndex;
+ update();
+ }
+}
+
+int KItemListHeaderWidget::roleIndexAt(const QPointF& pos) const
+{
+ int index = -1;
+
+ qreal x = 0;
+ foreach (const QByteArray& role, m_columns) {
+ ++index;
+ x += m_columnsWidths.value(role);
+ if (pos.x() <= x) {
+ break;
+ }
+ }
+
+ return index;
+}
+
+bool KItemListHeaderWidget::isAboveRoleGrip(const QPointF& pos, int roleIndex) const
+{
+ qreal x = 0;
+ for (int i = 0; i <= roleIndex; ++i) {
+ const QByteArray role = m_columns[i];
+ x += m_columnsWidths.value(role);
+ }
+
+ const int grip = style()->pixelMetric(QStyle::PM_HeaderGripMargin);
+ return pos.x() >= (x - grip) && pos.x() <= x;
+}
+
+QPixmap KItemListHeaderWidget::createRolePixmap(int roleIndex) const
+{
+ const QByteArray role = m_columns[roleIndex];
+ const qreal roleWidth = m_columnsWidths.value(role);
+ const QRect rect(0, 0, roleWidth, size().height());
+
+ QImage image(rect.size(), QImage::Format_ARGB32_Premultiplied);
+
+ QPainter painter(&image);
+ paintRole(&painter, role, rect, roleIndex);
+
+ // Apply a highlighting-color
+ const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive;
+ QColor highlightColor = palette().color(group, QPalette::Highlight);
+ highlightColor.setAlpha(64);
+ painter.fillRect(rect, highlightColor);
+
+ // Make the image transparent
+ painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
+ painter.fillRect(0, 0, image.width(), image.height(), QColor(0, 0, 0, 192));
+
+ return QPixmap::fromImage(image);
+}
+
+int KItemListHeaderWidget::targetOfMovingRole() const
+{
+ const int movingWidth = m_movingRole.pixmap.width();
+ const int movingLeft = m_movingRole.x;
+ const int movingRight = movingLeft + movingWidth - 1;
+
+ int targetIndex = 0;
+ qreal targetLeft = 0;
+ while (targetIndex < m_columns.count()) {
+ const QByteArray role = m_columns[targetIndex];
+ const qreal targetWidth = m_columnsWidths.value(role);
+ const qreal targetRight = targetLeft + targetWidth - 1;
+
+ const bool isInTarget = (targetWidth >= movingWidth &&
+ movingLeft >= targetLeft &&
+ movingRight <= targetRight) ||
+ (targetWidth < movingWidth &&
+ movingLeft <= targetLeft &&
+ movingRight >= targetRight);
+
+ if (isInTarget) {
+ return targetIndex;
+ }
+
+ targetLeft += targetWidth;
+ ++targetIndex;
+ }
+
+ return m_movingRole.index;
+}
+
+qreal KItemListHeaderWidget::roleXPosition(const QByteArray& role) const
+{
+ qreal x = 0;
+ foreach (const QByteArray& visibleRole, m_columns) {
+ if (visibleRole == role) {
+ return x;
+ }
+
+ x += m_columnsWidths.value(visibleRole);
+ }
+
+ return -1;
+}
+
+#include "kitemlistheaderwidget_p.moc"
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
-#ifndef KITEMLISTHEADER_H
-#define KITEMLISTHEADER_H
+#ifndef KITEMLISTHEADERWIDGET_H
+#define KITEMLISTHEADERWIDGET_H
#include <libdolphin_export.h>
#include <QGraphicsWidget>
class KItemModelBase;
/**
- * @brief Header for KItemListView that shows the currently used roles.
+ * @brief Widget the implements the header for KItemListView showing the currently used roles.
+ *
+ * The widget is an internal API, the user of KItemListView may only access the
+ * class KItemListHeader.
*/
-class LIBDOLPHINPRIVATE_EXPORT KItemListHeader : public QGraphicsWidget
+class LIBDOLPHINPRIVATE_EXPORT KItemListHeaderWidget : public QGraphicsWidget
{
Q_OBJECT
public:
- KItemListHeader(QGraphicsWidget* parent = 0);
- virtual ~KItemListHeader();
+ KItemListHeaderWidget(QGraphicsWidget* parent = 0);
+ virtual ~KItemListHeaderWidget();
void setModel(KItemModelBase* model);
KItemModelBase* model() const;
- void setVisibleRoles(const QList<QByteArray>& roles);
- QList<QByteArray> visibleRoles() const;
+ void setAutomaticColumnResizing(bool automatic);
+ bool automaticColumnResizing() const;
- void setVisibleRolesWidths(const QHash<QByteArray, qreal>& rolesWidths);
- QHash<QByteArray, qreal> visibleRolesWidths() const;
+ void setColumns(const QList<QByteArray>& roles);
+ QList<QByteArray> columns() const;
- qreal minimumRoleWidth() const;
+ void setColumnWidth(const QByteArray& role, qreal width);
+ qreal columnWidth(const QByteArray& role) const;
+
+ qreal minimumColumnWidth() const;
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
signals:
/**
* Is emitted if the width of a visible role has been adjusted by the user with the mouse
- * (no signal is emitted if KItemListHeader::setVisibleRoles() is invoked).
+ * (no signal is emitted if KItemListHeader::setVisibleRoleWidth() is invoked).
*/
- void visibleRoleWidthChanged(const QByteArray& role,
- qreal currentWidth,
- qreal previousWidth);
+ void columnWidthChanged(const QByteArray& role,
+ qreal currentWidth,
+ qreal previousWidth);
/**
- * Is emitted if the position of the visible role has been changed.
+ * Is emitted if the position of the column has been changed.
*/
- void visibleRoleMoved(const QByteArray& role, int currentIndex, int previousIndex);
+ void columnMoved(const QByteArray& role, int currentIndex, int previousIndex);
/**
* Is emitted if the user has changed the sort order by clicking on a
MoveRoleOperation
};
+ bool m_automaticColumnResizing;
KItemModelBase* m_model;
- QList<QByteArray> m_visibleRoles;
- QHash<QByteArray, qreal> m_visibleRolesWidths;
+ QList<QByteArray> m_columns;
+ QHash<QByteArray, qreal> m_columnsWidths;
int m_hoveredRoleIndex;
int m_pressedRoleIndex;
#include "kitemlistview.h"
#include "kitemlistcontroller.h"
-#include "kitemlistheader_p.h"
+#include "kitemlistheader.h"
+#include "kitemlistheaderwidget_p.h"
#include "kitemlistrubberband_p.h"
#include "kitemlistselectionmanager.h"
#include "kitemlistsizehintresolver_p.h"
m_controller(0),
m_model(0),
m_visibleRoles(),
- m_visibleRolesSizes(),
- m_stretchedVisibleRolesSizes(),
m_widgetCreator(0),
m_groupHeaderCreator(0),
m_styleOption(),
m_visibleItems(),
m_visibleGroups(),
+ m_columnWidthsCache(),
m_visibleCells(),
m_sizeHintResolver(0),
m_layouter(0),
m_autoScrollIncrement(0),
m_autoScrollTimer(0),
m_header(0),
- m_useHeaderWidths(false)
+ m_headerWidget(0)
{
setAcceptHoverEvents(true);
m_rubberBand = new KItemListRubberBand(this);
connect(m_rubberBand, SIGNAL(activationChanged(bool)), this, SLOT(slotRubberBandActivationChanged(bool)));
+
+ m_headerWidget = new KItemListHeaderWidget(this);
+ m_headerWidget->setVisible(false);
+
+ m_header = new KItemListHeader(this);
}
KItemListView::~KItemListView()
}
if (itemSize.isEmpty()) {
- updateVisibleRolesSizes();
+ if (m_headerWidget->automaticColumnResizing()) {
+ updateColumnWidthsCache();
+ } 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, itemSize.height());
+ m_layouter->setItemSize(newSize);
+ }
} else {
m_layouter->setItemSize(itemSize);
}
}
m_layouter->setItemOffset(offset);
- if (m_header) {
- m_header->setPos(-offset, 0);
+ if (m_headerWidget->isVisible()) {
+ m_headerWidget->setPos(-offset, 0);
}
// Don't check whether the m_layoutTimer is active: Changing the
it.next();
KItemListWidget* widget = it.value();
widget->setVisibleRoles(roles);
- widget->setVisibleRolesSizes(m_stretchedVisibleRolesSizes);
+ updateWidgetColumnWidths(widget);
if (alternateBackgroundsChanged) {
updateAlternateBackgroundForWidget(widget);
}
m_sizeHintResolver->clearCache();
m_layouter->markAsDirty();
- if (m_header) {
- m_header->setVisibleRoles(roles);
- m_header->setVisibleRolesWidths(headerRolesWidths());
- m_useHeaderWidths = false;
+ if (m_headerWidget->isVisible()) {
+ m_headerWidget->setColumns(roles);
+ m_headerWidget->setAutomaticColumnResizing(true);
}
- updateVisibleRolesSizes();
+ updateColumnWidthsCache();
doLayout(NoAnimation);
onVisibleRolesChanged(roles, previousRoles);
// Changing the geometry does not require to do an expensive
// update of the visible-roles sizes, only the stretched sizes
// need to be adjusted to the new size.
- updateStretchedVisibleRolesSizes();
+ updateColumnWidthsForHeader();
- if (m_useHeaderWidths) {
+ if (!m_headerWidget->automaticColumnResizing()) {
QSizeF dynamicItemSize = m_layouter->itemSize();
if (m_itemSize.width() < 0) {
- const qreal requiredWidth = visibleRolesSizesWidthSum();
+ const qreal requiredWidth = columnWidthsSum();
if (newSize.width() > requiredWidth) {
dynamicItemSize.setWidth(newSize.width());
}
const qreal headerWidth = qMax(newSize.width(), requiredWidth);
- m_header->resize(headerWidth, m_header->size().height());
- }
-
- if (m_itemSize.height() < 0) {
- const qreal requiredHeight = visibleRolesSizesHeightSum();
- if (newSize.height() > requiredHeight) {
- dynamicItemSize.setHeight(newSize.height());
- }
- // TODO: KItemListHeader is not prepared for vertical alignment
+ m_headerWidget->resize(headerWidth, m_headerWidget->size().height());
}
m_layouter->setItemSize(dynamicItemSize);
return itemSize();
}
-QHash<QByteArray, QSizeF> KItemListView::visibleRolesSizes(const KItemRangeList& itemRanges) const
+QHash<QByteArray, qreal> KItemListView::columnWidths(const KItemRangeList& itemRanges) const
{
Q_UNUSED(itemRanges);
- return QHash<QByteArray, QSizeF>();
+ return QHash<QByteArray, qreal>();
}
void KItemListView::setSupportsItemExpanding(bool supportsExpanding)
void KItemListView::scrollToItem(int index)
{
QRectF viewGeometry = geometry();
- if (m_header) {
- const qreal headerHeight = m_header->size().height();
+ if (m_headerWidget->isVisible()) {
+ const qreal headerHeight = m_headerWidget->size().height();
viewGeometry.adjust(0, headerHeight, 0, 0);
}
const QRectF currentRect = itemRect(index);
return m_activeTransactions > 0;
}
-void KItemListView::setHeaderShown(bool show)
+void KItemListView::setHeaderVisible(bool visible)
{
- if (show && !m_header) {
- m_header = new KItemListHeader(this);
- m_header->setPos(0, 0);
- m_header->setModel(m_model);
- m_header->setVisibleRoles(m_visibleRoles);
- m_header->setVisibleRolesWidths(headerRolesWidths());
- m_header->setZValue(1);
+ if (visible && !m_headerWidget->isVisible()) {
+ m_headerWidget->setPos(0, 0);
+ m_headerWidget->setModel(m_model);
+ m_headerWidget->setColumns(m_visibleRoles);
+ m_headerWidget->setZValue(1);
- connect(m_header, SIGNAL(visibleRoleWidthChanged(QByteArray,qreal,qreal)),
- this, SLOT(slotVisibleRoleWidthChanged(QByteArray,qreal,qreal)));
- connect(m_header, SIGNAL(visibleRoleMoved(QByteArray,int,int)),
- this, SLOT(slotVisibleRoleMoved(QByteArray,int,int)));
- connect(m_header, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
+ connect(m_headerWidget, SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)),
+ this, SLOT(slotHeaderColumnWidthChanged(QByteArray,qreal,qreal)));
+ connect(m_headerWidget, SIGNAL(columnMoved(QByteArray,int,int)),
+ this, SLOT(slotHeaderColumnMoved(QByteArray,int,int)));
+ connect(m_headerWidget, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
this, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)));
- connect(m_header, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
+ connect(m_headerWidget, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
this, SIGNAL(sortRoleChanged(QByteArray,QByteArray)));
- m_useHeaderWidths = false;
+ m_headerWidget->setAutomaticColumnResizing(true);
+
+ m_layouter->setHeaderHeight(m_headerWidget->size().height());
+ m_headerWidget->setVisible(true);
+ } else if (!visible && m_headerWidget->isVisible()) {
+ disconnect(m_headerWidget, SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)),
+ this, SLOT(slotHeaderColumnWidthChanged(QByteArray,qreal,qreal)));
+ disconnect(m_headerWidget, SIGNAL(columnMoved(QByteArray,int,int)),
+ this, SLOT(slotHeaderColumnMoved(QByteArray,int,int)));
+ disconnect(m_headerWidget, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
+ this, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)));
+ disconnect(m_headerWidget, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
+ this, SIGNAL(sortRoleChanged(QByteArray,QByteArray)));
- m_layouter->setHeaderHeight(m_header->size().height());
- } else if (!show && m_header) {
- delete m_header;
- m_header = 0;
- m_useHeaderWidths = false;
m_layouter->setHeaderHeight(0);
+ m_headerWidget->setAutomaticColumnResizing(true);
+ m_headerWidget->setVisible(false);
}
}
-bool KItemListView::isHeaderShown() const
+bool KItemListView::isHeaderVisible() const
{
- return m_header != 0;
+ return m_headerWidget->isVisible();
+}
+
+KItemListHeader* KItemListView::header() const
+{
+ return m_header;
}
QPixmap KItemListView::createDragPixmap(const QSet<int>& indexes) const
void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges)
{
- updateVisibleRolesSizes(itemRanges);
+ updateColumnWidthsCache(itemRanges);
const bool hasMultipleRanges = (itemRanges.count() > 1);
if (hasMultipleRanges) {
void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges)
{
- updateVisibleRolesSizes();
+ updateColumnWidthsCache();
const bool hasMultipleRanges = (itemRanges.count() > 1);
if (hasMultipleRanges) {
{
const bool updateSizeHints = itemSizeHintUpdateRequired(roles);
if (updateSizeHints) {
- updateVisibleRolesSizes(itemRanges);
+ updateColumnWidthsCache(itemRanges);
}
foreach (const KItemRange& itemRange, itemRanges) {
update();
}
-void KItemListView::slotVisibleRoleWidthChanged(const QByteArray& role,
- qreal currentWidth,
- qreal previousWidth)
+void KItemListView::slotHeaderColumnWidthChanged(const QByteArray& role,
+ qreal currentWidth,
+ qreal previousWidth)
{
Q_UNUSED(previousWidth);
- m_useHeaderWidths = true;
+ m_headerWidget->setAutomaticColumnResizing(false);
- if (m_visibleRolesSizes.contains(role)) {
- QSizeF roleSize = m_visibleRolesSizes.value(role);
- roleSize.setWidth(currentWidth);
- m_visibleRolesSizes.insert(role, roleSize);
- m_stretchedVisibleRolesSizes.insert(role, roleSize);
+ if (m_columnWidthsCache.contains(role)) {
+ m_columnWidthsCache.insert(role, currentWidth);
// Apply the new size to the layouter
- QSizeF dynamicItemSize = m_itemSize;
- if (dynamicItemSize.width() < 0) {
- const qreal requiredWidth = visibleRolesSizesWidthSum();
- dynamicItemSize.setWidth(qMax(size().width(), requiredWidth));
- }
- if (dynamicItemSize.height() < 0) {
- const qreal requiredHeight = visibleRolesSizesHeightSum();
- dynamicItemSize.setHeight(qMax(size().height(), requiredHeight));
- }
-
+ const qreal requiredWidth = columnWidthsSum();
+ const QSizeF dynamicItemSize(qMax(size().width(), requiredWidth),
+ m_itemSize.height());
m_layouter->setItemSize(dynamicItemSize);
// Update the role sizes for all visible widgets
QHashIterator<int, KItemListWidget*> it(m_visibleItems);
while (it.hasNext()) {
it.next();
- it.value()->setVisibleRolesSizes(m_stretchedVisibleRolesSizes);
+ updateWidgetColumnWidths(it.value());
}
doLayout(NoAnimation);
}
}
-void KItemListView::slotVisibleRoleMoved(const QByteArray& role,
- int currentIndex,
- int previousIndex)
+void KItemListView::slotHeaderColumnMoved(const QByteArray& role,
+ int currentIndex,
+ int previousIndex)
{
Q_ASSERT(m_visibleRoles[previousIndex] == role);
void KItemListView::updateWidgetProperties(KItemListWidget* widget, int index)
{
widget->setVisibleRoles(m_visibleRoles);
- widget->setVisibleRolesSizes(m_stretchedVisibleRolesSizes);
+ updateWidgetColumnWidths(widget);
widget->setStyleOption(m_styleOption);
const KItemListSelectionManager* selectionManager = m_controller->selectionManager();
return m_itemSize.isEmpty() && m_visibleRoles.count() > 1;
}
-QHash<QByteArray, qreal> KItemListView::headerRolesWidths() const
+void KItemListView::applyColumnWidthsFromHeader()
{
- QHash<QByteArray, qreal> rolesWidths;
+ qreal roleWidthSum = 0;
+ foreach (const QByteArray& role, m_visibleRoles) {
+ const qreal width = m_headerWidget->columnWidth(role);
+ m_columnWidthsCache.insert(role, width);
+ roleWidthSum += width;
+ }
+
+ // Apply the new size to the layouter
+ const QSizeF dynamicItemSize(qMax(size().width(), roleWidthSum),
+ m_itemSize.height());
+ m_layouter->setItemSize(dynamicItemSize);
- QHashIterator<QByteArray, QSizeF> it(m_stretchedVisibleRolesSizes);
+ // Update the role sizes for all visible widgets
+ QHashIterator<int, KItemListWidget*> it(m_visibleItems);
while (it.hasNext()) {
it.next();
- rolesWidths.insert(it.key(), it.value().width());
+ updateWidgetColumnWidths(it.value());
}
+ doLayout(NoAnimation);
+}
- return rolesWidths;
+void KItemListView::updateWidgetColumnWidths(KItemListWidget* widget)
+{
+ foreach (const QByteArray& role, m_visibleRoles) {
+ widget->setColumnWidth(role, m_headerWidget->columnWidth(role));
+ }
}
-void KItemListView::updateVisibleRolesSizes(const KItemRangeList& itemRanges)
+void KItemListView::updateColumnWidthsCache(const KItemRangeList& itemRanges)
{
- if (!m_itemSize.isEmpty() || m_useHeaderWidths) {
+ if (!m_itemSize.isEmpty() || !m_headerWidget->automaticColumnResizing()) {
return;
}
}
if (itemCount == rangesItemCount) {
- m_visibleRolesSizes = visibleRolesSizes(itemRanges);
- if (m_header) {
+ m_columnWidthsCache = columnWidths(itemRanges);
+ if (m_headerWidget->isVisible()) {
// Assure the the sizes are not smaller than the minimum defined by the header
- // TODO: Currently only implemented for a top-aligned header
- const qreal minHeaderRoleWidth = m_header->minimumRoleWidth();
- QMutableHashIterator<QByteArray, QSizeF> it (m_visibleRolesSizes);
+ const qreal minHeaderRoleWidth = m_headerWidget->minimumColumnWidth();
+ QMutableHashIterator<QByteArray, qreal> it (m_columnWidthsCache);
while (it.hasNext()) {
it.next();
- const QSizeF& size = it.value();
- if (size.width() < minHeaderRoleWidth) {
- const QSizeF newSize(minHeaderRoleWidth, size.height());
- m_visibleRolesSizes.insert(it.key(), newSize);
+ const qreal width = it.value();
+ if (width < minHeaderRoleWidth) {
+ m_columnWidthsCache.insert(it.key(), minHeaderRoleWidth);
}
}
}
} else {
// Only a sub range of the roles need to be determined.
- // The chances are good that the sizes of the sub ranges
- // already fit into the available sizes and hence no
+ // The chances are good that the widths of the sub ranges
+ // already fit into the available widths and hence no
// expensive update might be required.
bool updateRequired = false;
- const QHash<QByteArray, QSizeF> updatedSizes = visibleRolesSizes(itemRanges);
- QHashIterator<QByteArray, QSizeF> it(updatedSizes);
+ const QHash<QByteArray, qreal> updatedWidths = columnWidths(itemRanges);
+ QHashIterator<QByteArray, qreal> it(updatedWidths);
while (it.hasNext()) {
it.next();
const QByteArray& role = it.key();
- const QSizeF& updatedSize = it.value();
- const QSizeF currentSize = m_visibleRolesSizes.value(role);
- if (updatedSize.width() > currentSize.width() || updatedSize.height() > currentSize.height()) {
- m_visibleRolesSizes.insert(role, updatedSize);
+ const qreal updatedWidth = it.value();
+ const qreal currentWidth = m_columnWidthsCache.value(role);
+ if (updatedWidth > currentWidth) {
+ m_columnWidthsCache.insert(role, updatedWidth);
updateRequired = true;
}
}
}
}
- updateStretchedVisibleRolesSizes();
+ updateColumnWidthsForHeader();
}
-void KItemListView::updateVisibleRolesSizes()
+void KItemListView::updateColumnWidthsCache()
{
if (!m_model) {
return;
const int itemCount = m_model->count();
if (itemCount > 0) {
- updateVisibleRolesSizes(KItemRangeList() << KItemRange(0, itemCount));
+ updateColumnWidthsCache(KItemRangeList() << KItemRange(0, itemCount));
}
}
-void KItemListView::updateStretchedVisibleRolesSizes()
+void KItemListView::updateColumnWidthsForHeader()
{
- if (!m_itemSize.isEmpty() || m_useHeaderWidths || m_visibleRoles.isEmpty()) {
+ if (!m_itemSize.isEmpty() || !m_headerWidget->automaticColumnResizing() || m_visibleRoles.isEmpty()) {
return;
}
// visible role sizes and apply them to the layouter. If the
// size does not use the available view-size the size of the
// first role will get stretched.
- m_stretchedVisibleRolesSizes = m_visibleRolesSizes;
+
+ foreach (const QByteArray& role, m_visibleRoles) {
+ m_headerWidget->setColumnWidth(role, m_columnWidthsCache.value(role));
+ }
+
const QByteArray role = m_visibleRoles.first();
- QSizeF firstRoleSize = m_stretchedVisibleRolesSizes.value(role);
+ qreal firstColumnWidth = m_columnWidthsCache.value(role);
QSizeF dynamicItemSize = m_itemSize;
if (dynamicItemSize.width() <= 0) {
- const qreal requiredWidth = visibleRolesSizesWidthSum();
+ const qreal requiredWidth = columnWidthsSum();
const qreal availableWidth = size().width();
if (requiredWidth != availableWidth) {
// Stretch the first role to use the whole remaining width
- firstRoleSize.rwidth() += availableWidth - requiredWidth;
+ firstColumnWidth += availableWidth - requiredWidth;
// TODO: A proper calculation of the minimum width depends on the implementation
// of KItemListWidget. Probably a kind of minimum size-hint should be introduced
// later.
const qreal minWidth = m_styleOption.iconSize * 2 + 200;
- if (firstRoleSize.width() < minWidth) {
- firstRoleSize.rwidth() = minWidth;
+ if (firstColumnWidth < minWidth) {
+ firstColumnWidth = minWidth;
}
- m_stretchedVisibleRolesSizes.insert(role, firstRoleSize);
+
+ m_headerWidget->setColumnWidth(role, firstColumnWidth);
}
dynamicItemSize.rwidth() = qMax(requiredWidth, availableWidth);
}
- // TODO: A dynamic item height (dynamicItemSize.height() <= 0)
- // is not handled currently
-
m_layouter->setItemSize(dynamicItemSize);
- if (m_header) {
- m_header->setVisibleRolesWidths(headerRolesWidths());
- m_header->resize(dynamicItemSize.width(), m_header->size().height());
+ if (m_headerWidget->isVisible()) {
+ m_headerWidget->resize(dynamicItemSize.width(), m_headerWidget->size().height());
}
// Update the role sizes for all visible widgets
QHashIterator<int, KItemListWidget*> it(m_visibleItems);
while (it.hasNext()) {
it.next();
- it.value()->setVisibleRolesSizes(m_stretchedVisibleRolesSizes);
+ updateWidgetColumnWidths(it.value());
}
}
-qreal KItemListView::visibleRolesSizesWidthSum() const
+qreal KItemListView::columnWidthsSum() const
{
qreal widthSum = 0;
- QHashIterator<QByteArray, QSizeF> it(m_visibleRolesSizes);
+ QHashIterator<QByteArray, qreal> it(m_columnWidthsCache);
while (it.hasNext()) {
it.next();
- widthSum += it.value().width();
+ widthSum += it.value();
}
return widthSum;
}
-qreal KItemListView::visibleRolesSizesHeightSum() const
-{
- qreal heightSum = 0;
- QHashIterator<QByteArray, QSizeF> it(m_visibleRolesSizes);
- while (it.hasNext()) {
- it.next();
- heightSum += it.value().height();
- }
- return heightSum;
-}
-
QRectF KItemListView::headerBoundaries() const
{
- return m_header ? m_header->geometry() : QRectF();
+ return m_headerWidget->isVisible() ? m_headerWidget->geometry() : QRectF();
}
bool KItemListView::changesItemGridLayout(const QSizeF& newGridSize,
class KItemListController;
class KItemListGroupHeaderCreatorBase;
class KItemListHeader;
+class KItemListHeaderWidget;
class KItemListSizeHintResolver;
class KItemListRubberBand;
class KItemListViewAnimation;
virtual QSizeF itemSizeHint(int index) const;
/**
- * @param itemRanges Items that must be checked for getting the visible roles sizes.
- * @return The size of each visible role in case if KItemListView::itemSize()
- * is empty. This allows to have dynamic but equal role sizes between
- * all items, like used in the classic "table-views". Per default an
- * empty hash is returned.
+ * @param itemRanges Items that must be checked for getting the widths of columns.
+ * @return The width of the column of each visible role. The width will
+ * be respected if the width of the item size is <= 0 (see
+ * KItemListView::setItemSize()). Per default an empty hash
+ * is returned.
*/
- virtual QHash<QByteArray, QSizeF> visibleRolesSizes(const KItemRangeList& itemRanges) const;
+ virtual QHash<QByteArray, qreal> columnWidths(const KItemRangeList& itemRanges) const;
/**
* If set to true, items having child-items can be expanded to show the child-items as
bool isTransactionActive() const;
/**
- * Turns on the header if \p show is true. Per default the
- * header is not shown. Usually the header is turned on when
+ * Turns on the header if \p visible is true. Per default the
+ * header is not visible. Usually the header is turned on when
* showing a classic "table-view" to describe the shown columns.
*/
- void setHeaderShown(bool show);
- bool isHeaderShown() const;
+ void setHeaderVisible(bool visible);
+ bool isHeaderVisible() const;
+
+ /**
+ * @return Header of the list. The header is also available if it is not shown
+ * (see KItemListView::setHeaderShown()).
+ */
+ KItemListHeader* header() const;
/**
* @return Pixmap that is used for a drag operation based on the
void slotRubberBandActivationChanged(bool active);
/**
- * Is invoked if the visible role-width of one role in the header has
+ * Is invoked if the column-width of one role in the header has
* been changed by the user. It is remembered that the user has modified
* the role-width, so that it won't be changed anymore automatically to
* calculate an optimized width.
*/
- void slotVisibleRoleWidthChanged(const QByteArray& role,
- qreal currentWidth,
- qreal previousWidth);
+ void slotHeaderColumnWidthChanged(const QByteArray& role,
+ qreal currentWidth,
+ qreal previousWidth);
/**
- * Is invoked if a visible role has been moved by the user. Applies
+ * Is invoked if a column has been moved by the user. Applies
* the moved role to the view.
*/
- void slotVisibleRoleMoved(const QByteArray& role,
- int currentIndex,
- int previousIndex);
+ void slotHeaderColumnMoved(const QByteArray& role,
+ int currentIndex,
+ int previousIndex);
/**
* Triggers the autoscrolling if autoScroll() is enabled by checking the
*/
bool useAlternateBackgrounds() const;
+ void applyColumnWidthsFromHeader();
+
/**
- * @return The widths of each visible role that is shown in the KItemListHeader.
+ * Applies the roles-sizes from m_stretchedVisibleRolesSizes
+ * to \a widget.
*/
- QHash<QByteArray, qreal> headerRolesWidths() const;
+ void updateWidgetColumnWidths(KItemListWidget* widget);
/**
* Updates m_visibleRolesSizes by calling KItemListView::visibleRolesSizes().
* are used (see m_useHeaderWidths). Also m_strechedVisibleRolesSizes will be adjusted
* to respect the available view-size.
*/
- void updateVisibleRolesSizes(const KItemRangeList& itemRanges);
+ void updateColumnWidthsCache(const KItemRangeList& itemRanges);
/**
* Convenience method for updateVisibleRoleSizes(KItemRangeList() << KItemRange(0, m_model->count()).
*/
- void updateVisibleRolesSizes();
+ void updateColumnWidthsCache();
/**
- * Updates m_stretchedVisibleRolesSizes based on m_visibleRolesSizes and the available
- * view-size. Nothing will be done if m_itemRect is not empty or custom header-widths
- * are used (see m_useHeaderWidths).
+ * Updates the column widhts of the header based on m_columnWidthsCache and the available
+ * view-size.
*/
- void updateStretchedVisibleRolesSizes();
+ void updateColumnWidthsForHeader();
/**
- * @return Sum of the widths of all visible roles.
+ * @return Sum of the widths of all columns.
*/
- qreal visibleRolesSizesWidthSum() const;
-
- /**
- * @return Sum of the heights of all visible roles.
- */
- qreal visibleRolesSizesHeightSum() const;
+ qreal columnWidthsSum() const;
/**
* @return Boundaries of the header. An empty rectangle is returned
KItemListController* m_controller;
KItemModelBase* m_model;
QList<QByteArray> m_visibleRoles;
- QHash<QByteArray, QSizeF> m_visibleRolesSizes;
- QHash<QByteArray, QSizeF> m_stretchedVisibleRolesSizes;
KItemListWidgetCreatorBase* m_widgetCreator;
KItemListGroupHeaderCreatorBase* m_groupHeaderCreator;
KItemListStyleOption m_styleOption;
QHash<int, KItemListWidget*> m_visibleItems;
QHash<KItemListWidget*, KItemListGroupHeader*> m_visibleGroups;
+ QHash<QByteArray, qreal> m_columnWidthsCache; // Cache for columnWidths() result
+
struct Cell
{
Cell() : column(-1), row(-1) {}
QTimer* m_autoScrollTimer;
KItemListHeader* m_header;
- bool m_useHeaderWidths;
+ KItemListHeaderWidget* m_headerWidget;
friend class KItemListContainer; // Accesses scrollBarRequired()
+ friend class KItemListHeader; // Accesses m_headerWidget
friend class KItemListController;
friend class KItemListControllerTest;
};
m_enabledSelectionToggle(false),
m_data(),
m_visibleRoles(),
- m_visibleRolesSizes(),
+ m_columnWidths(),
m_styleOption(),
m_siblingsInfo(),
m_hoverOpacity(0),
return m_visibleRoles;
}
-void KItemListWidget::setVisibleRolesSizes(const QHash<QByteArray, QSizeF> rolesSizes)
-{
- const QHash<QByteArray, QSizeF> previousRolesSizes = m_visibleRolesSizes;
- m_visibleRolesSizes = rolesSizes;
- visibleRolesSizesChanged(rolesSizes, previousRolesSizes);
- update();
+void KItemListWidget::setColumnWidth(const QByteArray& role, qreal width)
+{
+ if (m_columnWidths.value(role) != width) {
+ const qreal previousWidth = width;
+ m_columnWidths.insert(role, width);
+ columnWidthChanged(role, width, previousWidth);
+ update();
+ }
}
-QHash<QByteArray, QSizeF> KItemListWidget::visibleRolesSizes() const
+qreal KItemListWidget::columnWidth(const QByteArray& role) const
{
- return m_visibleRolesSizes;
+ return m_columnWidths.value(role);
}
void KItemListWidget::setStyleOption(const KItemListStyleOption& option)
Q_UNUSED(previous);
}
-void KItemListWidget::visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current,
- const QHash<QByteArray, QSizeF>& previous)
+void KItemListWidget::columnWidthChanged(const QByteArray& role,
+ qreal current,
+ qreal previous)
{
+ Q_UNUSED(role);
Q_UNUSED(current);
Q_UNUSED(previous);
}
void setVisibleRoles(const QList<QByteArray>& roles);
QList<QByteArray> visibleRoles() const;
- void setVisibleRolesSizes(const QHash<QByteArray, QSizeF> rolesSizes);
- QHash<QByteArray, QSizeF> visibleRolesSizes() const;
+ /**
+ * Sets the width of a role that should be used if the alignment of the content
+ * should be done in columns.
+ */
+ void setColumnWidth(const QByteArray& role, qreal width);
+ qreal columnWidth(const QByteArray& role) const;
void setStyleOption(const KItemListStyleOption& option);
const KItemListStyleOption& styleOption() const;
protected:
virtual void dataChanged(const QHash<QByteArray, QVariant>& current, const QSet<QByteArray>& roles = QSet<QByteArray>());
virtual void visibleRolesChanged(const QList<QByteArray>& current, const QList<QByteArray>& previous);
- virtual void visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current, const QHash<QByteArray, QSizeF>& previous);
+ virtual void columnWidthChanged(const QByteArray& role, qreal current, qreal previous);
virtual void styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous);
virtual void currentChanged(bool current);
virtual void selectedChanged(bool selected);
bool m_enabledSelectionToggle;
QHash<QByteArray, QVariant> m_data;
QList<QByteArray> m_visibleRoles;
- QHash<QByteArray, QSizeF> m_visibleRolesSizes;
+ QHash<QByteArray, qreal> m_columnWidths;
KItemListStyleOption m_styleOption;
QBitArray m_siblingsInfo;
<default></default>
</entry>
+ <entry name="HeaderColumnWidths" type="IntList">
+ <label context="@label">Header column widths</label>
+ <default></default>
+ </entry>
+
<entry name="Timestamp" type="DateTime" >
<label context="@label">Properties last changed</label>
<whatsthis context="@info:whatsthis">The last time these properties were changed by the user.</whatsthis>
switch (layout) {
case KFileItemListView::IconsLayout:
m_fileItemListView->setScrollOrientation(Qt::Vertical);
- m_fileItemListView->setHeaderShown(false);
+ m_fileItemListView->setHeaderVisible(false);
m_fileItemListView->setSupportsItemExpanding(false);
break;
case KFileItemListView::DetailsLayout:
m_fileItemListView->setScrollOrientation(Qt::Vertical);
- m_fileItemListView->setHeaderShown(true);
+ m_fileItemListView->setHeaderVisible(true);
m_fileItemListView->setSupportsItemExpanding(DetailsModeSettings::expandableFolders());
break;
case KFileItemListView::CompactLayout:
m_fileItemListView->setScrollOrientation(Qt::Horizontal);
- m_fileItemListView->setHeaderShown(false);
+ m_fileItemListView->setHeaderVisible(false);
m_fileItemListView->setSupportsItemExpanding(false);
break;
default:
#include <KLocale>
#include <kitemviews/kfileitemmodel.h>
#include <kitemviews/kfileitemlistview.h>
+#include <kitemviews/kitemlistheader.h>
#include <kitemviews/kitemlistselectionmanager.h>
#include <kitemviews/kitemlistview.h>
#include <kitemviews/kitemlistcontroller.h>
this, SLOT(slotSortRoleChangedByHeader(QByteArray,QByteArray)));
connect(view, SIGNAL(visibleRolesChanged(QList<QByteArray>,QList<QByteArray>)),
this, SLOT(slotVisibleRolesChangedByHeader(QList<QByteArray>,QList<QByteArray>)));
+ connect(view->header(), SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)),
+ this, SLOT(slotHeaderColumnWidthChanged(QByteArray,qreal,qreal)));
KItemListSelectionManager* selectionManager = controller->selectionManager();
connect(selectionManager, SIGNAL(selectionChanged(QSet<int>,QSet<int>)),
void DolphinView::slotHeaderContextMenuRequested(const QPointF& pos)
{
- QWeakPointer<KMenu> menu = new KMenu(QApplication::activeWindow());
+ ViewProperties props(url());
+
+ QPointer<KMenu> menu = new KMenu(QApplication::activeWindow());
KItemListView* view = m_container->controller()->view();
const QSet<QByteArray> visibleRolesSet = view->visibleRoles().toSet();
const QString text = fileItemModel()->roleDescription(info.role);
QAction* action = 0;
if (info.group.isEmpty()) {
- action = menu.data()->addAction(text);
+ action = menu->addAction(text);
} else {
if (!groupMenu || info.group != groupName) {
groupName = info.group;
- groupMenu = menu.data()->addMenu(groupName);
+ groupMenu = menu->addMenu(groupName);
}
action = groupMenu->addAction(text);
action->setData(info.role);
}
- QAction* action = menu.data()->exec(pos.toPoint());
- if (action) {
- // Show or hide the selected role
- const QByteArray selectedRole = action->data().toByteArray();
+ menu->addSeparator();
+
+ QActionGroup* widthsGroup = new QActionGroup(menu);
+ const bool autoColumnWidths = props.headerColumnWidths().isEmpty();
+
+ QAction* autoAdjustWidthsAction = menu->addAction(i18nc("@action:inmenu", "Automatic Column Widths"));
+ autoAdjustWidthsAction->setCheckable(true);
+ autoAdjustWidthsAction->setChecked(autoColumnWidths);
+ autoAdjustWidthsAction->setActionGroup(widthsGroup);
+
+ QAction* customWidthsAction = menu->addAction(i18nc("@action:inmenu", "Custom Column Widths"));
+ customWidthsAction->setCheckable(true);
+ customWidthsAction->setChecked(!autoColumnWidths);
+ customWidthsAction->setActionGroup(widthsGroup);
+
+ QAction* action = menu->exec(pos.toPoint());
+ if (menu && action) {
+ if (action == autoAdjustWidthsAction) {
+ // Clear the column-widths from the viewproperties and turn on
+ // the automatic resizing of the columns
+ props.setHeaderColumnWidths(QList<int>());
+ KItemListHeader* header = m_container->controller()->view()->header();
+ header->setAutomaticColumnResizing(true);
+ } else if (action == customWidthsAction) {
+ // Apply the current column-widths as custom column-widths and turn
+ // off the automatic resizing of the columns
+ const KItemListView* view = m_container->controller()->view();
+ KItemListHeader* header = view->header();
+
+ QList<int> columnWidths;
+ foreach (const QByteArray& role, view->visibleRoles()) {
+ columnWidths.append(header->columnWidth(role));
+ }
- ViewProperties props(url());
- QList<QByteArray> visibleRoles = view->visibleRoles();
- if (action->isChecked()) {
- visibleRoles.append(selectedRole);
+ props.setHeaderColumnWidths(columnWidths);
+ header->setAutomaticColumnResizing(false);
} else {
- visibleRoles.removeOne(selectedRole);
+ // Show or hide the selected role
+ const QByteArray selectedRole = action->data().toByteArray();
+
+ QList<QByteArray> visibleRoles = view->visibleRoles();
+ if (action->isChecked()) {
+ visibleRoles.append(selectedRole);
+ } else {
+ visibleRoles.removeOne(selectedRole);
+ }
+
+ view->setVisibleRoles(visibleRoles);
+ props.setVisibleRoles(visibleRoles);
}
+ }
- view->setVisibleRoles(visibleRoles);
- props.setVisibleRoles(visibleRoles);
+ delete menu;
+}
+
+void DolphinView::slotHeaderColumnWidthChanged(const QByteArray& role, qreal current, qreal previous)
+{
+ Q_UNUSED(previous);
+
+ const QList<QByteArray> visibleRoles = m_container->visibleRoles();
+
+ ViewProperties props(url());
+ QList<int> columnWidths = props.headerColumnWidths();
+ if (columnWidths.count() != visibleRoles.count()) {
+ columnWidths.clear();
+ columnWidths.reserve(visibleRoles.count());
+ const KItemListHeader* header = m_container->controller()->view()->header();
+ foreach (const QByteArray& role, visibleRoles) {
+ const int width = header->columnWidth(role);
+ columnWidths.append(width);
+ }
}
- delete menu.data();
+ const int roleIndex = visibleRoles.indexOf(role);
+ Q_ASSERT(roleIndex >= 0 && roleIndex < columnWidths.count());
+ columnWidths[roleIndex] = current;
+
+ props.setHeaderColumnWidths(columnWidths);
}
void DolphinView::slotItemHovered(int index)
}
}
+ KItemListView* itemListView = m_container->controller()->view();
+ if (itemListView->isHeaderVisible()) {
+ KItemListHeader* header = itemListView->header();
+ const QList<int> headerColumnWidths = props.headerColumnWidths();
+ const int rolesCount = m_visibleRoles.count();
+ if (headerColumnWidths.count() == rolesCount) {
+ header->setAutomaticColumnResizing(false);
+
+ QHash<QByteArray, qreal> columnWidths;
+ for (int i = 0; i < rolesCount; ++i) {
+ columnWidths.insert(m_visibleRoles[i], headerColumnWidths[i]);
+ }
+ header->setColumnWidths(columnWidths);
+ } else {
+ header->setAutomaticColumnResizing(true);
+ }
+ }
+
m_container->endTransaction();
}
void slotItemContextMenuRequested(int index, const QPointF& pos);
void slotViewContextMenuRequested(const QPointF& pos);
void slotHeaderContextMenuRequested(const QPointF& pos);
+ void slotHeaderColumnWidthChanged(const QByteArray& role, qreal current, qreal previous);
void slotItemHovered(int index);
void slotItemUnhovered(int index);
void slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event);
return roles;
}
+void ViewProperties::setHeaderColumnWidths(const QList<int>& widths)
+{
+ if (m_node->headerColumnWidths() != widths) {
+ m_node->setHeaderColumnWidths(widths);
+ update();
+ }
+}
+
+QList<int> ViewProperties::headerColumnWidths() const
+{
+ return m_node->headerColumnWidths();
+}
+
void ViewProperties::setDirProperties(const ViewProperties& props)
{
setViewMode(props.viewMode());
setSortOrder(props.sortOrder());
setSortFoldersFirst(props.sortFoldersFirst());
setVisibleRoles(props.visibleRoles());
+ setHeaderColumnWidths(props.headerColumnWidths());
}
void ViewProperties::setAutoSaveEnabled(bool autoSave)
*/
QList<QByteArray> visibleRoles() const;
+ void setHeaderColumnWidths(const QList<int>& widths);
+ QList<int> headerColumnWidths() const;
+
/**
* Sets the directory properties view mode, show preview,
* show hidden files, sorting and sort order like