Developer Documentation
baseWidget.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41 
42 
43 
44 //== INCLUDES =================================================================
45 #include <QVBoxLayout>
46 #include <QPushButton>
47 #include <QDialog>
48 #include <QTextEdit>
49 #include <QFileDialog>
50 #include <QMenu>
51 #include <QMenuBar>
52 #include <QMessageBox>
53 
54 #include <QApplication>
55 #include <QClipboard>
56 #include <QScriptEngine>
57 
58 #include <QDomDocument>
59 
60 #include <QXmlQuery>
61 #include <QXmlResultItems>
62 
64 
65 #include "baseWidget.hh"
66 #include "toolBoxElement.hh"
67 #include "scene/sceneElement.hh"
68 
69 #include "parser/context.hh"
70 
71 //== NAMESPACES ===============================================================
72 namespace VSI {
73 
74 //=============================================================================
75 //
76 // CLASS BaseWidget - IMPLEMENTATION
77 //
78 //=============================================================================
79 
80 // static variable for singleton
81 BaseWidget * BaseWidget::base_ = NULL;
82 
83 //------------------------------------------------------------------------------
84 
85 // Constructor
86 BaseWidget::BaseWidget (Context *_ctx, QWidget *_parent) :
87  QMainWindow (_parent),
88  ctx_ (_ctx),
89  fileName_ (),
90  changedContent_ (false)
91 {
92  setWindowIcon (OpenFlipper::Options::OpenFlipperIcon ());
93  updateTitle ();
94 
95  splitter_ = new QSplitter (Qt::Horizontal, this);
96  toolbox_ = new QToolBox ();
97  views_ = new QStackedWidget ();
98 
99 
100  toolbox_->setMinimumWidth (275);
101 
102  QVBoxLayout *layout = new QVBoxLayout;
103 
104  layout->addWidget (toolbox_);
105 
106  QPushButton *execute = new QPushButton ("Execute");
107  layout->addWidget (execute);
108 
109  QWidget *w = new QWidget;
110  w->setLayout (layout);
111 
112  mainScene_ = new GraphicsScene (_ctx);
113  views_->addWidget(mainScene_->graphicsView ());
114 
115  splitter_->addWidget (w);
116  splitter_->addWidget (views_);
117  QList<int> sizes;
118  sizes << 275 << 10000;
119  splitter_->setSizes (sizes);
120 
121  setCentralWidget (splitter_);
122 
123  resize (1000, 700);
124 
125  setupUi ();
126 
127  connect (execute, SIGNAL (clicked (bool)), this, SLOT (executeCode ()));
128 
129  connect (mainScene_, SIGNAL (contentChanged()), this, SLOT (contentChanged()));
130 
131 
132  QMenu *menu = new QMenu (tr("&File"));
133  QIcon icon;
134 
135  icon.addFile(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"add-empty-object.png");
136  menu->addAction (icon, tr("New"), this, SLOT (newFile()), QKeySequence::New);
137 
138  icon.addFile(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"document-open.png");
139  menu->addAction (icon, tr("Open ..."), this, SLOT (load()), QKeySequence::Open);
140 
141  menu->addSeparator ();
142 
143  icon.addFile(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"document-save.png");
144  menu->addAction (icon, tr("Save"), this, SLOT (save()), QKeySequence::Save);
145 
146  icon.addFile(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"document-save-as.png");
147  menu->addAction (icon, tr("Save as ..."), this, SLOT (saveAs()), QKeySequence::SaveAs);
148  menu->addSeparator ();
149 
150  icon.addFile(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"window-close.png");
151  menu->addAction (icon, tr("Close"), this, SLOT (close()), QKeySequence::Close);
152 
153  menuBar()->addMenu (menu);
154 
155  menu = new QMenu (tr("&Script"));
156  icon.addFile(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"scriptEditor.png");
157  menu->addAction (icon, tr("Open in script editor"), this, SLOT (codeToScript()));
158 
159  menuBar()->addMenu (menu);
160 
161  base_ = this;
162 }
163 
164 //------------------------------------------------------------------------------
165 
167 BaseWidget::~BaseWidget ()
168 {
169  base_ = NULL;
170 }
171 
172 //------------------------------------------------------------------------------
173 
175 void BaseWidget::setupUi ()
176 {
177 
178  foreach (const QString &c, ctx_->categories ())
179  {
180  QWidget *widget = new QWidget (toolbox_);
181  QVBoxLayout *layout = new QVBoxLayout ();
182  foreach (Element *e, ctx_->elements (c))
183  {
184  if (!(e->flags () & ELEMENT_FLAG_SKIP_TOOLBOX))
185  {
186  ToolBoxElement *t = new ToolBoxElement (e, widget);
187  layout->addWidget (t);
188  }
189  }
190  layout->addStretch ();
191  widget->setLayout (layout);
192  toolbox_->addItem (widget, c);
193  }
194 }
195 
196 //------------------------------------------------------------------------------
197 
199 void BaseWidget::executeCode ()
200 {
201  QString errors = "";
202 
203  QString code = mainScene_->generateCode (errors);
204 
205  if (errors.isEmpty ())
206  {
207  ctx_->scriptEngine ()->pushContext ();
208  ctx_->scriptEngine ()->evaluate (code);
209  ctx_->scriptEngine ()->popContext ();
210  return;
211  }
212 
213  errors = "<h3> " + tr("Following Elements could not be processed:") + "</h3>" + errors;
214 
215  QMessageBox::warning (this, tr("Error during code generation"), errors,
216  QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
217 }
218 
219 //------------------------------------------------------------------------------
220 
222 void BaseWidget::codeToScript()
223 {
224 
225  QString errors = "";
226 
227  QString code = mainScene_->generateCode (errors);
228 
229  if (errors.isEmpty ())
230  {
231  emit codeToScriptEditor (code);
232  return;
233  }
234 
235  errors = "<qt> " + tr("Following Elements could not be processed:") + errors + "</qt>";
236 
237  QMessageBox::warning (this, tr("Error during code generation"), errors,
238  QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
239 }
240 
241 //------------------------------------------------------------------------------
242 
244 bool BaseWidget::saveAs()
245 {
246  return save (true);
247 }
248 
249 //------------------------------------------------------------------------------
250 
253 {
254  if (!saveIfChanged ())
255  return;
256 
257  QString filename = QFileDialog::getOpenFileName (this,
258  tr("Load Visual Script"),
259  OpenFlipperSettings().value("Core/CurrentDir").toString(),
260  tr("Visual Script File (*.ofvs)"));
261 
262  if (filename.isEmpty ())
263  return;
264 
265  // Get the chosen directory and remember it.
266  QFileInfo fileInfo(filename);
267  OpenFlipperSettings().setValue("Core/CurrentDir", fileInfo.absolutePath() );
268 
269  QFile f (filename);
270  if (!f.open (QFile::ReadOnly))
271  {
272  QMessageBox msgBox;
273  msgBox.setText(tr("Unable to open file."));
274  msgBox.setInformativeText(filename);
275  msgBox.exec();
276  return;
277  }
278 
279  while (!scenes_.isEmpty())
280  scenes_.pop ();
281  views_->setCurrentWidget (mainScene_->graphicsView());
282 
283  QXmlQuery query;
284  query.setFocus (&f);
285 
286  query.setQuery ("VisualScript");
287 
288  QXmlResultItems root;
289 
290  if (query.isValid ())
291  {
292  query.evaluateTo (&root);
293 
294  QXmlItem item (root.next ());
295  if (!item.isNull ())
296  {
297  QXmlQuery q (query);
298  q.setFocus (item);
299  mainScene_->loadFromXml (q);
300  }
301  }
302 
303  changedContent_ = false;
304  fileName_ = filename;
305  updateTitle ();
306 }
307 
308 //------------------------------------------------------------------------------
309 
311 bool BaseWidget::save(bool _newName)
312 {
313 
314  QString filename;
315 
316  if (fileName_.isEmpty () || _newName)
317  {
318  QFileDialog *d = new QFileDialog (this, tr("Save Visual Script"),
319  OpenFlipperSettings().value("Core/CurrentDir").toString(),
320  tr("Visual Script File (*.ofvs)"));
321 
322  d->setAcceptMode (QFileDialog::AcceptSave);
323  d->setDefaultSuffix ("ofvs");
324 
325  if (QDialog::Accepted == d->exec ())
326  filename = d->selectedFiles ()[0];
327  }
328  else
329  filename = fileName_;
330 
331  if (filename.isEmpty ())
332  return false;
333 
334  // Get the chosen directory and remember it.
335  QFileInfo fileInfo(filename);
336  OpenFlipperSettings().setValue("Core/CurrentDir", fileInfo.absolutePath() );
337 
338  QFile f (filename);
339  if (!f.open (QFile::WriteOnly))
340  {
341  QMessageBox msgBox;
342  msgBox.setText(tr("Unable to write file."));
343  msgBox.setInformativeText(filename);
344  msgBox.exec();
345  return false;
346  }
347 
348  QDomDocument doc("VisualScript");
349  QDomElement root = doc.createElement("VisualScript");
350  doc.appendChild(root);
351 
352  mainScene_->saveToXml (doc, root);
353 
354  f.write (doc.toString().toUtf8 ());
355  f.close ();
356 
357  changedContent_ = false;
358  fileName_ = filename;
359  updateTitle ();
360 
361  return true;
362 }
363 
364 //------------------------------------------------------------------------------
365 
367 void BaseWidget::newFile()
368 {
369  if (!saveIfChanged ())
370  return;
371 
372  while (!scenes_.isEmpty())
373  scenes_.pop ();
374  views_->setCurrentWidget (mainScene_->graphicsView());
375 
376  fileName_ = QString ();
377  mainScene_->clearScene ();
378  changedContent_ = false;
379  updateTitle ();
380 }
381 
382 //------------------------------------------------------------------------------
383 
385 void BaseWidget::updateTitle()
386 {
387  if (fileName_.isEmpty ())
388  setWindowTitle (tr("Untitled") + (changedContent_ ? " [" + tr("Modified") + "] " : "") + " - Visual Script Editor");
389  else
390  setWindowTitle (fileName_ + (changedContent_ ? " [" + tr("Modified") + "] " : "") + " - Visual Script Editor");
391 }
392 
393 //------------------------------------------------------------------------------
394 
396 void BaseWidget::contentChanged()
397 {
398  if (!changedContent_)
399  {
400  changedContent_ = true;
401  updateTitle ();
402  }
403 }
404 
405 //------------------------------------------------------------------------------
406 
408 bool BaseWidget::saveIfChanged()
409 {
410  if (changedContent_)
411  {
412  QMessageBox msgBox;
413  msgBox.setText(tr("The visual script has been modified."));
414  msgBox.setInformativeText(tr("Do you want to save your changes?"));
415  msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
416  msgBox.setDefaultButton(QMessageBox::Save);
417  int ret = msgBox.exec();
418  switch (ret) {
419  case QMessageBox::Save:
420  // Save was clicked
421  if (!save ())
422  return false;
423  break;
424  case QMessageBox::Discard:
425  // Don't Save was clicked
426  break;
427  case QMessageBox::Cancel:
428  // Cancel was clicked
429  return false;
430  break;
431  default:
432  // should never be reached
433  break;
434  }
435  return true;
436  }
437  else
438  return true;
439 }
440 
441 //------------------------------------------------------------------------------
442 
444 void BaseWidget::closeEvent(QCloseEvent *_event)
445 {
446  if (!saveIfChanged ())
447  _event->ignore ();
448  else
449  _event->accept();
450 }
451 
452 //------------------------------------------------------------------------------
453 
455 BaseWidget * BaseWidget::createBaseWidget (Context *_ctx, QWidget *_parent)
456 {
457  if (!base_)
458  base_ = new BaseWidget (_ctx, _parent);
459  return base_;
460 }
461 
462 //------------------------------------------------------------------------------
463 
465 BaseWidget * BaseWidget::getBaseWidget()
466 {
467  return base_;
468 }
469 
470 //------------------------------------------------------------------------------
471 
474 {
475  scenes_.push (_scene);
476  views_->setCurrentWidget (_scene->graphicsView());
477 }
478 
479 //------------------------------------------------------------------------------
480 
483 {
484  if (!scenes_.isEmpty())
485  scenes_.pop ();
486 
487  if (!scenes_.isEmpty())
488  views_->setCurrentWidget (scenes_.top ()->graphicsView());
489  else
490  views_->setCurrentWidget (mainScene_->graphicsView());
491 }
492 
493 //------------------------------------------------------------------------------
494 
497 {
498  views_->addWidget (_scene->graphicsView ());
499 }
500 
501 //------------------------------------------------------------------------------
502 
505 {
506  views_->removeWidget (_scene->graphicsView ());
507 }
508 
509 //------------------------------------------------------------------------------
510 }
511 
DLLEXPORT OpenFlipperQSettings & OpenFlipperSettings()
QSettings object containing all program settings of OpenFlipper.
GraphicsView * graphicsView()
Graphics view of the scene.
void setValue(const QString &key, const QVariant &value)
Wrapper function which makes it possible to enable Debugging output with -DOPENFLIPPER_SETTINGS_DEBUG...
void popScene()
go back to last scene (function)
Definition: baseWidget.cc:482
void pushScene(GraphicsScene *_scene)
show a new scene (function) in editor
Definition: baseWidget.cc:473
void removeScene(GraphicsScene *_scene)
remove a scene
Definition: baseWidget.cc:504
void load()
load file
Definition: baseWidget.cc:252
unsigned int flags() const
Flags.
Definition: element.hh:109
void addScene(GraphicsScene *_scene)
add a new scene
Definition: baseWidget.cc:496