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)  

OriginProjectParser.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  File : OriginProjectParser.h
3  Project : LabPlot
4  Description : parser for Origin projects
5  --------------------------------------------------------------------
6  Copyright : (C) 2017-2018 Alexander Semke (alexander.semke@web.de)
7  Copyright : (C) 2017-2019 Stefan Gerlach (stefan.gerlach@uni.kn)
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 
34 #include "backend/core/Project.h"
35 #include "backend/core/Workbook.h"
36 #include "backend/matrix/Matrix.h"
37 #include "backend/note/Note.h"
46 
47 #include <liborigin/OriginFile.h>
48 
49 #include <KLocalizedString>
50 
51 #include <QDir>
52 #include <QDateTime>
53 #include <QFontMetrics>
54 #include <QRegularExpression>
55 
56 /*!
57 \class OriginProjectParser
58 \brief parser for Origin projects.
59 
60 \ingroup datasources
61 */
62 
67 }
68 
69 bool OriginProjectParser::isOriginProject(const QString& fileName) {
70  //TODO add opju later when liborigin supports it
71  return fileName.endsWith(QLatin1String(".opj"), Qt::CaseInsensitive);
72 }
73 
74 void OriginProjectParser::setImportUnusedObjects(bool importUnusedObjects) {
75  m_importUnusedObjects = importUnusedObjects;
76 }
77 
79  m_originFile = new OriginFile((const char*)m_projectFileName.toLocal8Bit());
80  if (!m_originFile->parse()) {
81  delete m_originFile;
82  m_originFile = nullptr;
83  return false;
84  }
85 
86  for (unsigned int i = 0; i < m_originFile->spreadCount(); i++) {
87  const Origin::SpreadSheet& spread = m_originFile->spread(i);
88  if (spread.objectID < 0)
89  return true;
90  }
91  for (unsigned int i = 0; i < m_originFile->excelCount(); i++) {
92  const Origin::Excel& excel = m_originFile->excel(i);
93  if (excel.objectID < 0)
94  return true;
95  }
96  for (unsigned int i = 0; i < m_originFile->matrixCount(); i++) {
97  const Origin::Matrix& originMatrix = m_originFile->matrix(i);
98  if (originMatrix.objectID < 0)
99  return true;
100  }
101 
102  delete m_originFile;
103  m_originFile = nullptr;
104  return false;
105 }
106 
108  //TODO add opju later when liborigin supports it
109  static const QString extensions = "*.opj *.OPJ";
110  return extensions;
111 }
112 
113 unsigned int OriginProjectParser::findSpreadByName(const QString& name) {
114  for (unsigned int i = 0; i < m_originFile->spreadCount(); i++) {
115  const Origin::SpreadSheet& spread = m_originFile->spread(i);
116  if (spread.name == name.toStdString()) {
117  m_spreadNameList << name;
118  return i;
119  }
120  }
121  return 0;
122 }
123 unsigned int OriginProjectParser::findMatrixByName(const QString& name) {
124  for (unsigned int i = 0; i < m_originFile->matrixCount(); i++) {
125  const Origin::Matrix& originMatrix = m_originFile->matrix(i);
126  if (originMatrix.name == name.toStdString()) {
127  m_matrixNameList << name;
128  return i;
129  }
130  }
131  return 0;
132 }
133 unsigned int OriginProjectParser::findExcelByName(const QString& name) {
134  for (unsigned int i = 0; i < m_originFile->excelCount(); i++) {
135  const Origin::Excel& excel = m_originFile->excel(i);
136  if (excel.name == name.toStdString()) {
137  m_excelNameList << name;
138  return i;
139  }
140  }
141  return 0;
142 }
143 unsigned int OriginProjectParser::findGraphByName(const QString& name) {
144  for (unsigned int i = 0; i < m_originFile->graphCount(); i++) {
145  const Origin::Graph& graph = m_originFile->graph(i);
146  if (graph.name == name.toStdString()) {
147  m_graphNameList << name;
148  return i;
149  }
150  }
151  return 0;
152 }
153 unsigned int OriginProjectParser::findNoteByName(const QString& name) {
154  for (unsigned int i = 0; i < m_originFile->noteCount(); i++) {
155  const Origin::Note& originNote = m_originFile->note(i);
156  if (originNote.name == name.toStdString()) {
157  m_noteNameList << name;
158  return i;
159  }
160  }
161  return 0;
162 }
163 
164 //##############################################################################
165 //############## Deserialization from Origin's project tree ####################
166 //##############################################################################
167 bool OriginProjectParser::load(Project* project, bool preview) {
168  DEBUG("OriginProjectParser::load()");
169 
170  //read and parse the m_originFile-file
171  m_originFile = new OriginFile((const char*)m_projectFileName.toLocal8Bit());
172  if (!m_originFile->parse()) {
173  delete m_originFile;
174  m_originFile = nullptr;
175  return false;
176  }
177 
178  //Origin project tree and the iterator pointing to the root node
179  const tree<Origin::ProjectNode>* projectTree = m_originFile->project();
180  tree<Origin::ProjectNode>::iterator projectIt = projectTree->begin(projectTree->begin());
181 
182  m_spreadNameList.clear();
183  m_excelNameList.clear();
184  m_matrixNameList.clear();
185  m_graphNameList.clear();
186  m_noteNameList.clear();
187 
188  //convert the project tree from liborigin's representation to LabPlot's project object
189  project->setIsLoading(true);
190  if (projectIt.node) { // only opj files from version >= 6.0 do have project tree
191  DEBUG(" have a project tree");
192  QString name(QString::fromLatin1(projectIt->name.c_str()));
193  project->setName(name);
194  project->setCreationTime(creationTime(projectIt));
195  loadFolder(project, projectIt, preview);
196  } else { // for lower versions put all windows on rootfolder
197  DEBUG(" have no project tree");
198  int pos = m_projectFileName.lastIndexOf(QLatin1String("/")) + 1;
199  project->setName((const char*)m_projectFileName.mid(pos).toLocal8Bit());
200  }
201  // imports all loose windows (like prior version 6 which has no project tree)
202  handleLooseWindows(project, preview);
203 
204  //restore column pointers:
205  //1. extend the pathes to contain the parent structures first
206  //2. restore the pointers from the pathes
209  for (auto* curve : project->children<XYCurve>(AbstractAspect::ChildIndexFlag::Recursive)) {
210  curve->suppressRetransform(true);
211 
212  //x-column
213  QString spreadsheetName = curve->xColumnPath().left(curve->xColumnPath().indexOf(QLatin1Char('/')));
214  for (const auto* spreadsheet : spreadsheets) {
215  if (spreadsheet->name() == spreadsheetName) {
216  const QString& newPath = spreadsheet->parentAspect()->path() + '/' + curve->xColumnPath();
217  curve->setXColumnPath(newPath);
218 
219  for (auto* column : columns) {
220  if (!column)
221  continue;
222  if (column->path() == newPath) {
223  curve->setXColumn(column);
224  break;
225  }
226  }
227  break;
228  }
229  }
230 
231  //x-column
232  spreadsheetName = curve->yColumnPath().left(curve->yColumnPath().indexOf(QLatin1Char('/')));
233  for (const auto* spreadsheet : spreadsheets) {
234  if (spreadsheet->name() == spreadsheetName) {
235  const QString& newPath = spreadsheet->parentAspect()->path() + '/' + curve->yColumnPath();
236  curve->setYColumnPath(newPath);
237 
238  for (auto* column : columns) {
239  if (!column)
240  continue;
241  if (column->path() == newPath) {
242  curve->setYColumn(column);
243  break;
244  }
245  }
246  break;
247  }
248  }
249 
250  //TODO: error columns
251 
252 
253  curve->suppressRetransform(false);
254  }
255 
256  if (!preview) {
258  plot->setIsLoading(false);
259  plot->retransform();
260  }
261  }
262 
263  emit project->loaded();
264  project->setIsLoading(false);
265 
266  delete m_originFile;
267  m_originFile = nullptr;
268 
269  return true;
270 }
271 
273  DEBUG("OriginProjectParser::loadFolder()")
274  const tree<Origin::ProjectNode>* projectTree = m_originFile->project();
275 
276  // do not skip anything if pathesToLoad() contains only root folder
277  bool containsRootFolder = (folder->pathesToLoad().size() == 1 && folder->pathesToLoad().contains(folder->path()));
278  if (containsRootFolder) {
279  DEBUG(" pathesToLoad contains only folder path \"" << STDSTRING(folder->path()) << "\". Clearing pathes to load.")
280  folder->setPathesToLoad(QStringList());
281  }
282 
283  //load folder's children: logic for reading the selected objects only is similar to Folder::readChildAspectElement
284  for (tree<Origin::ProjectNode>::sibling_iterator it = projectTree->begin(baseIt); it != projectTree->end(baseIt); ++it) {
285  QString name(QString::fromLatin1(it->name.c_str())); //name of the current child
286  DEBUG(" * folder item name = " << STDSTRING(name))
287 
288  //check whether we need to skip the loading of the current child
289  if (!folder->pathesToLoad().isEmpty()) {
290  //child's path is not available yet (child not added yet) -> construct the path manually
291  const QString childPath = folder->path() + '/' + name;
292  DEBUG(" path = " << STDSTRING(childPath))
293 
294  //skip the current child aspect it is not in the list of aspects to be loaded
295  if (folder->pathesToLoad().indexOf(childPath) == -1) {
296  DEBUG(" skip it!")
297  continue;
298  }
299  }
300 
301  //load top-level children
302  AbstractAspect* aspect = nullptr;
303  switch (it->type) {
305  DEBUG(" top level folder");
306  Folder* f = new Folder(name);
307 
308  if (!folder->pathesToLoad().isEmpty()) {
309  //a child folder to be read -> provide the list of aspects to be loaded to the child folder, too.
310  //since the child folder and all its children are not added yet (path() returns empty string),
311  //we need to remove the path of the current child folder from the full pathes provided in pathesToLoad.
312  //E.g. we want to import the path "Project/Folder/Spreadsheet" in the following project
313  // Project
314  // \Spreadsheet
315  // \Folder
316  // \Spreadsheet
317  //
318  //Here, we remove the part "Project/Folder/" and proceed for this child folder with "Spreadsheet" only.
319  //With this the logic above where it is determined whether to import the child aspect or not works out.
320 
321  //manually construct the path of the child folder to be read
322  const QString& curFolderPath = folder->path() + '/' + name;
323 
324  //remove the path of the current child folder
325  QStringList pathesToLoadNew;
326  for (const auto& path : folder->pathesToLoad()) {
327  if (path.startsWith(curFolderPath))
328  pathesToLoadNew << path.right(path.length() - curFolderPath.length());
329  }
330 
331  f->setPathesToLoad(pathesToLoadNew);
332  }
333 
334  loadFolder(f, it, preview);
335  aspect = f;
336  break;
337  }
339  DEBUG(" top level spreadsheet");
340  Spreadsheet* spreadsheet = new Spreadsheet(name);
341  loadSpreadsheet(spreadsheet, preview, name);
342  aspect = spreadsheet;
343  break;
344  }
346  DEBUG(" top level graph");
347  Worksheet* worksheet = new Worksheet(name);
348  worksheet->setIsLoading(true);
349  worksheet->setTheme(QString());
350  loadWorksheet(worksheet, preview);
351  aspect = worksheet;
352  break;
353  }
355  DEBUG(" top level matrix");
356  const Origin::Matrix& originMatrix = m_originFile->matrix(findMatrixByName(name));
357  DEBUG(" matrix name = " << originMatrix.name);
358  DEBUG(" number of sheets = " << originMatrix.sheets.size());
359  if (originMatrix.sheets.size() == 1) {
360  // single sheet -> load into a matrix
361  Matrix* matrix = new Matrix(name);
362  loadMatrix(matrix, preview);
363  aspect = matrix;
364  } else {
365  // multiple sheets -> load into a workbook
366  Workbook* workbook = new Workbook(name);
367  loadMatrixWorkbook(workbook, preview);
368  aspect = workbook;
369  }
370  break;
371  }
373  DEBUG(" top level excel");
374  Workbook* workbook = new Workbook(name);
375  loadWorkbook(workbook, preview);
376  aspect = workbook;
377  break;
378  }
380  DEBUG("top level note");
381  Note* note = new Note(name);
382  loadNote(note, preview);
383  aspect = note;
384  break;
385  }
387  default:
388  //TODO: add UnsupportedAspect
389  break;
390  }
391 
392  if (aspect) {
393  folder->addChildFast(aspect);
394  aspect->setCreationTime(creationTime(it));
395  aspect->setIsLoading(false);
396  }
397  }
398 
399  // ResultsLog
400  QString resultsLog = QString::fromLatin1(m_originFile->resultsLogString().c_str());
401  if (resultsLog.length() > 0) {
402  DEBUG("Results log:\t\tyes");
403  Note* note = new Note("ResultsLog");
404 
405  if (preview)
406  folder->addChildFast(note);
407  else {
408  //only import the log if it is in the list of aspects to be loaded
409  const QString childPath = folder->path() + '/' + note->name();
410  if (folder->pathesToLoad().indexOf(childPath) != -1) {
411  note->setText(resultsLog);
412  folder->addChildFast(note);
413  }
414  }
415  } else
416  DEBUG("Results log:\t\tno");
417 
418  return folder;
419 }
420 
421 void OriginProjectParser::handleLooseWindows(Folder* folder, bool preview) {
422  DEBUG("OriginProjectParser::handleLooseWindows()");
423  QDEBUG("pathes to load:" << folder->pathesToLoad());
424  m_spreadNameList.removeDuplicates();
425  m_excelNameList.removeDuplicates();
426  m_matrixNameList.removeDuplicates();
427  m_graphNameList.removeDuplicates();
428  m_noteNameList.removeDuplicates();
429  QDEBUG(" spreads =" << m_spreadNameList);
430  QDEBUG(" excels =" << m_excelNameList);
431  QDEBUG(" matrices =" << m_matrixNameList);
432  QDEBUG(" graphs =" << m_graphNameList);
433  QDEBUG(" notes =" << m_noteNameList);
434 
435  DEBUG("Number of spreads loaded:\t" << m_spreadNameList.size() << ", in file: " << m_originFile->spreadCount());
436  DEBUG("Number of excels loaded:\t" << m_excelNameList.size() << ", in file: " << m_originFile->excelCount());
437  DEBUG("Number of matrices loaded:\t" << m_matrixNameList.size() << ", in file: " << m_originFile->matrixCount());
438  DEBUG("Number of graphs loaded:\t" << m_graphNameList.size() << ", in file: " << m_originFile->graphCount());
439  DEBUG("Number of notes loaded:\t\t" << m_noteNameList.size() << ", in file: " << m_originFile->noteCount());
440 
441  // loop over all spreads to find loose ones
442  for (unsigned int i = 0; i < m_originFile->spreadCount(); i++) {
443  AbstractAspect* aspect = nullptr;
444  const Origin::SpreadSheet& spread = m_originFile->spread(i);
445  QString name = QString::fromStdString(spread.name);
446 
447  DEBUG(" spread.objectId = " << spread.objectID);
448  // skip unused spreads if selected
449  if (spread.objectID < 0 && !m_importUnusedObjects) {
450  DEBUG(" Dropping unused loose spread: " << STDSTRING(name));
451  continue;
452  }
453 
454  const QString childPath = folder->path() + '/' + name;
455  // we could also use spread.loose
456  if (!m_spreadNameList.contains(name) && (preview || folder->pathesToLoad().indexOf(childPath) != -1)) {
457  DEBUG(" Adding loose spread: " << STDSTRING(name));
458 
459  Spreadsheet* spreadsheet = new Spreadsheet(name);
460  loadSpreadsheet(spreadsheet, preview, name);
461  aspect = spreadsheet;
462  }
463  if (aspect) {
464  folder->addChildFast(aspect);
465  DEBUG(" creation time as reported by liborigin: " << spread.creationDate);
466  aspect->setCreationTime(QDateTime::fromTime_t(spread.creationDate));
467  }
468  }
469  // loop over all excels to find loose ones
470  for (unsigned int i = 0; i < m_originFile->excelCount(); i++) {
471  AbstractAspect* aspect = nullptr;
472  const Origin::Excel& excel = m_originFile->excel(i);
473  QString name = QString::fromStdString(excel.name);
474 
475  DEBUG(" excel.objectId = " << excel.objectID);
476  // skip unused data sets if selected
477  if (excel.objectID < 0 && !m_importUnusedObjects) {
478  DEBUG(" Dropping unused loose excel: " << STDSTRING(name));
479  continue;
480  }
481 
482  const QString childPath = folder->path() + '/' + name;
483  // we could also use excel.loose
484  if (!m_excelNameList.contains(name) && (preview || folder->pathesToLoad().indexOf(childPath) != -1)) {
485  DEBUG(" Adding loose excel: " << STDSTRING(name));
486  DEBUG(" containing number of sheets = " << excel.sheets.size());
487 
488  Workbook* workbook = new Workbook(name);
489  loadWorkbook(workbook, preview);
490  aspect = workbook;
491  }
492  if (aspect) {
493  folder->addChildFast(aspect);
494  DEBUG(" creation time as reported by liborigin: " << excel.creationDate);
495  aspect->setCreationTime(QDateTime::fromTime_t(excel.creationDate));
496  }
497  }
498  // loop over all matrices to find loose ones
499  for (unsigned int i = 0; i < m_originFile->matrixCount(); i++) {
500  AbstractAspect* aspect = nullptr;
501  const Origin::Matrix& originMatrix = m_originFile->matrix(i);
502  QString name = QString::fromStdString(originMatrix.name);
503 
504  DEBUG(" originMatrix.objectId = " << originMatrix.objectID);
505  // skip unused data sets if selected
506  if (originMatrix.objectID < 0 && !m_importUnusedObjects) {
507  DEBUG(" Dropping unused loose matrix: " << STDSTRING(name));
508  continue;
509  }
510 
511  const QString childPath = folder->path() + '/' + name;
512  if (!m_matrixNameList.contains(name) && (preview || folder->pathesToLoad().indexOf(childPath) != -1)) {
513  DEBUG(" Adding loose matrix: " << STDSTRING(name));
514  DEBUG(" containing number of sheets = " << originMatrix.sheets.size());
515  if (originMatrix.sheets.size() == 1) { // single sheet -> load into a matrix
516  Matrix* matrix = new Matrix(name);
517  loadMatrix(matrix, preview);
518  aspect = matrix;
519  } else { // multiple sheets -> load into a workbook
520  Workbook* workbook = new Workbook(name);
521  loadMatrixWorkbook(workbook, preview);
522  aspect = workbook;
523  }
524  }
525  if (aspect) {
526  folder->addChildFast(aspect);
527  aspect->setCreationTime(QDateTime::fromTime_t(originMatrix.creationDate));
528  }
529  }
530  // handle loose graphs (is this even possible?)
531  for (unsigned int i = 0; i < m_originFile->graphCount(); i++) {
532  AbstractAspect* aspect = nullptr;
533  const Origin::Graph& graph = m_originFile->graph(i);
534  QString name = QString::fromStdString(graph.name);
535 
536  DEBUG(" graph.objectId = " << graph.objectID);
537  // skip unused graph if selected
538  if (graph.objectID < 0 && !m_importUnusedObjects) {
539  DEBUG(" Dropping unused loose graph: " << STDSTRING(name));
540  continue;
541  }
542 
543  const QString childPath = folder->path() + '/' + name;
544  if (!m_graphNameList.contains(name) && (preview || folder->pathesToLoad().indexOf(childPath) != -1)) {
545  DEBUG(" Adding loose graph: " << STDSTRING(name));
546  Worksheet* worksheet = new Worksheet(name);
547  loadWorksheet(worksheet, preview);
548  aspect = worksheet;
549  }
550  if (aspect) {
551  folder->addChildFast(aspect);
552  aspect->setCreationTime(QDateTime::fromTime_t(graph.creationDate));
553  }
554  }
555  // handle loose notes (is this even possible?)
556  for (unsigned int i = 0; i < m_originFile->noteCount(); i++) {
557  AbstractAspect* aspect = nullptr;
558  const Origin::Note& originNote = m_originFile->note(i);
559  QString name = QString::fromStdString(originNote.name);
560 
561  DEBUG(" originNote.objectId = " << originNote.objectID);
562  // skip unused notes if selected
563  if (originNote.objectID < 0 && !m_importUnusedObjects) {
564  DEBUG(" Dropping unused loose note: " << STDSTRING(name));
565  continue;
566  }
567 
568  const QString childPath = folder->path() + '/' + name;
569  if (!m_noteNameList.contains(name) && (preview || folder->pathesToLoad().indexOf(childPath) != -1)) {
570  DEBUG(" Adding loose note: " << STDSTRING(name));
571  Note* note = new Note(name);
572  loadNote(note, preview);
573  aspect = note;
574  }
575  if (aspect) {
576  folder->addChildFast(aspect);
577  aspect->setCreationTime(QDateTime::fromTime_t(originNote.creationDate));
578  }
579  }
580 }
581 
582 bool OriginProjectParser::loadWorkbook(Workbook* workbook, bool preview) {
583  DEBUG("loadWorkbook()");
584  //load workbook sheets
585  const Origin::Excel& excel = m_originFile->excel(findExcelByName(workbook->name()));
586  DEBUG(" excel name = " << excel.name);
587  DEBUG(" number of sheets = " << excel.sheets.size());
588  for (unsigned int s = 0; s < excel.sheets.size(); ++s) {
589  Spreadsheet* spreadsheet = new Spreadsheet(QString::fromLatin1(excel.sheets[s].name.c_str()));
590  loadSpreadsheet(spreadsheet, preview, workbook->name(), s);
591  workbook->addChildFast(spreadsheet);
592  }
593 
594  return true;
595 }
596 
597 // load spreadsheet from spread (sheetIndex == -1) or from excel (only sheet sheetIndex)
598 bool OriginProjectParser::loadSpreadsheet(Spreadsheet* spreadsheet, bool preview, const QString& name, int sheetIndex) {
599  DEBUG("loadSpreadsheet() sheetIndex = " << sheetIndex);
600 
601  //load spreadsheet data
602  Origin::SpreadSheet spread;
603  Origin::Excel excel;
604  if (sheetIndex == -1) // spread
605  spread = m_originFile->spread(findSpreadByName(name));
606  else {
607  excel = m_originFile->excel(findExcelByName(name));
608  spread = excel.sheets[sheetIndex];
609  }
610 
611  const size_t cols = spread.columns.size();
612  int rows = 0;
613  for (size_t j = 0; j < cols; ++j)
614  rows = std::max((int)spread.columns[j].data.size(), rows);
615  // alternative: int rows = excel.maxRows;
616  DEBUG("loadSpreadsheet() cols/maxRows = " << cols << "/" << rows);
617 
618  //TODO QLocale locale = mw->locale();
619 
620  spreadsheet->setRowCount(rows);
621  spreadsheet->setColumnCount((int)cols);
622  if (sheetIndex == -1)
623  spreadsheet->setComment(QString::fromLatin1(spread.label.c_str()));
624  else
625  spreadsheet->setComment(QString::fromLatin1(excel.label.c_str()));
626 
627  //in Origin column width is measured in characters, we need to convert to pixels
628  //TODO: determine the font used in Origin in order to get the same column width as in Origin
629  QFont font;
630  QFontMetrics fm(font);
631  const int scaling_factor = fm.maxWidth();
632 
633  for (size_t j = 0; j < cols; ++j) {
634  Origin::SpreadColumn column = spread.columns[j];
635  Column* col = spreadsheet->column((int)j);
636 
637  QString name(column.name.c_str());
638  col->setName(name.replace(QRegExp(".*_"), QString()));
639 
640  if (preview)
641  continue;
642 
643  //TODO: we don't support any formulas for cells yet.
644 // if (column.command.size() > 0)
645 // col->setFormula(Interval<int>(0, rows), QString(column.command.c_str()));
646 
647  col->setComment(QString::fromLatin1(column.comment.c_str()));
648  col->setWidth((int)column.width * scaling_factor);
649 
650  //plot designation
651  switch (column.type) {
654  break;
657  break;
660  break;
663  break;
666  break;
669  default:
671  }
672 
673  QString format;
674  switch (column.valueType) {
675  case Origin::Numeric: {
676  for (unsigned int i = column.beginRow; i < column.endRow; ++i) {
677  const double value = column.data.at(i).as_double();
678  if (value != _ONAN)
679  col->setValueAt(i, value);
680  }
681 
682  loadColumnNumericFormat(column, col);
683  break;
684  }
685  case Origin::TextNumeric: {
686  //A TextNumeric column can contain numeric and string values, there is no equivalent column mode in LabPlot.
687  // -> Set the column mode as 'Numeric' or 'Text' depending on the type of first non-empty element in column.
688  for (unsigned int i = column.beginRow; i < column.endRow; ++i) {
689  const Origin::variant value(column.data.at(i));
690  if (value.type() == Origin::Variant::V_DOUBLE) {
691  if (value.as_double() != _ONAN)
692  break;
693  } else {
694  if (value.as_string() != nullptr) {
696  break;
697  }
698  }
699  }
700 
702  for (unsigned int i = column.beginRow; i < column.endRow; ++i) {
703  const double value = column.data.at(i).as_double();
704  if (column.data.at(i).type() == Origin::Variant::V_DOUBLE && value != _ONAN)
705  col->setValueAt(i, value);
706  }
707  loadColumnNumericFormat(column, col);
708  } else {
709  for (unsigned int i = column.beginRow; i < column.endRow; ++i) {
710  const Origin::variant value(column.data.at(i));
711  if (value.type() == Origin::Variant::V_STRING) {
712  if (value.as_string() != nullptr)
713  col->setTextAt(i, value.as_string());
714  } else {
715  if (value.as_double() != _ONAN)
716  col->setTextAt(i, QString::number(value.as_double()));
717  }
718  }
719  }
720  break;
721  }
722  case Origin::Text:
724  for (int i = 0; i < min((int)column.data.size(), rows); ++i)
725  col->setTextAt(i, column.data[i].as_string());
726  break;
727  case Origin::Time: {
728  switch (column.valueTypeSpecification + 128) {
729  case Origin::TIME_HH_MM:
730  format = "hh:mm";
731  break;
732  case Origin::TIME_HH:
733  format = "hh";
734  break;
736  format = "hh:mm:ss";
737  break;
739  format = "hh:mm:ss.zzz";
740  break;
741  case Origin::TIME_HH_AP:
742  format = "hh ap";
743  break;
745  format = "hh:mm ap";
746  break;
747  case Origin::TIME_MM_SS:
748  format = "mm:ss";
749  break;
751  format = "mm:ss.zzz";
752  break;
753  case Origin::TIME_HHMM:
754  format = "hhmm";
755  break;
756  case Origin::TIME_HHMMSS:
757  format = "hhmmss";
758  break;
760  format = "hh:mm:ss.zzz";
761  break;
762  }
763 
764  for (int i = 0; i < min((int)column.data.size(), rows); ++i)
765  col->setValueAt(i, column.data[i].as_double());
767 
768  auto* filter = static_cast<DateTime2StringFilter*>(col->outputFilter());
769  filter->setFormat(format);
770  break;
771  }
772  case Origin::Date: {
773  switch (column.valueTypeSpecification) {
775  format = "dd/MM/yyyy";
776  break;
778  format = "dd/MM/yyyy HH:mm";
779  break;
781  format = "dd/MM/yyyy HH:mm:ss";
782  break;
786  format = "dd.MM.yyyy";
787  break;
788  case Origin::DATE_MMM_D:
789  format = "MMM d";
790  break;
791  case Origin::DATE_M_D:
792  format = "M/d";
793  break;
794  case Origin::DATE_D:
795  format = 'd';
796  break;
797  case Origin::DATE_DDD:
799  format = "ddd";
800  break;
801  case Origin::DATE_YYYY:
802  format = "yyyy";
803  break;
804  case Origin::DATE_YY:
805  format = "yy";
806  break;
807  case Origin::DATE_YYMMDD:
812  format = "yyMMdd";
813  break;
814  case Origin::DATE_MMM:
816  format = "MMM";
817  break;
819  format = "M-d-yyyy";
820  break;
821  default:
822  format = "dd.MM.yyyy";
823  }
824 
825  for (int i = 0; i < min((int)column.data.size(), rows); ++i)
826  col->setValueAt(i, column.data[i].as_double());
828 
829  auto* filter = static_cast<DateTime2StringFilter*>(col->outputFilter());
830  filter->setFormat(format);
831  break;
832  }
833  case Origin::Month: {
834  switch (column.valueTypeSpecification) {
835  case Origin::MONTH_MMM:
836  format = "MMM";
837  break;
838  case Origin::MONTH_MMMM:
839  format = "MMMM";
840  break;
842  format = 'M';
843  break;
844  }
845 
846  for (int i = 0; i < min((int)column.data.size(), rows); ++i)
847  col->setValueAt(i, column.data[i].as_double());
849 
850  auto* filter = static_cast<DateTime2StringFilter*>(col->outputFilter());
851  filter->setFormat(format);
852  break;
853  }
854  case Origin::Day: {
855  switch (column.valueTypeSpecification) {
856  case Origin::DAY_DDD:
857  format = "ddd";
858  break;
859  case Origin::DAY_DDDD:
860  format = "dddd";
861  break;
862  case Origin::DAY_LETTER:
863  format = 'd';
864  break;
865  }
866 
867  for (int i = 0; i < min((int)column.data.size(), rows); ++i)
868  col->setValueAt(i, column.data[i].as_double());
870 
871  auto* filter = static_cast<DateTime2StringFilter*>(col->outputFilter());
872  filter->setFormat(format);
873  break;
874  }
877  case Origin::Categorical:
878  break;
879  }
880  }
881 
882  //TODO: "hidden" not supported yet
883 // if (spread.hidden || spread.loose)
884 // mw->hideWindow(spreadsheet);
885 
886  return true;
887 }
888 
890  if (originColumn.numericDisplayType != 0) {
891  int fi = 0;
892  switch (originColumn.valueTypeSpecification) {
893  case Origin::Decimal:
894  fi = 1;
895  break;
896  case Origin::Scientific:
897  fi = 2;
898  break;
899  case Origin::Engineering:
901  break;
902  }
903 
904  auto* filter = static_cast<Double2StringFilter*>(column->outputFilter());
905  filter->setNumericFormat(fi);
906  filter->setNumDigits(originColumn.decimalPlaces);
907  }
908 }
909 
910 bool OriginProjectParser::loadMatrixWorkbook(Workbook* workbook, bool preview) {
911  DEBUG("loadMatrixWorkbook()");
912  //load matrix workbook sheets
913  const Origin::Matrix& originMatrix = m_originFile->matrix(findMatrixByName(workbook->name()));
914  for (size_t s = 0; s < originMatrix.sheets.size(); ++s) {
915  Matrix* matrix = new Matrix(QString::fromLatin1(originMatrix.sheets[s].name.c_str()));
916  loadMatrix(matrix, preview, s, workbook->name());
917  workbook->addChildFast(matrix);
918  }
919 
920  return true;
921 }
922 
923 bool OriginProjectParser::loadMatrix(Matrix* matrix, bool preview, size_t sheetIndex, const QString& mwbName) {
924  DEBUG("loadMatrix()");
925  //import matrix data
926  const Origin::Matrix& originMatrix = m_originFile->matrix(findMatrixByName(mwbName));
927 
928  if (preview)
929  return true;
930 
931 
932  //in Origin column width is measured in characters, we need to convert to pixels
933  //TODO: determine the font used in Origin in order to get the same column width as in Origin
934  QFont font;
935  QFontMetrics fm(font);
936  const int scaling_factor = fm.maxWidth();
937 
938  const Origin::MatrixSheet& layer = originMatrix.sheets[sheetIndex];
939  const int colCount = layer.columnCount;
940  const int rowCount = layer.rowCount;
941 
942  matrix->setRowCount(rowCount);
943  matrix->setColumnCount(colCount);
944  matrix->setFormula(layer.command.c_str());
945 
946  //TODO: how to handle different widths for different columns?
947  for (int j = 0; j < colCount; j++)
948  matrix->setColumnWidth(j, layer.width * scaling_factor);
949 
950  //TODO: check column major vs. row major to improve the performance here
951  for (int i = 0; i < rowCount; i++) {
952  for (int j = 0; j < colCount; j++)
953  matrix->setCell(i, j, layer.data[j + i*colCount]);
954  }
955 
956  char format = 'g';
957  //TODO: prec not support by Matrix
958  //int prec = 6;
959  switch (layer.valueTypeSpecification) {
960  case 0: //Decimal 1000
961  format = 'f';
962  // prec = layer.decimalPlaces;
963  break;
964  case 1: //Scientific
965  format = 'e';
966  // prec = layer.decimalPlaces;
967  break;
968  case 2: //Engineering
969  case 3: //Decimal 1,000
970  format = 'g';
971  // prec = layer.significantDigits;
972  break;
973  }
974 
975  matrix->setNumericFormat(format);
976 
977  return true;
978 }
979 
980 bool OriginProjectParser::loadWorksheet(Worksheet* worksheet, bool preview) {
981  DEBUG("OriginProjectParser::loadWorksheet()");
982 
983  //load worksheet data
984  const Origin::Graph& graph = m_originFile->graph(findGraphByName(worksheet->name()));
985  DEBUG(" graph name = " << graph.name);
986  worksheet->setComment(graph.label.c_str());
987 
988  //TODO: width, height, view mode (print view, page view, window view, draft view)
989  //Origin allows to freely resize the window and ajusts the size of the plot (layer) automatically
990  //by keeping a certain width-to-height ratio. It's not clear what the actual size of the plot/layer is and how to handle this.
991  //For now we simply create a new wokrsheet here with it's default size and make it using the whole view size.
992  //Later we can decide to use one of the following properties:
993  // 1) Window.frameRect gives Rect-corner coordinates (in pixels) of the Window object
994  // 2) GraphLayer.clientRect gives Rect-corner coordinates (pixels) of the Layer inside the (printer?) page.
995  // 3) Graph.width, Graph.height give the (printer?) page size in pixels.
996 // const QRectF size(0, 0,
997 // Worksheet::convertToSceneUnits(graph.width/600., Worksheet::Inch),
998 // Worksheet::convertToSceneUnits(graph.height/600., Worksheet::Inch));
999 // worksheet->setPageRect(size);
1000  worksheet->setUseViewSize(true);
1001 
1002  QHash<TextLabel*, QSizeF> textLabelPositions;
1003 
1004  // worksheet background color
1005  const Origin::ColorGradientDirection bckgColorGradient = graph.windowBackgroundColorGradient;
1006  const Origin::Color bckgBaseColor = graph.windowBackgroundColorBase;
1007  const Origin::Color bckgEndColor = graph.windowBackgroundColorEnd;
1008  worksheet->setBackgroundColorStyle(backgroundColorStyle(bckgColorGradient));
1009  switch (bckgColorGradient) {
1015  worksheet->setBackgroundFirstColor(color(bckgEndColor));
1016  worksheet->setBackgroundSecondColor(color(bckgBaseColor));
1017  break;
1019  break;
1024  worksheet->setBackgroundFirstColor(color(bckgBaseColor));
1025  worksheet->setBackgroundSecondColor(color(bckgEndColor));
1026  }
1027 
1028  //TODO: do we need changes on the worksheet layout?
1029 
1030  //add plots
1031  int index = 1;
1032  for (const auto& layer : graph.layers) {
1033  if (!layer.is3D()) {
1034  CartesianPlot* plot = new CartesianPlot(i18n("Plot%1", QString::number(index)));
1035  worksheet->addChildFast(plot);
1036  plot->setIsLoading(true);
1037  //TODO: width, height
1038 
1039  //background color
1040  const Origin::Color& regColor = layer.backgroundColor;
1041  if (regColor.type == Origin::Color::None)
1042  plot->plotArea()->setBackgroundOpacity(0);
1043  else
1044  plot->plotArea()->setBackgroundFirstColor(color(regColor));
1045 
1046  //border
1047  if (layer.borderType == Origin::BorderType::None)
1048  plot->plotArea()->setBorderPen(QPen(Qt::NoPen));
1049  else
1050  plot->plotArea()->setBorderPen(QPen(Qt::SolidLine));
1051 
1052  //ranges
1053  plot->setAutoScaleX(false);
1054  plot->setAutoScaleY(false);
1055  const Origin::GraphAxis& originXAxis = layer.xAxis;
1056  const Origin::GraphAxis& originYAxis = layer.yAxis;
1057  plot->setXMin(originXAxis.min);
1058  plot->setXMax(originXAxis.max);
1059  plot->setYMin(originYAxis.min);
1060  plot->setYMax(originYAxis.max);
1061 
1062  //scales
1063  switch (originXAxis.scale) {
1065  plot->setXScale(CartesianPlot::Scale::Linear);
1066  break;
1068  plot->setXScale(CartesianPlot::Scale::Log10);
1069  break;
1070  case Origin::GraphAxis::Ln:
1071  plot->setXScale(CartesianPlot::Scale::Ln);
1072  break;
1074  plot->setXScale(CartesianPlot::Scale::Log2);
1075  break;
1081  //TODO:
1082  plot->setXScale(CartesianPlot::Scale::Linear);
1083  break;
1084  }
1085 
1086  switch (originYAxis.scale) {
1088  plot->setYScale(CartesianPlot::Scale::Linear);
1089  break;
1091  plot->setYScale(CartesianPlot::Scale::Log10);
1092  break;
1093  case Origin::GraphAxis::Ln:
1094  plot->setYScale(CartesianPlot::Scale::Ln);
1095  break;
1097  plot->setYScale(CartesianPlot::Scale::Log2);
1098  break;
1104  //TODO:
1105  plot->setYScale(CartesianPlot::Scale::Linear);
1106  break;
1107  }
1108 
1109  //axes
1110  //x bottom
1111  if (layer.curves.size()) {
1112  Origin::GraphCurve originCurve = layer.curves[0];
1113  QString xColumnName = QString::fromLatin1(originCurve.xColumnName.c_str());
1114  //TODO: "Partikelgrö"
1115  DEBUG(" xColumnName = " << STDSTRING(xColumnName));
1116  QDEBUG(" UTF8 xColumnName = " << xColumnName.toUtf8());
1117  QString yColumnName = QString::fromLatin1(originCurve.yColumnName.c_str());
1118 
1119  if (!originXAxis.formatAxis[0].hidden) {
1120  Axis* axis = new Axis("x", Axis::Orientation::Horizontal);
1121  axis->setSuppressRetransform(true);
1122  axis->setPosition(Axis::Position::Bottom);
1123  plot->addChildFast(axis);
1124  loadAxis(originXAxis, axis, 0, xColumnName);
1125  axis->setSuppressRetransform(false);
1126  }
1127 
1128  //x top
1129  if (!originXAxis.formatAxis[1].hidden) {
1130  Axis* axis = new Axis("x top", Axis::Orientation::Horizontal);
1131  axis->setPosition(Axis::Position::Top);
1132  axis->setSuppressRetransform(true);
1133  plot->addChildFast(axis);
1134  loadAxis(originXAxis, axis, 1, xColumnName);
1135  axis->setSuppressRetransform(false);
1136  }
1137 
1138  //y left
1139  if (!originYAxis.formatAxis[0].hidden) {
1140  Axis* axis = new Axis("y", Axis::Orientation::Vertical);
1141  axis->setSuppressRetransform(true);
1142  axis->setPosition(Axis::Position::Left);
1143  plot->addChildFast(axis);
1144  loadAxis(originYAxis, axis, 0, yColumnName);
1145  axis->setSuppressRetransform(false);
1146  }
1147 
1148  //y right
1149  if (!originYAxis.formatAxis[1].hidden) {
1150  Axis* axis = new Axis("y right", Axis::Orientation::Vertical);
1151  axis->setSuppressRetransform(true);
1152  axis->setPosition(Axis::Position::Right);
1153  plot->addChildFast(axis);
1154  loadAxis(originYAxis, axis, 1, yColumnName);
1155  axis->setSuppressRetransform(false);
1156  }
1157  } else {
1158  //TODO: ?
1159  }
1160 
1161  //range breaks
1162  //TODO
1163 
1164  //add legend if available
1165  const Origin::TextBox& originLegend = layer.legend;
1166  const QString& legendText = QString::fromLatin1(originLegend.text.c_str());
1167  DEBUG(" legend text = " << STDSTRING(legendText));
1168  if (!originLegend.text.empty()) {
1169  auto* legend = new CartesianPlotLegend(plot, i18n("legend"));
1170 
1171  //Origin's legend uses "\l(...)" or "\L(...)" string to format the legend symbol
1172  // and "%(...) to format the legend text for each curve
1173  //s. a. https://www.originlab.com/doc/Origin-Help/Legend-ManualControl
1174  //the text before these formatting tags, if available, is interpreted as the legend title
1175  QString legendTitle;
1176 
1177  //search for the first occurrence of the legend symbol substring
1178  int index = legendText.indexOf(QLatin1String("\\l("), 0, Qt::CaseInsensitive);
1179  if (index != -1)
1180  legendTitle = legendText.left(index);
1181  else {
1182  //check legend text
1183  index = legendText.indexOf(QLatin1String("%("));
1184  if (index != -1)
1185  legendTitle = legendText.left(index);
1186  }
1187 
1188  legendTitle = legendTitle.trimmed();
1189  if (!legendTitle.isEmpty())
1190  legendTitle = parseOriginText(legendTitle);
1191 
1192  DEBUG(" legend title = " << STDSTRING(legendTitle));
1193  legend->title()->setText(legendTitle);
1194 
1195  //TODO: text color
1196  //const Origin::Color& originColor = originLegend.color;
1197 
1198  //position
1199  //TODO: for the first release with OPJ support we put the legend to the bottom left corner,
1200  //in the next release we'll evaluate originLegend.clientRect giving the position inside of the whole page in Origin.
1201  //In Origin the legend can be placed outside of the plot which is not possible in LabPlot.
1202  //To achieve this we'll need to increase padding area in the plot and to place the legend outside of the plot area.
1206  legend->setPosition(position);
1207 
1208  //rotation
1209  legend->setRotationAngle(originLegend.rotation);
1210 
1211  //border line
1212  if (originLegend.borderType == Origin::BorderType::None)
1213  legend->setBorderPen(QPen(Qt::NoPen));
1214  else
1215  legend->setBorderPen(QPen(Qt::SolidLine));
1216 
1217  //background color, determine it with the help of the border type
1218  if (originLegend.borderType == Origin::BorderType::DarkMarble)
1219  legend->setBackgroundFirstColor(Qt::darkGray);
1220  else if (originLegend.borderType == Origin::BorderType::BlackOut)
1221  legend->setBackgroundFirstColor(Qt::black);
1222  else
1223  legend->setBackgroundFirstColor(Qt::white);
1224 
1225  plot->addLegend(legend);
1226  }
1227 
1228  //texts
1229  for (const auto& s : layer.texts) {
1230  DEBUG("EXTRA TEXT = " << s.text.c_str());
1231  TextLabel* label = new TextLabel("text label");
1232  label->setText(parseOriginText(QString::fromLatin1(s.text.c_str())));
1233  plot->addChild(label);
1234  label->setParentGraphicsItem(plot->graphicsItem());
1235 
1236  //position
1237  //determine the relative position inside of the layer rect
1238  const float horRatio = (float)(s.clientRect.left-layer.clientRect.left)/(layer.clientRect.right-layer.clientRect.left);
1239  const float vertRatio = (float)(s.clientRect.top-layer.clientRect.top)/(layer.clientRect.bottom-layer.clientRect.top);
1240  textLabelPositions[label] = QSizeF(horRatio, vertRatio);
1241  DEBUG("horizontal/vertical ratio = " << horRatio << ", " << vertRatio);
1242 
1243  //rotation
1244  label->setRotationAngle(s.rotation);
1245 
1246  //TODO:
1247 // Color color;
1248 // unsigned short fontSize;
1249 // int tab;
1250 // BorderType borderType;
1251 // Attach attach;
1252  }
1253 
1254  //curves
1255  int curveIndex = 1;
1256  for (const auto& originCurve : layer.curves) {
1257 
1258  QString data(originCurve.dataName.c_str());
1259  switch (data[0].toLatin1()) {
1260  case 'T':
1261  case 'E': {
1262  if (originCurve.type == Origin::GraphCurve::Line || originCurve.type == Origin::GraphCurve::Scatter || originCurve.type == Origin::GraphCurve::LineSymbol
1263  || originCurve.type == Origin::GraphCurve::ErrorBar || originCurve.type == Origin::GraphCurve::XErrorBar) {
1264 
1265  // parse and use legend text
1266  // find substring between %c{curveIndex} and %c{curveIndex+1}
1267  int pos1 = legendText.indexOf(QString("\\c{%1}").arg(curveIndex)) + 5;
1268  int pos2 = legendText.indexOf(QString("\\c{%1}").arg(curveIndex+1));
1269  QString curveText = legendText.mid(pos1, pos2 - pos1);
1270  // replace %(1), %(2), etc. with curve name
1271  curveText.replace(QString("%(%1)").arg(curveIndex), QString::fromLatin1(originCurve.yColumnName.c_str()));
1272  curveText = curveText.trimmed();
1273  DEBUG(" curve " << curveIndex << " text = " << STDSTRING(curveText));
1274 
1275  //XYCurve* xyCurve = new XYCurve(i18n("Curve%1", QString::number(curveIndex)));
1276  //TODO: curve (legend) does not support HTML text yet.
1277  //XYCurve* xyCurve = new XYCurve(curveText);
1278  XYCurve* curve = new XYCurve(QString::fromLatin1(originCurve.yColumnName.c_str()));
1279  const QString& tableName = data.right(data.length() - 2);
1280  curve->setXColumnPath(tableName + '/' + originCurve.xColumnName.c_str());
1281  curve->setYColumnPath(tableName + '/' + originCurve.yColumnName.c_str());
1282 
1283  curve->suppressRetransform(true);
1284  if (!preview)
1285  loadCurve(originCurve, curve);
1286  plot->addChildFast(curve);
1287  curve->suppressRetransform(false);
1288  } else if (originCurve.type == Origin::GraphCurve::Column) {
1289  //vertical bars
1290 
1291  } else if (originCurve.type == Origin::GraphCurve::Bar) {
1292  //horizontal bars
1293 
1294  } else if (originCurve.type == Origin::GraphCurve::Histogram) {
1295 
1296  }
1297  }
1298  break;
1299  case 'F': {
1300  Origin::Function function;
1301  const vector<Origin::Function>::difference_type funcIndex = m_originFile->functionIndex(data.right(data.length()-2).toStdString().c_str());
1302  if (funcIndex < 0) {
1303  ++curveIndex;
1304  continue;
1305  }
1306 
1307  function = m_originFile->function(funcIndex);
1308 
1309  XYEquationCurve* xyEqCurve = new XYEquationCurve(function.name.c_str());
1311 
1312  eqData.count = function.totalPoints;
1313  eqData.expression1 = QString(function.formula.c_str());
1314 
1315  if (function.type == Origin::Function::Polar) {
1317 
1318  //replace 'x' by 'phi'
1319  eqData.expression1 = eqData.expression1.replace('x', "phi");
1320 
1321  //convert from degrees to radians
1322  eqData.min = QString::number(function.begin/180) + QLatin1String("*pi");
1323  eqData.max = QString::number(function.end/180) + QLatin1String("*pi");
1324  } else {
1325  eqData.expression1 = QString(function.formula.c_str());
1326  eqData.min = QString::number(function.begin);
1327  eqData.max = QString::number(function.end);
1328  }
1329 
1330  xyEqCurve->suppressRetransform(true);
1331  xyEqCurve->setEquationData(eqData);
1332  if (!preview)
1333  loadCurve(originCurve, xyEqCurve);
1334  plot->addChildFast(xyEqCurve);
1335  xyEqCurve->suppressRetransform(false);
1336  }
1337  }
1338 
1339  ++curveIndex;
1340  }
1341  } else {
1342  //no support for 3D plots yet
1343  //TODO: add an "UnsupportedAspect" here
1344  }
1345 
1346  ++index;
1347  }
1348 
1349  if (!preview) {
1350  worksheet->updateLayout();
1351 
1352  //worksheet and plots got their sizes,
1353  //-> position all text labels inside the plots correctly by converting
1354  //the relative positions determined above to the absolute values
1355  QHash<TextLabel*, QSizeF>::const_iterator it = textLabelPositions.constBegin();
1356  while (it != textLabelPositions.constEnd()) {
1357  TextLabel* label = it.key();
1358  const QSizeF& ratios = it.value();
1359  const CartesianPlot* plot = static_cast<const CartesianPlot*>(label->parentAspect());
1360 
1361  TextLabel::PositionWrapper position = label->position();
1362  position.point.setX(plot->dataRect().width()*(ratios.width()-0.5));
1363  position.point.setY(plot->dataRect().height()*(ratios.height()-0.5));
1364  label->setPosition(position);
1365 
1366  ++it;
1367  }
1368  }
1369 
1370  return true;
1371 }
1372 
1373 /*
1374  * sets the axis properties (format and ticks) as defined in \c originAxis in \c axis,
1375  * \c index being 0 or 1 for "top" and "bottom" or "left" and "right" for horizontal or vertical axes, respectively.
1376  */
1377 void OriginProjectParser::loadAxis(const Origin::GraphAxis& originAxis, Axis* axis, int index, const QString& axisTitle) const {
1378 // int axisPosition;
1379 // possible values:
1380 // 0: Axis is at default position
1381 // 1: Axis is at (axisPositionValue)% from standard position
1382 // 2: Axis is at (axisPositionValue) position of orthogonal axis
1383 // double axisPositionValue;
1384 
1385 // bool zeroLine;
1386 // bool oppositeLine;
1387 
1388  //ranges
1389  axis->setStart(originAxis.min);
1390  axis->setEnd(originAxis.max);
1391 
1392  //ticks
1393  axis->setMajorTicksType(Axis::TicksType::Spacing);
1394  axis->setMajorTicksSpacing(originAxis.step);
1395  axis->setMinorTicksType(Axis::TicksType::TotalNumber);
1396  axis->setMinorTicksNumber(originAxis.minorTicks);
1397 
1398  //scale
1399  switch (originAxis.scale) {
1401  axis->setScale(Axis::Scale::Linear);
1402  break;
1404  axis->setScale(Axis::Scale::Log10);
1405  break;
1406  case Origin::GraphAxis::Ln:
1407  axis->setScale(Axis::Scale::Ln);
1408  break;
1410  axis->setScale(Axis::Scale::Log2);
1411  break;
1417  //TODO:
1418  axis->setScale(Axis::Scale::Linear);
1419  break;
1420  }
1421 
1422  //major grid
1423  const Origin::GraphGrid& majorGrid = originAxis.majorGrid;
1424  QPen gridPen = axis->majorGridPen();
1425  Qt::PenStyle penStyle(Qt::NoPen);
1426  if (!majorGrid.hidden) {
1427  switch (majorGrid.style) {
1429  penStyle = Qt::SolidLine;
1430  break;
1433  penStyle = Qt::DashLine;
1434  break;
1437  penStyle = Qt::DotLine;
1438  break;
1441  penStyle = Qt::DashDotLine;
1442  break;
1444  penStyle = Qt::DashDotDotLine;
1445  break;
1446  }
1447  }
1448  gridPen.setStyle(penStyle);
1449 
1450  Origin::Color gridColor;
1451  gridColor.type = Origin::Color::ColorType::Regular;
1452  gridColor.regular = majorGrid.color;
1453  gridPen.setColor(OriginProjectParser::color(gridColor));
1454  gridPen.setWidthF(Worksheet::convertToSceneUnits(majorGrid.width, Worksheet::Unit::Point));
1455  axis->setMajorGridPen(gridPen);
1456 
1457  //minor grid
1458  const Origin::GraphGrid& minorGrid = originAxis.minorGrid;
1459  gridPen = axis->minorGridPen();
1460  penStyle = Qt::NoPen;
1461  if (!minorGrid.hidden) {
1462  switch (minorGrid.style) {
1464  penStyle = Qt::SolidLine;
1465  break;
1468  penStyle = Qt::DashLine;
1469  break;
1472  penStyle = Qt::DotLine;
1473  break;
1476  penStyle = Qt::DashDotLine;
1477  break;
1479  penStyle = Qt::DashDotDotLine;
1480  break;
1481  }
1482  }
1483  gridPen.setStyle(penStyle);
1484 
1485  gridColor.regular = minorGrid.color;
1486  gridPen.setColor(OriginProjectParser::color(gridColor));
1487  gridPen.setWidthF(Worksheet::convertToSceneUnits(minorGrid.width, Worksheet::Unit::Point));
1488  axis->setMinorGridPen(gridPen);
1489 
1490  //process Origin::GraphAxisFormat
1491  const Origin::GraphAxisFormat& axisFormat = originAxis.formatAxis[index];
1492 
1493  QPen pen;
1495  color.type = Origin::Color::ColorType::Regular;
1496  color.regular = axisFormat.color;
1497  pen.setColor(OriginProjectParser::color(color));
1499  axis->setLinePen(pen);
1500 
1501  axis->setMajorTicksLength( Worksheet::convertToSceneUnits(axisFormat.majorTickLength, Worksheet::Unit::Point) );
1502  axis->setMajorTicksDirection( (Axis::TicksFlags) axisFormat.majorTicksType);
1503  axis->setMajorTicksPen(pen);
1504  axis->setMinorTicksLength( axis->majorTicksLength()/2); // minorTicksLength is half of majorTicksLength
1505  axis->setMinorTicksDirection( (Axis::TicksFlags) axisFormat.minorTicksType);
1506  axis->setMinorTicksPen(pen);
1507 
1508  QString titleText = parseOriginText(QString::fromLatin1(axisFormat.label.text.c_str()));
1509  DEBUG(" axis title text = " << STDSTRING(titleText));
1510  //TODO: parseOriginText() returns html formatted string. What is axisFormat.color used for?
1511  //TODO: use axisFormat.fontSize to override the global font size for the hmtl string?
1512  //TODO: convert special character here too
1513  DEBUG(" curve name = " << STDSTRING(axisTitle));
1514  titleText.replace("%(?X)", axisTitle);
1515  titleText.replace("%(?Y)", axisTitle);
1516  DEBUG(" axis title = " << STDSTRING(titleText));
1517  axis->title()->setText(titleText);
1518  axis->title()->setRotationAngle(axisFormat.label.rotation);
1519 
1520  axis->setLabelsPrefix(axisFormat.prefix.c_str());
1521  axis->setLabelsSuffix(axisFormat.suffix.c_str());
1522  //TODO: handle string factor member in GraphAxisFormat
1523 
1524  //process Origin::GraphAxisTick
1525  const Origin::GraphAxisTick& tickAxis = originAxis.tickAxis[index];
1526  if (tickAxis.showMajorLabels) {
1527  color.type = Origin::Color::ColorType::Regular;
1528  color.regular = tickAxis.color;
1529  axis->setLabelsColor(OriginProjectParser::color(color));
1530  //TODO: how to set labels position (top vs. bottom)?
1531  } else {
1532  axis->setLabelsPosition(Axis::LabelsPosition::NoLabels);
1533  }
1534 
1535  //TODO: handle ValueType valueType member in GraphAxisTick
1536  //TODO: handle int valueTypeSpecification in GraphAxisTick
1537 
1538  //precision
1539  if (tickAxis.decimalPlaces == -1)
1540  axis->setLabelsAutoPrecision(true);
1541  else {
1542  axis->setLabelsPrecision(tickAxis.decimalPlaces);
1543  axis->setLabelsAutoPrecision(false);
1544  }
1545 
1546  QFont font;
1547  //TODO: font family?
1548  font.setPixelSize( Worksheet::convertToSceneUnits(tickAxis.fontSize, Worksheet::Unit::Point) );
1549  font.setBold(tickAxis.fontBold);
1550  axis->setLabelsFont(font);
1551  //TODO: handle string dataName member in GraphAxisTick
1552  //TODO: handle string columnName member in GraphAxisTick
1553  axis->setLabelsRotationAngle(tickAxis.rotation);
1554 }
1555 
1556 void OriginProjectParser::loadCurve(const Origin::GraphCurve& originCurve, XYCurve* curve) const {
1557  //line properties
1558  QPen pen = curve->linePen();
1559  Qt::PenStyle penStyle(Qt::NoPen);
1560  if (originCurve.type == Origin::GraphCurve::Line || originCurve.type == Origin::GraphCurve::LineSymbol) {
1561  switch (originCurve.lineConnect) {
1563  curve->setLineType(XYCurve::LineType::NoLine);
1564  break;
1566  curve->setLineType(XYCurve::LineType::Line);
1567  break;
1569  curve->setLineType(XYCurve::LineType::Segments2);
1570  break;
1572  curve->setLineType(XYCurve::LineType::Segments3);
1573  break;
1577  curve->setLineType(XYCurve::LineType::SplineCubicNatural);
1578  break;
1580  curve->setLineType(XYCurve::LineType::StartHorizontal);
1581  break;
1583  curve->setLineType(XYCurve::LineType::StartVertical);
1584  break;
1586  curve->setLineType(XYCurve::LineType::MidpointHorizontal);
1587  break;
1589  curve->setLineType(XYCurve::LineType::MidpointVertical);
1590  break;
1591  }
1592 
1593  switch (originCurve.lineStyle) {
1595  penStyle = Qt::SolidLine;
1596  break;
1599  penStyle = Qt::DashLine;
1600  break;
1603  penStyle = Qt::DotLine;
1604  break;
1607  penStyle = Qt::DashDotLine;
1608  break;
1610  penStyle = Qt::DashDotDotLine;
1611  break;
1612  }
1613 
1614  pen.setStyle(penStyle);
1615  pen.setWidthF( Worksheet::convertToSceneUnits(originCurve.lineWidth, Worksheet::Unit::Point) );
1616  pen.setColor(color(originCurve.lineColor));
1617  curve->setLineOpacity(1 - originCurve.lineTransparency/255);
1618 
1619  //TODO: handle unsigned char boxWidth of Origin::GraphCurve
1620  }
1621  pen.setStyle(penStyle);
1622  curve->setLinePen(pen);
1623 
1624 
1625  //symbol properties
1626  if (originCurve.type == Origin::GraphCurve::Scatter || originCurve.type == Origin::GraphCurve::LineSymbol) {
1627  //try to map the different symbols, mapping is not exact
1628  curve->setSymbolsRotationAngle(0);
1629  switch (originCurve.symbolShape) {
1630  case 0: //NoSymbol
1631  curve->setSymbolsStyle(Symbol::Style::NoSymbols);
1632  break;
1633  case 1: //Rect
1634  curve->setSymbolsStyle(Symbol::Style::Square);
1635  break;
1636  case 2: //Ellipse
1637  case 20://Sphere
1638  curve->setSymbolsStyle(Symbol::Style::Circle);
1639  break;
1640  case 3: //UTriangle
1641  curve->setSymbolsStyle(Symbol::Style::EquilateralTriangle);
1642  break;
1643  case 4: //DTriangle
1644  curve->setSymbolsStyle(Symbol::Style::EquilateralTriangle);
1645  break;
1646  case 5: //Diamond
1647  curve->setSymbolsStyle(Symbol::Style::Diamond);
1648  break;
1649  case 6: //Cross +
1650  curve->setSymbolsStyle(Symbol::Style::Cross);
1651  break;
1652  case 7: //Cross x
1653  curve->setSymbolsStyle(Symbol::Style::Cross);
1654  break;
1655  case 8: //Snow
1656  curve->setSymbolsStyle(Symbol::Style::Star4);
1657  break;
1658  case 9: //Horizontal -
1659  curve->setSymbolsStyle(Symbol::Style::Line);
1660  curve->setSymbolsRotationAngle(90);
1661  break;
1662  case 10: //Vertical |
1663  curve->setSymbolsStyle(Symbol::Style::Line);
1664  break;
1665  case 15: //LTriangle
1666  curve->setSymbolsStyle(Symbol::Style::EquilateralTriangle);
1667  break;
1668  case 16: //RTriangle
1669  curve->setSymbolsStyle(Symbol::Style::EquilateralTriangle);
1670  break;
1671  case 17: //Hexagon
1672  case 19: //Pentagon
1673  curve->setSymbolsStyle(Symbol::Style::Square);
1674  break;
1675  case 18: //Star
1676  curve->setSymbolsStyle(Symbol::Style::Star5);
1677  break;
1678  default:
1679  curve->setSymbolsStyle(Symbol::Style::NoSymbols);
1680  }
1681 
1682  //symbol size
1683  curve->setSymbolsSize(Worksheet::convertToSceneUnits(originCurve.symbolSize, Worksheet::Unit::Point));
1684 
1685  //symbol fill color
1686  QBrush brush = curve->symbolsBrush();
1687  if (originCurve.symbolFillColor.type == Origin::Color::ColorType::Automatic) {
1688  //"automatic" color -> the color of the line, if available, has to be used, black otherwise
1689  if (curve->lineType() != XYCurve::LineType::NoLine)
1690  brush.setColor(curve->linePen().color());
1691  else
1692  brush.setColor(Qt::black);
1693  } else
1694  brush.setColor(color(originCurve.symbolFillColor));
1695 
1696  curve->setSymbolsBrush(brush);
1697 
1698  //symbol border/edge color and width
1699  QPen pen = curve->symbolsPen();
1700  if (originCurve.symbolColor.type == Origin::Color::ColorType::Automatic) {
1701  //"automatic" color -> the color of the line, if available, has to be used, black otherwise
1702  if (curve->lineType() != XYCurve::LineType::NoLine)
1703  pen.setColor(curve->linePen().color());
1704  else
1705  pen.setColor(Qt::black);
1706  } else
1707  pen.setColor(color(originCurve.symbolColor));
1708 
1709  //border width (edge thickness in Origin) is given by percentage of the symbol radius
1710  pen.setWidthF(originCurve.symbolThickness/100.*curve->symbolsSize()/2.);
1711 
1712  curve->setSymbolsPen(pen);
1713 
1714  //handle unsigned char pointOffset member
1715  //handle bool connectSymbols member
1716  } else {
1717  curve->setSymbolsStyle(Symbol::Style::NoSymbols);
1718  }
1719 
1720  //filling properties
1721  if (originCurve.fillArea) {
1722  //TODO: handle unsigned char fillAreaType;
1723  //with 'fillAreaType'=0x10 the area between the curve and the x-axis is filled
1724  //with 'fillAreaType'=0x14 the area included inside the curve is filled. First and last curve points are joined by a line to close the otherwise open area.
1725  //with 'fillAreaType'=0x12 the area excluded outside the curve is filled. The inverse of fillAreaType=0x14 is filled.
1726  //At the moment we only support the first type, so set it to XYCurve::FillingBelow
1727  curve->setFillingPosition(XYCurve::FillingPosition::Below);
1728 
1729  if (originCurve.fillAreaPattern == 0) {
1730  curve->setFillingType(PlotArea::BackgroundType::Color);
1731  } else {
1732  curve->setFillingType(PlotArea::BackgroundType::Pattern);
1733 
1734  //map different patterns in originCurve.fillAreaPattern (has the values of Origin::FillPattern) to Qt::BrushStyle;
1735  switch (originCurve.fillAreaPattern) {
1736  case 0:
1737  curve->setFillingBrushStyle(Qt::NoBrush);
1738  break;
1739  case 1:
1740  case 2:
1741  case 3:
1742  curve->setFillingBrushStyle(Qt::BDiagPattern);
1743  break;
1744  case 4:
1745  case 5:
1746  case 6:
1747  curve->setFillingBrushStyle(Qt::FDiagPattern);
1748  break;
1749  case 7:
1750  case 8:
1751  case 9:
1752  curve->setFillingBrushStyle(Qt::DiagCrossPattern);
1753  break;
1754  case 10:
1755  case 11:
1756  case 12:
1757  curve->setFillingBrushStyle(Qt::HorPattern);
1758  break;
1759  case 13:
1760  case 14:
1761  case 15:
1762  curve->setFillingBrushStyle(Qt::VerPattern);
1763  break;
1764  case 16:
1765  case 17:
1766  case 18:
1767  curve->setFillingBrushStyle(Qt::CrossPattern);
1768  break;
1769  }
1770  }
1771 
1772  curve->setFillingFirstColor(color(originCurve.fillAreaColor));
1773  curve->setFillingOpacity(1 - originCurve.fillAreaTransparency/255);
1774 
1775  //Color fillAreaPatternColor - color for the pattern lines, not supported
1776  //double fillAreaPatternWidth - width of the pattern lines, not supported
1777  //bool fillAreaWithLineTransparency - transparency of the pattern lines independent of the area transparency, not supported
1778 
1779  //TODO:
1780  //unsigned char fillAreaPatternBorderStyle;
1781  //Color fillAreaPatternBorderColor;
1782  //double fillAreaPatternBorderWidth;
1783  //The Border properties are used only in "Column/Bar" (histogram) plots. Those properties are:
1784  //fillAreaPatternBorderStyle for the line style (use enum Origin::LineStyle here)
1785  //fillAreaPatternBorderColor for the line color
1786  //fillAreaPatternBorderWidth for the line width
1787  } else
1788  curve->setFillingPosition(XYCurve::FillingPosition::NoFilling);
1789 }
1790 
1791 bool OriginProjectParser::loadNote(Note* note, bool preview) {
1792  DEBUG("OriginProjectParser::loadNote()");
1793  //load note data
1794  const Origin::Note& originNote = m_originFile->note(findNoteByName(note->name()));
1795 
1796  if (preview)
1797  return true;
1798 
1799  note->setComment(originNote.label.c_str());
1800  note->setNote(QString::fromLatin1(originNote.text.c_str()));
1801 
1802  return true;
1803 }
1804 
1805 //##############################################################################
1806 //########################### Helper functions ################################
1807 //##############################################################################
1809  //this logic seems to be correct only for the first node (project node). For other nodes the current time is returned.
1810  char time_str[21];
1811  strftime(time_str, sizeof(time_str), "%F %T", gmtime(&(*it).creationDate));
1812  return QDateTime::fromString(QString(time_str), Qt::ISODate);
1813 }
1814 
1815 QString OriginProjectParser::parseOriginText(const QString &str) const {
1816  DEBUG("parseOriginText()");
1817  QStringList lines = str.split('\n');
1818  QString text;
1819  for (int i = 0; i < lines.size(); ++i) {
1820  if (i > 0)
1821  text.append("<br>");
1822  text.append(parseOriginTags(lines[i]));
1823  }
1824 
1825  DEBUG(" PARSED TEXT = " << STDSTRING(text));
1826 
1827  return text;
1828 }
1829 
1831  switch (color.type) {
1832  case Origin::Color::ColorType::Regular:
1833  switch (color.regular) {
1834  case Origin::Color::Black:
1835  return QColor{Qt::black};
1836  case Origin::Color::Red:
1837  return QColor{Qt::red};
1838  case Origin::Color::Green:
1839  return QColor{Qt::green};
1840  case Origin::Color::Blue:
1841  return QColor{Qt::blue};
1842  case Origin::Color::Cyan:
1843  return QColor{Qt::cyan};
1845  return QColor{Qt::magenta};
1846  case Origin::Color::Yellow:
1847  return QColor{Qt::yellow};
1849  return QColor{Qt::darkYellow};
1850  case Origin::Color::Navy:
1851  return QColor{0, 0, 128};
1852  case Origin::Color::Purple:
1853  return QColor{128, 0, 128};
1854  case Origin::Color::Wine:
1855  return QColor{128, 0, 0};
1856  case Origin::Color::Olive:
1857  return QColor{0, 128, 0};
1859  return QColor{Qt::darkCyan};
1860  case Origin::Color::Royal:
1861  return QColor{0, 0, 160};
1862  case Origin::Color::Orange:
1863  return QColor{255, 128, 0};
1864  case Origin::Color::Violet:
1865  return QColor{128, 0, 255};
1866  case Origin::Color::Pink:
1867  return QColor{255, 0, 128};
1868  case Origin::Color::White:
1869  return QColor{Qt::white};
1871  return QColor{Qt::lightGray};
1872  case Origin::Color::Gray:
1873  return QColor{Qt::gray};
1875  return QColor{255, 0, 128};
1876  case Origin::Color::LTCyan:
1877  return QColor{128, 255, 255};
1879  return QColor{255, 128, 255};
1881  return QColor{Qt::darkGray};
1883  return QColor{Qt::black};
1884  }
1885  break;
1886  case Origin::Color::ColorType::Custom:
1887  return QColor{color.custom[0], color.custom[1], color.custom[2]};
1889  case Origin::Color::ColorType::Automatic:
1890  case Origin::Color::ColorType::Increment:
1891  case Origin::Color::ColorType::Indexing:
1892  case Origin::Color::ColorType::RGB:
1893  case Origin::Color::ColorType::Mapping:
1894  break;
1895  }
1896 
1897  return QColor(Qt::white);
1898 }
1899 
1901  switch (colorGradient) {
1922  }
1923 
1925 }
1926 
1927 QString strreverse(const QString &str) { //QString reversing
1928  QByteArray ba = str.toLocal8Bit();
1929  std::reverse(ba.begin(), ba.end());
1930 
1931  return QString(ba);
1932 }
1933 
1935  QList<QPair<QString, QString>> replacements;
1936 
1937  // TODO: probably missed some. Is there any generic method?
1938  replacements << qMakePair(QString("ä"), QString("&auml;"));
1939  replacements << qMakePair(QString("ö"), QString("&ouml;"));
1940  replacements << qMakePair(QString("ü"), QString("&uuml;"));
1941  replacements << qMakePair(QString("Ä"), QString("&Auml;"));
1942  replacements << qMakePair(QString("Ö"), QString("&Ouml;"));
1943  replacements << qMakePair(QString("Ü"), QString("&Uuml;"));
1944  replacements << qMakePair(QString("ß"), QString("&szlig;"));
1945  replacements << qMakePair(QString("€"), QString("&euro;"));
1946  replacements << qMakePair(QString("£"), QString("&pound;"));
1947  replacements << qMakePair(QString("¥"), QString("&yen;"));
1948  replacements << qMakePair(QString("¤"), QString("&curren;")); // krazy:exclude=spelling
1949  replacements << qMakePair(QString("¦"), QString("&brvbar;"));
1950  replacements << qMakePair(QString("§"), QString("&sect;"));
1951  replacements << qMakePair(QString("µ"), QString("&micro;"));
1952  replacements << qMakePair(QString("¹"), QString("&sup1;"));
1953  replacements << qMakePair(QString("²"), QString("&sup2;"));
1954  replacements << qMakePair(QString("³"), QString("&sup3;"));
1955  replacements << qMakePair(QString("¶"), QString("&para;"));
1956  replacements << qMakePair(QString("ø"), QString("&oslash;"));
1957  replacements << qMakePair(QString("æ"), QString("&aelig;"));
1958  replacements << qMakePair(QString("ð"), QString("&eth;"));
1959  replacements << qMakePair(QString("ħ"), QString("&hbar;"));
1960  replacements << qMakePair(QString("ĸ"), QString("&kappa;"));
1961  replacements << qMakePair(QString("¢"), QString("&cent;"));
1962  replacements << qMakePair(QString("¼"), QString("&frac14;"));
1963  replacements << qMakePair(QString("½"), QString("&frac12;"));
1964  replacements << qMakePair(QString("¾"), QString("&frac34;"));
1965  replacements << qMakePair(QString("¬"), QString("&not;"));
1966  replacements << qMakePair(QString("©"), QString("&copy;"));
1967  replacements << qMakePair(QString("®"), QString("&reg;"));
1968  replacements << qMakePair(QString("ª"), QString("&ordf;"));
1969  replacements << qMakePair(QString("º"), QString("&ordm;"));
1970  replacements << qMakePair(QString("±"), QString("&plusmn;"));
1971  replacements << qMakePair(QString("¿"), QString("&iquest;"));
1972  replacements << qMakePair(QString("×"), QString("&times;"));
1973  replacements << qMakePair(QString("°"), QString("&deg;"));
1974  replacements << qMakePair(QString("«"), QString("&laquo;"));
1975  replacements << qMakePair(QString("»"), QString("&raquo;"));
1976  replacements << qMakePair(QString("¯"), QString("&macr;"));
1977  replacements << qMakePair(QString("¸"), QString("&cedil;"));
1978  replacements << qMakePair(QString("À"), QString("&Agrave;"));
1979  replacements << qMakePair(QString("Á"), QString("&Aacute;"));
1980  replacements << qMakePair(QString("Â"), QString("&Acirc;"));
1981  replacements << qMakePair(QString("Ã"), QString("&Atilde;"));
1982  replacements << qMakePair(QString("Å"), QString("&Aring;"));
1983  replacements << qMakePair(QString("Æ"), QString("&AElig;"));
1984  replacements << qMakePair(QString("Ç"), QString("&Ccedil;"));
1985  replacements << qMakePair(QString("È"), QString("&Egrave;"));
1986  replacements << qMakePair(QString("É"), QString("&Eacute;"));
1987  replacements << qMakePair(QString("Ê"), QString("&Ecirc;"));
1988  replacements << qMakePair(QString("Ë"), QString("&Euml;"));
1989  replacements << qMakePair(QString("Ì"), QString("&Igrave;"));
1990  replacements << qMakePair(QString("Í"), QString("&Iacute;"));
1991  replacements << qMakePair(QString("Î"), QString("&Icirc;"));
1992  replacements << qMakePair(QString("Ï"), QString("&Iuml;"));
1993  replacements << qMakePair(QString("Ð"), QString("&ETH;"));
1994  replacements << qMakePair(QString("Ñ"), QString("&Ntilde;"));
1995  replacements << qMakePair(QString("Ò"), QString("&Ograve;"));
1996  replacements << qMakePair(QString("Ó"), QString("&Oacute;"));
1997  replacements << qMakePair(QString("Ô"), QString("&Ocirc;"));
1998  replacements << qMakePair(QString("Õ"), QString("&Otilde;"));
1999  replacements << qMakePair(QString("Ù"), QString("&Ugrave;"));
2000  replacements << qMakePair(QString("Ú"), QString("&Uacute;"));
2001  replacements << qMakePair(QString("Û"), QString("&Ucirc;"));
2002  replacements << qMakePair(QString("Ý"), QString("&Yacute;"));
2003  replacements << qMakePair(QString("Þ"), QString("&THORN;"));
2004  replacements << qMakePair(QString("à"), QString("&agrave;"));
2005  replacements << qMakePair(QString("á"), QString("&aacute;"));
2006  replacements << qMakePair(QString("â"), QString("&acirc;"));
2007  replacements << qMakePair(QString("ã"), QString("&atilde;"));
2008  replacements << qMakePair(QString("å"), QString("&aring;"));
2009  replacements << qMakePair(QString("ç"), QString("&ccedil;"));
2010  replacements << qMakePair(QString("è"), QString("&egrave;"));
2011  replacements << qMakePair(QString("é"), QString("&eacute;"));
2012  replacements << qMakePair(QString("ê"), QString("&ecirc;"));
2013  replacements << qMakePair(QString("ë"), QString("&euml;"));
2014  replacements << qMakePair(QString("ì"), QString("&igrave;"));
2015  replacements << qMakePair(QString("í"), QString("&iacute;"));
2016  replacements << qMakePair(QString("î"), QString("&icirc;"));
2017  replacements << qMakePair(QString("ï"), QString("&iuml;"));
2018  replacements << qMakePair(QString("ñ"), QString("&ntilde;"));
2019  replacements << qMakePair(QString("ò"), QString("&ograve;"));
2020  replacements << qMakePair(QString("ó"), QString("&oacute;"));
2021  replacements << qMakePair(QString("ô"), QString("&ocirc;"));
2022  replacements << qMakePair(QString("õ"), QString("&otilde;"));
2023  replacements << qMakePair(QString("÷"), QString("&divide;"));
2024  replacements << qMakePair(QString("ù"), QString("&ugrave;"));
2025  replacements << qMakePair(QString("ú"), QString("&uacute;"));
2026  replacements << qMakePair(QString("û"), QString("&ucirc;"));
2027  replacements << qMakePair(QString("ý"), QString("&yacute;"));
2028  replacements << qMakePair(QString("þ"), QString("&thorn;"));
2029  replacements << qMakePair(QString("ÿ"), QString("&yuml;"));
2030  replacements << qMakePair(QString("Œ"), QString("&#338;"));
2031  replacements << qMakePair(QString("œ"), QString("&#339;"));
2032  replacements << qMakePair(QString("Š"), QString("&#352;"));
2033  replacements << qMakePair(QString("š"), QString("&#353;"));
2034  replacements << qMakePair(QString("Ÿ"), QString("&#376;"));
2035  replacements << qMakePair(QString("†"), QString("&#8224;"));
2036  replacements << qMakePair(QString("‡"), QString("&#8225;"));
2037  replacements << qMakePair(QString("…"), QString("&#8230;"));
2038  replacements << qMakePair(QString("‰"), QString("&#8240;"));
2039  replacements << qMakePair(QString("™"), QString("&#8482;"));
2040 
2041  return replacements;
2042 }
2043 
2044 QString OriginProjectParser::replaceSpecialChars(const QString& text) const {
2045  QString t = text;
2046  for (const auto& r : charReplacementList())
2047  t.replace(r.first, r.second);
2048  return t;
2049 }
2050 
2051 /*!
2052  * converts the string with Origin's syntax for text formatting/highlighting
2053  * to a string in the richtext/html format supported by Qt.
2054  * For the supported syntax, see:
2055  * https://www.originlab.com/doc/LabTalk/ref/Label-cmd
2056  * https://www.originlab.com/doc/Origin-Help/TextOb-Prop-Text-tab
2057  * https://doc.qt.io/qt-5/richtext-html-subset.html
2058  */
2059 QString OriginProjectParser::parseOriginTags(const QString& str) const {
2060  DEBUG("parseOriginTags()");
2061  DEBUG(" string: " << STDSTRING(str));
2062  QDEBUG(" UTF8 string: " << str.toUtf8());
2063  QString line = str;
2064 
2065  //replace %(...) tags
2066 // QRegExp rxcol("\\%\\‍(\\d+\\‍)");
2067 
2068  // replace \l(x) (plot legend tags) with \\c{x}, where x is a digit
2069  line.replace(QRegularExpression(QStringLiteral("\\\\\\s*l\\s*\\(\\s*(\\d+)\\s*\\)")), QStringLiteral("\\c{\\1}"));
2070 
2071  // replace umlauts etc.
2072  line = replaceSpecialChars(line);
2073 
2074  // replace tabs (not really supported)
2075  line.replace('\t', "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
2076 
2077  // In PCRE2 (which is what QRegularExpression uses) variable-length lookbehind is supposed to be
2078  // exprimental in Perl 5.30; which means it doesn't work at the moment, i.e. using a variable-length
2079  // negative lookbehind isn't valid syntax from QRegularExpression POV.
2080  // Ultimately we have to reverse the string and use a negative _lookahead_ instead.
2081  // The goal is to temporatily replace '(' and ')' that don't denote tags; this is so that we
2082  // can handle parenthesis that are inside the tag, e.g. '\b(bold (cf))', we want the '(cf)' part
2083  // to remain as is.
2084  const QRegularExpression nonTagsRe(R"(\)([^)(]*)\‍((?!\s*([buigs\+\-]|\d{1,3}\s*[pc]|[\w ]+\s*:\s*f)\s*\\))");
2085  QString linerev = strreverse(line);
2086  const QString lBracket = strreverse("&lbracket;");
2087  const QString rBracket = strreverse("&rbracket;");
2088  linerev.replace(nonTagsRe, rBracket + QStringLiteral("\\1") + lBracket);
2089 
2090  // change the line back to normal
2091  line = strreverse(linerev);
2092 
2093  //replace \-(...), \+(...), \b(...), \i(...), \u(...), \s(....), \g(...), \f:font(...),
2094  // \c'number'(...), \p'size'(...) tags with equivalent supported HTML syntax
2095  const QRegularExpression tagsRe(QStringLiteral("\\\\\\s*([-+bgisu]|f:(\\w[\\w ]+)|[pc]\\s*(\\d+))\\s*\\(([^()]+?)\\)"));
2096  QRegularExpressionMatch rmatch;
2097  while (line.contains(tagsRe, &rmatch)) {
2098  QString rep;
2099  const QString tagText = rmatch.captured(4);
2100  const QString marker = rmatch.captured(1);
2101  if (marker.startsWith(QLatin1Char('-'))) {
2102  rep = QStringLiteral("<sub>%1</sub>").arg(tagText);
2103  } else if (marker.startsWith(QLatin1Char('+'))) {
2104  rep = QStringLiteral("<sup>%1</sup>").arg(tagText);
2105  } else if (marker.startsWith(QLatin1Char('b'))) {
2106  rep = QStringLiteral("<b>%1</b>").arg(tagText);
2107  } else if (marker.startsWith(QLatin1Char('g'))) { // greek symbols e.g. α φ
2108  rep = QStringLiteral("<font face=Symbol>%1</font>").arg(tagText);
2109  } else if (marker.startsWith(QLatin1Char('i'))) {
2110  rep = QStringLiteral("<i>%1</i>").arg(tagText);
2111  } else if (marker.startsWith(QLatin1Char('s'))) {
2112  rep = QStringLiteral("<s>%1</s>").arg(tagText);
2113  } else if (marker.startsWith(QLatin1Char('u'))) {
2114  rep = QStringLiteral("<u>%1</u>").arg(tagText);
2115  } else if (marker.startsWith(QLatin1Char('f'))) {
2116  rep = QStringLiteral("<font face=\"%1\">%2</font>").arg(rmatch.captured(2).trimmed(), tagText);
2117  } else if (marker.startsWith(QLatin1Char('p'))) { // e.g. \p200(...), means use font-size 200%
2118  rep = QStringLiteral("<span style=\"font-size: %1%\">%2</span>").arg(rmatch.captured(3), tagText);
2119  } else if (marker.startsWith(QLatin1Char('c'))) {
2120  // e.g. \c12(...), set the text color to the corresponding color from
2121  // the color drop-down list in OriginLab
2122  const int colorIndex = rmatch.captured(3).toInt();
2123  Origin::Color c;
2124  c.type = Origin::Color::ColorType::Regular;
2125  c.regular = colorIndex <= 23 ? static_cast<Origin::Color::RegularColor>(colorIndex)
2126  : Origin::Color::RegularColor::Black;
2127  QColor color = OriginProjectParser::color(c);
2128  rep = QStringLiteral("<span style=\"color: %1\">%2</span>").arg(color.name(), tagText);
2129  }
2130  line.replace(rmatch.capturedStart(0), rmatch.capturedLength(0), rep);
2131  }
2132 
2133  // put non-tag '(' and ')' back in their places
2134  line.replace("&lbracket;", "(");
2135  line.replace("&rbracket;", ")");
2136 
2137  // special characters
2138  line.replace(QRegularExpression(QStringLiteral("\\\\\\((\\d+)\\)")), "&#\\1;");
2139 
2140  DEBUG(" result: " << STDSTRING(line));
2141 
2142  return line;
2143 }
@ CartesianPlotLegend
static const QRgb white
Definition: ImageEditor.cpp:37
static const QRgb black
Definition: ImageEditor.cpp:38
#define _ONAN
Definition: OriginObj.h:41
QString strreverse(const QString &str)
Base class of all persistent objects in a Project.
void setIsLoading(bool)
@ Recursive
Recursively handle all descendents, not just immediate children.
void addChildFast(AbstractAspect *)
Add the given Aspect to my list of children without any checks and without putting this step onto the...
void addChild(AbstractAspect *)
Add the given Aspect to my list of children.
void setCreationTime(const QDateTime &)
QString name() const
bool setName(const QString &, bool autoUnique=true)
AbstractAspect::setName sets the name of the abstract aspect.
AbstractAspect * parentAspect() const
Return my parent Aspect or 0 if I currently don't have one.
virtual QString path() const
Return the path that leads from the top-most Aspect (usually a Project) to me.
QVector< AbstractAspect * > children(AspectType type, ChildIndexFlags flags={}) const
void setComment(const QString &)
PlotArea * plotArea()
Axis for cartesian coordinate systems.
Definition: Axis.h:42
TicksFlags
Definition: Axis.h:48
void setSuppressRetransform(bool)
Definition: Axis.cpp:362
A xy-plot.
Definition: CartesianPlot.h:58
QRectF dataRect() const
void addLegend(CartesianPlotLegend *)
Aspect that manages a column.
Definition: Column.h:42
void setColumnMode(AbstractColumn::ColumnMode) override
Set the column mode.
Definition: Column.cpp:289
void setValueAt(int, double) override
Set the content of row 'row'.
Definition: Column.cpp:579
void setPlotDesignation(AbstractColumn::PlotDesignation) override
Set the column plot designation.
Definition: Column.cpp:403
AbstractColumn::ColumnMode columnMode() const override
Return the column mode.
Definition: Column.cpp:1388
AbstractSimpleFilter * outputFilter() const
Definition: Column.cpp:1444
void setWidth(const int)
Set width.
Definition: Column.cpp:418
void setTextAt(int, const QString &) override
Set the content of row 'row'.
Definition: Column.cpp:523
Conversion filter QDateTime -> QString.
void setFormat(const QString &format)
Set the format string to be used for conversion.
Locale-aware conversion filter double -> QString.
void setNumericFormat(char format)
Set format character as in QString::number.
Folder in a project.
Definition: Folder.h:35
const QStringList & pathesToLoad() const
Definition: Folder.cpp:164
void setPathesToLoad(const QStringList &)
Definition: Folder.cpp:160
Definition: Matrix.h:41
void setColumnWidth(int col, int width)
This method should only be called by the view.
Definition: Matrix.cpp:603
void setCell(int row, int col, T value)
Set the value of the cell (needs explicit instantiation)
Definition: Matrix.cpp:446
Definition: Note.h:41
void setNote(const QString &)
Definition: Note.cpp:79
void setText(const QString &s)
Definition: Note.h:55
const tree< Origin::ProjectNode > * project() const
get project tree
Definition: OriginFile.cpp:178
vector< Origin::Graph >::size_type graphCount() const
get number of graphs
Definition: OriginFile.cpp:228
Origin::Note & note(vector< Origin::Note >::size_type n) const
get note n
Definition: OriginFile.cpp:243
Origin::Graph & graph(vector< Origin::Graph >::size_type g) const
get graph g
Definition: OriginFile.cpp:233
string resultsLogString() const
get Results Log
Definition: OriginFile.cpp:258
Origin::Excel & excel(vector< Origin::Excel >::size_type e) const
get excel e
Definition: OriginFile.cpp:253
bool parse()
parse Origin file
Definition: OriginFile.cpp:164
vector< Origin::Excel >::size_type excelCount() const
get number of excels
Definition: OriginFile.cpp:248
vector< Origin::Function >::difference_type functionIndex(const string &name) const
get index (or -1) of function named name
Definition: OriginFile.cpp:218
vector< Origin::Matrix >::size_type matrixCount() const
get number of matrices
Definition: OriginFile.cpp:203
vector< Origin::Note >::size_type noteCount() const
get number of notes
Definition: OriginFile.cpp:238
Origin::SpreadSheet & spread(vector< Origin::SpreadSheet >::size_type s) const
get spreadsheet s
Definition: OriginFile.cpp:198
vector< Origin::SpreadSheet >::size_type spreadCount() const
get number of spreadsheets
Definition: OriginFile.cpp:193
Origin::Matrix & matrix(vector< Origin::Matrix >::size_type m) const
get matrix m
Definition: OriginFile.cpp:208
Origin::Function & function(vector< Origin::Function >::size_type f) const
get function f
Definition: OriginFile.cpp:223
QColor color(Origin::Color) const
bool load(Project *, bool) override
QDateTime creationTime(tree< Origin::ProjectNode >::iterator) const
bool loadMatrix(Matrix *, bool preview, size_t sheetIndex=0, const QString &mwbName=QString())
QString parseOriginText(const QString &) const
QString replaceSpecialChars(const QString &) const
bool loadMatrixWorkbook(Workbook *, bool preview)
bool loadWorkbook(Workbook *, bool preview)
bool loadFolder(Folder *, tree< Origin::ProjectNode >::iterator, bool preview)
void loadAxis(const Origin::GraphAxis &, Axis *, int index, const QString &axisTitle=QString()) const
unsigned int findSpreadByName(const QString &)
unsigned int findExcelByName(const QString &)
unsigned int findMatrixByName(const QString &)
void loadCurve(const Origin::GraphCurve &, XYCurve *) const
unsigned int findNoteByName(const QString &)
QList< QPair< QString, QString > > charReplacementList() const
bool loadSpreadsheet(Spreadsheet *, bool preview, const QString &wbName=QString(), int sheetIndex=-1)
static bool isOriginProject(const QString &fileName)
unsigned int findGraphByName(const QString &)
QString parseOriginTags(const QString &) const
PlotArea::BackgroundColorStyle backgroundColorStyle(Origin::ColorGradientDirection) const
void loadColumnNumericFormat(const Origin::SpreadColumn &originColumn, Column *column) const
void handleLooseWindows(Folder *, bool preview)
static QString supportedExtensions()
bool loadNote(Note *, bool preview)
bool loadWorksheet(Worksheet *, bool preview)
BackgroundColorStyle
Definition: PlotArea.h:46
base class for project parsers
Definition: ProjectParser.h:40
QString m_projectFileName
Definition: ProjectParser.h:58
QList< AspectType > m_topLevelClasses
Definition: ProjectParser.h:60
Represents a project.
Definition: Project.h:42
void loaded()
Aspect providing a spreadsheet table with column logic.
Definition: Spreadsheet.h:40
Column * column(int index) const
void setColumnCount(int)
void setRowCount(int)
A label supporting rendering of html- and tex-formatted texts.
Definition: TextLabel.h:44
void setParentGraphicsItem(QGraphicsItem *)
Definition: TextLabel.cpp:160
void setPosition(QPointF)
Definition: TextLabel.cpp:265
Top-level container for Spreadsheet and Matrix.
Definition: Workbook.h:40
QGraphicsItem * graphicsItem() const override
Return the graphics item representing this element.
Top-level container for worksheet elements like plot, labels, etc.
Definition: Worksheet.h:46
void updateLayout()
Definition: Worksheet.cpp:445
void setTheme(const QString &)
Definition: Worksheet.cpp:724
static double convertToSceneUnits(const double value, const Worksheet::Unit unit)
Definition: Worksheet.cpp:113
A 2D-curve, provides an interface for editing many properties of the curve.
Definition: XYCurve.h:46
void suppressRetransform(bool)
Definition: XYCurve.cpp:752
A xy-curve defined by a mathematical equation.
tree_node * node
Definition: tree.hh:156
Depth-first iterator, first accessing the node, then its children.
Definition: tree.hh:162
Iterator which traverses only the nodes which are siblings of each other.
Definition: tree.hh:246
Definition: tree.hh:108
pre_order_iterator begin() const
Return iterator to the beginning of the tree.
Definition: tree.hh:644
pre_order_iterator end() const
Return iterator to the end of the tree.
Definition: tree.hh:650
#define STDSTRING(qstr)
Definition: macros.h:67
#define DEBUG(x)
Definition: macros.h:50
#define QDEBUG(x)
Definition: macros.h:47
@ DAY_LETTER
Definition: OriginObj.h:67
@ DAY_DDD
Definition: OriginObj.h:67
@ DAY_DDDD
Definition: OriginObj.h:67
@ Decimal
Definition: OriginObj.h:48
@ DecimalWithMarks
Definition: OriginObj.h:48
@ Scientific
Definition: OriginObj.h:48
@ Engineering
Definition: OriginObj.h:48
@ None
Definition: OriginObj.h:71
@ BlackOut
Definition: OriginObj.h:71
@ DarkMarble
Definition: OriginObj.h:71
ColorGradientDirection
Definition: OriginObj.h:75
@ NoGradient
Definition: OriginObj.h:75
@ BottomRight
Definition: OriginObj.h:75
@ Top
Definition: OriginObj.h:75
@ TopRight
Definition: OriginObj.h:75
@ TopLeft
Definition: OriginObj.h:75
@ Center
Definition: OriginObj.h:75
@ BottomLeft
Definition: OriginObj.h:75
@ Bottom
Definition: OriginObj.h:75
@ Right
Definition: OriginObj.h:75
@ Left
Definition: OriginObj.h:75
class Origin::Variant variant
@ MONTH_MMM
Definition: OriginObj.h:65
@ MONTH_MMMM
Definition: OriginObj.h:65
@ MONTH_LETTER
Definition: OriginObj.h:65
@ DATE_YYMMDD_HH_MM
Definition: OriginObj.h:61
@ DATE_M_D
Definition: OriginObj.h:60
@ DATE_DD_MM_YYYY
Definition: OriginObj.h:59
@ DATE_YYYY
Definition: OriginObj.h:60
@ DATE_D
Definition: OriginObj.h:60
@ DATE_YYMMDD_HH_MM_SS
Definition: OriginObj.h:61
@ DATE_DDD
Definition: OriginObj.h:60
@ DATE_YYMMDD
Definition: OriginObj.h:61
@ DATE_YYMMDD_HHMM
Definition: OriginObj.h:61
@ DATE_YYMMDD_HHMMSS
Definition: OriginObj.h:61
@ DATE_DDMMYYYY_HH_MM
Definition: OriginObj.h:60
@ DATE_YY
Definition: OriginObj.h:60
@ DATE_DD_MM_YYYY_HH_MM_SS
Definition: OriginObj.h:59
@ DATE_MMM_D
Definition: OriginObj.h:59
@ DATE_DDMMYYYY
Definition: OriginObj.h:59
@ DATE_DD_MM_YYYY_HH_MM
Definition: OriginObj.h:59
@ DATE_DAY_LETTER
Definition: OriginObj.h:60
@ DATE_M_D_YYYY
Definition: OriginObj.h:62
@ DATE_DDMMYYYY_HH_MM_SS
Definition: OriginObj.h:60
@ DATE_MMM
Definition: OriginObj.h:61
@ DATE_MONTH_LETTER
Definition: OriginObj.h:62
@ TIME_HH_MM_AP
Definition: OriginObj.h:52
@ TIME_HH
Definition: OriginObj.h:52
@ TIME_HH_MM
Definition: OriginObj.h:52
@ TIME_HHMMSS
Definition: OriginObj.h:53
@ TIME_HHMM
Definition: OriginObj.h:53
@ TIME_HH_AP
Definition: OriginObj.h:52
@ TIME_HH_MM_SS
Definition: OriginObj.h:52
@ TIME_HH_MM_SS_ZZ
Definition: OriginObj.h:52
@ TIME_MM_SS
Definition: OriginObj.h:52
@ TIME_HH_MM_SS_ZZZ
Definition: OriginObj.h:53
@ TIME_MM_SS_ZZ
Definition: OriginObj.h:53
@ TickIndexedDataset
Definition: OriginObj.h:45
@ Month
Definition: OriginObj.h:45
@ ColumnHeading
Definition: OriginObj.h:45
@ Categorical
Definition: OriginObj.h:45
@ TextNumeric
Definition: OriginObj.h:45
@ Date
Definition: OriginObj.h:45
@ Time
Definition: OriginObj.h:45
@ Numeric
Definition: OriginObj.h:45
@ Text
Definition: OriginObj.h:45
@ Day
Definition: OriginObj.h:45
#define i18n(m)
Definition: nsl_common.h:38
ColorType type
Definition: OriginObj.h:84
unsigned char regular
Definition: OriginObj.h:87
vector< SpreadSheet > sheets
Definition: OriginObj.h:305
unsigned char color
Definition: OriginObj.h:679
unsigned char color
Definition: OriginObj.h:695
unsigned short fontSize
Definition: OriginObj.h:699
GraphGrid majorGrid
Definition: OriginObj.h:720
unsigned char minorTicks
Definition: OriginObj.h:718
GraphAxisTick tickAxis[2]
Definition: OriginObj.h:723
GraphGrid minorGrid
Definition: OriginObj.h:721
GraphAxisFormat formatAxis[2]
Definition: OriginObj.h:722
unsigned char scale
Definition: OriginObj.h:719
unsigned char fillAreaPattern
Definition: OriginObj.h:603
unsigned char symbolThickness
Definition: OriginObj.h:619
unsigned char lineConnect
Definition: OriginObj.h:597
unsigned char fillAreaTransparency
Definition: OriginObj.h:605
unsigned char lineStyle
Definition: OriginObj.h:596
unsigned char lineTransparency
Definition: OriginObj.h:595
unsigned char type
Definition: OriginObj.h:588
unsigned char symbolShape
Definition: OriginObj.h:614
unsigned char style
Definition: OriginObj.h:672
unsigned char color
Definition: OriginObj.h:671
vector< GraphLayer > layers
Definition: OriginObj.h:958
unsigned short width
Definition: OriginObj.h:327
vector< double > data
Definition: OriginObj.h:331
unsigned short rowCount
Definition: OriginObj.h:320
unsigned short columnCount
Definition: OriginObj.h:321
vector< MatrixSheet > sheets
Definition: OriginObj.h:355
string text
Definition: OriginObj.h:978
vector< variant > data
Definition: OriginObj.h:266
unsigned int beginRow
Definition: OriginObj.h:264
unsigned int endRow
Definition: OriginObj.h:265
ValueType valueType
Definition: OriginObj.h:252
NumericDisplayType numericDisplayType
Definition: OriginObj.h:256
vector< SpreadColumn > columns
Definition: OriginObj.h:291
BorderType borderType
Definition: OriginObj.h:395
time_t creationDate
Definition: OriginObj.h:160
Color windowBackgroundColorEnd
Definition: OriginObj.h:164
Color windowBackgroundColorBase
Definition: OriginObj.h:163
string label
Definition: OriginObj.h:154
ColorGradientDirection windowBackgroundColorGradient
Definition: OriginObj.h:162
WorksheetElement::VerticalPosition verticalPosition
WorksheetElement::HorizontalPosition horizontalPosition