File indexing completed on 2024-05-12 15:27:35

0001 /***************************************************************************
0002     File                 : WorksheetElement.cpp
0003     Project              : LabPlot
0004     Description          : Base class for all Worksheet children.
0005     --------------------------------------------------------------------
0006     Copyright            : (C) 2009 Tilman Benkert (thzs@gmx.net)
0007     Copyright            : (C) 2012-2017 by Alexander Semke (alexander.semke@web.de)
0008 
0009  ***************************************************************************/
0010 
0011 /***************************************************************************
0012  *                                                                         *
0013  *  This program is free software; you can redistribute it and/or modify   *
0014  *  it under the terms of the GNU General Public License as published by   *
0015  *  the Free Software Foundation; either version 2 of the License, or      *
0016  *  (at your option) any later version.                                    *
0017  *                                                                         *
0018  *  This program is distributed in the hope that it will be useful,        *
0019  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0020  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0021  *  GNU General Public License for more details.                           *
0022  *                                                                         *
0023  *   You should have received a copy of the GNU General Public License     *
0024  *   along with this program; if not, write to the Free Software           *
0025  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0026  *   Boston, MA  02110-1301  USA                                           *
0027  *                                                                         *
0028  ***************************************************************************/
0029 
0030 #include "backend/worksheet/Worksheet.h"
0031 #include "backend/worksheet/WorksheetElement.h"
0032 #include "backend/worksheet/plots/AbstractPlot.h"
0033 
0034 #include <QGraphicsItem>
0035 #include <QMenu>
0036 #include <QPen>
0037 #include <KLocalizedString>
0038 
0039 /**
0040  * \class WorksheetElement
0041  * \brief Base class for all Worksheet children.
0042  *
0043  */
0044 WorksheetElement::WorksheetElement(const QString &name, AspectType type)
0045     : AbstractAspect(name, type) {
0046 
0047     m_drawingOrderMenu = new QMenu(i18n("Drawing &order"));
0048     m_drawingOrderMenu->setIcon(QIcon::fromTheme("layer-bottom"));
0049     m_moveBehindMenu = new QMenu(i18n("Move &behind"));
0050     m_moveBehindMenu->setIcon(QIcon::fromTheme("draw-arrow-down"));
0051     m_moveInFrontOfMenu = new QMenu(i18n("Move in &front of"));
0052     m_moveInFrontOfMenu->setIcon(QIcon::fromTheme("draw-arrow-up"));
0053     m_drawingOrderMenu->addMenu(m_moveBehindMenu);
0054     m_drawingOrderMenu->addMenu(m_moveInFrontOfMenu);
0055 
0056     connect(m_moveBehindMenu, &QMenu::aboutToShow, this, &WorksheetElement::prepareMoveBehindMenu);
0057     connect(m_moveInFrontOfMenu, &QMenu::aboutToShow, this, &WorksheetElement::prepareMoveInFrontOfMenu);
0058     connect(m_moveBehindMenu, &QMenu::triggered, this, &WorksheetElement::execMoveBehind);
0059     connect(m_moveInFrontOfMenu, &QMenu::triggered, this, &WorksheetElement::execMoveInFrontOf);
0060 }
0061 
0062 WorksheetElement::~WorksheetElement() {
0063     delete m_moveBehindMenu;
0064     delete m_moveInFrontOfMenu;
0065     delete m_drawingOrderMenu;
0066 }
0067 
0068 /**
0069  * \fn QGraphicsItem *WorksheetElement::graphicsItem() const
0070  * \brief Return the graphics item representing this element.
0071  *
0072  */
0073 
0074 /**
0075  * \fn void WorksheetElement::setVisible(bool on)
0076  * \brief Show/hide the element.
0077  *
0078  */
0079 
0080 /**
0081  * \fn bool WorksheetElement::isVisible() const
0082  * \brief Return whether the element is (at least) partially visible.
0083  *
0084  */
0085 
0086 /**
0087  * \brief Return whether the element is fully visible (i.e., including all child elements).
0088  *
0089  * The standard implementation returns isVisible().
0090  */
0091 bool WorksheetElement::isFullyVisible() const {
0092     return isVisible();
0093 }
0094 
0095 /**
0096  * \fn void WorksheetElement::setPrinting(bool on)
0097  * \brief Switches the printing mode on/off
0098  *
0099  */
0100 
0101 /**
0102  * \fn void WorksheetElement::retransform()
0103  * \brief Tell the element to newly transform its graphics item into its coordinate system.
0104  *
0105  * This method must not change the undo-aware data of the element, only
0106  * the graphics item which represents the item is to be updated.
0107  */
0108 
0109 void WorksheetElement::setZValue(qreal value) {
0110     graphicsItem()->setZValue(value);
0111 }
0112 
0113 /**
0114     This does exactly what Qt internally does to creates a shape from a painter path.
0115 */
0116 QPainterPath WorksheetElement::shapeFromPath(const QPainterPath &path, const QPen &pen) {
0117     if (path == QPainterPath())
0118         return path;
0119 
0120 //  PERFTRACE("WorksheetElement::shapeFromPath()");
0121 
0122     // TODO: We unfortunately need this hack as QPainterPathStroker will set a width of 1.0
0123     // if we pass a value of 0.0 to QPainterPathStroker::setWidth()
0124     const qreal penWidthZero = qreal(1.e-8);
0125 
0126     QPainterPathStroker ps;
0127     ps.setCapStyle(pen.capStyle());
0128     if (pen.widthF() <= 0.0)
0129         ps.setWidth(penWidthZero);
0130     else
0131         ps.setWidth(pen.widthF());
0132     ps.setJoinStyle(pen.joinStyle());
0133     ps.setMiterLimit(pen.miterLimit());
0134 
0135     QPainterPath p = ps.createStroke(path);
0136     p.addPath(path);
0137 
0138     return p;
0139 }
0140 
0141 QMenu* WorksheetElement::createContextMenu() {
0142     QMenu* menu = AbstractAspect::createContextMenu();
0143 
0144     //add the sub-menu for the drawing order
0145 
0146     //don't add the drawing order menu for axes and legends, they're always drawn on top of each other elements
0147     if (type() == AspectType::Axis || type() == AspectType::CartesianPlotLegend)
0148         return menu;
0149 
0150     //for plots in a worksheet with an active layout the Z-factor is not relevant but we still
0151     //want to use the "Drawing order" menu to be able to change the position/order of the plot in the layout.
0152     //Since the order of the child in the list of children is opposite to the Z-factor, we change
0153     //the names of the menus to adapt to the reversed logic.
0154     if (dynamic_cast<AbstractPlot*>(this) ) {
0155         const Worksheet* w = dynamic_cast<const Worksheet*>(this->parentAspect());
0156         if (!w)
0157             return menu;
0158 
0159         if (w->layout() != Worksheet::Layout::NoLayout) {
0160             m_moveBehindMenu->setTitle(i18n("Move in &front of"));
0161             m_moveBehindMenu->setIcon(QIcon::fromTheme("draw-arrow-up"));
0162             m_moveInFrontOfMenu->setTitle(i18n("Move &behind"));
0163             m_moveInFrontOfMenu->setIcon(QIcon::fromTheme("draw-arrow-down"));
0164         } else {
0165             m_moveBehindMenu->setTitle(i18n("Move &behind"));
0166             m_moveBehindMenu->setIcon(QIcon::fromTheme("draw-arrow-down"));
0167             m_moveInFrontOfMenu->setTitle(i18n("Move in &front of"));
0168             m_moveInFrontOfMenu->setIcon(QIcon::fromTheme("draw-arrow-up"));
0169         }
0170     }
0171 
0172     //don't add the drawing order menu if the parent element has no other children
0173     int children = 0;
0174     for (auto* child : parentAspect()->children<WorksheetElement>()) {
0175         if (child->type() != AspectType::Axis && child->type() != AspectType::CartesianPlotLegend)
0176             children++;
0177     }
0178 
0179     if (children > 1) {
0180         menu->addSeparator();
0181         menu->addMenu(m_drawingOrderMenu);
0182     }
0183 
0184     return menu;
0185 }
0186 
0187 void WorksheetElement::prepareMoveBehindMenu() {
0188     m_moveBehindMenu->clear();
0189     AbstractAspect* parent = parentAspect();
0190     int index = parent->indexOfChild<WorksheetElement>(this);
0191     const QVector<WorksheetElement*>& children = parent->children<WorksheetElement>();
0192 
0193     for (int i = 0; i < index; ++i) {
0194         const WorksheetElement* elem = children.at(i);
0195         //axes and legends are always drawn on top of other elements, don't add them to the menu
0196         if (elem->type() != AspectType::Axis && elem->type() != AspectType::CartesianPlotLegend) {
0197             QAction* action = m_moveBehindMenu->addAction(elem->icon(), elem->name());
0198             action->setData(i);
0199         }
0200     }
0201 
0202     //TODO: doesn't always work properly
0203     //hide the "move behind" menu if it doesn't have any entries, show if not shown yet otherwise
0204     //m_moveBehindMenu->menuAction()->setVisible(!m_moveBehindMenu->isEmpty());
0205 }
0206 
0207 void WorksheetElement::prepareMoveInFrontOfMenu() {
0208     m_moveInFrontOfMenu->clear();
0209     AbstractAspect* parent = parentAspect();
0210     int index = parent->indexOfChild<WorksheetElement>(this);
0211     const QVector<WorksheetElement*>& children = parent->children<WorksheetElement>();
0212 
0213     for (int i = index + 1; i < children.size(); ++i) {
0214         const WorksheetElement* elem = children.at(i);
0215         //axes and legends are always drawn on top of other elements, don't add them to the menu
0216         if (elem->type() != AspectType::Axis && elem->type() != AspectType::CartesianPlotLegend) {
0217             QAction* action = m_moveInFrontOfMenu->addAction(elem->icon(), elem->name());
0218             action->setData(i);
0219         }
0220     }
0221 
0222     //TODO: doesn't alway work properly
0223     //hide the "move in front" menu if it doesn't have any entries, show if not shown yet otherwise
0224     //m_moveInFrontOfMenu->menuAction()->setVisible(!m_moveInFrontOfMenu->isEmpty());
0225 }
0226 
0227 void WorksheetElement::execMoveInFrontOf(QAction* action) {
0228     AbstractAspect* parent = parentAspect();
0229     int index = action->data().toInt();
0230     AbstractAspect* sibling1 = parent->child<WorksheetElement>(index);
0231     AbstractAspect* sibling2 = parent->child<WorksheetElement>(index + 1);
0232     beginMacro(i18n("%1: move behind %2.", name(), sibling1->name()));
0233     remove();
0234     parent->insertChildBefore(this, sibling2);
0235     endMacro();
0236 }
0237 
0238 void WorksheetElement::execMoveBehind(QAction* action) {
0239     AbstractAspect* parent = parentAspect();
0240     int index = action->data().toInt();
0241     AbstractAspect* sibling = parent->child<WorksheetElement>(index);
0242     beginMacro(i18n("%1: move in front of %2.", name(), sibling->name()));
0243     remove();
0244     parent->insertChildBefore(this, sibling);
0245     endMacro();
0246 }
0247 
0248 void WorksheetElement::loadThemeConfig(const KConfig &) {
0249 }
0250 
0251 void WorksheetElement::saveThemeConfig(const KConfig &) {
0252 }