File indexing completed on 2025-01-26 03:34:10
0001 /* 0002 File : CustomPoint.cpp 0003 Project : LabPlot 0004 Description : Custom user-defined point on the plot 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2015 Ankit Wagadre <wagadre.ankit@gmail.com> 0007 SPDX-FileCopyrightText: 2015-2021 Alexander Semke <alexander.semke@web.de> 0008 SPDX-FileCopyrightText: 2020 Martin Marmsoler <martin.marmsoler@gmail.com> 0009 SPDX-FileCopyrightText: 2021 Stefan Gerlach <stefan.gerlach@uni.kn> 0010 SPDX-License-Identifier: GPL-2.0-or-later 0011 */ 0012 0013 #include "CustomPoint.h" 0014 #include "CustomPointPrivate.h" 0015 #include "backend/core/Project.h" 0016 #include "backend/lib/XmlStreamReader.h" 0017 #include "backend/lib/commandtemplates.h" 0018 #include "backend/lib/macros.h" 0019 #include "backend/worksheet/Worksheet.h" 0020 #include "backend/worksheet/plots/PlotArea.h" 0021 #include "backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h" 0022 #include "backend/worksheet/plots/cartesian/CartesianPlot.h" 0023 #include "backend/worksheet/plots/cartesian/Symbol.h" 0024 0025 #include <QGraphicsSceneMouseEvent> 0026 #include <QMenu> 0027 #include <QPainter> 0028 0029 #include <KConfig> 0030 #include <KConfigGroup> 0031 #include <KLocalizedString> 0032 0033 using Dimension = CartesianCoordinateSystem::Dimension; 0034 0035 /** 0036 * \class CustomPoint 0037 * \brief A customizable point. 0038 * 0039 * The position can be either specified by mouse events or by providing the 0040 * x- and y- coordinates in parent's coordinate system 0041 */ 0042 0043 CustomPoint::CustomPoint(CartesianPlot* plot, const QString& name) 0044 : WorksheetElement(name, new CustomPointPrivate(this), AspectType::CustomPoint) { 0045 m_plot = plot; 0046 DEBUG(Q_FUNC_INFO << ", cSystem index = " << m_cSystemIndex) 0047 DEBUG(Q_FUNC_INFO << ", plot cSystem count = " << m_plot->coordinateSystemCount()) 0048 cSystem = dynamic_cast<const CartesianCoordinateSystem*>(m_plot->coordinateSystem(m_cSystemIndex)); 0049 0050 init(); 0051 } 0052 0053 // no need to delete the d-pointer here - it inherits from QGraphicsItem 0054 // and is deleted during the cleanup in QGraphicsScene 0055 CustomPoint::~CustomPoint() = default; 0056 0057 void CustomPoint::init() { 0058 Q_D(CustomPoint); 0059 0060 // default position 0061 if (plot()) { 0062 d->coordinateBindingEnabled = true; // By default on 0063 auto cs = plot()->coordinateSystem(plot()->defaultCoordinateSystemIndex()); 0064 const auto x = m_plot->range(Dimension::X, cs->index(Dimension::X)).center(); 0065 const auto y = m_plot->range(Dimension::Y, cs->index(Dimension::Y)).center(); 0066 DEBUG(Q_FUNC_INFO << ", x/y pos = " << x << " / " << y) 0067 d->positionLogical = QPointF(x, y); 0068 } else 0069 d->position.point = QPointF(0, 0); 0070 d->updatePosition(); // To update also scene coordinates 0071 0072 // initialize the symbol 0073 d->symbol = new Symbol(QString()); 0074 addChild(d->symbol); 0075 d->symbol->setHidden(true); 0076 connect(d->symbol, &Symbol::updateRequested, [=] { 0077 d->recalcShapeAndBoundingRect(); 0078 }); 0079 connect(d->symbol, &Symbol::updatePixmapRequested, [=] { 0080 d->update(); 0081 }); 0082 KConfig config; 0083 d->symbol->init(config.group(QStringLiteral("CustomPoint"))); 0084 0085 initActions(); 0086 } 0087 0088 void CustomPoint::initActions() { 0089 } 0090 0091 /*! 0092 Returns an icon to be used in the project explorer. 0093 */ 0094 QIcon CustomPoint::icon() const { 0095 return QIcon::fromTheme(QStringLiteral("draw-cross")); 0096 } 0097 0098 QMenu* CustomPoint::createContextMenu() { 0099 // no context menu if the custom point is a child of an InfoElement, 0100 // everything is controlled by the parent 0101 if (parentAspect()->type() == AspectType::InfoElement) 0102 return nullptr; 0103 0104 return WorksheetElement::createContextMenu(); 0105 ; 0106 } 0107 0108 void CustomPoint::retransform() { 0109 DEBUG(Q_FUNC_INFO) 0110 Q_D(CustomPoint); 0111 d->retransform(); 0112 } 0113 0114 void CustomPoint::handleResize(double /*horizontalRatio*/, double /*verticalRatio*/, bool /*pageResize*/) { 0115 } 0116 0117 Symbol* CustomPoint::symbol() const { 0118 Q_D(const CustomPoint); 0119 return d->symbol; 0120 } 0121 0122 // ############################################################################## 0123 // ####################### Private implementation ############################### 0124 // ############################################################################## 0125 CustomPointPrivate::CustomPointPrivate(CustomPoint* owner) 0126 : WorksheetElementPrivate(owner) 0127 , q(owner) { 0128 setFlag(QGraphicsItem::ItemSendsGeometryChanges); 0129 setFlag(QGraphicsItem::ItemIsMovable); 0130 setFlag(QGraphicsItem::ItemIsSelectable); 0131 setFlag(QGraphicsItem::ItemIsFocusable); 0132 setAcceptHoverEvents(true); 0133 } 0134 0135 const CartesianPlot* CustomPointPrivate::plot() { 0136 return q->m_plot; 0137 } 0138 0139 /*! 0140 calculates the position and the bounding box of the item/point. Called on geometry or properties changes. 0141 */ 0142 void CustomPointPrivate::retransform() { 0143 DEBUG(Q_FUNC_INFO) 0144 if (suppressRetransform || q->isLoading()) 0145 return; 0146 0147 updatePosition(); // needed, because CartesianPlot calls retransform if some operations are done 0148 recalcShapeAndBoundingRect(); 0149 } 0150 0151 /*! 0152 recalculates the outer bounds and the shape of the item. 0153 */ 0154 void CustomPointPrivate::recalcShapeAndBoundingRect() { 0155 prepareGeometryChange(); 0156 0157 m_shape = QPainterPath(); 0158 if (insidePlot && symbol->style() != Symbol::Style::NoSymbols) { 0159 QPainterPath path = Symbol::stylePath(symbol->style()); 0160 0161 QTransform trafo; 0162 trafo.scale(symbol->size(), symbol->size()); 0163 path = trafo.map(path); 0164 trafo.reset(); 0165 0166 if (symbol->rotationAngle() != 0.) { 0167 trafo.rotate(symbol->rotationAngle()); 0168 path = trafo.map(path); 0169 } 0170 0171 m_shape.addPath(WorksheetElement::shapeFromPath(trafo.map(path), symbol->pen())); 0172 m_boundingRectangle = m_shape.boundingRect(); 0173 } 0174 } 0175 0176 void CustomPointPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget*) { 0177 if (!insidePlot) 0178 return; 0179 0180 if (symbol->style() != Symbol::Style::NoSymbols) { 0181 painter->setOpacity(symbol->opacity()); 0182 painter->setPen(symbol->pen()); 0183 painter->setBrush(symbol->brush()); 0184 painter->drawPath(m_shape); 0185 } 0186 0187 if (m_hovered && !isSelected() && !q->isPrinting()) { 0188 painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine)); 0189 painter->drawPath(m_shape); 0190 } 0191 0192 if (isSelected() && !q->isPrinting()) { 0193 painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine)); 0194 painter->drawPath(m_shape); 0195 } 0196 } 0197 0198 void CustomPointPrivate::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { 0199 // don't move when the parent is a InfoElement, because there 0200 // the custompoint position changes by the mouse are not allowed 0201 if (q->parentAspect()->type() == AspectType::InfoElement) 0202 return; 0203 0204 WorksheetElementPrivate::mouseReleaseEvent(event); 0205 } 0206 0207 // ############################################################################## 0208 // ################## Serialization/Deserialization ########################### 0209 // ############################################################################## 0210 //! Save as XML 0211 void CustomPoint::save(QXmlStreamWriter* writer) const { 0212 Q_D(const CustomPoint); 0213 0214 writer->writeStartElement(QStringLiteral("customPoint")); 0215 writeBasicAttributes(writer); 0216 writeCommentElement(writer); 0217 0218 // geometry 0219 writer->writeStartElement(QStringLiteral("geometry")); 0220 WorksheetElement::save(writer); 0221 writer->writeEndElement(); 0222 0223 d->symbol->save(writer); 0224 0225 writer->writeEndElement(); // close "CustomPoint" section 0226 } 0227 0228 //! Load from XML 0229 bool CustomPoint::load(XmlStreamReader* reader, bool preview) { 0230 Q_D(CustomPoint); 0231 0232 if (!readBasicAttributes(reader)) 0233 return false; 0234 0235 QXmlStreamAttributes attribs; 0236 QString str; 0237 0238 while (!reader->atEnd()) { 0239 reader->readNext(); 0240 if (reader->isEndElement() && reader->name() == QLatin1String("customPoint")) 0241 break; 0242 0243 if (!reader->isStartElement()) 0244 continue; 0245 0246 if (!preview && reader->name() == QLatin1String("comment")) { 0247 if (!readCommentElement(reader)) 0248 return false; 0249 } else if (!preview && reader->name() == QLatin1String("geometry")) { 0250 WorksheetElement::load(reader, preview); 0251 if (project()->xmlVersion() < 6) { 0252 // Before version 6 the position in the file was always a logical position 0253 d->positionLogical = d->position.point; 0254 d->position.point = QPointF(0, 0); 0255 d->coordinateBindingEnabled = true; 0256 } 0257 } else if (!preview && reader->name() == QLatin1String("symbol")) { 0258 d->symbol->load(reader, preview); 0259 } else { // unknown element 0260 reader->raiseUnknownElementWarning(); 0261 if (!reader->skipToEndElement()) 0262 return false; 0263 } 0264 } 0265 return true; 0266 }