VPTissue Reference Manual
PTreeEditor.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2016 Universiteit Antwerpen
3  *
4  * Licensed under the EUPL, Version 1.1 or as soon they will be approved by
5  * the European Commission - subsequent versions of the EUPL (the "Licence");
6  * You may not use this work except in compliance with the Licence.
7  * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl5
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the Licence is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the Licence for the specific language governing
13  * permissions and limitations under the Licence.
14  */
20 #include "PTreeEditor.h"
21 
22 #include "gui/PTreeView.h"
23 #include "gui/qtmodel/PTreeModel.h"
25 
26 #include <QCloseEvent>
27 #include <QFileDialog>
28 #include <QInputDialog>
29 #include <QMenu>
30 #include <QMenuBar>
31 #include <QMessageBox>
32 #include <QStatusBar>
33 #include <QToolBar>
34 #include <boost/property_tree/ptree.hpp>
35 #include <boost/property_tree/xml_parser.hpp>
36 
37 using namespace std;
38 using namespace boost::property_tree;
39 using namespace boost::property_tree::xml_parser;
40 using namespace SimShell::Gui;
41 using namespace SimPT_Sim::Util;
42 
43 namespace SimPT_Shell {
44 namespace Gui {
45 
46 QString const PTreeEditor::g_caption("PTree Editor");
47 QString const PTreeEditor::g_caption_with_file("PTree Editor: %1");
48 
49 PTreeEditor::PTreeEditor(QWidget* parent)
50  : QMainWindow(parent),
51  HasUnsavedChangesPrompt("Editor")
52 {
53  setWindowTitle(g_caption);
54 
55  /* tree view */
56  ptree_view = new PTreeView(this);
57  setCentralWidget(ptree_view);
58 
59  /* no model until file is opened */
60  ptree_model = 0;
61 
62  /* status bar */
63  statusbar = new QStatusBar(this);
64  statusbar->clearMessage();
65  setStatusBar(statusbar);
66 
67  /* actions */
68  action_new = new QAction(QIcon::fromTheme("document-new"), "&New...", this);
69  action_open = new QAction(QIcon::fromTheme("document-open"), "&Open...", this);
70  action_save = new QAction(QIcon::fromTheme("document-save"), "&Save", this);
71  action_save_as = new QAction(QIcon::fromTheme("document-save-as"), "Save &As...", this);
72  action_close = new QAction("&Close", this);
73  action_quit = new QAction(QIcon::fromTheme("application-exit"), "&Quit", this);
74  action_view_toolbar = new QAction("&Toolbar", this);
75  action_view_statusbar = new QAction("&Statusbar", this);
76  action_fullscreen = new QAction(QIcon::fromTheme("view-fullscreen"), "&Fullscreen", this);
77  action_about = new QAction("&About", this);
78 
79  action_save->setEnabled(false);
80  action_save_as->setEnabled(false);
81  action_close->setEnabled(false);
82 
83  action_view_toolbar->setCheckable(true);
84  action_view_statusbar->setCheckable(true);
85  action_fullscreen->setCheckable(true);
86  action_view_toolbar->setChecked(true);
87  action_view_statusbar->setChecked(true);
88  action_fullscreen->setChecked(false);
89 
90  action_new->setShortcut(QKeySequence("Ctrl+N"));
91  action_open->setShortcut(QKeySequence("Ctrl+O"));
92  action_save->setShortcut(QKeySequence("Ctrl+S"));
93  action_save_as->setShortcut(QKeySequence("Shift+Ctrl+S"));
94  action_close->setShortcut(QKeySequence("Ctrl+W"));
95  action_quit->setShortcut(QKeySequence("Ctrl+Q"));
96  action_fullscreen->setShortcut(QKeySequence("F11"));
97 
98  /* borrow some actions from view and model */
99  QAction* action_undo = ptree_view->GetUndoAction();
100  QAction* action_redo = ptree_view->GetRedoAction();
101  QAction* action_cut = ptree_view->GetCutAction();
102  QAction* action_copy = ptree_view->GetCopyAction();
103  QAction* action_paste = ptree_view->GetPasteAction();
104  QAction* action_find = ptree_view->GetFindDialogAction();
105  QAction* action_clear_highlight = ptree_view->GetClearHighlightAction();
106  QAction* action_insert_before = ptree_view->GetInsertBeforeAction();
107  QAction* action_insert_child = ptree_view->GetInsertChildAction();
108  QAction* action_move_up = ptree_view->GetMoveUpAction();
109  QAction* action_move_down = ptree_view->GetMoveDownAction();
110  QAction* action_remove = ptree_view->GetRemoveAction();
111  QAction* action_expand_all = ptree_view->GetExpandAllAction();
112  QAction* action_expand_none = ptree_view->GetExpandNoneAction();
113 
114 
115  /* menus */
116  QMenuBar* menu = menuBar();
117 
118  QMenu* menu_file = new QMenu("&File", menu);
119  menu_file->addAction(action_new);
120  menu_file->addAction(action_open);
121  menu_file->addSeparator();
122  menu_file->addAction(action_save);
123  menu_file->addAction(action_save_as);
124  menu_file->addSeparator();
125  menu_file->addAction(action_close);
126  menu_file->addAction(action_quit);
127  menu->addMenu(menu_file);
128 
129  QMenu* menu_edit = new QMenu("&Edit", menu);
130  menu_edit->addAction(action_undo);
131  menu_edit->addAction(action_redo);
132  menu_edit->addSeparator();
133  menu_edit->addAction(action_cut);
134  menu_edit->addAction(action_copy);
135  menu_edit->addAction(action_paste);
136  menu->addMenu(menu_edit);
137 
138  QMenu* menu_item = new QMenu("&Item", menu);
139  menu_item->addAction(action_insert_before);
140  menu_item->addAction(action_insert_child);
141  menu_item->addSeparator();
142  menu_item->addAction(action_move_up);
143  menu_item->addAction(action_move_down);
144  menu_item->addSeparator();
145  menu_item->addAction(action_remove);
146  menu->addMenu(menu_item);
147 
148  QMenu* menu_view = new QMenu("&View", menu);
149  menu_view->addAction(action_expand_all);
150  menu_view->addAction(action_expand_none);
151  menu_view->addSeparator();
152  menu_view->addAction(action_view_toolbar);
153  menu_view->addAction(action_view_statusbar);
154  menu_view->addSeparator();
155  menu_view->addAction(action_fullscreen);
156  menu->addMenu(menu_view);
157 
158  QMenu* menu_search = new QMenu("&Search", menu);
159  menu_search->addAction(action_find);
160  menu_search->addSeparator();
161  menu_search->addAction(action_clear_highlight);
162  menu->addMenu(menu_search);
163 
164  QMenu* menu_help = new QMenu("&Help", menu);
165  menu_help->addAction(action_about);
166  menu->addMenu(menu_help);
167 
168  /* toolbar */
169  toolbar = new QToolBar(this);
170  toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
171  toolbar->setMovable(false);
172  toolbar->addAction(action_new);
173  toolbar->addAction(action_open);
174  toolbar->addAction(action_save);
175  toolbar->addSeparator();
176  toolbar->addAction(action_undo);
177  toolbar->addAction(action_redo);
178  toolbar->addSeparator();
179  toolbar->addAction(action_find);
180  toolbar->addSeparator();
181  toolbar->addAction(action_fullscreen);
182  addToolBar(toolbar);
183 
184  connect(action_new, SIGNAL(triggered()), this, SLOT(SLOT_New()));
185  connect(action_open, SIGNAL(triggered()), this, SLOT(SLOT_OpenDialog()));
186  connect(action_save, SIGNAL(triggered()), this, SLOT(SLOT_Save()));
187  connect(action_save_as, SIGNAL(triggered()), this, SLOT(SLOT_SaveAsDialog()));
188  connect(action_close, SIGNAL(triggered()), this, SLOT(SLOT_PromptClose()));
189  connect(action_quit, SIGNAL(triggered()), this, SLOT(close()));
190  connect(action_view_toolbar, SIGNAL(toggled(bool)), toolbar, SLOT(setVisible(bool)));
191  connect(action_view_statusbar, SIGNAL(toggled(bool)), statusbar, SLOT(setVisible(bool)));
192  connect(action_fullscreen, SIGNAL(toggled(bool)), this, SLOT(SLOT_SetFullscreenMode(bool)));
193  connect(action_about, SIGNAL(triggered()), this, SLOT(SLOT_AboutDialog()));
194  connect(ptree_view, SIGNAL(CleanChanged(bool)), this, SLOT(SLOT_SetClean(bool)));
195  connect(ptree_view, SIGNAL(StatusChanged(QString const &)), statusbar, SLOT(showMessage(QString const &)));
196 }
197 
198 void PTreeEditor::closeEvent(QCloseEvent *event)
199 {
200  if (PromptClose(this)) {
201  event->accept();
202  } else {
203  event->ignore();
204  }
205 }
206 
207 void PTreeEditor::InternalForceClose() {
208  if (ptree_model) {
209  ptree_view->setModel(nullptr);
210  delete ptree_model;
211  ptree_model = nullptr;
212 
213  action_save->setEnabled(false);
214  action_save_as->setEnabled(false);
215  action_close->setEnabled(false);
216  setWindowTitle(g_caption);
217  }
218 }
219 
220 bool PTreeEditor::InternalIsClean() const
221 {
222  return !IsOpened() || ptree_view->IsClean();
223 }
224 
225 bool PTreeEditor::InternalSave()
226 {
227  if (opened_path.empty())
228  return SLOT_SaveAsDialog();
229  else
230  return SavePath(opened_path);
231 }
232 
233 bool PTreeEditor::IsOpened() const {
234  return ptree_model;
235 }
236 
237 bool PTreeEditor::OpenPath(string const& path)
238 {
239  if (ptree_model) {
240  // another file was already opened
241  if (!PromptClose()) { // close file first, showing a "save changes?" dialog if necessary
242  return false;
243  }
244  }
245 
246  QString filename = QString::fromStdString(path).split("/").last();
247  ptree edit_pt;
248  try {
249  read_xml(path, root_pt, trim_whitespace);
250 
251  // ask for subtree
252  bool ok;
253  if (root_pt.empty()) {
254  QMessageBox::warning(this, QString("Could not open file ") + filename, QString("File is empty!"));
255  return false;
256  }
257  string default_subtree;
258  for (auto child : root_pt) {
259  if (child.first == "<xmlcomment>")
260  continue;
261  default_subtree = child.first;
262  }
263  string const subtree = QInputDialog::getText(this, "", "Please specify path of subtree to edit:",
264  QLineEdit::Normal, QString::fromStdString(default_subtree), &ok).toStdString();
265  if (ok) {
266  edit_pt = root_pt.get_child(subtree);
267  // Create model and view
268  opened_path = path;
269  setWindowTitle(g_caption_with_file.arg(QString::fromStdString(path)));
270  subtree_key = subtree;
271  ptree_model = new PTreeModel(edit_pt);
272  ptree_view->setModel(ptree_model);
273 
274  action_close->setEnabled(true);
275  action_save_as->setEnabled(true);
276  return true;
277  } else {
278  return false;
279  }
280  }
281  catch (xml_parser_error& e) {
282  QMessageBox::warning(this, QString("Could not open file ") + filename,
283  QString("Exception xml_parser_error ") + e.what());
284  }
285  catch (ptree_bad_path& e) {
286  QMessageBox::warning(this, QString("Could not open file ") + filename,
287  QString("Exception ptree_bad_path ") + e.what());
288  }
289  catch (ptree_bad_data& e) {
290  QMessageBox::warning(this, QString("Could not open file ") + filename,
291  QString("Exception ptree_bad_data ") + e.what());
292  }
293  return false;
294 }
295 
296 void PTreeEditor::SLOT_AboutDialog()
297 {
298  const QString app_name = "PTree Editor";
299  const QString about = "<h3>PTree editor</h3>\
300  <p>(c) 2012, <a href=\"http://www.comp.ua.ac.be/\">J. Broeckhove</a> <i>et al.</i><br>\
301  <a href=\"http://www.comp.ua.ac.be\">Computational Modeling and Programming</a><br>\
302  Universiteit Antwerpen, Belgium.</p>";
303 
304  QMessageBox::about(this,app_name, about);
305 }
306 
307 void PTreeEditor::SLOT_New()
308 {
309  if (ptree_model)
310  // another file was already opened
311  if (!PromptClose()) // close file first, showing a "save changes?" dialog if necessary
312  return;
313 
314  bool ok;
315  string const subtree =
316  QInputDialog::getText(this, "New...", "Root element:",
317  QLineEdit::Normal, "vleaf_sim", &ok).toStdString();
318  if (ok) {
319  ptree edit_pt;
320  edit_pt.add_child("<xmlcomment>", ptree("File created by simPT Ptree Editor."));
321  root_pt.clear();
322 
323  // Create model and view
324  opened_path = "";
325  setWindowTitle(g_caption_with_file.arg("Untitled"));
326  subtree_key = subtree;
327  ptree_model = new PTreeModel(edit_pt);
328  ptree_view->setModel(ptree_model);
329 
330  action_close->setEnabled(true);
331  action_save->setEnabled(true);
332  action_save_as->setEnabled(true);
333  }
334 }
335 
336 void PTreeEditor::SLOT_OpenDialog()
337 {
338  QFileDialog* fd = new QFileDialog(this, "Open", QString(), "Ptree files (*.xml)");
339  fd->setFilter(QDir::Hidden);
340  fd->setFileMode(QFileDialog::AnyFile);
341  fd->setAcceptMode(QFileDialog::AcceptOpen);
342 
343  if (fd->exec() == QDialog::Accepted) {
344  QStringList files = fd->selectedFiles();
345  QString filename;
346  if (!files.empty()) {
347  filename = files.first();
348  } else {
349  return;
350  }
351  QFileInfo fi(filename);
352  if (!QFile::exists(filename) && QFile::exists(filename + ".xml")) {
353  filename += ".xml";
354  }
355 
356  OpenPath(filename.toStdString());
357  }
358 }
359 
360 void PTreeEditor::SLOT_PromptClose()
361 {
362  PromptClose();
363  /*if (ptree_model) {
364  if (!ptree_view->IsClean()) {
365  QString filename = QString::fromStdString(opened_path).split("/").last();
366  if (filename.length() == 0) {
367  filename = "Untitled";
368  }
369  switch (QMessageBox::question(this, "Save changes?", filename + " has been modified.",
370  QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save)) {
371  case QMessageBox::Save:
372  if (!Save()) {
373  return false;
374  }
375  break;
376  case QMessageBox::Discard:
377  break;
378  case QMessageBox::Cancel:
379  return false;
380  default:
381  break;
382  }
383  }
384  }
385  ForceClose();
386  return true;*/
387 }
388 
389 void PTreeEditor::SLOT_Save()
390 {
391  InternalSave();
392 }
393 
394 bool PTreeEditor::SLOT_SaveAsDialog()
395 {
396  QFileDialog* fd = new QFileDialog(this, "Save As", QString(), "XML files (*.xml)");
397  fd->setFileMode(QFileDialog::AnyFile);
398  fd->setAcceptMode(QFileDialog::AcceptSave);
399 
400  if (fd->exec() == QDialog::Accepted) {
401  QStringList files = fd->selectedFiles();
402  QString filename;
403  if (!files.empty())
404  filename = files.first();
405  QFileInfo fi(filename);
406  if (fi.suffix().isEmpty())
407  filename += ".xml";
408 
409  return SavePath(filename.toStdString());
410  } else {
411  return false;
412  }
413 }
414 
415 bool PTreeEditor::SavePath(std::string const& path)
416 {
417  if (ptree_model) {
418  try {
419  ptree param_pt = ptree_model->Store();
420  root_pt.put_child(subtree_key, param_pt);
421  write_xml(path, root_pt, std::locale(), XmlWriterSettings::GetTab());
422  }
423  catch (xml_parser_error & e) {
424  QMessageBox::warning(this, "Could not save to file", QString("Exception xml_parser_error ") + e.what());
425  return false;
426  }
427 
428  ptree_view->SetClean();
429  opened_path = path;
430  setWindowTitle(g_caption_with_file.arg(QString::fromStdString(opened_path)));
431  return true;
432  }
433  return false;
434 }
435 
436 void PTreeEditor::SLOT_SetClean(bool clean)
437 {
438  if (!opened_path.empty()) {
439  action_save->setEnabled(!clean);
440  }
441 }
442 
443 void PTreeEditor::SLOT_SetFullscreenMode(bool checked)
444 {
445  if (checked) {
446  setWindowState(windowState() | Qt::WindowFullScreen);
447  } else {
448  setWindowState(windowState() & ~Qt::WindowFullScreen);
449  }
450 }
451 
452 } // namespace
453 } // namespace
STL namespace.
see the online Qt documentation
Namespace for miscellaneous utilities.
Definition: PTreeFile.cpp:44
HasUnsavedChanges with the ability of displaying a "save-discard-cancel" dialog to the user before cl...
Namespace for SimPT shell package.
Definition: Client.cpp:50
Namespace for graphical interface classes.
Qt model reflecting hierarchical structure of a ptree.
Definition: PTreeModel.h:37
see the online Qt documentation
TreeView widget that presents an editable ptree to the user.
Definition: PTreeView.h:37
see the online Qt documentation
Interface for PTreeEditor.
see the online Qt documentation
Interface for PTreeView.
Interface for PTreeModel.
Xml writer settings class.