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 }