"Fossies" - the Fresh Open Source Software Archive

Member "labplot-2.8.2/src/backend/worksheet/plots/cartesian/CustomPoint.cpp" (24 Feb 2021, 16671 Bytes) of package /linux/privat/labplot-2.8.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "CustomPoint.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.8.1_vs_2.8.2.

    1 /***************************************************************************
    2     File                 : CustomPoint.cpp
    3     Project              : LabPlot
    4     Description          : Custom user-defined point on the plot
    5     --------------------------------------------------------------------
    6     Copyright            : (C) 2015 Ankit Wagadre (wagadre.ankit@gmail.com)
    7     Copyright            : (C) 2015-2020 Alexander Semke (alexander.semke@web.de)
    8  ***************************************************************************/
    9 /***************************************************************************
   10  *                                                                         *
   11  *  This program is free software; you can redistribute it and/or modify   *
   12  *  it under the terms of the GNU General Public License as published by   *
   13  *  the Free Software Foundation; either version 2 of the License, or      *
   14  *  (at your option) any later version.                                    *
   15  *                                                                         *
   16  *  This program is distributed in the hope that it will be useful,        *
   17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
   18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
   19  *  GNU General Public License for more details.                           *
   20  *                                                                         *
   21  *   You should have received a copy of the GNU General Public License     *
   22  *   along with this program; if not, write to the Free Software           *
   23  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
   24  *   Boston, MA  02110-1301  USA                                           *
   25  *                                                                         *
   26  ***************************************************************************/
   27 
   28 #include "CustomPoint.h"
   29 #include "CustomPointPrivate.h"
   30 #include "backend/worksheet/Worksheet.h"
   31 #include "backend/worksheet/plots/cartesian/CartesianPlot.h"
   32 #include "backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h"
   33 #include "backend/lib/commandtemplates.h"
   34 #include "backend/lib/XmlStreamReader.h"
   35 
   36 #include <QPainter>
   37 #include <QMenu>
   38 #include <QGraphicsSceneMouseEvent>
   39 
   40 #include <KConfig>
   41 #include <KConfigGroup>
   42 #include <KLocalizedString>
   43 
   44 
   45 /**
   46  * \class CustomPoint
   47  * \brief A customizable point.
   48  *
   49  * The position can be either specified by mouse events or by providing the
   50  * x- and y- coordinates in parent's coordinate system
   51  */
   52 
   53 CustomPoint::CustomPoint(const CartesianPlot* plot, const QString& name)
   54     : WorksheetElement(name, AspectType::CustomPoint), d_ptr(new CustomPointPrivate(this, plot)) {
   55 
   56     init();
   57 }
   58 
   59 CustomPoint::CustomPoint(const QString& name, CustomPointPrivate* dd)
   60     : WorksheetElement(name, AspectType::CustomPoint), d_ptr(dd) {
   61 
   62     init();
   63 }
   64 
   65 //no need to delete the d-pointer here - it inherits from QGraphicsItem
   66 //and is deleted during the cleanup in QGraphicsScene
   67 CustomPoint::~CustomPoint() = default;
   68 
   69 void CustomPoint::init() {
   70     Q_D(CustomPoint);
   71 
   72     KConfig config;
   73     KConfigGroup group;
   74     group = config.group("CustomPoint");
   75     d->position.setX( group.readEntry("PositionXValue", d->plot->xMin() + (d->plot->xMax()-d->plot->xMin())/2) );
   76     d->position.setY( group.readEntry("PositionYValue", d->plot->yMin() + (d->plot->yMax()-d->plot->yMin())/2) );
   77 
   78     d->symbolStyle = (Symbol::Style)group.readEntry("SymbolStyle", (int)Symbol::Style::Circle);
   79     d->symbolSize = group.readEntry("SymbolSize", Worksheet::convertToSceneUnits(5, Worksheet::Unit::Point));
   80     d->symbolRotationAngle = group.readEntry("SymbolRotation", 0.0);
   81     d->symbolOpacity = group.readEntry("SymbolOpacity", 1.0);
   82     d->symbolBrush.setStyle( (Qt::BrushStyle)group.readEntry("SymbolFillingStyle", (int)Qt::SolidPattern) );
   83     d->symbolBrush.setColor( group.readEntry("SymbolFillingColor", QColor(Qt::red)) );
   84     d->symbolPen.setStyle( (Qt::PenStyle)group.readEntry("SymbolBorderStyle", (int)Qt::SolidLine) );
   85     d->symbolPen.setColor( group.readEntry("SymbolBorderColor", QColor(Qt::black)) );
   86     d->symbolPen.setWidthF( group.readEntry("SymbolBorderWidth", Worksheet::convertToSceneUnits(0.0, Worksheet::Unit::Point)) );
   87 
   88     this->initActions();
   89 }
   90 
   91 void CustomPoint::initActions() {
   92     visibilityAction = new QAction(i18n("Visible"), this);
   93     visibilityAction->setCheckable(true);
   94     connect(visibilityAction, &QAction::triggered, this, &CustomPoint::visibilityChanged);
   95 }
   96 
   97 /*!
   98     Returns an icon to be used in the project explorer.
   99 */
  100 QIcon CustomPoint::icon() const {
  101     return  QIcon::fromTheme("draw-cross");
  102 }
  103 
  104 QMenu* CustomPoint::createContextMenu() {
  105     QMenu* menu = WorksheetElement::createContextMenu();
  106     QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action"
  107     visibilityAction->setChecked(isVisible());
  108     menu->insertAction(firstAction, visibilityAction);
  109 
  110     return menu;
  111 }
  112 
  113 QGraphicsItem* CustomPoint::graphicsItem() const {
  114     return d_ptr;
  115 }
  116 
  117 void CustomPoint::retransform() {
  118     Q_D(CustomPoint);
  119     d->retransform();
  120 }
  121 
  122 void CustomPoint::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) {
  123     Q_UNUSED(horizontalRatio);
  124     Q_UNUSED(verticalRatio);
  125     Q_UNUSED(pageResize);
  126 }
  127 
  128 /* ============================ getter methods ================= */
  129 CLASS_SHARED_D_READER_IMPL(CustomPoint, QPointF, position, position)
  130 
  131 //symbols
  132 BASIC_SHARED_D_READER_IMPL(CustomPoint, Symbol::Style, symbolStyle, symbolStyle)
  133 BASIC_SHARED_D_READER_IMPL(CustomPoint, qreal, symbolOpacity, symbolOpacity)
  134 BASIC_SHARED_D_READER_IMPL(CustomPoint, qreal, symbolRotationAngle, symbolRotationAngle)
  135 BASIC_SHARED_D_READER_IMPL(CustomPoint, qreal, symbolSize, symbolSize)
  136 CLASS_SHARED_D_READER_IMPL(CustomPoint, QBrush, symbolBrush, symbolBrush)
  137 CLASS_SHARED_D_READER_IMPL(CustomPoint, QPen, symbolPen, symbolPen)
  138 
  139 /* ============================ setter methods and undo commands ================= */
  140 STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetPosition, QPointF, position, retransform)
  141 void CustomPoint::setPosition(QPointF position) {
  142     Q_D(CustomPoint);
  143     if (position != d->position)
  144         exec(new CustomPointSetPositionCmd(d, position, ki18n("%1: set position")));
  145 }
  146 
  147 //Symbol
  148 STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetSymbolStyle, Symbol::Style, symbolStyle, recalcShapeAndBoundingRect)
  149 void CustomPoint::setSymbolStyle(Symbol::Style style) {
  150     Q_D(CustomPoint);
  151     if (style != d->symbolStyle)
  152         exec(new CustomPointSetSymbolStyleCmd(d, style, ki18n("%1: set symbol style")));
  153 }
  154 
  155 STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetSymbolSize, qreal, symbolSize, recalcShapeAndBoundingRect)
  156 void CustomPoint::setSymbolSize(qreal size) {
  157     Q_D(CustomPoint);
  158     if (!qFuzzyCompare(1 + size, 1 + d->symbolSize))
  159         exec(new CustomPointSetSymbolSizeCmd(d, size, ki18n("%1: set symbol size")));
  160 }
  161 
  162 STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetSymbolRotationAngle, qreal, symbolRotationAngle, recalcShapeAndBoundingRect)
  163 void CustomPoint::setSymbolRotationAngle(qreal angle) {
  164     Q_D(CustomPoint);
  165     if (!qFuzzyCompare(1 + angle, 1 + d->symbolRotationAngle))
  166         exec(new CustomPointSetSymbolRotationAngleCmd(d, angle, ki18n("%1: rotate symbols")));
  167 }
  168 
  169 STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetSymbolBrush, QBrush, symbolBrush, update)
  170 void CustomPoint::setSymbolBrush(const QBrush &brush) {
  171     Q_D(CustomPoint);
  172     if (brush != d->symbolBrush)
  173         exec(new CustomPointSetSymbolBrushCmd(d, brush, ki18n("%1: set symbol filling")));
  174 }
  175 
  176 STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetSymbolPen, QPen, symbolPen, recalcShapeAndBoundingRect)
  177 void CustomPoint::setSymbolPen(const QPen &pen) {
  178     Q_D(CustomPoint);
  179     if (pen != d->symbolPen)
  180         exec(new CustomPointSetSymbolPenCmd(d, pen, ki18n("%1: set symbol outline style")));
  181 }
  182 
  183 STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetSymbolOpacity, qreal, symbolOpacity, update)
  184 void CustomPoint::setSymbolOpacity(qreal opacity) {
  185     Q_D(CustomPoint);
  186     if (opacity != d->symbolOpacity)
  187         exec(new CustomPointSetSymbolOpacityCmd(d, opacity, ki18n("%1: set symbol opacity")));
  188 }
  189 
  190 STD_SWAP_METHOD_SETTER_CMD_IMPL_F(CustomPoint, SetVisible, bool, swapVisible, update);
  191 void CustomPoint::setVisible(bool on) {
  192     Q_D(CustomPoint);
  193     exec(new CustomPointSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible")));
  194 }
  195 
  196 bool CustomPoint::isVisible() const {
  197     Q_D(const CustomPoint);
  198     return d->isVisible();
  199 }
  200 
  201 void CustomPoint::setPrinting(bool on) {
  202     Q_D(CustomPoint);
  203     d->m_printing = on;
  204 }
  205 
  206 //##############################################################################
  207 //######  SLOTs for changes triggered via QActions in the context menu  ########
  208 //##############################################################################
  209 void CustomPoint::visibilityChanged() {
  210     Q_D(const CustomPoint);
  211     this->setVisible(!d->isVisible());
  212 }
  213 
  214 //##############################################################################
  215 //####################### Private implementation ###############################
  216 //##############################################################################
  217 CustomPointPrivate::CustomPointPrivate(CustomPoint* owner, const CartesianPlot* p) : plot(p), q(owner) {
  218     setFlag(QGraphicsItem::ItemSendsGeometryChanges);
  219     setFlag(QGraphicsItem::ItemIsMovable);
  220     setFlag(QGraphicsItem::ItemIsSelectable);
  221     setAcceptHoverEvents(true);
  222 }
  223 
  224 QString CustomPointPrivate::name() const {
  225     return q->name();
  226 }
  227 
  228 /*!
  229     calculates the position and the bounding box of the item/point. Called on geometry or properties changes.
  230  */
  231 void CustomPointPrivate::retransform() {
  232     if (suppressRetransform)
  233         return;
  234 
  235     //calculate the point in the scene coordinates
  236     const auto* cSystem = dynamic_cast<const CartesianCoordinateSystem*>(plot->coordinateSystem());
  237     QVector<QPointF> listScene = cSystem->mapLogicalToScene(QVector<QPointF>{position});
  238     if (!listScene.isEmpty()) {
  239         m_visible = true;
  240         positionScene = listScene.at(0);
  241         suppressItemChangeEvent = true;
  242         setPos(positionScene);
  243         suppressItemChangeEvent = false;
  244     } else
  245         m_visible = false;
  246 
  247     recalcShapeAndBoundingRect();
  248 }
  249 
  250 bool CustomPointPrivate::swapVisible(bool on) {
  251     bool oldValue = isVisible();
  252 
  253     //When making a graphics item invisible, it gets deselected in the scene.
  254     //In this case we don't want to deselect the item in the project explorer.
  255     //We need to supress the deselection in the view.
  256     auto* worksheet = static_cast<Worksheet*>(q->parent(AspectType::Worksheet));
  257     worksheet->suppressSelectionChangedEvent(true);
  258     setVisible(on);
  259     worksheet->suppressSelectionChangedEvent(false);
  260 
  261     emit q->changed();
  262     emit q->visibleChanged(on);
  263     return oldValue;
  264 }
  265 
  266 /*!
  267     Returns the outer bounds of the item as a rectangle.
  268  */
  269 QRectF CustomPointPrivate::boundingRect() const {
  270     return transformedBoundingRectangle;
  271 }
  272 
  273 /*!
  274     Returns the shape of this item as a QPainterPath in local coordinates.
  275 */
  276 QPainterPath CustomPointPrivate::shape() const {
  277     return pointShape;
  278 }
  279 
  280 /*!
  281   recalculates the outer bounds and the shape of the item.
  282 */
  283 void CustomPointPrivate::recalcShapeAndBoundingRect() {
  284     prepareGeometryChange();
  285 
  286     pointShape = QPainterPath();
  287     if (m_visible && symbolStyle != Symbol::Style::NoSymbols) {
  288         QPainterPath path = Symbol::pathFromStyle(symbolStyle);
  289 
  290         QTransform trafo;
  291         trafo.scale(symbolSize, symbolSize);
  292         path = trafo.map(path);
  293         trafo.reset();
  294 
  295         if (symbolRotationAngle != 0) {
  296             trafo.rotate(symbolRotationAngle);
  297             path = trafo.map(path);
  298         }
  299 
  300         pointShape.addPath(WorksheetElement::shapeFromPath(trafo.map(path), symbolPen));
  301         transformedBoundingRectangle = pointShape.boundingRect();
  302     }
  303 }
  304 
  305 void CustomPointPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
  306     Q_UNUSED(option)
  307     Q_UNUSED(widget)
  308 
  309     if (!m_visible)
  310         return;
  311 
  312     if (symbolStyle != Symbol::Style::NoSymbols) {
  313         painter->setOpacity(symbolOpacity);
  314         painter->setPen(symbolPen);
  315         painter->setBrush(symbolBrush);
  316         painter->drawPath(pointShape);
  317     }
  318 
  319     if (m_hovered && !isSelected() && !m_printing) {
  320         painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine));
  321         painter->drawPath(pointShape);
  322     }
  323 
  324     if (isSelected() && !m_printing) {
  325         painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine));
  326         painter->drawPath(pointShape);
  327     }
  328 }
  329 
  330 QVariant CustomPointPrivate::itemChange(GraphicsItemChange change, const QVariant &value) {
  331     if (suppressItemChangeEvent)
  332         return value;
  333 
  334     if (change == QGraphicsItem::ItemPositionChange) {
  335         //emit the signals in order to notify the UI.
  336         //we don't set the position related member variables during the mouse movements.
  337         //this is done on mouse release events only.
  338         const auto* cSystem = dynamic_cast<const CartesianCoordinateSystem*>(plot->coordinateSystem());
  339         emit q->positionChanged(cSystem->mapSceneToLogical(value.toPointF()));
  340     }
  341 
  342     return QGraphicsItem::itemChange(change, value);
  343 }
  344 
  345 void CustomPointPrivate::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) {
  346     //position was changed -> set the position member variables
  347     suppressRetransform = true;
  348     const auto* cSystem = dynamic_cast<const CartesianCoordinateSystem*>(plot->coordinateSystem());
  349     q->setPosition(cSystem->mapSceneToLogical(pos()));
  350     suppressRetransform = false;
  351 
  352     QGraphicsItem::mouseReleaseEvent(event);
  353 }
  354 
  355 void CustomPointPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) {
  356     q->createContextMenu()->exec(event->screenPos());
  357 }
  358 
  359 void CustomPointPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) {
  360     if (!isSelected()) {
  361         m_hovered = true;
  362         emit q->hovered();
  363         update();
  364     }
  365 }
  366 
  367 void CustomPointPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
  368     if (m_hovered) {
  369         m_hovered = false;
  370         emit q->unhovered();
  371         update();
  372     }
  373 }
  374 
  375 //##############################################################################
  376 //##################  Serialization/Deserialization  ###########################
  377 //##############################################################################
  378 //! Save as XML
  379 void CustomPoint::save(QXmlStreamWriter* writer) const {
  380     Q_D(const CustomPoint);
  381 
  382     writer->writeStartElement("customPoint");
  383     writeBasicAttributes(writer);
  384     writeCommentElement(writer);
  385 
  386     //geometry
  387     writer->writeStartElement("geometry");
  388     writer->writeAttribute( "x", QString::number(d->position.x()) );
  389     writer->writeAttribute( "y", QString::number(d->position.y()) );
  390     writer->writeAttribute( "visible", QString::number(d->isVisible()) );
  391     writer->writeEndElement();
  392 
  393     //Symbols
  394     writer->writeStartElement("symbol");
  395     writer->writeAttribute( "symbolStyle", QString::number(static_cast<int>(d->symbolStyle)) );
  396     writer->writeAttribute( "opacity", QString::number(d->symbolOpacity) );
  397     writer->writeAttribute( "rotation", QString::number(d->symbolRotationAngle) );
  398     writer->writeAttribute( "size", QString::number(d->symbolSize) );
  399     WRITE_QBRUSH(d->symbolBrush);
  400     WRITE_QPEN(d->symbolPen);
  401     writer->writeEndElement();
  402 
  403     writer->writeEndElement(); // close "CustomPoint" section
  404 }
  405 
  406 //! Load from XML
  407 bool CustomPoint::load(XmlStreamReader* reader, bool preview) {
  408     Q_D(CustomPoint);
  409 
  410     if (!readBasicAttributes(reader))
  411         return false;
  412 
  413     KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used");
  414     QXmlStreamAttributes attribs;
  415     QString str;
  416 
  417     while (!reader->atEnd()) {
  418         reader->readNext();
  419         if (reader->isEndElement() && reader->name() == "customPoint")
  420             break;
  421 
  422         if (!reader->isStartElement())
  423             continue;
  424 
  425         if (!preview && reader->name() == "comment") {
  426             if (!readCommentElement(reader)) return false;
  427         } else if (!preview && reader->name() == "geometry") {
  428             attribs = reader->attributes();
  429 
  430             str = attribs.value("x").toString();
  431             if (str.isEmpty())
  432                 reader->raiseWarning(attributeWarning.subs("x").toString());
  433             else
  434                 d->position.setX(str.toDouble());
  435 
  436             str = attribs.value("y").toString();
  437             if (str.isEmpty())
  438                 reader->raiseWarning(attributeWarning.subs("y").toString());
  439             else
  440                 d->position.setY(str.toDouble());
  441 
  442             str = attribs.value("visible").toString();
  443             if (str.isEmpty())
  444                 reader->raiseWarning(attributeWarning.subs("visible").toString());
  445             else
  446                 d->setVisible(str.toInt());
  447         } else if (!preview && reader->name() == "symbol") {
  448             attribs = reader->attributes();
  449 
  450             READ_INT_VALUE("symbolStyle", symbolStyle, Symbol::Style);
  451             READ_DOUBLE_VALUE("opacity", symbolOpacity);
  452             READ_DOUBLE_VALUE("rotation", symbolRotationAngle);
  453             READ_DOUBLE_VALUE("size", symbolSize);
  454             READ_QBRUSH(d->symbolBrush);
  455             READ_QPEN(d->symbolPen);
  456         } else { // unknown element
  457             reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
  458             if (!reader->skipToEndElement()) return false;
  459         }
  460     }
  461 
  462     retransform();
  463     return true;
  464 }