File indexing completed on 2024-05-12 15:26:40
0001 /*************************************************************************** 0002 File : Datapicker.cpp 0003 Project : LabPlot 0004 Description : Datapicker 0005 -------------------------------------------------------------------- 0006 Copyright : (C) 2015 by Ankit Wagadre (wagadre.ankit@gmail.com) 0007 Copyright : (C) 2015-2019 Alexander Semke (alexander.semke@web.de) 0008 0009 ***************************************************************************/ 0010 /*************************************************************************** 0011 * * 0012 * This program is free software; you can redistribute it and/or modify * 0013 * it under the terms of the GNU General Public License as published by * 0014 * the Free Software Foundation; either version 2 of the License, or * 0015 * (at your option) any later version. * 0016 * * 0017 * This program is distributed in the hope that it will be useful, * 0018 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0020 * GNU General Public License for more details. * 0021 * * 0022 * You should have received a copy of the GNU General Public License * 0023 * along with this program; if not, write to the Free Software * 0024 * Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0025 * Boston, MA 02110-1301 USA * 0026 * * 0027 ***************************************************************************/ 0028 0029 #include "Datapicker.h" 0030 #include "backend/spreadsheet/Spreadsheet.h" 0031 #include "backend/datapicker/DatapickerImage.h" 0032 #include "backend/lib/XmlStreamReader.h" 0033 #include "commonfrontend/datapicker/DatapickerView.h" 0034 #include "backend/datapicker/DatapickerCurve.h" 0035 #include "backend/datapicker/Transform.h" 0036 #include "backend/datapicker/DatapickerPoint.h" 0037 0038 #include <QGraphicsScene> 0039 #include "QIcon" 0040 #include <KLocalizedString> 0041 0042 /** 0043 * \class Datapicker 0044 * \brief Top-level container for DatapickerCurve and DatapickerImage. 0045 * \ingroup backend 0046 */ 0047 Datapicker::Datapicker(const QString& name, const bool loading) 0048 : AbstractPart(name, AspectType::Datapicker), m_transform(new Transform()) 0049 { 0050 connect(this, &Datapicker::aspectAdded, this, &Datapicker::handleAspectAdded); 0051 connect(this, &Datapicker::aspectAboutToBeRemoved, this, &Datapicker::handleAspectAboutToBeRemoved); 0052 0053 if (!loading) 0054 init(); 0055 } 0056 0057 Datapicker::~Datapicker() { 0058 delete m_transform; 0059 } 0060 0061 void Datapicker::init() { 0062 m_image = new DatapickerImage(i18n("Plot")); 0063 m_image->setHidden(true); 0064 setUndoAware(false); 0065 addChild(m_image); 0066 setUndoAware(true); 0067 0068 connect(m_image, &DatapickerImage::statusInfo, this, &Datapicker::statusInfo); 0069 } 0070 0071 /*! 0072 Returns an icon to be used in the project explorer. 0073 */ 0074 QIcon Datapicker::icon() const { 0075 return QIcon::fromTheme("color-picker-black"); 0076 } 0077 0078 /*! 0079 * Returns a new context menu. The caller takes ownership of the menu. 0080 */ 0081 QMenu* Datapicker::createContextMenu() { 0082 QMenu* menu = AbstractPart::createContextMenu(); 0083 Q_ASSERT(menu); 0084 m_image->createContextMenu(menu); 0085 return menu; 0086 } 0087 0088 QWidget* Datapicker::view() const { 0089 if (!m_partView) { 0090 m_view = new DatapickerView(const_cast<Datapicker*>(this)); 0091 m_partView = m_view; 0092 } 0093 return m_partView; 0094 } 0095 0096 0097 bool Datapicker::exportView() const { 0098 Spreadsheet* s = currentSpreadsheet(); 0099 bool ret; 0100 if (s) 0101 ret = s->exportView(); 0102 else 0103 ret = m_image->exportView(); 0104 return ret; 0105 } 0106 0107 bool Datapicker::printView() { 0108 Spreadsheet* s = currentSpreadsheet(); 0109 bool ret; 0110 if (s) 0111 ret = s->printView(); 0112 else 0113 ret = m_image->printView(); 0114 return ret; 0115 } 0116 0117 bool Datapicker::printPreview() const { 0118 Spreadsheet* s = currentSpreadsheet(); 0119 bool ret; 0120 if (s) 0121 ret = s->printPreview(); 0122 else 0123 ret = m_image->printPreview(); 0124 return ret; 0125 } 0126 0127 DatapickerCurve* Datapicker::activeCurve() { 0128 return m_activeCurve; 0129 } 0130 0131 Spreadsheet* Datapicker::currentSpreadsheet() const { 0132 if (!m_view) 0133 return nullptr; 0134 0135 const int index = m_view->currentIndex(); 0136 if (index > 0) { 0137 auto* curve = child<DatapickerCurve>(index-1); 0138 return curve->child<Spreadsheet>(0); 0139 } 0140 return nullptr; 0141 } 0142 0143 DatapickerImage* Datapicker::image() const { 0144 return m_image; 0145 } 0146 0147 /*! 0148 this slot is called when a datapicker child is selected in the project explorer. 0149 emits \c datapickerItemSelected() to forward this event to the \c DatapickerView 0150 in order to select the corresponding tab. 0151 */ 0152 void Datapicker::childSelected(const AbstractAspect* aspect) { 0153 Q_ASSERT(aspect); 0154 m_activeCurve = dynamic_cast<DatapickerCurve*>(const_cast<AbstractAspect*>(aspect)); 0155 0156 int index = -1; 0157 if (m_activeCurve) { 0158 //if one of the curves is currently selected, select the image with the plot (the very first child) 0159 index = 0; 0160 emit statusInfo(i18n("%1, active curve \"%2\"", this->name(), m_activeCurve->name())); 0161 emit requestUpdateActions(); 0162 } else if (aspect) { 0163 const auto* curve = aspect->ancestor<const DatapickerCurve>(); 0164 index = indexOfChild<AbstractAspect>(curve); 0165 ++index; //+1 because of the hidden plot image being the first child and shown in the first tab in the view 0166 } 0167 0168 emit datapickerItemSelected(index); 0169 } 0170 0171 /*! 0172 this slot is called when a worksheet element is deselected in the project explorer. 0173 */ 0174 void Datapicker::childDeselected(const AbstractAspect* aspect) { 0175 Q_UNUSED(aspect); 0176 } 0177 0178 /*! 0179 * Emits the signal to select or to deselect the datapicker item (spreadsheet or image) with the index \c index 0180 * in the project explorer, if \c selected=true or \c selected=false, respectively. 0181 * The signal is handled in \c AspectTreeModel and forwarded to the tree view in \c ProjectExplorer. 0182 * This function is called in \c DatapickerView when the current tab was changed 0183 */ 0184 void Datapicker::setChildSelectedInView(int index, bool selected) { 0185 //select/deselect the datapicker itself if the first tab "representing" the plot image and the curves was selected in the view 0186 if (index == 0) { 0187 if (selected) 0188 emit childAspectSelectedInView(this); 0189 else { 0190 emit childAspectDeselectedInView(this); 0191 0192 //deselect also all curves (they don't have any tab index in the view) that were potentially selected before 0193 for (const auto* curve : children<const DatapickerCurve>()) 0194 emit childAspectDeselectedInView(curve); 0195 } 0196 0197 return; 0198 } 0199 0200 --index; //-1 because of the first tab in the view being reserved for the plot image and curves 0201 0202 //select/deselect the data spreadhseets 0203 auto spreadsheets = children<const Spreadsheet>(ChildIndexFlag::Recursive); 0204 const AbstractAspect* aspect = spreadsheets.at(index); 0205 if (selected) { 0206 emit childAspectSelectedInView(aspect); 0207 0208 //deselect the datapicker in the project explorer, if a child (spreadsheet or image) was selected. 0209 //prevents unwanted multiple selection with datapicker if it was selected before. 0210 emit childAspectDeselectedInView(this); 0211 } else { 0212 emit childAspectDeselectedInView(aspect); 0213 0214 //deselect also all children that were potentially selected before (columns of a spreadsheet) 0215 for (const auto* child : aspect->children<const AbstractAspect>()) 0216 emit childAspectDeselectedInView(child); 0217 } 0218 } 0219 0220 /*! 0221 Selects or deselects the datapicker or its current active curve in the project explorer. 0222 This function is called in \c DatapickerImageView. 0223 */ 0224 void Datapicker::setSelectedInView(const bool b) { 0225 if (b) 0226 emit childAspectSelectedInView(this); 0227 else 0228 emit childAspectDeselectedInView(this); 0229 } 0230 0231 void Datapicker::addNewPoint(QPointF pos, AbstractAspect* parentAspect) { 0232 auto points = parentAspect->children<DatapickerPoint>(ChildIndexFlag::IncludeHidden); 0233 0234 auto* newPoint = new DatapickerPoint(i18n("Point %1", points.count() + 1)); 0235 newPoint->setPosition(pos); 0236 newPoint->setHidden(true); 0237 0238 beginMacro(i18n("%1: add %2", parentAspect->name(), newPoint->name())); 0239 parentAspect->addChild(newPoint); 0240 newPoint->retransform(); 0241 0242 auto* datapickerCurve = static_cast<DatapickerCurve*>(parentAspect); 0243 if (m_image == parentAspect) { 0244 DatapickerImage::ReferencePoints axisPoints = m_image->axisPoints(); 0245 axisPoints.scenePos[points.count()].setX(pos.x()); 0246 axisPoints.scenePos[points.count()].setY(pos.y()); 0247 m_image->setAxisPoints(axisPoints); 0248 } else if (datapickerCurve) { 0249 newPoint->initErrorBar(datapickerCurve->curveErrorTypes()); 0250 datapickerCurve->updatePoint(newPoint); 0251 } 0252 0253 endMacro(); 0254 emit requestUpdateActions(); 0255 } 0256 0257 QVector3D Datapicker::mapSceneToLogical(QPointF point) const { 0258 return m_transform->mapSceneToLogical(point, m_image->axisPoints()); 0259 } 0260 0261 QVector3D Datapicker::mapSceneLengthToLogical(QPointF point) const { 0262 return m_transform->mapSceneLengthToLogical(point, m_image->axisPoints()); 0263 } 0264 0265 void Datapicker::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) { 0266 const auto* curve = qobject_cast<const DatapickerCurve*>(aspect); 0267 if (curve) { 0268 //clear scene 0269 auto points = curve->children<DatapickerPoint>(ChildIndexFlag::IncludeHidden); 0270 for (auto* point : points) 0271 handleChildAspectAboutToBeRemoved(point); 0272 0273 if (curve == m_activeCurve) { 0274 m_activeCurve = nullptr; 0275 emit statusInfo(QString()); 0276 } 0277 } else 0278 handleChildAspectAboutToBeRemoved(aspect); 0279 0280 emit requestUpdateActions(); 0281 } 0282 0283 void Datapicker::handleAspectAdded(const AbstractAspect* aspect) { 0284 const auto* addedPoint = qobject_cast<const DatapickerPoint*>(aspect); 0285 const auto* curve = qobject_cast<const DatapickerCurve*>(aspect); 0286 if (addedPoint) 0287 handleChildAspectAdded(addedPoint); 0288 else if (curve) { 0289 connect(m_image, &DatapickerImage::axisPointsChanged, curve, &DatapickerCurve::updatePoints); 0290 auto points = curve->children<DatapickerPoint>(ChildIndexFlag::IncludeHidden); 0291 for (auto* point : points) 0292 handleChildAspectAdded(point); 0293 } else 0294 return; 0295 0296 qreal zVal = 0; 0297 auto points = m_image->children<DatapickerPoint>(ChildIndexFlag::IncludeHidden); 0298 for (auto* point : points) 0299 point->graphicsItem()->setZValue(zVal++); 0300 0301 for (const auto* curve : children<DatapickerCurve>()) { 0302 for (auto* point : curve->children<DatapickerPoint>(ChildIndexFlag::IncludeHidden)) 0303 point->graphicsItem()->setZValue(zVal++); 0304 } 0305 0306 emit requestUpdateActions(); 0307 } 0308 0309 void Datapicker::handleChildAspectAboutToBeRemoved(const AbstractAspect* aspect) { 0310 const auto* removedPoint = qobject_cast<const DatapickerPoint*>(aspect); 0311 if (removedPoint) { 0312 QGraphicsItem* item = removedPoint->graphicsItem(); 0313 Q_ASSERT(item != nullptr); 0314 Q_ASSERT(m_image != nullptr); 0315 m_image->scene()->removeItem(item); 0316 } 0317 } 0318 0319 void Datapicker::handleChildAspectAdded(const AbstractAspect* aspect) { 0320 const auto* addedPoint = qobject_cast<const DatapickerPoint*>(aspect); 0321 if (addedPoint) { 0322 QGraphicsItem* item = addedPoint->graphicsItem(); 0323 Q_ASSERT(item != nullptr); 0324 Q_ASSERT(m_image != nullptr); 0325 m_image->scene()->addItem(item); 0326 } 0327 } 0328 0329 //############################################################################## 0330 //################## Serialization/Deserialization ########################### 0331 //############################################################################## 0332 0333 //! Save as XML 0334 void Datapicker::save(QXmlStreamWriter* writer) const { 0335 writer->writeStartElement( "datapicker" ); 0336 writeBasicAttributes(writer); 0337 writeCommentElement(writer); 0338 0339 //serialize all children 0340 for (auto* child : children<AbstractAspect>(ChildIndexFlag::IncludeHidden)) 0341 child->save(writer); 0342 0343 writer->writeEndElement(); // close "datapicker" section 0344 } 0345 0346 //! Load from XML 0347 bool Datapicker::load(XmlStreamReader* reader, bool preview) { 0348 if (!readBasicAttributes(reader)) 0349 return false; 0350 0351 while (!reader->atEnd()) { 0352 reader->readNext(); 0353 if (reader->isEndElement() && reader->name() == "datapicker") 0354 break; 0355 0356 if (!reader->isStartElement()) 0357 continue; 0358 0359 if (reader->name() == "comment") { 0360 if (!readCommentElement(reader)) 0361 return false; 0362 } else if (reader->name() == "datapickerImage") { 0363 auto* plot = new DatapickerImage(i18n("Plot"), true); 0364 if (!plot->load(reader, preview)) { 0365 delete plot; 0366 return false; 0367 } else { 0368 plot->setHidden(true); 0369 addChild(plot); 0370 m_image = plot; 0371 } 0372 } else if (reader->name() == "datapickerCurve") { 0373 auto* curve = new DatapickerCurve(QString()); 0374 if (!curve->load(reader, preview)) { 0375 delete curve; 0376 return false; 0377 } else 0378 addChild(curve); 0379 } else { // unknown element 0380 reader->raiseWarning(i18n("unknown datapicker element '%1'", reader->name().toString())); 0381 if (!reader->skipToEndElement()) return false; 0382 } 0383 } 0384 0385 for (auto* aspect : children<AbstractAspect>(ChildIndexFlag::IncludeHidden)) { 0386 for (auto* point : aspect->children<DatapickerPoint>(ChildIndexFlag::IncludeHidden)) 0387 handleAspectAdded(point); 0388 } 0389 0390 return true; 0391 }