File indexing completed on 2024-05-19 15:02:14

0001 /***************************************************************************
0002     File                 : WorksheetElementContainer.cpp
0003     Project              : LabPlot
0004     Description          : Worksheet element container - parent of multiple elements
0005     --------------------------------------------------------------------
0006     Copyright            : (C) 2009 Tilman Benkert (thzs@gmx.net)
0007     Copyright            : (C) 2012-2015 by Alexander Semke (alexander.semke@web.de)
0008  ***************************************************************************/
0009 
0010 /***************************************************************************
0011  *                                                                         *
0012  *  This program is free software; you can redistribute it and/or modify   *
0013  *  it under the terms of the GNU General Public License as published by   *
0014  *  the Free Software Foundation; either version 2 of the License, or      *
0015  *  (at your option) any later version.                                    *
0016  *                                                                         *
0017  *  This program is distributed in the hope that it will be useful,        *
0018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0020  *  GNU General Public License for more details.                           *
0021  *                                                                         *
0022  *   You should have received a copy of the GNU General Public License     *
0023  *   along with this program; if not, write to the Free Software           *
0024  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0025  *   Boston, MA  02110-1301  USA                                           *
0026  *                                                                         *
0027  ***************************************************************************/
0028 
0029 #include "backend/worksheet/WorksheetElementContainer.h"
0030 #include "backend/worksheet/WorksheetElementContainerPrivate.h"
0031 #include "backend/worksheet/plots/cartesian/Axis.h"
0032 #include "backend/worksheet/Worksheet.h"
0033 #include "backend/lib/commandtemplates.h"
0034 #include "backend/lib/macros.h"
0035 #include "backend/lib/trace.h"
0036 
0037 #include <QGraphicsScene>
0038 #include <QGraphicsSceneContextMenuEvent>
0039 #include <QMenu>
0040 #include <QPainter>
0041 
0042 #include <KLocalizedString>
0043 
0044 /**
0045  * \class WorksheetElementContainer
0046  * \ingroup worksheet
0047  * \brief Worksheet element container - parent of multiple elements
0048  * This class provides the functionality for a containers of multiple
0049  * worksheet elements. Such a container can be a plot or group of elements.
0050  */
0051 
0052 WorksheetElementContainer::WorksheetElementContainer(const QString& name, AspectType type)
0053     : WorksheetElement(name, type), d_ptr(new WorksheetElementContainerPrivate(this)) {
0054 
0055     connect(this, &WorksheetElementContainer::aspectAdded, this, &WorksheetElementContainer::handleAspectAdded);
0056 }
0057 
0058 WorksheetElementContainer::WorksheetElementContainer(const QString& name, WorksheetElementContainerPrivate* dd, AspectType type)
0059     : WorksheetElement(name, type), d_ptr(dd) {
0060 
0061     connect(this, &WorksheetElementContainer::aspectAdded, this, &WorksheetElementContainer::handleAspectAdded);
0062 }
0063 
0064 //no need to delete the d-pointer here - it inherits from QGraphicsItem
0065 //and is deleted during the cleanup in QGraphicsScene
0066 WorksheetElementContainer::~WorksheetElementContainer() = default;
0067 
0068 QGraphicsItem* WorksheetElementContainer::graphicsItem() const {
0069     return const_cast<QGraphicsItem*>(static_cast<const QGraphicsItem*>(d_ptr));
0070 }
0071 
0072 QRectF WorksheetElementContainer::rect() const {
0073     Q_D(const WorksheetElementContainer);
0074     return d->rect;
0075 }
0076 
0077 STD_SWAP_METHOD_SETTER_CMD_IMPL(WorksheetElementContainer, SetVisible, bool, swapVisible)
0078 void WorksheetElementContainer::setVisible(bool on) {
0079     Q_D(WorksheetElementContainer);
0080 
0081     //take care of proper ordering on the undo-stack,
0082     //when making the container and all its children visible/invisible.
0083     //if visible is set true, change the visibility of the container first
0084     if (on) {
0085         beginMacro( i18n("%1: set visible", name()) );
0086         exec( new WorksheetElementContainerSetVisibleCmd(d, on, ki18n("%1: set visible")) );
0087     } else
0088         beginMacro( i18n("%1: set invisible", name()) );
0089 
0090     //change the visibility of all children
0091     QVector<WorksheetElement*> childList = children<WorksheetElement>(AbstractAspect::ChildIndexFlag::IncludeHidden | AbstractAspect::ChildIndexFlag::Compress);
0092     for (auto* elem : childList) {
0093         auto* curve = dynamic_cast<XYCurve*>(elem);
0094         if (curve) {
0095             //making curves invisible triggers the recalculation of plot ranges if auto-scale is active.
0096             //this needs to avoided by supressing the retransformation in the curves.
0097             curve->suppressRetransform(true);
0098             elem->setVisible(on);
0099             curve->suppressRetransform(false);
0100         } else if (elem)
0101             elem->setVisible(on);
0102     }
0103 
0104     //if visible is set false, change the visibility of the container last
0105     if (!on)
0106         exec(new WorksheetElementContainerSetVisibleCmd(d, false, ki18n("%1: set invisible")));
0107 
0108     endMacro();
0109 }
0110 
0111 bool WorksheetElementContainer::isVisible() const {
0112     Q_D(const WorksheetElementContainer);
0113     return d->isVisible();
0114 }
0115 
0116 bool WorksheetElementContainer::isFullyVisible() const {
0117     QVector<WorksheetElement*> childList = children<WorksheetElement>(AbstractAspect::ChildIndexFlag::IncludeHidden | AbstractAspect::ChildIndexFlag::Compress);
0118     for (const auto* elem : childList) {
0119         if (!elem->isVisible())
0120             return false;
0121     }
0122     return true;
0123 }
0124 
0125 void WorksheetElementContainer::setPrinting(bool on) {
0126     Q_D(WorksheetElementContainer);
0127     d->m_printing = on;
0128 }
0129 
0130 void WorksheetElementContainer::retransform() {
0131 //  if (isLoading())
0132 //      return;
0133 
0134     PERFTRACE("WorksheetElementContainer::retransform()");
0135     Q_D(WorksheetElementContainer);
0136 
0137     QVector<WorksheetElement*> childList = children<WorksheetElement>(AbstractAspect::ChildIndexFlag::IncludeHidden | AbstractAspect::ChildIndexFlag::Compress);
0138     for (auto* child : childList)
0139         child->retransform();
0140 
0141     d->recalcShapeAndBoundingRect();
0142 }
0143 
0144 /*!
0145  * called if the size of the worksheet page was changed and the content has to be adjusted/resized (\c pageResize = true)
0146  * or if a new rectangular for the element container was set (\c pageResize = false).
0147  * In the second case, \c WorksheetElement::handleResize() is called for every worksheet child to adjuste the content to the new size.
0148  * In the first case, a new rectangular for the container is calculated and set first, which on the other hand, triggers the content adjustments
0149  * in the container children.
0150  */
0151 void WorksheetElementContainer::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) {
0152     DEBUG("WorksheetElementContainer::handleResize()");
0153     Q_D(const WorksheetElementContainer);
0154     if (pageResize) {
0155         QRectF rect(d->rect);
0156         rect.setWidth(d->rect.width()*horizontalRatio);
0157         rect.setHeight(d->rect.height()*verticalRatio);
0158         setRect(rect);
0159     } else {
0160 //      for (auto* elem : children<WorksheetElement>(IncludeHidden))
0161 //          elem->handleResize(horizontalRatio, verticalRatio);
0162     }
0163 }
0164 
0165 void WorksheetElementContainer::handleAspectAdded(const AbstractAspect* aspect) {
0166     Q_D(WorksheetElementContainer);
0167 
0168     const auto* element = qobject_cast<const WorksheetElement*>(aspect);
0169     if (element && (aspect->parentAspect() == this)) {
0170         connect(element, &WorksheetElement::hovered, this, &WorksheetElementContainer::childHovered);
0171         connect(element, &WorksheetElement::unhovered, this, &WorksheetElementContainer::childUnhovered);
0172         element->graphicsItem()->setParentItem(d);
0173 
0174         qreal zVal = 0;
0175         for (auto* child : children<WorksheetElement>(ChildIndexFlag::IncludeHidden))
0176             child->setZValue(zVal++);
0177     }
0178 
0179     if (!isLoading())
0180         d->recalcShapeAndBoundingRect();
0181 }
0182 
0183 void WorksheetElementContainer::childHovered() {
0184     Q_D(WorksheetElementContainer);
0185     if (!d->isSelected()) {
0186         if (d->m_hovered)
0187             d->m_hovered = false;
0188         d->update();
0189     }
0190 }
0191 
0192 void WorksheetElementContainer::childUnhovered() {
0193     Q_D(WorksheetElementContainer);
0194     if (!d->isSelected()) {
0195         d->m_hovered = true;
0196         d->update();
0197     }
0198 }
0199 
0200 void WorksheetElementContainer::prepareGeometryChange() {
0201     Q_D(WorksheetElementContainer);
0202     d->prepareGeometryChangeRequested();
0203 }
0204 
0205 //################################################################
0206 //################### Private implementation ##########################
0207 //################################################################
0208 WorksheetElementContainerPrivate::WorksheetElementContainerPrivate(WorksheetElementContainer *owner) : q(owner) {
0209     setAcceptHoverEvents(true);
0210 }
0211 
0212 QString WorksheetElementContainerPrivate::name() const {
0213     return q->name();
0214 }
0215 
0216 void WorksheetElementContainerPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) {
0217     scene()->clearSelection();
0218     setSelected(true);
0219     QMenu* menu = q->createContextMenu();
0220     menu->exec(event->screenPos());
0221 }
0222 
0223 void WorksheetElementContainerPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) {
0224     if (!isSelected()) {
0225         m_hovered = true;
0226         update();
0227     }
0228 }
0229 
0230 void WorksheetElementContainerPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
0231     if (m_hovered) {
0232         m_hovered = false;
0233         update();
0234     }
0235 }
0236 
0237 bool WorksheetElementContainerPrivate::swapVisible(bool on) {
0238     bool oldValue = isVisible();
0239 
0240     //When making a graphics item invisible, it gets deselected in the scene.
0241     //In this case we don't want to deselect the item in the project explorer.
0242     //We need to supress the deselection in the view.
0243     auto* worksheet = static_cast<Worksheet*>(q->parent(AspectType::Worksheet));
0244     worksheet->suppressSelectionChangedEvent(true);
0245     setVisible(on);
0246     emit q->visibleChanged(on);
0247     worksheet->suppressSelectionChangedEvent(false);
0248 
0249     return oldValue;
0250 }
0251 
0252 void WorksheetElementContainerPrivate::prepareGeometryChangeRequested() {
0253     prepareGeometryChange();
0254     recalcShapeAndBoundingRect();
0255 }
0256 
0257 void WorksheetElementContainerPrivate::recalcShapeAndBoundingRect() {
0258 //  if (q->isLoading())
0259 //      return;
0260 
0261     //old logic calculating the bounding box as as the box covering all children.
0262     //we might need this logic later once we implement something like selection of multiple plots, etc.
0263 //  boundingRectangle = QRectF();
0264 //  QVector<WorksheetElement*> childList = q->children<WorksheetElement>(AbstractAspect::IncludeHidden | AbstractAspect::Compress);
0265 //  foreach (const WorksheetElement* elem, childList)
0266 //      boundingRectangle |= elem->graphicsItem()->mapRectToParent(elem->graphicsItem()->boundingRect());
0267 //
0268     float penWidth = 2.;
0269     boundingRectangle = q->rect();
0270     boundingRectangle = QRectF(-boundingRectangle.width()/2 - penWidth / 2, -boundingRectangle.height()/2 - penWidth / 2,
0271                   boundingRectangle.width() + penWidth, boundingRectangle.height() + penWidth);
0272 
0273     QPainterPath path;
0274     path.addRect(boundingRectangle);
0275 
0276     //make the shape somewhat thicker then the hoveredPen to make the selection/hovering box more visible
0277     containerShape = QPainterPath();
0278     containerShape.addPath(WorksheetElement::shapeFromPath(path, QPen(QBrush(), penWidth)));
0279 }
0280 
0281 // Inherited from QGraphicsItem
0282 QRectF WorksheetElementContainerPrivate::boundingRect() const {
0283     return boundingRectangle;
0284 }
0285 
0286 // Inherited from QGraphicsItem
0287 void WorksheetElementContainerPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
0288     Q_UNUSED(option)
0289     Q_UNUSED(widget)
0290 
0291     if (!isVisible())
0292         return;
0293 
0294     if (m_hovered && !isSelected() && !m_printing) {
0295         painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine));
0296         painter->drawPath(containerShape);
0297     }
0298 
0299     if (isSelected() && !m_printing) {
0300         painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine));
0301         painter->drawPath(containerShape);
0302     }
0303 }