labplot  2.8.2
About: LabPlot is an application for plotting and analysis of 2D and 3D functions and data. It is a complete rewrite of LabPlot1 and lacks in the first release a lot of features available in the predecessor. On the other hand, the GUI and the usability is more superior.
  Fossies Dox: labplot-2.8.2.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

CantorWorksheet.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  File : CantorWorksheet.cpp
3  Project : LabPlot
4  Description : Aspect providing a Cantor Worksheets for Multiple backends
5  --------------------------------------------------------------------
6  Copyright : (C) 2015 Garvit Khatri (garvitdelhi@gmail.com)
7  Copyright : (C) 2016 by Alexander Semke (alexander.semke@web.de)
8 
9  ***************************************************************************/
10 
11 /***************************************************************************
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * *
18  * This program is distributed in the hope that it will be useful, *
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
21  * GNU General Public License for more details. *
22  * *
23  * You should have received a copy of the GNU General Public License *
24  * along with this program; if not, write to the Free Software *
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
26  * Boston, MA 02110-1301 USA *
27  * *
28  ***************************************************************************/
29 #include "CantorWorksheet.h"
30 #include "VariableParser.h"
33 #include "backend/core/Project.h"
35 
36 #include <cantor/cantorlibs_version.h>
37 #include "3rdparty/cantor/cantor_part.h"
38 #include <cantor/worksheetaccess.h>
39 
40 #ifdef HAVE_NEW_CANTOR_LIBS
41 #include <cantor/panelpluginhandler.h>
42 #include <cantor/panelplugin.h>
43 #else
44 #include "3rdparty/cantor/panelpluginhandler.h"
45 #include "3rdparty/cantor/panelplugin.h"
46 #endif
47 
48 #include <QAction>
49 #include <QFileInfo>
50 #include <QModelIndex>
51 
52 #include <KLocalizedString>
53 #include <KMessageBox>
54 #include <KParts/ReadWritePart>
55 
56 CantorWorksheet::CantorWorksheet(const QString &name, bool loading)
57  : AbstractPart(name, AspectType::CantorWorksheet), m_backendName(name) {
58 
59  if (!loading)
60  init();
61 }
62 
63 /*!
64  initializes Cantor's part and plugins
65 */
66 bool CantorWorksheet::init(QByteArray* content) {
67  KPluginLoader loader(QLatin1String("cantorpart"));
68  KPluginFactory* factory = loader.factory();
69 
70  if (!factory) {
71  //we can only get to this here if we open a project having Cantor content and Cantor plugins were not found.
72  //return false here, a proper error message will be created in load() and propagated further.
73  WARN("Failed to load Cantor plugin:")
74  WARN("Cantor Part file name: " << STDSTRING(loader.fileName()))
75  WARN(" " << STDSTRING(loader.errorString()))
76  return false;
77  } else {
78  m_part = factory->create<KParts::ReadWritePart>(this, QVariantList() << m_backendName << QLatin1String("--noprogress"));
79  if (!m_part) {
80  DEBUG("Could not create the Cantor Part.")
81  return false;
82  }
83  m_worksheetAccess = m_part->findChild<Cantor::WorksheetAccessInterface*>(Cantor::WorksheetAccessInterface::Name);
84 
85  //load worksheet content if available
86  if (content)
87  m_worksheetAccess->loadWorksheetFromByteArray(content);
88 
89  connect(m_worksheetAccess, SIGNAL(modified()), this, SLOT(modified()));
90 
91  //Cantor's session
92  m_session = m_worksheetAccess->session();
93  connect(m_session, SIGNAL(statusChanged(Cantor::Session::Status)), this, SIGNAL(statusChanged(Cantor::Session::Status)));
94 
95  //variable model
96  m_variableModel = m_session->variableDataModel();
97  connect(m_variableModel, &QAbstractItemModel::dataChanged, this, &CantorWorksheet::dataChanged);
98  connect(m_variableModel, &QAbstractItemModel::rowsInserted, this, &CantorWorksheet::rowsInserted);
99  connect(m_variableModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &CantorWorksheet::rowsAboutToBeRemoved);
100  connect(m_variableModel, &QAbstractItemModel::modelReset, this, &CantorWorksheet::modelReset);
101 
102  //available plugins
103 #ifdef HAVE_NEW_CANTOR_LIBS
104  auto* handler = new Cantor::PanelPluginHandler(this);
105  handler->loadPlugins();
106  m_plugins = handler->activePluginsForSession(m_session, Cantor::PanelPluginHandler::PanelStates());
107  for (auto* plugin : m_plugins)
108  plugin->connectToShell(m_part);
109 #else
110  auto* handler = m_part->findChild<Cantor::PanelPluginHandler*>(QLatin1String("PanelPluginHandler"));
111  if (!handler) {
112  KMessageBox::error(nullptr, i18n("No PanelPluginHandle found for the Cantor Part."));
113  return false;
114  }
115  m_plugins = handler->plugins();
116 #endif
117  }
118 
119  return true;
120 }
121 
122 //SLots
123 void CantorWorksheet::dataChanged(const QModelIndex& index) {
124  const QString& name = m_variableModel->data(m_variableModel->index(index.row(), 0)).toString();
125  Column* col = child<Column>(name);
126  if (col) {
127  // Cantor::DefaultVariableModel::DataRole == 257
128  QVariant dataValue = m_variableModel->data(m_variableModel->index(index.row(), 1), 257);
129  if (dataValue.isNull())
130  dataValue = m_variableModel->data(m_variableModel->index(index.row(), 1));
131  const QString& value = dataValue.toString();
133  if (parser.isParsed())
134  col->replaceValues(0, parser.values());
135  }
136 
137 }
138 
139 void CantorWorksheet::rowsInserted(const QModelIndex& parent, int first, int last) {
140  Q_UNUSED(parent)
141  for (int i = first; i <= last; ++i) {
142  const QString& name = m_variableModel->data(m_variableModel->index(i, 0)).toString();
143  QVariant dataValue = m_variableModel->data(m_variableModel->index(i, 1), 257);
144  if (dataValue.isNull())
145  dataValue = m_variableModel->data(m_variableModel->index(i, 1));
146  const QString& value = dataValue.toString();
148  if (parser.isParsed()) {
149  Column* col = child<Column>(name);
150  if (col) {
151  col->replaceValues(0, parser.values());
152  } else {
153  col = new Column(name, parser.values());
154  col->setUndoAware(false);
155  addChild(col);
156 
157  //TODO: Cantor currently ignores the order of variables in the worksheets
158  //and adds new variables at the last position in the model.
159  //Fix this in Cantor and switch to insertChildBefore here later.
160  //insertChildBefore(col, child<Column>(i));
161  }
162  } else {
163  //the already existing variable doesn't contain any numerical values -> remove it
164  Column* col = child<Column>(name);
165  if (col)
166  removeChild(col);
167  }
168  }
169 
170  project()->setChanged(true);
171 }
172 
174  project()->setChanged(true);
175 }
176 
178  for (int i = 0; i < childCount<Column>(); ++i)
179  child<Column>(i)->remove();
180 }
181 
182 void CantorWorksheet::rowsAboutToBeRemoved(const QModelIndex & parent, int first, int last) {
183  Q_UNUSED(parent);
184 
185  for (int i = first; i <= last; ++i) {
186  const QString& name = m_variableModel->data(m_variableModel->index(first, 0)).toString();
187  Column* column = child<Column>(name);
188  if (column)
189  column->remove();
190  }
191 }
192 
194  return m_plugins;
195 }
196 
197 KParts::ReadWritePart* CantorWorksheet::part() {
198  return m_part;
199 }
200 
201 QIcon CantorWorksheet::icon() const {
202  if (m_session)
203  return QIcon::fromTheme(m_session->backend()->icon());
204  return QIcon();
205 }
206 
207 QWidget* CantorWorksheet::view() const {
208  if (!m_partView) {
209  m_view = new CantorWorksheetView(const_cast<CantorWorksheet*>(this));
210  m_view->setBaseSize(1500, 1500);
211  m_partView = m_view;
212  // connect(m_view, SIGNAL(statusInfo(QString)), this, SIGNAL(statusInfo(QString)));
213 
214  //set the current path in the session to the path of the project file
215  const Project* project = const_cast<CantorWorksheet*>(this)->project();
216  const QString& fileName = project->fileName();
217  if (!fileName.isEmpty()) {
218  QFileInfo fi(fileName);
219  m_session->setWorksheetPath(fi.filePath());
220  }
221  }
222  return m_partView;
223 }
224 
225 //! Return a new context menu.
226 /**
227  * The caller takes ownership of the menu.
228  */
230  QMenu* menu = AbstractPart::createContextMenu();
231  Q_ASSERT(menu);
232  emit requestProjectContextMenu(menu);
233  return menu;
234 }
235 
237  return this->m_backendName;
238 }
239 
240 //TODO
242  return false;
243 }
244 
246  m_part->action("file_print")->trigger();
247  return true;
248 }
249 
251  m_part->action("file_print_preview")->trigger();
252  return true;
253 }
254 
255 //##############################################################################
256 //################## Serialization/Deserialization ###########################
257 //##############################################################################
258 
259 //! Save as XML
260 void CantorWorksheet::save(QXmlStreamWriter* writer) const{
261  writer->writeStartElement("cantorWorksheet");
262  writeBasicAttributes(writer);
263  writeCommentElement(writer);
264 
265  //general
266  writer->writeStartElement( "general" );
267  writer->writeAttribute( "backend_name", m_backendName);
268  //TODO: save worksheet settings
269  writer->writeEndElement();
270 
271  //save the content of Cantor's worksheet
272  QByteArray content = m_worksheetAccess->saveWorksheetToByteArray();
273  writer->writeStartElement("worksheet");
274  writer->writeAttribute("content", content.toBase64());
275  writer->writeEndElement();
276 
277  //save columns(variables)
278  for (auto* col : children<Column>(ChildIndexFlag::IncludeHidden))
279  col->save(writer);
280 
281  writer->writeEndElement(); // close "cantorWorksheet" section
282 }
283 
284 //! Load from XML
285 bool CantorWorksheet::load(XmlStreamReader* reader, bool preview) {
286  //reset the status of the reader differentiating between
287  //"failed because of the missing CAS" and "failed because of the broken XML"
288  reader->setFailedCASMissing(false);
289 
290  if (!readBasicAttributes(reader))
291  return false;
292 
293  KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used");
294  QXmlStreamAttributes attribs;
295  bool rc = false;
296 
297  while (!reader->atEnd()) {
298  reader->readNext();
299  if (reader->isEndElement() && reader->name() == "cantorWorksheet")
300  break;
301 
302  if (!reader->isStartElement())
303  continue;
304 
305  if (reader->name() == "comment") {
306  if (!readCommentElement(reader))
307  return false;
308  } else if (!preview && reader->name() == "general") {
309  attribs = reader->attributes();
310 
311  m_backendName = attribs.value("backend_name").toString().trimmed();
312  if (m_backendName.isEmpty())
313  reader->raiseWarning(attributeWarning.subs("backend_name").toString());
314  } else if (!preview && reader->name() == "worksheet") {
315  attribs = reader->attributes();
316 
317  QString str = attribs.value("content").toString().trimmed();
318  if (str.isEmpty())
319  reader->raiseWarning(attributeWarning.subs("content").toString());
320 
321  QByteArray content = QByteArray::fromBase64(str.toLatin1());
322  rc = init(&content);
323  if (!rc) {
325 
326  //failed to load this object because of the missing CAS plugin
327  //and not because of the broken project XML. Set this flag to
328  //handle this case correctly.
329  //TODO: we also can fail in the limit in cases where Cantor's content is broken
330  //and not because of the missing CAS plugin. This also needs to be treated accrodingly...
331  reader->setFailedCASMissing(true);
332  return false;
333  }
334  } else if (!preview && reader->name() == "column") {
335  Column* column = new Column(QString());
336  column->setUndoAware(false);
337  if (!column->load(reader, preview)) {
338  delete column;
339  return false;
340  }
341  addChild(column);
342  } else { // unknown element
343  reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
344  if (!reader->skipToEndElement()) return false;
345  }
346  }
347 
348  return true;
349 }
AspectType
void setUndoAware(bool)
bool readCommentElement(XmlStreamReader *)
Load comment from an XML element.
@ IncludeHidden
Include aspects marked as "hidden" in numbering or listing children.
void addChild(AbstractAspect *)
Add the given Aspect to my list of children.
AbstractAspect * parent(AspectType type) const
In the parent-child hierarchy, return the first parent of type.
void remove()
Remove me from my parent's list of children.
QString name() const
void writeBasicAttributes(QXmlStreamWriter *) const
Save name and creation time to XML.
bool readBasicAttributes(XmlStreamReader *)
Load name and creation time from XML.
void writeCommentElement(QXmlStreamWriter *) const
Save the comment to XML.
void removeChild(AbstractAspect *)
Remove the given Aspect from my list of children.
virtual Project * project()
Return the Project this Aspect belongs to, or 0 if it is currently not part of one.
Base class of Aspects with MDI windows as views (AspectParts).
Definition: AbstractPart.h:36
QMenu * createContextMenu() override
Return AbstractAspect::createContextMenu() plus operations on the primary view.
QWidget * m_partView
Definition: AbstractPart.h:65
void dataChanged(const QModelIndex &)
bool init(QByteArray *content=nullptr)
QList< Cantor::PanelPlugin * > getPlugins()
bool load(XmlStreamReader *, bool preview) override
Load from XML.
QAbstractItemModel * m_variableModel
KParts::ReadWritePart * m_part
void save(QXmlStreamWriter *) const override
Save as XML.
void statusChanged(Cantor::Session::Status)
KParts::ReadWritePart * part()
QList< Cantor::PanelPlugin * > m_plugins
CantorWorksheet(const QString &name, bool loading=false)
QMenu * createContextMenu() override
Return a new context menu.
void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
CantorWorksheetView * m_view
bool exportView() const override
Cantor::Session * m_session
QWidget * view() const override
Construct a primary view on me.
Cantor::WorksheetAccessInterface * m_worksheetAccess
void rowsInserted(const QModelIndex &parent, int first, int last)
void requestProjectContextMenu(QMenu *)
QIcon icon() const override
Return an icon to be used for decorating my views.
bool printPreview() const override
bool printView() override
Aspect that manages a column.
Definition: Column.h:42
void replaceValues(int, const QVector< double > &) override
Replace a range of values.
Definition: Column.cpp:588
bool load(XmlStreamReader *, bool preview) override
Load the column from XML.
Definition: Column.cpp:1152
Represents a project.
Definition: Project.h:42
void setChanged(const bool value=true)
Definition: Project.cpp:193
XML stream parser that supports errors as well as warnings. This class also adds line and column numb...
void setFailedCASMissing(bool)
void raiseWarning(const QString &)
void raiseMissingCASWarning(const QString &)
#define WARN(x)
Definition: macros.h:43
#define STDSTRING(qstr)
Definition: macros.h:67
#define DEBUG(x)
Definition: macros.h:50
#define i18n(m)
Definition: nsl_common.h:38