File indexing completed on 2024-05-12 03:48:22
0001 /* 0002 File : Worksheet.cpp 0003 Project : LabPlot 0004 Description : Worksheet 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2009 Tilman Benkert <thzs@gmx.net> 0007 SPDX-FileCopyrightText: 2011-2022 Alexander Semke <alexander.semke@web.de> 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 #include "Worksheet.h" 0013 #include "Background.h" 0014 #include "WorksheetElement.h" 0015 #include "WorksheetPrivate.h" 0016 #include "backend/core/Project.h" 0017 #include "backend/core/Settings.h" 0018 #include "backend/lib/XmlStreamReader.h" 0019 #include "backend/lib/commandtemplates.h" 0020 #include "backend/worksheet/Image.h" 0021 #include "backend/worksheet/Line.h" 0022 #include "backend/worksheet/TextLabel.h" 0023 #include "backend/worksheet/TreeModel.h" 0024 #include "backend/worksheet/plots/cartesian/CartesianPlot.h" 0025 #include "backend/worksheet/plots/cartesian/XYCurve.h" 0026 #include "commonfrontend/worksheet/WorksheetView.h" 0027 #include "kdefrontend/ThemeHandler.h" 0028 #include "kdefrontend/worksheet/ExportWorksheetDialog.h" 0029 0030 #ifndef SDK 0031 #include <QPrintDialog> 0032 #include <QPrintPreviewDialog> 0033 #include <QPrinter> 0034 #endif 0035 0036 #include <QDir> 0037 #include <QGraphicsItem> 0038 #include <QIcon> 0039 #include <QMenu> 0040 0041 #include <KConfig> 0042 #include <KConfigGroup> 0043 #include <KLocalizedString> 0044 0045 /** 0046 * \class Worksheet 0047 * \brief Top-level container for worksheet elements like plot, labels, etc. 0048 * 0049 * The worksheet is, besides the data containers \c Spreadsheet and \c Matrix, another central part of the application 0050 * and provides an area for showing and grouping together different kinds of worksheet objects - plots, labels &etc; 0051 * 0052 * * \ingroup worksheet 0053 */ 0054 Worksheet::Worksheet(const QString& name, bool loading) 0055 : AbstractPart(name, AspectType::Worksheet) 0056 , d_ptr(new WorksheetPrivate(this)) { 0057 Q_D(Worksheet); 0058 d->background = new Background(QString()); 0059 addChild(d->background); 0060 d->background->setHidden(true); 0061 connect(d->background, &Background::updateRequested, [=] { 0062 d->update(); 0063 }); 0064 0065 connect(this, &Worksheet::childAspectAdded, this, &Worksheet::handleAspectAdded); 0066 connect(this, &Worksheet::childAspectAboutToBeRemoved, this, &Worksheet::handleAspectAboutToBeRemoved); 0067 connect(this, &Worksheet::childAspectRemoved, this, &Worksheet::handleAspectRemoved); 0068 0069 if (!loading) 0070 init(); 0071 } 0072 0073 Worksheet::~Worksheet() { 0074 delete d_ptr; 0075 } 0076 0077 void Worksheet::init() { 0078 Q_D(Worksheet); 0079 KConfig config; 0080 auto group = config.group(QStringLiteral("Worksheet")); 0081 0082 // size 0083 d->scaleContent = group.readEntry(QStringLiteral("ScaleContent"), false); 0084 d->useViewSize = group.readEntry(QStringLiteral("UseViewSize"), false); 0085 d->pageRect.setX(0); 0086 d->pageRect.setY(0); 0087 d->pageRect.setWidth(group.readEntry(QStringLiteral("Width"), 1000)); 0088 d->pageRect.setHeight(group.readEntry(QStringLiteral("Height"), 1000)); 0089 d->m_scene->setSceneRect(d->pageRect); 0090 0091 // background 0092 d->background->init(group); 0093 0094 // layout 0095 d->layout = (Layout)group.readEntry(QStringLiteral("Layout"), static_cast<int>(Layout::VerticalLayout)); 0096 d->layoutTopMargin = group.readEntry(QStringLiteral("LayoutTopMargin"), convertToSceneUnits(0.5, Unit::Centimeter)); 0097 d->layoutBottomMargin = group.readEntry(QStringLiteral("LayoutBottomMargin"), convertToSceneUnits(0.5, Unit::Centimeter)); 0098 d->layoutLeftMargin = group.readEntry(QStringLiteral("LayoutLeftMargin"), convertToSceneUnits(0.5, Unit::Centimeter)); 0099 d->layoutRightMargin = group.readEntry(QStringLiteral("LayoutRightMargin"), convertToSceneUnits(0.5, Unit::Centimeter)); 0100 d->layoutVerticalSpacing = group.readEntry(QStringLiteral("LayoutVerticalSpacing"), convertToSceneUnits(0.5, Unit::Centimeter)); 0101 d->layoutHorizontalSpacing = group.readEntry(QStringLiteral("LayoutHorizontalSpacing"), convertToSceneUnits(0.5, Unit::Centimeter)); 0102 d->layoutRowCount = group.readEntry(QStringLiteral("LayoutRowCount"), 2); 0103 d->layoutColumnCount = group.readEntry(QStringLiteral("LayoutColumnCount"), 2); 0104 0105 // default theme 0106 auto settings = Settings::group(QStringLiteral("Settings_Worksheet")); 0107 d->theme = settings.readEntry(QStringLiteral("Theme"), QString()); 0108 loadTheme(d->theme); 0109 } 0110 0111 /*! 0112 converts from \c unit to the scene units. At the moment, 1 scene unit corresponds to 1/10 mm. 0113 */ 0114 double Worksheet::convertToSceneUnits(const double value, const Worksheet::Unit unit) { 0115 switch (unit) { 0116 case Unit::Millimeter: 0117 return value * 10.0; 0118 case Unit::Centimeter: 0119 return value * 100.0; 0120 case Unit::Inch: 0121 return value * 25.4 * 10.; 0122 case Unit::Point: 0123 return value * 25.4 / 72. * 10.; 0124 } 0125 0126 return 0; 0127 } 0128 0129 /*! 0130 converts from the scene units to \c unit . At the moment, 1 scene unit corresponds to 1/10 mm. 0131 */ 0132 double Worksheet::convertFromSceneUnits(const double value, const Worksheet::Unit unit) { 0133 switch (unit) { 0134 case Unit::Millimeter: 0135 return value / 10.0; 0136 case Unit::Centimeter: 0137 return value / 100.0; 0138 case Unit::Inch: 0139 return value / 25.4 / 10.; 0140 case Unit::Point: 0141 return value / 25.4 / 10. * 72.; 0142 } 0143 0144 return 0; 0145 } 0146 0147 QIcon Worksheet::icon() const { 0148 return QIcon::fromTheme(QStringLiteral("labplot-worksheet")); 0149 } 0150 0151 /** 0152 * Return a new context menu. The caller takes ownership of the menu. 0153 */ 0154 QMenu* Worksheet::createContextMenu() { 0155 QMenu* menu = AbstractPart::createContextMenu(); 0156 Q_ASSERT(menu); 0157 Q_EMIT requestProjectContextMenu(menu); 0158 return menu; 0159 } 0160 0161 //! Construct a primary view on me. 0162 /** 0163 * This method may be called multiple times during the life time of an Aspect, or it might not get 0164 * called at all. Aspects must not depend on the existence of a view for their operation. 0165 */ 0166 QWidget* Worksheet::view() const { 0167 DEBUG(Q_FUNC_INFO) 0168 if (!m_partView) { 0169 m_view = new WorksheetView(const_cast<Worksheet*>(this)); 0170 m_partView = m_view; 0171 connect(m_view, &WorksheetView::statusInfo, this, &Worksheet::statusInfo); 0172 connect(m_view, &WorksheetView::propertiesExplorerRequested, this, &Worksheet::propertiesExplorerRequested); 0173 connect(this, &Worksheet::cartesianPlotMouseModeChanged, m_view, &WorksheetView::cartesianPlotMouseModeChangedSlot); 0174 connect(this, &Worksheet::childContextMenuRequested, m_view, &WorksheetView::childContextMenuRequested); 0175 connect(this, &Worksheet::viewAboutToBeDeleted, [this]() { 0176 m_view = nullptr; 0177 }); 0178 Q_EMIT const_cast<Worksheet*>(this)->changed(); 0179 } 0180 return m_partView; 0181 } 0182 0183 /*! 0184 * returns the list of all parent aspects (folders and sub-folders) 0185 * together with all the data containers required to plot the data in the worksheet 0186 */ 0187 QVector<AbstractAspect*> Worksheet::dependsOn() const { 0188 // add all parent aspects (folders and sub-folders) 0189 auto aspects = AbstractAspect::dependsOn(); 0190 0191 // traverse all plots and add all data containers they depend on 0192 for (const auto* plot : children<AbstractPlot>()) 0193 aspects << plot->dependsOn(); 0194 0195 return aspects; 0196 } 0197 0198 QVector<AspectType> Worksheet::pasteTypes() const { 0199 return QVector<AspectType>{AspectType::CartesianPlot, AspectType::TextLabel, AspectType::Image}; 0200 } 0201 0202 bool Worksheet::exportView() const { 0203 #ifndef SDK 0204 auto* dlg = new ExportWorksheetDialog(m_view); 0205 dlg->setProjectFileName(const_cast<Worksheet*>(this)->project()->fileName()); 0206 dlg->setFileName(name()); 0207 bool ret; 0208 if ((ret = (dlg->exec() == QDialog::Accepted))) { 0209 QString path = dlg->path(); 0210 const WorksheetView::ExportFormat format = dlg->exportFormat(); 0211 const WorksheetView::ExportArea area = dlg->exportArea(); 0212 const bool background = dlg->exportBackground(); 0213 const int resolution = dlg->exportResolution(); 0214 0215 WAIT_CURSOR; 0216 m_view->exportToFile(path, format, area, background, resolution); 0217 RESET_CURSOR; 0218 } 0219 delete dlg; 0220 return ret; 0221 #else 0222 return true; 0223 #endif 0224 } 0225 0226 bool Worksheet::exportView(QPixmap& pixmap) const { 0227 if (!m_view) 0228 return false; 0229 0230 m_view->exportToPixmap(pixmap); 0231 return true; 0232 } 0233 0234 bool Worksheet::printView() { 0235 #ifndef SDK 0236 QPrinter printer; 0237 auto* dlg = new QPrintDialog(&printer, m_view); 0238 dlg->setWindowTitle(i18nc("@title:window", "Print Worksheet")); 0239 bool ret; 0240 if ((ret = (dlg->exec() == QDialog::Accepted))) 0241 m_view->print(&printer); 0242 0243 delete dlg; 0244 return ret; 0245 #else 0246 return true; 0247 #endif 0248 } 0249 0250 bool Worksheet::printPreview() const { 0251 #ifndef SDK 0252 auto* dlg = new QPrintPreviewDialog(m_view); 0253 connect(dlg, &QPrintPreviewDialog::paintRequested, m_view, &WorksheetView::print); 0254 return dlg->exec(); 0255 #else 0256 return true; 0257 #endif 0258 } 0259 0260 void Worksheet::handleAspectAdded(const AbstractAspect* aspect) { 0261 DEBUG(Q_FUNC_INFO) 0262 Q_D(Worksheet); 0263 const auto* addedElement = dynamic_cast<const WorksheetElement*>(aspect); 0264 if (!addedElement) 0265 return; 0266 0267 if (aspect->parentAspect() != this) 0268 return; 0269 0270 // add the GraphicsItem of the added child to the scene 0271 DEBUG(Q_FUNC_INFO << ", ADDING child to SCENE") 0272 auto* item = addedElement->graphicsItem(); 0273 d->m_scene->addItem(item); 0274 0275 connect(aspect, &AbstractAspect::contextMenuRequested, this, &Worksheet::childContextMenuRequested); 0276 connect(addedElement, &WorksheetElement::changed, this, &Worksheet::changed); 0277 0278 // for containers, connect to visilibity changes and update the layout accordingly 0279 if (dynamic_cast<const WorksheetElementContainer*>(addedElement)) 0280 connect(addedElement, &WorksheetElement::visibleChanged, this, [=]() { 0281 if (layout() != Worksheet::Layout::NoLayout) 0282 updateLayout(); 0283 }); 0284 0285 const auto* plot = dynamic_cast<const CartesianPlot*>(aspect); 0286 if (plot) { 0287 connect(plot, &CartesianPlot::axisShiftSignal, this, &Worksheet::cartesianPlotAxisShift); 0288 connect(plot, &CartesianPlot::wheelEventSignal, this, &Worksheet::cartesianPlotWheelEvent); 0289 connect(plot, &CartesianPlot::mouseMoveCursorModeSignal, this, &Worksheet::cartesianPlotMouseMoveCursorMode); 0290 connect(plot, &CartesianPlot::mouseMoveSelectionModeSignal, this, &Worksheet::cartesianPlotMouseMoveSelectionMode); 0291 connect(plot, &CartesianPlot::mouseMoveZoomSelectionModeSignal, this, &Worksheet::cartesianPlotMouseMoveZoomSelectionMode); 0292 connect(plot, &CartesianPlot::mousePressCursorModeSignal, this, &Worksheet::cartesianPlotMousePressCursorMode); 0293 connect(plot, &CartesianPlot::mousePressZoomSelectionModeSignal, this, &Worksheet::cartesianPlotMousePressZoomSelectionMode); 0294 connect(plot, &CartesianPlot::mouseReleaseZoomSelectionModeSignal, this, &Worksheet::cartesianPlotMouseReleaseZoomSelectionMode); 0295 connect(plot, &CartesianPlot::mouseHoverZoomSelectionModeSignal, this, &Worksheet::cartesianPlotMouseHoverZoomSelectionMode); 0296 connect(plot, &CartesianPlot::mouseHoverOutsideDataRectSignal, this, &Worksheet::cartesianPlotMouseHoverOutsideDataRect); 0297 connect(plot, &CartesianPlot::aspectDescriptionChanged, this, &Worksheet::updateCompleteCursorTreeModel); 0298 connect(plot, &CartesianPlot::curveNameChanged, this, &Worksheet::updateCompleteCursorTreeModel); 0299 connect(plot, &CartesianPlot::curveRemoved, this, &Worksheet::curveRemoved); 0300 connect(plot, &CartesianPlot::curveAdded, this, &Worksheet::curveAdded); 0301 connect(plot, &CartesianPlot::visibleChanged, this, &Worksheet::updateCompleteCursorTreeModel); 0302 connect(plot, &CartesianPlot::curveVisibilityChangedSignal, this, &Worksheet::updateCompleteCursorTreeModel); 0303 connect(plot, &CartesianPlot::curveDataChanged, this, &Worksheet::curveDataChanged); 0304 connect(plot, 0305 static_cast<void (CartesianPlot::*)(const QColor&, const QString&)>(&CartesianPlot::plotColorChanged), 0306 this, 0307 &Worksheet::updateCurveBackground); 0308 connect(plot, &CartesianPlot::mouseModeChanged, this, &Worksheet::cartesianPlotMouseModeChangedSlot); 0309 auto* p = const_cast<CartesianPlot*>(plot); 0310 p->setInteractive(d->plotsInteractive); 0311 0312 cursorModelPlotAdded(p->name()); 0313 } 0314 qreal zVal = 0; 0315 for (auto* child : children<WorksheetElement>(ChildIndexFlag::IncludeHidden)) 0316 child->graphicsItem()->setZValue(zVal++); 0317 0318 // if a theme was selected in the worksheet, apply this theme for newly added children 0319 if (!d->theme.isEmpty() && !isLoading() && !pasted() && !aspect->pasted()) { 0320 KConfig config(ThemeHandler::themeFilePath(d->theme), KConfig::SimpleConfig); 0321 const_cast<WorksheetElement*>(addedElement)->loadThemeConfig(config); 0322 } 0323 0324 // recalculate the layout if enabled, set the currently added plot resizable otherwise 0325 if (!isLoading()) { 0326 if (d->layout != Worksheet::Layout::NoLayout) 0327 d->updateLayout(false); 0328 else { 0329 if (plot) { 0330 // make other plots non-resizable 0331 const auto& containers = children<WorksheetElementContainer>(); 0332 for (auto* container : containers) 0333 container->setResizeEnabled(false); 0334 0335 // make the newly added plot resizable 0336 const_cast<CartesianPlot*>(plot)->setResizeEnabled(true); 0337 } 0338 } 0339 } 0340 } 0341 0342 void Worksheet::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) { 0343 Q_D(Worksheet); 0344 const auto* removedElement = qobject_cast<const WorksheetElement*>(aspect); 0345 if (removedElement) { 0346 QGraphicsItem* item = removedElement->graphicsItem(); 0347 // TODO: disabled until Origin project import is fixed 0348 if (item->scene() == d->m_scene) 0349 d->m_scene->removeItem(item); 0350 } 0351 } 0352 0353 void Worksheet::handleAspectRemoved(const AbstractAspect* /*parent*/, const AbstractAspect* /*before*/, const AbstractAspect* child) { 0354 Q_D(Worksheet); 0355 if (d->layout != Worksheet::Layout::NoLayout) 0356 d->updateLayout(false); 0357 auto* plot = dynamic_cast<const CartesianPlot*>(child); 0358 if (plot) 0359 cursorModelPlotRemoved(plot->name()); 0360 } 0361 0362 QGraphicsScene* Worksheet::scene() const { 0363 Q_D(const Worksheet); 0364 return d->m_scene; 0365 } 0366 0367 QRectF Worksheet::pageRect() const { 0368 Q_D(const Worksheet); 0369 return d->m_scene->sceneRect(); 0370 } 0371 0372 double Worksheet::zoomFactor() const { 0373 return m_view->zoomFactor(); 0374 } 0375 0376 /*! 0377 this slot is called when a worksheet element is selected in the project explorer. 0378 emits \c itemSelected() which forwards this event to the \c WorksheetView 0379 in order to select the corresponding \c QGraphicsItem. 0380 */ 0381 void Worksheet::childSelected(const AbstractAspect* aspect) { 0382 auto* element = qobject_cast<WorksheetElement*>(const_cast<AbstractAspect*>(aspect)); 0383 if (element) 0384 Q_EMIT itemSelected(element->graphicsItem()); 0385 } 0386 0387 /*! 0388 this slot is called when a worksheet element is deselected in the project explorer. 0389 emits \c itemDeselected() which forwards this event to \c WorksheetView 0390 in order to deselect the corresponding \c QGraphicsItem. 0391 */ 0392 void Worksheet::childDeselected(const AbstractAspect* aspect) { 0393 auto* element = qobject_cast<WorksheetElement*>(const_cast<AbstractAspect*>(aspect)); 0394 if (element) 0395 Q_EMIT itemDeselected(element->graphicsItem()); 0396 } 0397 0398 /*! 0399 * Emits the signal to select or to deselect the aspect corresponding to \c QGraphicsItem \c item in the project explorer, 0400 * if \c selected=true or \c selected=false, respectively. 0401 * The signal is handled in \c AspectTreeModel and forwarded to the tree view in \c ProjectExplorer. 0402 * This function is called in \c WorksheetView upon selection changes. 0403 */ 0404 void Worksheet::setItemSelectedInView(const QGraphicsItem* item, const bool b) { 0405 // determine the corresponding aspect 0406 AbstractAspect* aspect(nullptr); 0407 for (const auto* child : children<WorksheetElement>(ChildIndexFlag::IncludeHidden)) { 0408 aspect = this->aspectFromGraphicsItem(child, item); 0409 if (aspect) 0410 break; 0411 } 0412 0413 if (!aspect) 0414 return; 0415 0416 // forward selection/deselection to AbstractTreeModel 0417 if (b) 0418 Q_EMIT childAspectSelectedInView(aspect); 0419 else 0420 Q_EMIT childAspectDeselectedInView(aspect); 0421 0422 // handle the resize items on selection changes 0423 if (layout() == Worksheet::Layout::NoLayout) { 0424 // only one selected plot can be made resizable 0425 if (b) { 0426 const auto& items = m_view->selectedItems(); 0427 if (items.size() == 1) { 0428 // only one object is selected. 0429 // make it resiable if its a container 0430 auto* container = dynamic_cast<WorksheetElementContainer*>(aspect); 0431 if (container) 0432 container->setResizeEnabled(true); 0433 } else if (items.size() > 1) { 0434 // multiple objects are selected, make all containers non-resizable 0435 const auto& elements = children<WorksheetElement>(); 0436 for (auto* element : elements) { 0437 auto* container = dynamic_cast<WorksheetElementContainer*>(element); 0438 if (container) 0439 container->setResizeEnabled(false); 0440 } 0441 } 0442 } else { 0443 auto* container = dynamic_cast<WorksheetElementContainer*>(aspect); 0444 if (container) 0445 container->setResizeEnabled(false); 0446 } 0447 } 0448 } 0449 0450 /*! 0451 * helper function: checks whether \c aspect or one of its children has the \c GraphicsItem \c item 0452 * Returns a pointer to \c WorksheetElement having this item. 0453 */ 0454 WorksheetElement* Worksheet::aspectFromGraphicsItem(const WorksheetElement* parent, const QGraphicsItem* item) const { 0455 if (parent->graphicsItem() == item) 0456 return const_cast<WorksheetElement*>(parent); 0457 else { 0458 for (const auto* child : parent->children<WorksheetElement>(AbstractAspect::ChildIndexFlag::IncludeHidden)) { 0459 WorksheetElement* a = this->aspectFromGraphicsItem(child, item); 0460 if (a) 0461 return a; 0462 } 0463 return nullptr; 0464 } 0465 } 0466 0467 /*! 0468 Selects or deselects the worksheet in the project explorer. 0469 This function is called in \c WorksheetView. 0470 The worksheet gets deselected if there are selected items in the view, 0471 and selected if there are no selected items in the view. 0472 */ 0473 void Worksheet::setSelectedInView(const bool b) { 0474 if (b) 0475 Q_EMIT childAspectSelectedInView(this); 0476 else 0477 Q_EMIT childAspectDeselectedInView(this); 0478 } 0479 0480 void Worksheet::deleteAspectFromGraphicsItem(const QGraphicsItem* item) { 0481 Q_ASSERT(item); 0482 // determine the corresponding aspect 0483 AbstractAspect* aspect(nullptr); 0484 for (const auto* child : children<WorksheetElement>(ChildIndexFlag::IncludeHidden)) { 0485 aspect = this->aspectFromGraphicsItem(child, item); 0486 if (aspect) 0487 break; 0488 } 0489 0490 if (!aspect) 0491 return; 0492 0493 if (aspect->parentAspect()) 0494 aspect->parentAspect()->removeChild(aspect); 0495 else 0496 this->removeChild(aspect); 0497 } 0498 0499 void Worksheet::setIsClosing() { 0500 if (m_view) 0501 m_view->setIsClosing(); 0502 } 0503 0504 void Worksheet::suppressSelectionChangedEvent(bool value) { 0505 if (m_view) 0506 m_view->suppressSelectionChangedEvent(value); 0507 } 0508 0509 /*! 0510 * \brief Worksheet::plotCount 0511 * \return number of CartesianPlot's in the Worksheet 0512 */ 0513 int Worksheet::plotCount() { 0514 return children<CartesianPlot>().length(); 0515 } 0516 0517 /*! 0518 * \brief Worksheet::plot 0519 * \param index Number of plot which should be returned 0520 * \return Pointer to the CartesianPlot which was searched with index 0521 */ 0522 CartesianPlot* Worksheet::plot(int index) { 0523 auto plots = children<CartesianPlot>(); 0524 if (plots.length() - 1 >= index) 0525 return plots.at(index); 0526 return nullptr; 0527 } 0528 0529 TreeModel* Worksheet::cursorModel() { 0530 Q_D(const Worksheet); 0531 return d->cursorData; 0532 } 0533 0534 void Worksheet::update() { 0535 Q_EMIT requestUpdate(); 0536 Q_EMIT changed(); 0537 } 0538 0539 void Worksheet::setSuppressLayoutUpdate(bool value) { 0540 Q_D(Worksheet); 0541 d->suppressLayoutUpdate = value; 0542 } 0543 0544 void Worksheet::updateLayout() { 0545 Q_D(Worksheet); 0546 d->updateLayout(); 0547 } 0548 0549 Worksheet::CartesianPlotActionMode Worksheet::cartesianPlotActionMode() const { 0550 Q_D(const Worksheet); 0551 return d->cartesianPlotActionMode; 0552 } 0553 0554 Worksheet::CartesianPlotActionMode Worksheet::cartesianPlotCursorMode() const { 0555 Q_D(const Worksheet); 0556 return d->cartesianPlotCursorMode; 0557 } 0558 0559 bool Worksheet::plotsInteractive() const { 0560 Q_D(const Worksheet); 0561 return d->plotsInteractive; 0562 } 0563 0564 void Worksheet::setCartesianPlotActionMode(Worksheet::CartesianPlotActionMode mode) { 0565 Q_D(Worksheet); 0566 if (d->cartesianPlotActionMode == mode) 0567 return; 0568 0569 d->cartesianPlotActionMode = mode; 0570 project()->setChanged(true); 0571 } 0572 0573 void Worksheet::setCartesianPlotCursorMode(Worksheet::CartesianPlotActionMode mode) { 0574 Q_D(Worksheet); 0575 if (d->cartesianPlotCursorMode == mode) 0576 return; 0577 0578 d->cartesianPlotCursorMode = mode; 0579 0580 if (mode == CartesianPlotActionMode::ApplyActionToAll) { 0581 d->suppressCursorPosChanged = true; 0582 const auto& plots = children<CartesianPlot>(); 0583 QPointF logicPos; 0584 if (!plots.isEmpty()) { 0585 for (int i = 0; i < 2; i++) { 0586 logicPos = QPointF(plots[0]->cursorPos(i), 0); // y value does not matter 0587 cartesianPlotMousePressCursorMode(i, logicPos); 0588 } 0589 } 0590 d->suppressCursorPosChanged = false; 0591 } 0592 updateCompleteCursorTreeModel(); 0593 project()->setChanged(true); 0594 } 0595 0596 void Worksheet::setInteractive(bool value) { 0597 if (!m_view) 0598 view(); 0599 m_view->setInteractive(value); 0600 } 0601 0602 void Worksheet::setPlotsInteractive(bool interactive) { 0603 Q_D(Worksheet); 0604 if (d->plotsInteractive == interactive) 0605 return; 0606 0607 d->plotsInteractive = interactive; 0608 0609 for (auto* plot : children<CartesianPlot>()) 0610 plot->setInteractive(interactive); 0611 0612 project()->setChanged(true); 0613 } 0614 0615 void Worksheet::registerShortcuts() { 0616 m_view->registerShortcuts(); 0617 } 0618 0619 WorksheetElement* Worksheet::currentSelection() { 0620 if (!m_view) { 0621 view(); 0622 return nullptr; 0623 } 0624 0625 return m_view->selectedElement(); 0626 } 0627 0628 void Worksheet::unregisterShortcuts() { 0629 m_view->unregisterShortcuts(); 0630 } 0631 0632 /* =============================== getter methods for general options ==================================== */ 0633 BASIC_D_READER_IMPL(Worksheet, bool, scaleContent, scaleContent) 0634 BASIC_D_READER_IMPL(Worksheet, bool, useViewSize, useViewSize) 0635 BASIC_D_READER_IMPL(Worksheet, Worksheet::ZoomFit, zoomFit, zoomFit) 0636 0637 // background 0638 Background* Worksheet::background() const { 0639 Q_D(const Worksheet); 0640 return d->background; 0641 } 0642 0643 /* =============================== getter methods for layout options ====================================== */ 0644 BASIC_D_READER_IMPL(Worksheet, Worksheet::Layout, layout, layout) 0645 BASIC_D_READER_IMPL(Worksheet, double, layoutTopMargin, layoutTopMargin) 0646 BASIC_D_READER_IMPL(Worksheet, double, layoutBottomMargin, layoutBottomMargin) 0647 BASIC_D_READER_IMPL(Worksheet, double, layoutLeftMargin, layoutLeftMargin) 0648 BASIC_D_READER_IMPL(Worksheet, double, layoutRightMargin, layoutRightMargin) 0649 BASIC_D_READER_IMPL(Worksheet, double, layoutHorizontalSpacing, layoutHorizontalSpacing) 0650 BASIC_D_READER_IMPL(Worksheet, double, layoutVerticalSpacing, layoutVerticalSpacing) 0651 BASIC_D_READER_IMPL(Worksheet, int, layoutRowCount, layoutRowCount) 0652 BASIC_D_READER_IMPL(Worksheet, int, layoutColumnCount, layoutColumnCount) 0653 0654 BASIC_D_READER_IMPL(Worksheet, QString, theme, theme) 0655 0656 /* ============================ setter methods and undo commands for general options ===================== */ 0657 STD_SETTER_CMD_IMPL_S(Worksheet, SetUseViewSize, bool, useViewSize) 0658 void Worksheet::setUseViewSize(bool useViewSize) { 0659 Q_D(Worksheet); 0660 if (useViewSize != d->useViewSize) 0661 exec(new WorksheetSetUseViewSizeCmd(d, useViewSize, ki18n("%1: change size type"))); 0662 } 0663 0664 void Worksheet::setZoomFit(ZoomFit zoomFit) { 0665 Q_D(Worksheet); 0666 d->zoomFit = zoomFit; // No need to undo 0667 } 0668 0669 STD_SETTER_CMD_IMPL_S(Worksheet, SetScaleContent, bool, scaleContent) 0670 void Worksheet::setScaleContent(bool scaleContent) { 0671 Q_D(Worksheet); 0672 if (scaleContent != d->scaleContent) 0673 exec(new WorksheetSetScaleContentCmd(d, scaleContent, ki18n("%1: change \"rescale the content\" property"))); 0674 } 0675 0676 /* ============================ setter methods and undo commands for layout options ================= */ 0677 STD_SETTER_CMD_IMPL_F_S(Worksheet, SetLayout, Worksheet::Layout, layout, updateLayout) 0678 void Worksheet::setLayout(Worksheet::Layout layout) { 0679 Q_D(Worksheet); 0680 if (layout != d->layout) { 0681 beginMacro(i18n("%1: set layout", name())); 0682 exec(new WorksheetSetLayoutCmd(d, layout, ki18n("%1: set layout"))); 0683 endMacro(); 0684 } 0685 } 0686 0687 STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutTopMargin, double, layoutTopMargin, updateLayout) 0688 void Worksheet::setLayoutTopMargin(double margin) { 0689 Q_D(Worksheet); 0690 if (margin != d->layoutTopMargin) { 0691 beginMacro(i18n("%1: set layout top margin", name())); 0692 exec(new WorksheetSetLayoutTopMarginCmd(d, margin, ki18n("%1: set layout top margin"))); 0693 endMacro(); 0694 } 0695 } 0696 0697 STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutBottomMargin, double, layoutBottomMargin, updateLayout) 0698 void Worksheet::setLayoutBottomMargin(double margin) { 0699 Q_D(Worksheet); 0700 if (margin != d->layoutBottomMargin) { 0701 beginMacro(i18n("%1: set layout bottom margin", name())); 0702 exec(new WorksheetSetLayoutBottomMarginCmd(d, margin, ki18n("%1: set layout bottom margin"))); 0703 endMacro(); 0704 } 0705 } 0706 0707 STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutLeftMargin, double, layoutLeftMargin, updateLayout) 0708 void Worksheet::setLayoutLeftMargin(double margin) { 0709 Q_D(Worksheet); 0710 if (margin != d->layoutLeftMargin) { 0711 beginMacro(i18n("%1: set layout left margin", name())); 0712 exec(new WorksheetSetLayoutLeftMarginCmd(d, margin, ki18n("%1: set layout left margin"))); 0713 endMacro(); 0714 } 0715 } 0716 0717 STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutRightMargin, double, layoutRightMargin, updateLayout) 0718 void Worksheet::setLayoutRightMargin(double margin) { 0719 Q_D(Worksheet); 0720 if (margin != d->layoutRightMargin) { 0721 beginMacro(i18n("%1: set layout right margin", name())); 0722 exec(new WorksheetSetLayoutRightMarginCmd(d, margin, ki18n("%1: set layout right margin"))); 0723 endMacro(); 0724 } 0725 } 0726 0727 STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutVerticalSpacing, double, layoutVerticalSpacing, updateLayout) 0728 void Worksheet::setLayoutVerticalSpacing(double spacing) { 0729 Q_D(Worksheet); 0730 if (spacing != d->layoutVerticalSpacing) { 0731 beginMacro(i18n("%1: set layout vertical spacing", name())); 0732 exec(new WorksheetSetLayoutVerticalSpacingCmd(d, spacing, ki18n("%1: set layout vertical spacing"))); 0733 endMacro(); 0734 } 0735 } 0736 0737 STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutHorizontalSpacing, double, layoutHorizontalSpacing, updateLayout) 0738 void Worksheet::setLayoutHorizontalSpacing(double spacing) { 0739 Q_D(Worksheet); 0740 if (spacing != d->layoutHorizontalSpacing) { 0741 beginMacro(i18n("%1: set layout horizontal spacing", name())); 0742 exec(new WorksheetSetLayoutHorizontalSpacingCmd(d, spacing, ki18n("%1: set layout horizontal spacing"))); 0743 endMacro(); 0744 } 0745 } 0746 0747 STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutRowCount, int, layoutRowCount, updateLayout) 0748 void Worksheet::setLayoutRowCount(int count) { 0749 Q_D(Worksheet); 0750 if (count != d->layoutRowCount) { 0751 beginMacro(i18n("%1: set layout row count", name())); 0752 exec(new WorksheetSetLayoutRowCountCmd(d, count, ki18n("%1: set layout row count"))); 0753 endMacro(); 0754 } 0755 } 0756 0757 STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutColumnCount, int, layoutColumnCount, updateLayout) 0758 void Worksheet::setLayoutColumnCount(int count) { 0759 Q_D(Worksheet); 0760 if (count != d->layoutColumnCount) { 0761 beginMacro(i18n("%1: set layout column count", name())); 0762 exec(new WorksheetSetLayoutColumnCountCmd(d, count, ki18n("%1: set layout column count"))); 0763 endMacro(); 0764 } 0765 } 0766 0767 class WorksheetSetPageRectCmd : public StandardMacroSetterCmd<Worksheet::Private, QRectF> { 0768 public: 0769 WorksheetSetPageRectCmd(Worksheet::Private* target, QRectF newValue, const KLocalizedString& description) 0770 : StandardMacroSetterCmd<Worksheet::Private, QRectF>(target, &Worksheet::Private::pageRect, newValue, description) { 0771 } 0772 void finalize() override { 0773 m_target->updatePageRect(); 0774 Q_EMIT m_target->q->pageRectChanged(m_target->*m_field); 0775 } 0776 void finalizeUndo() override { 0777 m_target->m_scene->setSceneRect(m_target->*m_field); 0778 Q_EMIT m_target->q->pageRectChanged(m_target->*m_field); 0779 } 0780 }; 0781 0782 void Worksheet::setPageRect(const QRectF& rect) { 0783 Q_D(Worksheet); 0784 // don't allow any rectangulars of width/height equal to zero 0785 if (qFuzzyCompare(rect.width(), 0.) || qFuzzyCompare(rect.height(), 0.)) { 0786 Q_EMIT pageRectChanged(d->pageRect); 0787 return; 0788 } 0789 0790 if (rect != d->pageRect) { 0791 if (!d->useViewSize) { 0792 beginMacro(i18n("%1: set page size", name())); 0793 exec(new WorksheetSetPageRectCmd(d, rect, ki18n("%1: set page size"))); 0794 endMacro(); 0795 } else { 0796 d->pageRect = rect; 0797 d->updatePageRect(); 0798 Q_EMIT pageRectChanged(d->pageRect); 0799 } 0800 } 0801 } 0802 0803 void Worksheet::setPrinting(bool on) const { 0804 const auto& elements = children<WorksheetElement>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden); 0805 for (auto* elem : elements) 0806 elem->setPrinting(on); 0807 } 0808 0809 STD_SETTER_CMD_IMPL_S(Worksheet, SetTheme, QString, theme) 0810 void Worksheet::setTheme(const QString& theme) { 0811 Q_D(Worksheet); 0812 QString info; 0813 if (!theme.isEmpty()) 0814 info = i18n("%1: load theme %2", name(), theme); 0815 else 0816 info = i18n("%1: load default theme", name()); 0817 beginMacro(info); 0818 exec(new WorksheetSetThemeCmd(d, theme, ki18n("%1: set theme"))); 0819 loadTheme(theme); 0820 endMacro(); 0821 } 0822 0823 void Worksheet::cartesianPlotMousePressZoomSelectionMode(QPointF logicPos) { 0824 auto senderPlot = static_cast<CartesianPlot*>(QObject::sender()); 0825 auto mouseMode = senderPlot->mouseMode(); 0826 auto actionMode = cartesianPlotActionMode(); 0827 if (actionMode == CartesianPlotActionMode::ApplyActionToAll) { 0828 const auto& plots = children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden); 0829 for (auto* plot : plots) 0830 plot->mousePressZoomSelectionMode(logicPos, -1); 0831 } else if ((actionMode == CartesianPlotActionMode::ApplyActionToAllX && mouseMode != CartesianPlot::MouseMode::ZoomYSelection) 0832 || (actionMode == CartesianPlotActionMode::ApplyActionToAllY && mouseMode != CartesianPlot::MouseMode::ZoomXSelection)) { 0833 const auto& plots = children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden); 0834 for (auto* plot : plots) { 0835 if (plot != senderPlot) { 0836 if (actionMode == CartesianPlotActionMode::ApplyActionToAllX) 0837 plot->setMouseMode(CartesianPlot::MouseMode::ZoomXSelection); 0838 else if (actionMode == CartesianPlotActionMode::ApplyActionToAllY) 0839 plot->setMouseMode(CartesianPlot::MouseMode::ZoomYSelection); 0840 } 0841 plot->mousePressZoomSelectionMode(logicPos, -1); 0842 } 0843 } else { 0844 int index = CartesianPlot::cSystemIndex(m_view->selectedElement()); 0845 senderPlot->mousePressZoomSelectionMode(logicPos, index); 0846 } 0847 } 0848 0849 void Worksheet::cartesianPlotMouseReleaseZoomSelectionMode() { 0850 auto senderPlot = static_cast<CartesianPlot*>(QObject::sender()); 0851 auto mouseMode = senderPlot->mouseMode(); 0852 auto actionMode = cartesianPlotActionMode(); 0853 if (actionMode == CartesianPlotActionMode::ApplyActionToAll 0854 || (actionMode == CartesianPlotActionMode::ApplyActionToAllX && mouseMode != CartesianPlot::MouseMode::ZoomYSelection) 0855 || (actionMode == CartesianPlotActionMode::ApplyActionToAllY && mouseMode != CartesianPlot::MouseMode::ZoomXSelection)) { 0856 const auto& plots = children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden); 0857 for (auto* plot : plots) { 0858 plot->mouseReleaseZoomSelectionMode(-1); 0859 plot->setMouseMode(mouseMode); 0860 } 0861 } else { 0862 int index = CartesianPlot::cSystemIndex(m_view->selectedElement()); 0863 auto* plot = static_cast<CartesianPlot*>(QObject::sender()); 0864 plot->mouseReleaseZoomSelectionMode(index); 0865 } 0866 } 0867 0868 void Worksheet::cartesianPlotMousePressCursorMode(int cursorNumber, QPointF logicPos) { 0869 if (cartesianPlotCursorMode() == CartesianPlotActionMode::ApplyActionToAll) { 0870 const auto& plots = children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden); 0871 for (auto* plot : plots) 0872 plot->mousePressCursorMode(cursorNumber, logicPos); 0873 } else { 0874 auto* plot = static_cast<CartesianPlot*>(QObject::sender()); 0875 plot->mousePressCursorMode(cursorNumber, logicPos); 0876 } 0877 0878 cursorPosChanged(cursorNumber, logicPos.x()); 0879 } 0880 0881 void Worksheet::cartesianPlotMouseMoveZoomSelectionMode(QPointF logicPos) { 0882 auto senderPlot = static_cast<CartesianPlot*>(QObject::sender()); 0883 auto actionMode = cartesianPlotActionMode(); 0884 auto mouseMode = senderPlot->mouseMode(); 0885 if (actionMode == CartesianPlotActionMode::ApplyActionToAll 0886 || (actionMode == CartesianPlotActionMode::ApplyActionToAllX && mouseMode != CartesianPlot::MouseMode::ZoomYSelection) 0887 || (actionMode == CartesianPlotActionMode::ApplyActionToAllY && mouseMode != CartesianPlot::MouseMode::ZoomXSelection)) { 0888 const auto& plots = children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden); 0889 for (auto* plot : plots) 0890 plot->mouseMoveZoomSelectionMode(logicPos, -1); 0891 } else 0892 senderPlot->mouseMoveZoomSelectionMode(logicPos, CartesianPlot::cSystemIndex(m_view->selectedElement())); 0893 } 0894 0895 void Worksheet::cartesianPlotMouseMoveSelectionMode(QPointF logicStart, QPointF logicEnd) { 0896 auto* senderPlot = static_cast<CartesianPlot*>(QObject::sender()); 0897 auto actionMode = cartesianPlotActionMode(); 0898 if (actionMode == CartesianPlotActionMode::ApplyActionToAll) { 0899 const auto& plots = children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden); 0900 for (auto* plot : plots) 0901 plot->mouseMoveSelectionMode(logicStart, logicEnd); 0902 } else if (actionMode == CartesianPlotActionMode::ApplyActionToSelection) { 0903 senderPlot->mouseMoveSelectionMode(logicStart, logicEnd); 0904 } else { 0905 const auto& plots = children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden); 0906 if (actionMode == CartesianPlotActionMode::ApplyActionToAllX) { 0907 // value does not matter, only difference 0908 logicStart.setY(0); 0909 logicEnd.setY(0); 0910 for (auto* plot : plots) 0911 plot->mouseMoveSelectionMode(logicStart, logicEnd); 0912 } else if (actionMode == CartesianPlotActionMode::ApplyActionToAllY) { 0913 // value does not matter, only difference 0914 logicStart.setX(0); 0915 logicEnd.setX(0); 0916 for (auto* plot : plots) 0917 plot->mouseMoveSelectionMode(logicStart, logicEnd); 0918 } 0919 } 0920 } 0921 0922 void Worksheet::cartesianPlotMouseHoverZoomSelectionMode(QPointF logicPos) { 0923 auto senderPlot = static_cast<CartesianPlot*>(QObject::sender()); 0924 auto actionMode = cartesianPlotActionMode(); 0925 auto mouseMode = senderPlot->mouseMode(); 0926 if (actionMode == CartesianPlotActionMode::ApplyActionToAll 0927 || (actionMode == CartesianPlotActionMode::ApplyActionToAllX && mouseMode != CartesianPlot::MouseMode::ZoomYSelection) 0928 || (actionMode == CartesianPlotActionMode::ApplyActionToAllY && mouseMode != CartesianPlot::MouseMode::ZoomXSelection)) { 0929 const auto& plots = children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden); 0930 for (auto* plot : plots) 0931 plot->mouseHoverZoomSelectionMode(logicPos, -1); 0932 } else { 0933 if (m_view->selectedElement()->parent(AspectType::CartesianPlot) == senderPlot) 0934 senderPlot->mouseHoverZoomSelectionMode(logicPos, CartesianPlot::cSystemIndex(m_view->selectedElement())); 0935 else 0936 senderPlot->mouseHoverZoomSelectionMode(logicPos, -1); 0937 } 0938 } 0939 0940 void Worksheet::cartesianPlotMouseHoverOutsideDataRect() { 0941 auto senderPlot = static_cast<CartesianPlot*>(QObject::sender()); 0942 auto mode = cartesianPlotActionMode(); 0943 auto mouseMode = senderPlot->mouseMode(); 0944 if (cartesianPlotActionMode() == CartesianPlotActionMode::ApplyActionToAll 0945 || (mode == CartesianPlotActionMode::ApplyActionToAllX && mouseMode != CartesianPlot::MouseMode::ZoomYSelection) 0946 || (mode == CartesianPlotActionMode::ApplyActionToAllY && mouseMode != CartesianPlot::MouseMode::ZoomXSelection)) { 0947 const auto& plots = children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden); 0948 for (auto* plot : plots) 0949 plot->mouseHoverOutsideDataRect(); 0950 } else 0951 senderPlot->mouseHoverOutsideDataRect(); 0952 } 0953 0954 void Worksheet::cartesianPlotAxisShift(int delta, Dimension dim, int index) { 0955 const auto& plots = children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden); 0956 const auto cursorMode = cartesianPlotActionMode(); 0957 bool leftOrDown = false; 0958 if (delta < 0) 0959 leftOrDown = true; 0960 0961 switch (cursorMode) { 0962 case CartesianPlotActionMode::ApplyActionToAll: { 0963 for (auto* plot : plots) 0964 plot->shift(-1, dim, leftOrDown); 0965 break; 0966 } 0967 case CartesianPlotActionMode::ApplyActionToAllX: { 0968 switch (dim) { 0969 case Dimension::X: { 0970 for (auto* plot : plots) 0971 plot->shift(-1, dim, leftOrDown); 0972 break; 0973 } 0974 case Dimension::Y: { 0975 auto* plot = static_cast<CartesianPlot*>(QObject::sender()); 0976 plot->shift(index, dim, leftOrDown); 0977 break; 0978 } 0979 } 0980 break; 0981 } 0982 case CartesianPlotActionMode::ApplyActionToAllY: { 0983 switch (dim) { 0984 case Dimension::X: { 0985 for (auto* plot : plots) 0986 plot->shift(index, dim, leftOrDown); 0987 break; 0988 } 0989 case Dimension::Y: { 0990 auto* plot = static_cast<CartesianPlot*>(QObject::sender()); 0991 plot->shift(-1, dim, leftOrDown); 0992 break; 0993 } 0994 } 0995 break; 0996 } 0997 case CartesianPlotActionMode::ApplyActionToSelection: { 0998 auto* plot = static_cast<CartesianPlot*>(QObject::sender()); 0999 plot->shift(index, dim, leftOrDown); 1000 break; 1001 } 1002 } 1003 } 1004 1005 void Worksheet::cartesianPlotWheelEvent(const QPointF& sceneRelPos, int delta, int xIndex, int yIndex, bool considerDimension, Dimension dim) { 1006 const auto& plots = children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden); 1007 const auto cursorMode = cartesianPlotActionMode(); 1008 if (considerDimension) { 1009 if ((dim == Dimension::X && (cursorMode == CartesianPlotActionMode::ApplyActionToAllX || cursorMode == CartesianPlotActionMode::ApplyActionToAll)) 1010 || (dim == Dimension::Y && (cursorMode == CartesianPlotActionMode::ApplyActionToAllY || cursorMode == CartesianPlotActionMode::ApplyActionToAll))) { 1011 for (auto* plot : plots) 1012 plot->wheelEvent(sceneRelPos, delta, -1, -1, considerDimension, dim); 1013 } else { 1014 auto* plot = static_cast<CartesianPlot*>(QObject::sender()); 1015 plot->wheelEvent(sceneRelPos, delta, xIndex, yIndex, considerDimension, dim); 1016 } 1017 1018 } else { 1019 switch (cursorMode) { 1020 case CartesianPlotActionMode::ApplyActionToAll: { 1021 for (auto* plot : plots) 1022 plot->wheelEvent(sceneRelPos, delta, -1, -1, considerDimension, dim); 1023 break; 1024 } 1025 case CartesianPlotActionMode::ApplyActionToAllX: { 1026 auto* plot = static_cast<CartesianPlot*>(QObject::sender()); 1027 plot->wheelEvent(sceneRelPos, delta, -1, yIndex, considerDimension, dim); 1028 for (auto* p : plots) { 1029 if (p != plot) { 1030 // The yIndex must not be available in the other plots 1031 // yIndex does not matter, because considerDimension is true 1032 p->wheelEvent(sceneRelPos, delta, -1, -1, true, Dimension::X); 1033 } 1034 } 1035 break; 1036 } 1037 case CartesianPlotActionMode::ApplyActionToAllY: { 1038 auto* plot = static_cast<CartesianPlot*>(QObject::sender()); 1039 // wheelEvent on all y in that plot 1040 plot->wheelEvent(sceneRelPos, delta, xIndex, -1, considerDimension, dim); 1041 for (auto* p : plots) { 1042 if (p != plot) { 1043 // The xIndex must not be available in the other plots 1044 // xIndex does not matter, because considerDimension is true 1045 p->wheelEvent(sceneRelPos, delta, -1, -1, true, Dimension::Y); 1046 } 1047 } 1048 break; 1049 } 1050 case CartesianPlotActionMode::ApplyActionToSelection: { 1051 auto* plot = static_cast<CartesianPlot*>(QObject::sender()); 1052 plot->wheelEvent(sceneRelPos, delta, xIndex, yIndex, considerDimension, dim); 1053 break; 1054 } 1055 } 1056 } 1057 } 1058 1059 void Worksheet::cartesianPlotMouseMoveCursorMode(int cursorNumber, QPointF logicPos) { 1060 if (cartesianPlotCursorMode() == CartesianPlotActionMode::ApplyActionToAll) { 1061 const auto& plots = children<CartesianPlot>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden); 1062 for (auto* plot : plots) 1063 plot->mouseMoveCursorMode(cursorNumber, logicPos); 1064 } else { 1065 auto* plot = static_cast<CartesianPlot*>(QObject::sender()); 1066 plot->mouseMoveCursorMode(cursorNumber, logicPos); 1067 } 1068 1069 cursorPosChanged(cursorNumber, logicPos.x()); 1070 } 1071 1072 QString dateTimeDiffToString(const QDateTime& dt0, const QDateTime& dt1) { 1073 // dt0 to dt1 is positive if dt0 is smaller than dt1 1074 QString result; 1075 qint64 diff; 1076 bool negative = false; 1077 ; 1078 if (dt0 < dt1) 1079 diff = dt0.msecsTo(dt1); 1080 else { 1081 diff = dt1.msecsTo(dt0); 1082 negative = true; 1083 } 1084 const qint64 dayToMsecs = 24 * 3600 * 1000; 1085 const qint64 hourToMsecs = 3600 * 1000; 1086 const qint64 minutesToMsecs = 60 * 1000; 1087 1088 qint64 days = diff / dayToMsecs; 1089 diff -= days * dayToMsecs; 1090 qint64 hours = diff / hourToMsecs; 1091 diff -= hours * hourToMsecs; 1092 qint64 minutes = diff / minutesToMsecs; 1093 diff -= minutes * minutesToMsecs; 1094 qint64 seconds = diff / 1000; 1095 diff -= seconds * 1000; 1096 qint64 msecs = diff; 1097 1098 if (negative) 1099 result += QStringLiteral("- "); 1100 1101 if (days > 0) 1102 result += QString::number(days) + QStringLiteral(" ") + QObject::tr("days") + QStringLiteral(" "); 1103 1104 if (hours > 0) 1105 result += QString::number(hours) + QStringLiteral(":"); 1106 else 1107 result += QStringLiteral("00:"); 1108 1109 if (minutes > 0) 1110 result += QString::number(minutes) + QStringLiteral(":"); 1111 else 1112 result += QStringLiteral("00:"); 1113 1114 if (seconds > 0) 1115 result += QString::number(seconds) + QStringLiteral("."); 1116 else 1117 result += QStringLiteral("00."); 1118 1119 if (msecs > 0) 1120 result += QString::number(msecs); 1121 else 1122 result += QStringLiteral("000"); 1123 1124 return result; 1125 } 1126 1127 /*! 1128 * \brief Worksheet::cursorPosChanged 1129 * Updates the cursor treemodel with the new data 1130 * \param xPos: new position of the cursor 1131 * It is assumed, that the plots/curves are in the same order than receiving from 1132 * the children() function. It's not checked if the names are the same 1133 */ 1134 void Worksheet::cursorPosChanged(int cursorNumber, double xPos) { 1135 Q_D(const Worksheet); 1136 if (d->suppressCursorPosChanged) 1137 return; 1138 1139 auto* sender = dynamic_cast<CartesianPlot*>(QObject::sender()); 1140 if (!sender) 1141 return; 1142 1143 TreeModel* treeModel = cursorModel(); 1144 1145 // if ApplyActionToSelection, each plot has it's own x value 1146 bool isDatetime = sender->xRangeFormatDefault() == RangeT::Format::DateTime; 1147 if (cartesianPlotCursorMode() == CartesianPlotActionMode::ApplyActionToAll) { 1148 // x values 1149 int rowPlot = 1; 1150 QModelIndex xName = treeModel->index(0, static_cast<int>(WorksheetPrivate::TreeModelColumn::SIGNALNAME)); 1151 treeModel->setData(xName, QVariant(QStringLiteral("X"))); 1152 double valueCursor[2]; 1153 QDateTime datetime[2]; 1154 for (int i = 0; i < 2; i++) { // need both cursors to calculate diff 1155 QVariant data; 1156 valueCursor[i] = sender->cursorPos(i); 1157 if (isDatetime) { 1158 datetime[i] = QDateTime::fromMSecsSinceEpoch(valueCursor[i], Qt::UTC); 1159 data = datetime[i].toString(sender->rangeDateTimeFormat(Dimension::X)); 1160 } else 1161 data = QVariant(valueCursor[i]); 1162 treeModel->setTreeData(data, 0, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR0) + i); 1163 } 1164 if (isDatetime) 1165 treeModel->setTreeData(dateTimeDiffToString(datetime[0], datetime[1]), 0, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF)); 1166 else 1167 treeModel->setTreeData(QVariant(valueCursor[1] - valueCursor[0]), 0, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF)); 1168 1169 // y values 1170 for (int i = 0; i < plotCount(); i++) { // i=0 is the x Axis 1171 1172 auto* p = plot(i); 1173 if (!p || !p->isVisible()) 1174 continue; 1175 1176 QModelIndex plotIndex = treeModel->index(rowPlot, static_cast<int>(WorksheetPrivate::TreeModelColumn::PLOTNAME)); 1177 1178 // curves 1179 int rowCurve = 0; 1180 for (int j = 0; j < p->curveCount(); j++) { 1181 // assumption: index of signals in model is the same than the index of the signal in the plot 1182 bool valueFound; 1183 1184 const XYCurve* curve = p->getCurve(j); 1185 if (!curve->isVisible()) 1186 continue; 1187 1188 double value = curve->y(xPos, valueFound); 1189 if (cursorNumber == 0) { 1190 treeModel->setTreeData(QVariant(value), rowCurve, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR0), plotIndex); 1191 double valueCursor1 = treeModel->treeData(rowCurve, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR1), plotIndex).toDouble(); 1192 treeModel->setTreeData(QVariant(valueCursor1 - value), 1193 rowCurve, 1194 static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), 1195 plotIndex); 1196 } else { 1197 treeModel->setTreeData(QVariant(value), rowCurve, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR1), plotIndex); 1198 double valueCursor0 = treeModel->treeData(rowCurve, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR0), plotIndex).toDouble(); 1199 treeModel->setTreeData(QVariant(value - valueCursor0), 1200 rowCurve, 1201 static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), 1202 plotIndex); 1203 } 1204 rowCurve++; 1205 } 1206 rowPlot++; 1207 } 1208 } else { // apply to selection 1209 // assumption: plot is visible 1210 int rowCount = treeModel->rowCount(); 1211 for (int i = 0; i < rowCount; i++) { 1212 QModelIndex plotIndex = treeModel->index(i, static_cast<int>(WorksheetPrivate::TreeModelColumn::PLOTNAME)); 1213 if (plotIndex.data().toString().compare(sender->name()) != 0) 1214 continue; 1215 1216 // x values (first row always exist) 1217 treeModel->setTreeData(QVariant(QStringLiteral("X")), 0, static_cast<int>(WorksheetPrivate::TreeModelColumn::SIGNALNAME), plotIndex); 1218 double valueCursor[2]; 1219 for (int i = 0; i < 2; i++) { // need both cursors to calculate diff 1220 valueCursor[i] = sender->cursorPos(i); 1221 treeModel->setTreeData(QVariant(valueCursor[i]), 0, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR0) + i, plotIndex); 1222 } 1223 treeModel->setTreeData(QVariant(valueCursor[1] - valueCursor[0]), 0, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), plotIndex); 1224 1225 // y values 1226 int rowCurve = 1; // first is x value 1227 for (int j = 0; j < sender->curveCount(); j++) { // j=0 are the x values 1228 1229 const XYCurve* curve = sender->getCurve(j); // -1 because we start with 1 for the x axis 1230 if (!curve->isVisible()) 1231 continue; 1232 1233 // assumption: index of signals in model is the same than the index of the signal in the plot 1234 bool valueFound; 1235 1236 double value = curve->y(xPos, valueFound); 1237 if (cursorNumber == 0) { 1238 treeModel->setTreeData(QVariant(value), rowCurve, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR0), plotIndex); 1239 double valueCursor1 = treeModel->treeData(rowCurve, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR1), plotIndex).toDouble(); 1240 treeModel->setTreeData(QVariant(valueCursor1 - value), 1241 rowCurve, 1242 static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), 1243 plotIndex); 1244 } else { 1245 treeModel->setTreeData(QVariant(value), rowCurve, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR1), plotIndex); 1246 double valueCursor0 = treeModel->treeData(rowCurve, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR0), plotIndex).toDouble(); 1247 treeModel->setTreeData(QVariant(value - valueCursor0), 1248 rowCurve, 1249 static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), 1250 plotIndex); 1251 } 1252 rowCurve++; 1253 } 1254 } 1255 } 1256 } 1257 1258 void Worksheet::cursorModelPlotAdded(const QString& /*name*/) { 1259 // TreeModel* treeModel = cursorModel(); 1260 // int rowCount = treeModel->rowCount(); 1261 // // add plot at the end 1262 // treeModel->insertRows(rowCount, 1); // add empty rows. Then they become filled 1263 // treeModel->setTreeData(QVariant(name), rowCount, WorksheetPrivate::TreeModelColumn::PLOTNAME); // rowCount instead of rowCount -1 because first row is 1264 // the x value 1265 updateCompleteCursorTreeModel(); 1266 } 1267 1268 void Worksheet::cursorModelPlotRemoved(const QString& name) { 1269 TreeModel* treeModel = cursorModel(); 1270 int rowCount = treeModel->rowCount(); 1271 1272 // first is x Axis 1273 for (int i = 1; i < rowCount; i++) { 1274 QModelIndex plotIndex = treeModel->index(i, static_cast<int>(WorksheetPrivate::TreeModelColumn::PLOTNAME)); 1275 if (plotIndex.data().toString().compare(name) != 0) 1276 continue; 1277 treeModel->removeRows(plotIndex.row(), 1); 1278 return; 1279 } 1280 } 1281 1282 void Worksheet::cartesianPlotMouseModeChangedSlot(CartesianPlot::MouseMode mode) { 1283 Q_D(Worksheet); 1284 if (d->updateCompleteCursorModel) { 1285 updateCompleteCursorTreeModel(); 1286 d->updateCompleteCursorModel = false; 1287 } 1288 1289 Q_EMIT cartesianPlotMouseModeChanged(mode); 1290 } 1291 1292 void Worksheet::curveDataChanged(const XYCurve* curve) { 1293 auto* plot = dynamic_cast<CartesianPlot*>(QObject::sender()); 1294 if (!plot || !curve) 1295 return; 1296 1297 TreeModel* treeModel = cursorModel(); 1298 int rowCount = treeModel->rowCount(); 1299 1300 for (int i = 0; i < rowCount; i++) { 1301 QModelIndex plotIndex = treeModel->index(i, static_cast<int>(WorksheetPrivate::TreeModelColumn::PLOTNAME)); 1302 if (plotIndex.data().toString().compare(plot->name()) != 0) 1303 continue; 1304 1305 for (int j = 0; j < plot->curveCount(); j++) { 1306 if (plot->getCurve(j)->name().compare(curve->name()) != 0) 1307 continue; 1308 1309 treeModel->setTreeData(QVariant(curve->name()), j, static_cast<int>(WorksheetPrivate::TreeModelColumn::SIGNALNAME), plotIndex); 1310 1311 bool valueFound; 1312 double valueCursor0 = curve->y(plot->cursorPos(0), valueFound); 1313 treeModel->setTreeData(QVariant(valueCursor0), j, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR0), plotIndex); 1314 1315 double valueCursor1 = curve->y(plot->cursorPos(1), valueFound); 1316 treeModel->setTreeData(QVariant(valueCursor1), j, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR1), plotIndex); 1317 1318 treeModel->setTreeData(QVariant(valueCursor1 - valueCursor0), j, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), plotIndex); 1319 break; 1320 } 1321 break; 1322 } 1323 } 1324 1325 void Worksheet::curveAdded(const XYCurve* curve) { 1326 Q_D(const Worksheet); 1327 auto* plot = dynamic_cast<CartesianPlot*>(QObject::sender()); 1328 if (!plot) 1329 return; 1330 1331 TreeModel* treeModel = cursorModel(); 1332 int rowCount = treeModel->rowCount(); 1333 1334 int i = 0; 1335 // first row is the x axis when applied to all plots. Starting at the second row 1336 if (cartesianPlotCursorMode() == CartesianPlotActionMode::ApplyActionToAll) 1337 i = 1; 1338 1339 for (; i < rowCount; i++) { 1340 QModelIndex plotIndex = treeModel->index(i, static_cast<int>(WorksheetPrivate::TreeModelColumn::PLOTNAME)); 1341 if (plotIndex.data().toString().compare(plot->name()) != 0) 1342 continue; 1343 int row = 0; 1344 for (int j = 0; j < plot->curveCount(); j++) { 1345 if (plot->getCurve(j)->name().compare(curve->name()) != 0) { 1346 if (plot->getCurve(j)->isVisible()) 1347 row++; 1348 continue; 1349 } 1350 1351 treeModel->insertRow(row, plotIndex); 1352 1353 treeModel->setTreeData(QVariant(curve->name()), row, static_cast<int>(WorksheetPrivate::TreeModelColumn::SIGNALNAME), plotIndex); 1354 QColor curveColor = curve->line()->pen().color(); 1355 curveColor.setAlpha(d->cursorTreeModelCurveBackgroundAlpha); 1356 treeModel->setTreeData(QVariant(curveColor), row, static_cast<int>(WorksheetPrivate::TreeModelColumn::SIGNALNAME), plotIndex, Qt::BackgroundRole); 1357 bool valueFound; 1358 double valueCursor0 = curve->y(plot->cursorPos(0), valueFound); 1359 treeModel->setTreeData(QVariant(valueCursor0), row, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR0), plotIndex); 1360 1361 double valueCursor1 = curve->y(plot->cursorPos(1), valueFound); 1362 treeModel->setTreeData(QVariant(valueCursor1), row, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR1), plotIndex); 1363 1364 treeModel->setTreeData(QVariant(valueCursor1 - valueCursor0), row, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), plotIndex); 1365 break; 1366 } 1367 break; 1368 } 1369 } 1370 1371 void Worksheet::curveRemoved(const XYCurve* curve) { 1372 auto* plot = dynamic_cast<CartesianPlot*>(QObject::sender()); 1373 if (!plot) 1374 return; 1375 1376 TreeModel* treeModel = cursorModel(); 1377 int rowCount = treeModel->rowCount(); 1378 1379 for (int i = 0; i < rowCount; i++) { 1380 QModelIndex plotIndex = treeModel->index(i, static_cast<int>(WorksheetPrivate::TreeModelColumn::PLOTNAME)); 1381 if (plotIndex.data().toString().compare(plot->name()) != 0) 1382 continue; 1383 1384 int curveCount = treeModel->rowCount(plotIndex); 1385 for (int j = 0; j < curveCount; j++) { 1386 QModelIndex curveIndex = treeModel->index(j, static_cast<int>(WorksheetPrivate::TreeModelColumn::SIGNALNAME), plotIndex); 1387 1388 if (curveIndex.data().toString().compare(curve->name()) != 0) 1389 continue; 1390 treeModel->removeRow(j, plotIndex); 1391 break; 1392 } 1393 break; 1394 } 1395 } 1396 1397 /*! 1398 * Updates the background of the cuves entry in the treeview 1399 * @param pen Pen of the curve 1400 * @param curveName Curve name to find in treemodel 1401 */ 1402 void Worksheet::updateCurveBackground(QColor color, const QString& curveName) { 1403 Q_D(const Worksheet); 1404 const auto* plot = static_cast<const CartesianPlot*>(QObject::sender()); 1405 auto* treeModel = cursorModel(); 1406 int rowCount = treeModel->rowCount(); 1407 1408 for (int i = 0; i < rowCount; i++) { 1409 auto plotIndex = treeModel->index(i, static_cast<int>(WorksheetPrivate::TreeModelColumn::PLOTNAME)); 1410 if (plotIndex.data().toString().compare(plot->name()) != 0) 1411 continue; 1412 1413 int curveCount = treeModel->rowCount(plotIndex); 1414 for (int j = 0; j < curveCount; j++) { 1415 auto curveIndex = treeModel->index(j, static_cast<int>(WorksheetPrivate::TreeModelColumn::SIGNALNAME), plotIndex); 1416 1417 if (curveIndex.data().toString().compare(curveName) != 0) 1418 continue; 1419 1420 color.setAlpha(d->cursorTreeModelCurveBackgroundAlpha); 1421 treeModel->setTreeData(QVariant(color), j, static_cast<int>(WorksheetPrivate::TreeModelColumn::SIGNALNAME), plotIndex, Qt::BackgroundRole); 1422 return; 1423 } 1424 return; 1425 } 1426 } 1427 1428 /** 1429 * @brief Worksheet::updateCompleteCursorTreeModel 1430 * If the plot or the curve are not available, the plot/curve is not in the treemodel! 1431 */ 1432 void Worksheet::updateCompleteCursorTreeModel() { 1433 Q_D(const Worksheet); 1434 if (isLoading()) 1435 return; 1436 1437 TreeModel* treeModel = cursorModel(); 1438 1439 if (treeModel->rowCount() > 0) 1440 treeModel->removeRows(0, treeModel->rowCount()); // remove all data 1441 1442 int pc = plotCount(); 1443 if (pc < 1) 1444 return; 1445 1446 if (cartesianPlotCursorMode() == CartesianPlotActionMode::ApplyActionToAll) { 1447 // 1 because of the X data 1448 treeModel->insertRows(0, 1); //, treeModel->index(0,0)); // add empty rows. Then they become filled 1449 1450 // set X data 1451 QModelIndex xName = treeModel->index(0, static_cast<int>(WorksheetPrivate::TreeModelColumn::SIGNALNAME)); 1452 treeModel->setData(xName, QVariant(QStringLiteral("X"))); 1453 auto* plot0 = plot(0); 1454 if (plot0) { 1455 double valueCursor[2]; 1456 for (int i = 0; i < 2; i++) { 1457 valueCursor[i] = plot0->cursorPos(i); 1458 QModelIndex cursor = treeModel->index(0, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR0) + i); 1459 1460 treeModel->setData(cursor, QVariant(valueCursor[i])); 1461 } 1462 QModelIndex diff = treeModel->index(0, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF)); 1463 treeModel->setData(diff, QVariant(valueCursor[1] - valueCursor[0])); 1464 } 1465 } else { 1466 // treeModel->insertRows(0, plotCount, treeModel->index(0,0)); // add empty rows. Then they become filled 1467 } 1468 1469 // set plot name, y value, background 1470 for (int i = 0; i < pc; i++) { 1471 auto* p = plot(i); 1472 QModelIndex plotName; 1473 int addOne = 0; 1474 1475 if (!p || !p->isVisible()) 1476 continue; 1477 1478 // add new entry for the plot 1479 treeModel->insertRows(treeModel->rowCount(), 1); //, treeModel->index(0, 0)); 1480 1481 // add plot name and X row if needed 1482 if (cartesianPlotCursorMode() == CartesianPlotActionMode::ApplyActionToAll) { 1483 plotName = treeModel->index(i + 1, static_cast<int>(WorksheetPrivate::TreeModelColumn::PLOTNAME)); // plus one because first row are the x values 1484 treeModel->setData(plotName, QVariant(p->name())); 1485 } else { 1486 addOne = 1; 1487 plotName = treeModel->index(i, static_cast<int>(WorksheetPrivate::TreeModelColumn::PLOTNAME)); 1488 treeModel->setData(plotName, QVariant(p->name())); 1489 treeModel->insertRows(0, 1, plotName); // one, because the first row are the x values 1490 1491 QModelIndex xName = treeModel->index(0, static_cast<int>(WorksheetPrivate::TreeModelColumn::SIGNALNAME), plotName); 1492 treeModel->setData(xName, QVariant(QStringLiteral("X"))); 1493 double valueCursor[2]; 1494 for (int i = 0; i < 2; i++) { 1495 valueCursor[i] = p->cursorPos(i); 1496 QModelIndex cursor = treeModel->index(0, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR0) + i, plotName); 1497 1498 treeModel->setData(cursor, QVariant(valueCursor[i])); 1499 } 1500 QModelIndex diff = treeModel->index(0, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), plotName); 1501 treeModel->setData(diff, QVariant(valueCursor[1] - valueCursor[0])); 1502 } 1503 1504 int rowCurve = addOne; 1505 for (int j = 0; j < p->curveCount(); j++) { 1506 double cursorValue[2] = {NAN, NAN}; 1507 const XYCurve* curve = p->getCurve(j); 1508 1509 if (!curve->isVisible()) 1510 continue; 1511 1512 for (int k = 0; k < 2; k++) { 1513 double xPos = p->cursorPos(k); 1514 bool valueFound; 1515 cursorValue[k] = curve->y(xPos, valueFound); 1516 } 1517 treeModel->insertRows(rowCurve, 1, plotName); 1518 QColor curveColor = curve->line()->pen().color(); 1519 curveColor.setAlpha(d->cursorTreeModelCurveBackgroundAlpha); 1520 treeModel->setTreeData(QVariant(curveColor), rowCurve, 0, plotName, Qt::BackgroundRole); 1521 treeModel->setTreeData(QVariant(curve->name()), rowCurve, static_cast<int>(WorksheetPrivate::TreeModelColumn::SIGNALNAME), plotName); 1522 treeModel->setTreeData(QVariant(cursorValue[0]), rowCurve, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR0), plotName); 1523 treeModel->setTreeData(QVariant(cursorValue[1]), rowCurve, static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR1), plotName); 1524 treeModel->setTreeData(QVariant(cursorValue[1] - cursorValue[0]), 1525 rowCurve, 1526 static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), 1527 plotName); 1528 rowCurve++; 1529 } 1530 } 1531 } 1532 1533 // ############################################################################## 1534 // ###################### Private implementation ############################### 1535 // ############################################################################## 1536 WorksheetPrivate::WorksheetPrivate(Worksheet* owner) 1537 : q(owner) 1538 , m_scene(new QGraphicsScene()) { 1539 QStringList headers = {i18n("Curves"), QStringLiteral("V1"), QStringLiteral("V2"), QStringLiteral("V2-V1")}; 1540 cursorData = new TreeModel(headers, nullptr); 1541 } 1542 1543 QString WorksheetPrivate::name() const { 1544 return q->name(); 1545 } 1546 1547 /*! 1548 * called if the worksheet page (the actual size of worksheet's rectangular) was changed. 1549 * if a layout is active, it is is updated - this adjusts the sizes of the elements in the layout to the new page size. 1550 * if no layout is active and the option "scale content" is active, \c handleResize() is called to adjust the properties. 1551 */ 1552 void WorksheetPrivate::updatePageRect() { 1553 if (q->isLoading()) 1554 return; 1555 1556 QRectF oldRect = m_scene->sceneRect(); 1557 m_scene->setSceneRect(pageRect); 1558 1559 if (layout != Worksheet::Layout::NoLayout) 1560 updateLayout(); 1561 else { 1562 if (scaleContent) { 1563 qreal horizontalRatio = pageRect.width() / oldRect.width(); 1564 qreal verticalRatio = pageRect.height() / oldRect.height(); 1565 const auto& children = q->children<WorksheetElement>(AbstractAspect::ChildIndexFlag::IncludeHidden); 1566 if (useViewSize) { 1567 // don't make the change of the geometry undoable/redoable if the view size is used. 1568 for (auto* elem : children) { 1569 elem->setUndoAware(false); 1570 elem->handleResize(horizontalRatio, verticalRatio, true); 1571 elem->setUndoAware(true); 1572 } 1573 } else { 1574 // for (auto* child : children) 1575 // child->handleResize(horizontalRatio, verticalRatio, true); 1576 } 1577 } 1578 } 1579 } 1580 1581 void WorksheetPrivate::update() { 1582 q->update(); 1583 } 1584 1585 WorksheetPrivate::~WorksheetPrivate() { 1586 delete m_scene; 1587 delete cursorData; 1588 } 1589 1590 void WorksheetPrivate::updateLayout(bool undoable) { 1591 if (suppressLayoutUpdate) 1592 return; 1593 1594 const auto& list = q->children<WorksheetElementContainer>(); 1595 int count = 0; 1596 for (auto* elem : list) 1597 if (elem->isVisible()) 1598 ++count; 1599 1600 if (count == 0) 1601 return; 1602 1603 // determine the currently selected plot/container and make it 1604 // resizable or not depending on the layout settings 1605 bool resizable = (layout == Worksheet::Layout::NoLayout); 1606 if (q->m_view) { 1607 const auto& items = q->m_view->selectedItems(); 1608 if (items.size() == 1) { 1609 const auto& item = items.constFirst(); 1610 const auto& containers = q->children<WorksheetElementContainer>(); 1611 for (auto* container : containers) { 1612 if (container->graphicsItem() == item) { 1613 container->setResizeEnabled(resizable); 1614 break; 1615 } 1616 } 1617 } 1618 } 1619 1620 if (layout == Worksheet::Layout::NoLayout) { 1621 for (auto* elem : list) 1622 elem->graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, true); 1623 1624 return; 1625 } 1626 1627 double x = layoutLeftMargin; 1628 double y = layoutTopMargin; 1629 double w, h; 1630 if (layout == Worksheet::Layout::VerticalLayout) { 1631 w = m_scene->sceneRect().width() - layoutLeftMargin - layoutRightMargin; 1632 h = (m_scene->sceneRect().height() - layoutTopMargin - layoutBottomMargin - (count - 1) * layoutVerticalSpacing) / count; 1633 for (auto* elem : list) { 1634 if (!elem->isVisible()) 1635 continue; 1636 setContainerRect(elem, x, y, h, w, undoable); 1637 y += h + layoutVerticalSpacing; 1638 } 1639 } else if (layout == Worksheet::Layout::HorizontalLayout) { 1640 w = (m_scene->sceneRect().width() - layoutLeftMargin - layoutRightMargin - (count - 1) * layoutHorizontalSpacing) / count; 1641 h = m_scene->sceneRect().height() - layoutTopMargin - layoutBottomMargin; 1642 for (auto* elem : list) { 1643 if (!elem->isVisible()) 1644 continue; 1645 setContainerRect(elem, x, y, h, w, undoable); 1646 x += w + layoutHorizontalSpacing; 1647 } 1648 } else { // GridLayout 1649 // add new rows, if not sufficient 1650 if (count > layoutRowCount * layoutColumnCount) { 1651 layoutRowCount = floor((double)count / layoutColumnCount + 0.5); 1652 Q_EMIT q->layoutRowCountChanged(layoutRowCount); 1653 } 1654 1655 w = (m_scene->sceneRect().width() - layoutLeftMargin - layoutRightMargin - (layoutColumnCount - 1) * layoutHorizontalSpacing) / layoutColumnCount; 1656 h = (m_scene->sceneRect().height() - layoutTopMargin - layoutBottomMargin - (layoutRowCount - 1) * layoutVerticalSpacing) / layoutRowCount; 1657 int columnIndex = 0; // counts the columns in a row 1658 for (auto* elem : list) { 1659 if (!elem->isVisible()) 1660 continue; 1661 setContainerRect(elem, x, y, h, w, undoable); 1662 x += w + layoutHorizontalSpacing; 1663 columnIndex++; 1664 if (columnIndex == layoutColumnCount) { 1665 columnIndex = 0; 1666 x = layoutLeftMargin; 1667 y += h + layoutVerticalSpacing; 1668 } 1669 } 1670 } 1671 1672 Q_EMIT q->changed(); 1673 } 1674 1675 void WorksheetPrivate::setContainerRect(WorksheetElementContainer* elem, double x, double y, double h, double w, bool undoable) { 1676 if (useViewSize) { 1677 // when using the view size, no need to put rect changes onto the undo-stack 1678 elem->setUndoAware(false); 1679 elem->setRect(QRectF(x, y, w, h)); 1680 elem->setUndoAware(true); 1681 } else { 1682 // don't put rect changed onto the undo-stack if undoable-flag is set to true, 1683 // e.g. when new child is added or removed (the layout and the childrend rects will be updated anyway) 1684 if (!undoable) { 1685 elem->setUndoAware(false); 1686 elem->setRect(QRectF(x, y, w, h)); 1687 elem->setUndoAware(true); 1688 } else 1689 elem->setRect(QRectF(x, y, w, h)); 1690 } 1691 elem->graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, false); 1692 } 1693 1694 // ############################################################################## 1695 // ################## Serialization/Deserialization ########################### 1696 // ############################################################################## 1697 1698 //! Save as XML 1699 void Worksheet::save(QXmlStreamWriter* writer) const { 1700 Q_D(const Worksheet); 1701 writer->writeStartElement(QStringLiteral("worksheet")); 1702 writeBasicAttributes(writer); 1703 writeCommentElement(writer); 1704 1705 // applied theme 1706 if (!d->theme.isEmpty()) { 1707 writer->writeStartElement(QStringLiteral("theme")); 1708 writer->writeAttribute(QStringLiteral("name"), d->theme); 1709 writer->writeEndElement(); 1710 } 1711 1712 // geometry 1713 writer->writeStartElement(QStringLiteral("geometry")); 1714 QRectF rect = d->m_scene->sceneRect(); 1715 writer->writeAttribute(QStringLiteral("x"), QString::number(rect.x())); 1716 writer->writeAttribute(QStringLiteral("y"), QString::number(rect.y())); 1717 writer->writeAttribute(QStringLiteral("width"), QString::number(rect.width())); 1718 writer->writeAttribute(QStringLiteral("height"), QString::number(rect.height())); 1719 writer->writeAttribute(QStringLiteral("useViewSize"), QString::number(d->useViewSize)); 1720 writer->writeAttribute(QStringLiteral("zoomFit"), QString::number((int)d->zoomFit)); 1721 writer->writeEndElement(); 1722 1723 // layout 1724 writer->writeStartElement(QStringLiteral("layout")); 1725 writer->writeAttribute(QStringLiteral("layout"), QString::number(static_cast<int>(d->layout))); 1726 writer->writeAttribute(QStringLiteral("topMargin"), QString::number(d->layoutTopMargin)); 1727 writer->writeAttribute(QStringLiteral("bottomMargin"), QString::number(d->layoutBottomMargin)); 1728 writer->writeAttribute(QStringLiteral("leftMargin"), QString::number(d->layoutLeftMargin)); 1729 writer->writeAttribute(QStringLiteral("rightMargin"), QString::number(d->layoutRightMargin)); 1730 writer->writeAttribute(QStringLiteral("verticalSpacing"), QString::number(d->layoutVerticalSpacing)); 1731 writer->writeAttribute(QStringLiteral("horizontalSpacing"), QString::number(d->layoutHorizontalSpacing)); 1732 writer->writeAttribute(QStringLiteral("columnCount"), QString::number(d->layoutColumnCount)); 1733 writer->writeAttribute(QStringLiteral("rowCount"), QString::number(d->layoutRowCount)); 1734 writer->writeEndElement(); 1735 1736 // background properties 1737 d->background->save(writer); 1738 1739 // cartesian properties 1740 writer->writeStartElement(QStringLiteral("plotProperties")); 1741 writer->writeAttribute(QStringLiteral("plotInteractive"), QString::number(d->plotsInteractive)); 1742 writer->writeAttribute(QStringLiteral("cartesianPlotActionMode"), QString::number(static_cast<int>(d->cartesianPlotActionMode))); 1743 writer->writeAttribute(QStringLiteral("cartesianPlotCursorMode"), QString::number(static_cast<int>(d->cartesianPlotCursorMode))); 1744 writer->writeEndElement(); 1745 1746 // serialize all children 1747 for (auto* child : children<WorksheetElement>(ChildIndexFlag::IncludeHidden)) 1748 child->save(writer); 1749 1750 writer->writeEndElement(); // close "worksheet" section 1751 } 1752 1753 //! Load from XML 1754 bool Worksheet::load(XmlStreamReader* reader, bool preview) { 1755 if (!readBasicAttributes(reader)) 1756 return false; 1757 1758 Q_D(Worksheet); 1759 1760 // clear the theme that was potentially set in init() in order to correctly load here the worksheets without any theme used 1761 d->theme.clear(); 1762 1763 QXmlStreamAttributes attribs; 1764 QString str; 1765 1766 while (!reader->atEnd()) { 1767 reader->readNext(); 1768 if (reader->isEndElement() && reader->name() == QLatin1String("worksheet")) 1769 break; 1770 1771 if (!reader->isStartElement()) 1772 continue; 1773 1774 if (reader->name() == QLatin1String("comment")) { 1775 if (!readCommentElement(reader)) 1776 return false; 1777 } else if (!preview && reader->name() == QLatin1String("theme")) { 1778 attribs = reader->attributes(); 1779 d->theme = attribs.value(QStringLiteral("name")).toString(); 1780 } else if (!preview && reader->name() == QLatin1String("geometry")) { 1781 attribs = reader->attributes(); 1782 1783 str = attribs.value(QStringLiteral("x")).toString(); 1784 if (str.isEmpty()) 1785 reader->raiseMissingAttributeWarning(QStringLiteral("x")); 1786 else 1787 d->pageRect.setX(str.toDouble()); 1788 1789 str = attribs.value(QStringLiteral("y")).toString(); 1790 if (str.isEmpty()) 1791 reader->raiseMissingAttributeWarning(QStringLiteral("y")); 1792 else 1793 d->pageRect.setY(str.toDouble()); 1794 1795 str = attribs.value(QStringLiteral("width")).toString(); 1796 if (str.isEmpty()) 1797 reader->raiseMissingAttributeWarning(QStringLiteral("width")); 1798 else 1799 d->pageRect.setWidth(str.toDouble()); 1800 1801 str = attribs.value(QStringLiteral("height")).toString(); 1802 if (str.isEmpty()) 1803 reader->raiseMissingAttributeWarning(QStringLiteral("height")); 1804 else 1805 d->pageRect.setHeight(str.toDouble()); 1806 1807 READ_INT_VALUE("useViewSize", useViewSize, int); 1808 READ_INT_VALUE("zoomFit", zoomFit, ZoomFit); 1809 } else if (!preview && reader->name() == QLatin1String("layout")) { 1810 attribs = reader->attributes(); 1811 1812 READ_INT_VALUE("layout", layout, Worksheet::Layout); 1813 READ_DOUBLE_VALUE("topMargin", layoutTopMargin); 1814 READ_DOUBLE_VALUE("bottomMargin", layoutBottomMargin); 1815 READ_DOUBLE_VALUE("leftMargin", layoutLeftMargin); 1816 READ_DOUBLE_VALUE("rightMargin", layoutRightMargin); 1817 READ_DOUBLE_VALUE("verticalSpacing", layoutVerticalSpacing); 1818 READ_DOUBLE_VALUE("horizontalSpacing", layoutHorizontalSpacing); 1819 READ_INT_VALUE("columnCount", layoutColumnCount, int); 1820 READ_INT_VALUE("rowCount", layoutRowCount, int); 1821 } else if (!preview && reader->name() == QLatin1String("background")) 1822 d->background->load(reader, preview); 1823 else if (!preview && reader->name() == QLatin1String("plotProperties")) { 1824 attribs = reader->attributes(); 1825 1826 str = attribs.value(QStringLiteral("plotInteractive")).toString(); 1827 if (str.isEmpty()) { 1828 str = attribs.value(QStringLiteral("plotLocked")).toString(); 1829 if (str.isEmpty()) 1830 reader->raiseMissingAttributeWarning(QStringLiteral("plotLocked")); 1831 else 1832 d->plotsInteractive = !static_cast<bool>(str.toInt()); 1833 } else 1834 d->plotsInteractive = static_cast<bool>(str.toInt()); 1835 1836 READ_INT_VALUE("cartesianPlotActionMode", cartesianPlotActionMode, Worksheet::CartesianPlotActionMode); 1837 READ_INT_VALUE("cartesianPlotCursorMode", cartesianPlotCursorMode, Worksheet::CartesianPlotActionMode); 1838 } else if (reader->name() == QLatin1String("cartesianPlot")) { 1839 auto* plot = new CartesianPlot(QString()); 1840 plot->setIsLoading(true); 1841 if (!plot->load(reader, preview)) { 1842 delete plot; 1843 return false; 1844 } else 1845 addChildFast(plot); 1846 } else if (!preview && reader->name() == QLatin1String("textLabel")) { 1847 auto* label = new TextLabel(QString()); 1848 label->setIsLoading(true); 1849 if (!label->load(reader, preview)) { 1850 delete label; 1851 return false; 1852 } else 1853 addChildFast(label); 1854 } else if (!preview && reader->name() == QLatin1String("image")) { 1855 Image* image = new Image(QString()); 1856 image->setIsLoading(true); 1857 if (!image->load(reader, preview)) { 1858 delete image; 1859 return false; 1860 } else 1861 addChildFast(image); 1862 } else { // unknown element 1863 reader->raiseUnknownElementWarning(); 1864 if (!reader->skipToEndElement()) 1865 return false; 1866 } 1867 } 1868 1869 if (!preview) { 1870 d->m_scene->setSceneRect(d->pageRect); 1871 d->updateLayout(); 1872 updateCompleteCursorTreeModel(); 1873 } 1874 1875 return true; 1876 } 1877 1878 // ############################################################################## 1879 // ######################### Theme management ################################## 1880 // ############################################################################## 1881 void Worksheet::loadTheme(const QString& theme) { 1882 Q_D(Worksheet); 1883 KConfigGroup group; 1884 KConfig* config = nullptr; 1885 if (!theme.isEmpty()) { 1886 // load values from the theme config 1887 config = new KConfig(ThemeHandler::themeFilePath(theme), KConfig::SimpleConfig); 1888 1889 // apply the same background color for Worksheet as for the CartesianPlot 1890 group = config->group(QStringLiteral("CartesianPlot")); 1891 1892 // load the theme for all the children 1893 const auto& children = this->children<WorksheetElement>(ChildIndexFlag::IncludeHidden); 1894 for (auto* child : children) 1895 child->loadThemeConfig(*config); 1896 } else { 1897 // load default values 1898 config = new KConfig(); 1899 group = config->group(QStringLiteral("Worksheet")); 1900 } 1901 1902 // load background properties 1903 d->background->loadThemeConfig(group); 1904 1905 // load the theme for all the children 1906 const auto& children = this->children<WorksheetElement>(ChildIndexFlag::IncludeHidden); 1907 for (auto* child : children) 1908 child->loadThemeConfig(*config); 1909 1910 delete config; 1911 1912 Q_EMIT changed(); 1913 }