File indexing completed on 2024-05-12 03:48:24
0001 /* 0002 File : WorksheetElementContainer.cpp 0003 Project : LabPlot 0004 Description : Worksheet element container - parent of multiple elements 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2009 Tilman Benkert <thzs@gmx.net> 0007 SPDX-FileCopyrightText: 2012-2021 Alexander Semke <alexander.semke@web.de> 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 #include "backend/worksheet/WorksheetElementContainer.h" 0013 #include "backend/lib/commandtemplates.h" 0014 #include "backend/lib/macros.h" 0015 #include "backend/lib/trace.h" 0016 #include "backend/worksheet/ResizeItem.h" 0017 #include "backend/worksheet/Worksheet.h" 0018 #include "backend/worksheet/WorksheetElementContainerPrivate.h" 0019 #include "backend/worksheet/plots/cartesian/XYCurve.h" 0020 0021 #include <QGraphicsScene> 0022 #include <QGraphicsSceneContextMenuEvent> 0023 #include <QMenu> 0024 #include <QPainter> 0025 0026 #include <KLocalizedString> 0027 0028 /** 0029 * \class WorksheetElementContainer 0030 * \ingroup worksheet 0031 * \brief Worksheet element container - parent of multiple elements 0032 * This class provides the functionality for a containers of multiple 0033 * worksheet elements. Such a container can be a plot or group of elements. 0034 */ 0035 0036 WorksheetElementContainer::WorksheetElementContainer(const QString& name, AspectType type) 0037 : WorksheetElement(name, new WorksheetElementContainerPrivate(this), type) { 0038 connect(this, &WorksheetElementContainer::childAspectAdded, this, &WorksheetElementContainer::handleAspectAdded); 0039 } 0040 0041 WorksheetElementContainer::WorksheetElementContainer(const QString& name, WorksheetElementContainerPrivate* dd, AspectType type) 0042 : WorksheetElement(name, dd, type) { 0043 connect(this, &WorksheetElementContainer::childAspectAdded, this, &WorksheetElementContainer::handleAspectAdded); 0044 } 0045 0046 // no need to delete the d-pointer here - it inherits from QGraphicsItem 0047 // and is deleted during the cleanup in QGraphicsScene 0048 WorksheetElementContainer::~WorksheetElementContainer() = default; 0049 0050 QRectF WorksheetElementContainer::rect() const { 0051 Q_D(const WorksheetElementContainer); 0052 return d->rect; 0053 } 0054 0055 STD_SWAP_METHOD_SETTER_CMD_IMPL(WorksheetElementContainer, SetVisible, bool, swapVisible) 0056 void WorksheetElementContainer::setVisible(bool on) { 0057 Q_D(WorksheetElementContainer); 0058 0059 // take care of proper ordering on the undo-stack, 0060 // when making the container and all its children visible/invisible. 0061 // if visible is set true, change the visibility of the container first 0062 if (on) { 0063 beginMacro(i18n("%1: set visible", name())); 0064 exec(new WorksheetElementContainerSetVisibleCmd(d, on, ki18n("%1: set visible"))); 0065 } else 0066 beginMacro(i18n("%1: set invisible", name())); 0067 0068 // change the visibility of all children 0069 const auto& elements = children<WorksheetElement>(AbstractAspect::ChildIndexFlag::IncludeHidden | AbstractAspect::ChildIndexFlag::Compress); 0070 for (auto* elem : elements) { 0071 auto* curve = dynamic_cast<XYCurve*>(elem); 0072 if (curve) { 0073 // making curves invisible triggers the recalculation of plot ranges if auto-scale is active. 0074 // this should be avoided by supressing the retransformation in the curves. 0075 curve->setSuppressRetransform(true); 0076 elem->setVisible(on); 0077 curve->setSuppressRetransform(false); 0078 } else if (elem) 0079 elem->setVisible(on); 0080 } 0081 0082 // if visible is set false, change the visibility of the container last 0083 if (!on) 0084 exec(new WorksheetElementContainerSetVisibleCmd(d, false, ki18n("%1: set invisible"))); 0085 0086 endMacro(); 0087 } 0088 0089 bool WorksheetElementContainer::isFullyVisible() const { 0090 const auto& elements = children<WorksheetElement>(AbstractAspect::ChildIndexFlag::IncludeHidden | AbstractAspect::ChildIndexFlag::Compress); 0091 for (const auto* elem : elements) { 0092 if (!elem->isVisible()) 0093 return false; 0094 } 0095 return true; 0096 } 0097 0098 void WorksheetElementContainer::setPrinting(bool on) { 0099 Q_D(WorksheetElementContainer); 0100 d->m_printing = on; 0101 } 0102 0103 void WorksheetElementContainer::setResizeEnabled(bool enabled) { 0104 if (m_resizeItem) 0105 m_resizeItem->setVisible(enabled); 0106 else { 0107 if (enabled) { 0108 m_resizeItem = new ResizeItem(this); 0109 m_resizeItem->setRect(rect()); 0110 } 0111 } 0112 } 0113 0114 void WorksheetElementContainer::retransform() { 0115 if (isLoading()) 0116 return; 0117 0118 PERFTRACE(QStringLiteral("WorksheetElementContainer::retransform()")); 0119 Q_D(WorksheetElementContainer); 0120 0121 const auto& elements = children<WorksheetElement>(AbstractAspect::ChildIndexFlag::IncludeHidden | AbstractAspect::ChildIndexFlag::Compress); 0122 for (auto* child : elements) 0123 child->retransform(); 0124 0125 d->recalcShapeAndBoundingRect(); 0126 0127 if (m_resizeItem) 0128 m_resizeItem->setRect(rect()); 0129 } 0130 0131 /*! 0132 * called if the size of the worksheet page was changed and the content has to be adjusted/resized (\c pageResize = true) 0133 * or if a new rectangular for the element container was set (\c pageResize = false). 0134 * In the second case, \c WorksheetElement::handleResize() is called for every worksheet child to adjust the content to the new size. 0135 * In the first case, a new rectangular for the container is calculated and set first, which on the other hand, triggers the content adjustments 0136 * in the container children. 0137 */ 0138 void WorksheetElementContainer::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { 0139 DEBUG(Q_FUNC_INFO); 0140 Q_D(const WorksheetElementContainer); 0141 if (pageResize) { 0142 QRectF rect(d->rect); 0143 rect.setWidth(d->rect.width() * horizontalRatio); 0144 rect.setHeight(d->rect.height() * verticalRatio); 0145 setRect(rect); 0146 } else { 0147 // for (auto* elem : children<WorksheetElement>(IncludeHidden)) 0148 // elem->handleResize(horizontalRatio, verticalRatio); 0149 } 0150 } 0151 0152 void WorksheetElementContainer::handleAspectAdded(const AbstractAspect* aspect) { 0153 Q_D(WorksheetElementContainer); 0154 0155 const auto* element = qobject_cast<const WorksheetElement*>(aspect); 0156 if (element && (aspect->parentAspect() == this)) { 0157 connect(element, &WorksheetElement::hovered, this, &WorksheetElementContainer::childHovered); 0158 connect(element, &WorksheetElement::unhovered, this, &WorksheetElementContainer::childUnhovered); 0159 connect(element, &WorksheetElement::changed, this, &WorksheetElementContainer::changed); 0160 element->graphicsItem()->setParentItem(d); 0161 0162 qreal zVal = 0; 0163 for (auto* child : children<WorksheetElement>(ChildIndexFlag::IncludeHidden)) 0164 child->setZValue(zVal++); 0165 } 0166 0167 if (!isLoading()) 0168 d->recalcShapeAndBoundingRect(); 0169 } 0170 0171 void WorksheetElementContainer::childHovered() { 0172 Q_D(WorksheetElementContainer); 0173 if (!d->isSelected()) { 0174 if (isHovered()) 0175 setHover(false); 0176 else 0177 d->update(); 0178 } 0179 } 0180 0181 void WorksheetElementContainer::childUnhovered() { 0182 Q_D(WorksheetElementContainer); 0183 if (!d->isSelected()) { 0184 setHover(true); 0185 } 0186 } 0187 0188 void WorksheetElementContainer::prepareGeometryChange() { 0189 Q_D(WorksheetElementContainer); 0190 d->prepareGeometryChangeRequested(); 0191 } 0192 0193 // ################################################################ 0194 // ################### Private implementation ########################## 0195 // ################################################################ 0196 WorksheetElementContainerPrivate::WorksheetElementContainerPrivate(WorksheetElementContainer* owner) 0197 : WorksheetElementPrivate(owner) 0198 , q(owner) { 0199 setAcceptHoverEvents(true); 0200 } 0201 0202 void WorksheetElementContainerPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { 0203 scene()->clearSelection(); 0204 setSelected(true); 0205 QMenu* menu = q->createContextMenu(); 0206 menu->exec(event->screenPos()); 0207 } 0208 0209 void WorksheetElementContainerPrivate::prepareGeometryChangeRequested() { 0210 prepareGeometryChange(); // this is not const! 0211 recalcShapeAndBoundingRect(); 0212 } 0213 0214 void WorksheetElementContainerPrivate::recalcShapeAndBoundingRect() { 0215 // if (q->isLoading()) 0216 // return; 0217 0218 // old logic calculating the bounding box as the box covering all children. 0219 // we might need this logic later once we implement something like selection of multiple plots, etc. 0220 // boundingRectangle = QRectF(); 0221 // QVector<WorksheetElement*> childList = q->children<WorksheetElement>(AbstractAspect::IncludeHidden | AbstractAspect::Compress); 0222 // foreach (const WorksheetElement* elem, childList) 0223 // boundingRectangle |= elem->graphicsItem()->mapRectToParent(elem->graphicsItem()->boundingRect()); 0224 // 0225 qreal penWidth = 2.; 0226 m_boundingRectangle = q->rect(); 0227 // QDEBUG(Q_FUNC_INFO << ", bound rect = " << boundingRectangle) 0228 m_boundingRectangle = QRectF(-m_boundingRectangle.width() / 2. - penWidth / 2., 0229 -m_boundingRectangle.height() / 2. - penWidth / 2., 0230 m_boundingRectangle.width() + penWidth, 0231 m_boundingRectangle.height() + penWidth); 0232 0233 QPainterPath path; 0234 path.addRect(m_boundingRectangle); 0235 0236 // make the shape somewhat thicker than the hoveredPen to make the selection/hovering box more visible 0237 m_shape = QPainterPath(); 0238 m_shape.addPath(WorksheetElement::shapeFromPath(path, QPen(QBrush(), penWidth))); 0239 } 0240 0241 // Inherited from QGraphicsItem 0242 QRectF WorksheetElementContainerPrivate::boundingRect() const { 0243 return m_boundingRectangle; 0244 } 0245 0246 // Inherited from QGraphicsItem 0247 void WorksheetElementContainerPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget*) { 0248 if (!isVisible()) 0249 return; 0250 0251 if (m_hovered && !isSelected() && !m_printing) { 0252 painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine)); 0253 painter->drawPath(m_shape); 0254 } 0255 0256 if (isSelected() && !m_printing) { 0257 painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine)); 0258 painter->drawPath(m_shape); 0259 } 0260 } 0261 0262 void WorksheetElementContainerPrivate::retransform() { 0263 }