00001 #include "function_editor.h"
00002 #include "custom_function.h"
00003 #include "plotwidget.h"
00004 #include <QDebug>
00005 #include <QMessageBox>
00006 #include <QFont>
00007 #include <QDomDocument>
00008 #include <QDomElement>
00009 #include <QFontDatabase>
00010 #include <QFile>
00011 #include <QMenu>
00012 #include <QAction>
00013 #include <QDir>
00014 #include <QFileDialog>
00015 #include <QFileInfo>
00016 #include <QSettings>
00017 #include <QByteArray>
00018 #include <QInputDialog>
00019
00020 AddCustomPlotDialog::AddCustomPlotDialog(PlotDataMapRef &plotMapData,
00021 const CustomPlotMap &mapped_custom_plots,
00022 QWidget *parent) :
00023 QDialog(parent),
00024 _plot_map_data(plotMapData),
00025 _custom_plots(mapped_custom_plots),
00026 ui(new Ui::FunctionEditor),
00027 _v_count(1)
00028 {
00029 ui->setupUi(this);
00030
00031 this->setWindowTitle("Create a custom timeseries");
00032 ui->mathEquation->setPlainText("return value*2");
00033
00034 const QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
00035 ui->globalVarsTextField->setFont(fixedFont);
00036 ui->mathEquation->setFont(fixedFont);
00037 ui->snippetTextEdit->setFont(fixedFont);
00038
00039 QStringList numericPlotNames;
00040 for(const auto &p : _plot_map_data.numeric)
00041 {
00042 QString name = QString::fromStdString(p.first);
00043 numericPlotNames.push_back(name);
00044 }
00045 numericPlotNames.sort(Qt::CaseInsensitive);
00046 for(const QString& name : numericPlotNames)
00047 {
00048 ui->combo_linkedChannel->addItem(name);
00049 ui->curvesListWidget->addItem(name);
00050 }
00051
00052 QSettings settings;
00053 QByteArray saved_xml = settings.value("AddCustomPlotDialog.savedXML", QByteArray() ).toByteArray();
00054 restoreGeometry(settings.value("AddCustomPlotDialog.geometry").toByteArray());
00055
00056 if( saved_xml.isEmpty() )
00057 {
00058 QFile file("://resources/default.snippets.xml");
00059 if(!file.open(QIODevice::ReadOnly))
00060 {
00061 throw std::runtime_error( "problem with default.snippets.xml" );
00062 }
00063 saved_xml = file.readAll();
00064 }
00065
00066 importSnippets(saved_xml);
00067
00068 ui->snippetsListRecent->setContextMenuPolicy(Qt::CustomContextMenu);
00069
00070 connect(ui->snippetsListRecent, &QListWidget::customContextMenuRequested,
00071 this,&AddCustomPlotDialog::recentContextMenu );
00072
00073 ui->snippetsListSaved->setContextMenuPolicy(Qt::CustomContextMenu);
00074
00075 connect(ui->snippetsListSaved, &QListWidget::customContextMenuRequested,
00076 this, &AddCustomPlotDialog::savedContextMenu );
00077
00078 ui->splitter->setStretchFactor(0,3);
00079 ui->splitter->setStretchFactor(1,2);
00080 }
00081
00082 AddCustomPlotDialog::~AddCustomPlotDialog()
00083 {
00084 QSettings settings;
00085 settings.setValue("AddCustomPlotDialog.savedXML", exportSnippets() );
00086 settings.setValue("AddCustomPlotDialog.geometry", saveGeometry());
00087 delete ui;
00088 }
00089
00090 void AddCustomPlotDialog::setLinkedPlotName(const QString &linkedPlotName)
00091 {
00092
00093 int idx = ui->combo_linkedChannel->findText(linkedPlotName);
00094 if (idx == -1)
00095 {
00096 idx = 0;
00097 ui->combo_linkedChannel->insertItem(idx, linkedPlotName);
00098 }
00099 ui->combo_linkedChannel->setCurrentIndex(idx);
00100 }
00101
00102 void AddCustomPlotDialog::setEditorMode(EditorMode mode)
00103 {
00104 ui->label_linkeChannel->setVisible( mode != FUNCTION_ONLY );
00105 ui->combo_linkedChannel->setVisible( mode != FUNCTION_ONLY );
00106 ui->pushButtonCreate->setVisible( mode != FUNCTION_ONLY );
00107 ui->pushButtonSave->setVisible( mode != TIMESERIES_ONLY );
00108 }
00109
00110 QString AddCustomPlotDialog::getLinkedData() const
00111 {
00112 return ui->combo_linkedChannel->currentText();
00113 }
00114
00115 QString AddCustomPlotDialog::getGlobalVars() const
00116 {
00117 return ui->globalVarsTextField->toPlainText();
00118 }
00119
00120 QString AddCustomPlotDialog::getEquation() const
00121 {
00122 return ui->mathEquation->toPlainText();
00123 }
00124
00125 QString AddCustomPlotDialog::getName() const
00126 {
00127 return ui->nameLineEdit->text();
00128 }
00129
00130 void AddCustomPlotDialog::editExistingPlot(CustomPlotPtr data)
00131 {
00132 ui->globalVarsTextField->setPlainText(data->globalVars());
00133 ui->mathEquation->setPlainText(data->function());
00134 setLinkedPlotName(QString::fromStdString(data->linkedPlotName()));
00135 ui->pushButtonCreate->setText("Update");
00136 ui->nameLineEdit->setText(QString::fromStdString(data->name()));
00137 ui->nameLineEdit->setEnabled(false);
00138
00139 _is_new = false;
00140 }
00141
00142 CustomPlotPtr AddCustomPlotDialog::getCustomPlotData() const
00143 {
00144 return _plot;
00145 }
00146
00147 void AddCustomPlotDialog::on_curvesListWidget_doubleClicked(const QModelIndex &index)
00148 {
00149 QString appendString = QString("V%1 = $$%2$$\n")
00150 .arg(_v_count++)
00151 .arg(ui->curvesListWidget->item(index.row())->text());
00152
00153 QPlainTextEdit* edit = ui->mathEquation;
00154
00155 if(ui->globalVarsTextField->hasFocus())
00156 {
00157 edit = ui->globalVarsTextField;
00158 }
00159
00160 if( !edit->toPlainText().endsWith("\n") )
00161 {
00162 edit->insertPlainText("\n");
00163 }
00164 edit->insertPlainText(appendString);
00165 }
00166
00167 void AddCustomPlotDialog::importSnippets(const QByteArray& xml_text)
00168 {
00169 ui->snippetsListSaved->clear();
00170
00171 _snipped_saved = GetSnippetsFromXML(xml_text);
00172
00173 for(const auto &it : _snipped_saved)
00174 {
00175 ui->snippetsListSaved->addItem( it.first );
00176 }
00177
00178 for( const auto& custom_it: _custom_plots)
00179 {
00180 const auto& math_plot = custom_it.second;
00181 SnippetData snippet;
00182 snippet.name = QString::fromStdString(math_plot->name());
00183
00184 if( _snipped_saved.count( snippet.name ) > 0 )
00185 {
00186 continue;
00187 }
00188
00189 snippet.globalVars = math_plot->globalVars();
00190 snippet.equation = math_plot->function();
00191 _snipped_recent.insert( {snippet.name, snippet } );
00192 ui->snippetsListRecent->addItem( snippet.name );
00193 }
00194 ui->snippetsListRecent->sortItems();
00195 ui->snippetsListSaved->sortItems();
00196 }
00197
00198 QByteArray AddCustomPlotDialog::exportSnippets() const
00199 {
00200 QDomDocument doc;
00201 auto root = ExportSnippets( _snipped_saved, doc);
00202 doc.appendChild( root );
00203 return doc.toByteArray(2);
00204 }
00205
00206
00207 void AddCustomPlotDialog::on_snippetsListSaved_currentRowChanged(int current_row)
00208 {
00209 if( current_row < 0 )
00210 {
00211 ui->snippetTextEdit->setPlainText("");
00212 return;
00213 }
00214 const auto& name = ui->snippetsListSaved->currentItem()->text();
00215 const SnippetData& snippet = _snipped_saved.at(name);
00216 QString desc = QString("%1\n\nfunction calc(time,value)\n{\n%2\n}").arg(snippet.globalVars).arg(snippet.equation);
00217 ui->snippetTextEdit->setPlainText(desc);
00218 }
00219
00220 void AddCustomPlotDialog::on_snippetsListSaved_doubleClicked(const QModelIndex &index)
00221 {
00222 const auto& name = ui->snippetsListSaved->item( index.row() )->text();
00223 const SnippetData& snippet = _snipped_saved.at(name);
00224
00225 ui->globalVarsTextField->setPlainText(snippet.globalVars);
00226 ui->mathEquation->setPlainText(snippet.equation);
00227 }
00228
00229 void AddCustomPlotDialog::on_snippetsListRecent_currentRowChanged(int current_row)
00230 {
00231 if( current_row < 0 )
00232 {
00233 ui->snippetTextEdit->setPlainText("");
00234 return;
00235 }
00236 const auto& name = ui->snippetsListRecent->currentItem()->text();
00237 const SnippetData& snippet = _snipped_recent.at(name);
00238
00239 QString desc = QString("%1\n\nfunction calc(time,value)\n{\n%2\n}")
00240 .arg(snippet.globalVars)
00241 .arg(snippet.equation);
00242 ui->snippetTextEdit->setPlainText(desc);
00243 }
00244
00245 void AddCustomPlotDialog::on_snippetsListRecent_doubleClicked(const QModelIndex &index)
00246 {
00247 const auto& name = ui->snippetsListRecent->item( index.row() )->text();
00248 const SnippetData& snippet = _snipped_recent.at(name);
00249
00250 ui->globalVarsTextField->setPlainText(snippet.globalVars);
00251 ui->mathEquation->setPlainText(snippet.equation);
00252 }
00253
00254 void AddCustomPlotDialog::recentContextMenu(const QPoint &pos)
00255 {
00256 auto list_recent = ui->snippetsListRecent;
00257
00258 if( list_recent->selectedItems().size() != 1)
00259 {
00260 return;
00261 }
00262
00263 auto list_saved = ui->snippetsListSaved;
00264
00265 auto item = list_recent->selectedItems().first();
00266 const auto& name = item->text();
00267
00268 QMenu menu;
00269 QAction* move_item = new QAction("Move to Saved", this);
00270 menu.addAction( move_item );
00271
00272 connect(move_item, &QAction::triggered,
00273 this, [=]()
00274 {
00275 auto snippet_it = _snipped_recent.find(name);
00276
00277 if( addToSaved( name, snippet_it->second ) )
00278 {
00279 _snipped_recent.erase( snippet_it );
00280 delete list_recent->takeItem( list_recent->row(item) );
00281 }
00282 });
00283
00284 menu.exec( list_recent->mapToGlobal(pos) );
00285 }
00286
00287 void AddCustomPlotDialog::savedContextMenu(const QPoint &pos)
00288 {
00289 auto list_saved = ui->snippetsListSaved;
00290
00291 if( list_saved->selectedItems().size() != 1)
00292 {
00293 return;
00294 }
00295
00296 QMenu menu;
00297
00298 QAction* rename_item = new QAction("Rename...", this);
00299 menu.addAction( rename_item );
00300
00301 connect(rename_item, &QAction::triggered, this, &AddCustomPlotDialog::onRenameSaved);
00302
00303 QAction* remove_item = new QAction("Remove", this);
00304 menu.addAction( remove_item );
00305
00306 connect(remove_item, &QAction::triggered,
00307 this, [list_saved, this]()
00308 {
00309 const auto& item = list_saved->selectedItems().first();
00310 _snipped_saved.erase( item->text() );
00311 delete list_saved->takeItem( list_saved->row(item) );
00312 });
00313
00314 menu.exec( list_saved->mapToGlobal(pos) );
00315 }
00316
00317 void AddCustomPlotDialog::on_nameLineEdit_textChanged(const QString &name)
00318 {
00319 ui->pushButtonCreate->setEnabled( !name.isEmpty() );
00320 ui->pushButtonSave->setEnabled( !name.isEmpty() );
00321
00322 if( _plot_map_data.numeric.count( name.toStdString() ) == 0)
00323 {
00324 ui->pushButtonCreate->setText("Create New Timeseries");
00325 }
00326 else{
00327 ui->pushButtonCreate->setText("Modify Timeseries");
00328 }
00329 }
00330
00331 void AddCustomPlotDialog::on_buttonLoadFunctions_clicked()
00332 {
00333 QSettings settings;
00334 QString directory_path = settings.value("AddCustomPlotDialog.loadDirectory",
00335 QDir::currentPath() ).toString();
00336
00337 QString fileName = QFileDialog::getOpenFileName(this, tr("Open Snippet Library"),
00338 directory_path,
00339 tr("Snippets (*.snippets.xml)"));
00340 if (fileName.isEmpty()){
00341 return;
00342 }
00343
00344 QFile file(fileName);
00345
00346 if ( !file.open(QIODevice::ReadOnly)) {
00347 QMessageBox::critical(this, "Error", QString("Failed to open the file [%1]").arg(fileName) );
00348 return;
00349 }
00350
00351 directory_path = QFileInfo(fileName).absolutePath();
00352 settings.setValue("AddCustomPlotDialog.loadDirectory", directory_path );
00353
00354 importSnippets( file.readAll() );
00355 }
00356
00357 void AddCustomPlotDialog::on_buttonSaveFunctions_clicked()
00358 {
00359 QSettings settings;
00360 QString directory_path = settings.value("AddCustomPlotDialog.loadDirectory",
00361 QDir::currentPath() ).toString();
00362
00363 QString fileName = QFileDialog::getSaveFileName(this, tr("Open Snippet Library"),
00364 directory_path,
00365 tr("Snippets (*.snippets.xml)"));
00366
00367 if (fileName.isEmpty()){
00368 return;
00369 }
00370 if( !fileName.endsWith(".snippets.xml") )
00371 {
00372 fileName.append(".snippets.xml");
00373 }
00374
00375 QFile file(fileName);
00376 if ( !file.open(QIODevice::WriteOnly)) {
00377 QMessageBox::critical(this, "Error", QString("Failed to open the file [%1]").arg(fileName));
00378 return;
00379 }
00380 auto data = exportSnippets();
00381
00382 file.write(data);
00383 file.close();
00384 }
00385
00386 void AddCustomPlotDialog::on_pushButtonSave_clicked()
00387 {
00388 QString name = ui->nameLineEdit->text();
00389
00390 SnippetData snippet;
00391 snippet.name = name;
00392 snippet.globalVars = ui->globalVarsTextField->toPlainText();
00393 snippet.equation = ui->mathEquation->toPlainText();
00394
00395 addToSaved( name, snippet);
00396
00397 on_snippetsListSaved_currentRowChanged( ui->snippetsListSaved->currentRow() );
00398 }
00399
00400 bool AddCustomPlotDialog::addToSaved(const QString& name, const SnippetData& snippet)
00401 {
00402 if( _snipped_saved.count(name) )
00403 {
00404 QMessageBox msgBox(this);
00405 msgBox.setWindowTitle("Warning");
00406 msgBox.setText(tr("A function with the same name exists already in the list of saved functions.\n"));
00407 msgBox.addButton(QMessageBox::Cancel);
00408 QPushButton* button = msgBox.addButton(tr("Overwrite"), QMessageBox::YesRole);
00409 msgBox.setDefaultButton(button);
00410
00411 int res = msgBox.exec();
00412
00413 if( res < 0 || res == QMessageBox::Cancel) {
00414 return false;
00415 }
00416 }
00417 else{
00418 ui->snippetsListSaved->addItem(name);
00419 ui->snippetsListSaved->sortItems();
00420 }
00421 _snipped_saved[name] = snippet;
00422 return true;
00423 }
00424
00425 void AddCustomPlotDialog::onRenameSaved()
00426 {
00427 auto list_saved = ui->snippetsListSaved;
00428 auto item = list_saved->selectedItems().first();
00429 const auto& name = item->text();
00430
00431 bool ok;
00432 QString new_name = QInputDialog::getText(this, tr("Change the name of the function"),
00433 tr("New name:"), QLineEdit::Normal,
00434 name, &ok);
00435
00436 if (!ok || new_name.isEmpty() || new_name == name)
00437 {
00438 return;
00439 }
00440
00441 SnippetData snippet = _snipped_saved[ name ];
00442 _snipped_saved.erase( name );
00443 snippet.name = new_name;
00444
00445 _snipped_saved.insert( {new_name, snippet} );
00446 item->setText( new_name );
00447 ui->snippetsListSaved->sortItems();
00448 }
00449
00450
00451 void AddCustomPlotDialog::on_pushButtonCreate_clicked()
00452 {
00453 try {
00454 std::string plotName = getName().toStdString();
00455
00456
00457 if(_plot_map_data.numeric.count(plotName) != 0 &&
00458 _custom_plots.count(plotName) == 0 )
00459 {
00460 throw std::runtime_error("plot name already exists and can't be modified");
00461 }
00462
00463 _plot = std::make_shared<CustomFunction>(getLinkedData().toStdString(),
00464 plotName,
00465 getGlobalVars(),
00466 getEquation());
00467 QDialog::accept();
00468 }
00469 catch (const std::runtime_error &e) {
00470 _plot.reset();
00471 QMessageBox::critical(this, "Error", "Failed to create math plot : "
00472 + QString::fromStdString(e.what()));
00473 QDialog::reject();
00474 }
00475 }
00476
00477 void AddCustomPlotDialog::on_lineEditFilter_textChanged(const QString &search_string)
00478 {
00479 QStringList spaced_items = search_string.split(' ');
00480
00481 for (int row=0; row < ui->curvesListWidget->count(); row++)
00482 {
00483 auto item = ui->curvesListWidget->item(row);
00484 QString name = item->text();
00485 bool toHide = false;
00486
00487 for (const auto& item: spaced_items)
00488 {
00489 if( !name.contains(item) )
00490 {
00491 toHide = true;
00492 break;
00493 }
00494 }
00495 ui->curvesListWidget->setRowHidden(row, toHide );
00496 }
00497 }