-/***************************************************************************
- * 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 *
- ***************************************************************************/
+/*
+ * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
-#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 "kitemlistview.h"
+#include "private/kitemlistheaderwidget.h"
KItemListHeader::~KItemListHeader()
{
}
-void KItemListHeader::setModel(KItemModelBase* model)
+void KItemListHeader::setAutomaticColumnResizing(bool automatic)
{
- 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);
+ if (m_headerWidget->automaticColumnResizing() != automatic) {
+ m_headerWidget->setAutomaticColumnResizing(automatic);
+ if (automatic) {
+ m_view->applyAutomaticColumnWidths();
+ m_view->doLayout(KItemListView::NoAnimation);
}
}
-
- update();
-}
-
-QHash<QByteArray, qreal> KItemListHeader::visibleRolesWidths() const
-{
- return m_visibleRolesWidths;
}
-qreal KItemListHeader::minimumRoleWidth() const
+bool KItemListHeader::automaticColumnResizing() const
{
- QFontMetricsF fontMetrics(font());
- return fontMetrics.height() * 4;
+ return m_headerWidget->automaticColumnResizing();
}
-void KItemListHeader::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
+void KItemListHeader::setColumnWidth(const QByteArray &role, qreal width)
{
- 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);
+ if (!m_headerWidget->automaticColumnResizing()) {
+ m_headerWidget->setColumnWidth(role, width);
+ m_view->applyColumnWidthsFromHeader();
+ m_view->doLayout(KItemListView::NoAnimation);
}
}
-void KItemListHeader::mousePressEvent(QGraphicsSceneMouseEvent* event)
+qreal KItemListHeader::columnWidth(const QByteArray &role) const
{
- 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)
-{
- 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();
+ return m_headerWidget->columnWidth(role);
}
-void KItemListHeader::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
+void KItemListHeader::setColumnWidths(const QHash<QByteArray, qreal> &columnWidths)
{
- 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();
- }
- }
- 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);
- }
+ if (!m_headerWidget->automaticColumnResizing()) {
+ const auto visibleRoles = m_view->visibleRoles();
+ for (const QByteArray &role : visibleRoles) {
+ const qreal width = columnWidths.value(role);
+ m_headerWidget->setColumnWidth(role, width);
}
- 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();
+ m_view->applyColumnWidthsFromHeader();
+ m_view->doLayout(KItemListView::NoAnimation);
}
}
-void KItemListHeader::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous)
+qreal KItemListHeader::preferredColumnWidth(const QByteArray &role) const
{
- 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);
+ return m_headerWidget->preferredColumnWidth(role);
}
-void KItemListHeader::updatePressedRoleIndex(const QPointF& pos)
+void KItemListHeader::setSidePadding(qreal leftPaddingWidth, qreal rightPaddingWidth)
{
- 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
-{
- int index = -1;
-
- qreal x = 0;
- foreach (const QByteArray& role, m_visibleRoles) {
- ++index;
- x += m_visibleRolesWidths.value(role);
- if (pos.x() <= x) {
- break;
+ if (m_headerWidget->leftPadding() != leftPaddingWidth || m_headerWidget->rightPadding() != rightPaddingWidth) {
+ m_headerWidget->setSidePadding(leftPaddingWidth, rightPaddingWidth);
+ if (m_headerWidget->automaticColumnResizing()) {
+ m_view->applyAutomaticColumnWidths();
}
+ m_view->doLayout(KItemListView::NoAnimation);
}
-
- return index;
-}
-
-bool KItemListHeader::isAboveRoleGrip(const QPointF& pos, int roleIndex) const
-{
- qreal x = 0;
- for (int i = 0; i <= roleIndex; ++i) {
- const QByteArray role = m_visibleRoles[i];
- x += m_visibleRolesWidths.value(role);
- }
-
- const int grip = style()->pixelMetric(QStyle::PM_HeaderGripMargin);
- return pos.x() >= (x - grip) && pos.x() <= x;
}
-QPixmap KItemListHeader::createRolePixmap(int roleIndex) const
+qreal KItemListHeader::leftPadding() 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->leftPadding();
}
-int KItemListHeader::targetOfMovingRole() const
+qreal KItemListHeader::rightPadding() 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_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;
- }
-
- targetLeft += targetWidth;
- ++targetIndex;
- }
-
- return m_movingRole.index;
+ return m_headerWidget->rightPadding();
}
-qreal KItemListHeader::roleXPosition(const QByteArray& role) const
+KItemListHeader::KItemListHeader(KItemListView *listView)
+ : QObject(listView)
+ , 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, &KItemListHeaderWidget::columnWidthChanged, this, &KItemListHeader::columnWidthChanged);
+ connect(m_headerWidget, &KItemListHeaderWidget::columnWidthChangeFinished, this, &KItemListHeader::columnWidthChangeFinished);
+ connect(m_headerWidget, &KItemListHeaderWidget::sidePaddingChanged, this, &KItemListHeader::sidePaddingChanged);
}
-#include "kitemlistheader_p.moc"
+#include "moc_kitemlistheader.cpp"