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 * $Revision$ *
45 * $LastChangedBy$ *
46 * $Date$ *
47 * *
48 \*===========================================================================*/
49 
50 //== INCLUDES =================================================================
51 #include <QVBoxLayout>
52 #include <QPushButton>
53 #include <QDialog>
54 #include <QTextEdit>
55 #include <QFileDialog>
56 #include <QMenu>
57 #include <QMenuBar>
58 #include <QMessageBox>
59 
60 #include <QApplication>
61 #include <QClipboard>
62 #include <QScriptEngine>
63 
64 #include <QDomDocument>
65 
66 #include <QXmlQuery>
67 #include <QXmlResultItems>
68 
70 
71 #include "baseWidget.hh"
72 #include "toolBoxElement.hh"
73 #include "scene/sceneElement.hh"
74 
75 #include "parser/context.hh"
76 
77 //== NAMESPACES ===============================================================
78 namespace VSI {
79 
80 //=============================================================================
81 //
82 // CLASS BaseWidget - IMPLEMENTATION
83 //
84 //=============================================================================
85 
86 // static variable for singleton
87 BaseWidget * BaseWidget::base_ = NULL;
88 
89 //------------------------------------------------------------------------------
90 
91 // Constructor
92 BaseWidget::BaseWidget (Context *_ctx, QWidget *_parent) :
93  QMainWindow (_parent),
94  ctx_ (_ctx),
95  fileName_ (),
96  changedContent_ (false)
97 {
98  setWindowIcon (OpenFlipper::Options::OpenFlipperIcon ());
99  updateTitle ();
100 
101  splitter_ = new QSplitter (Qt::Horizontal, this);
102  toolbox_ = new QToolBox ();
103  views_ = new QStackedWidget ();
104 
105 
106  toolbox_->setMinimumWidth (275);
107 
108  QVBoxLayout *layout = new QVBoxLayout;
109 
110  layout->addWidget (toolbox_);
111 
112  QPushButton *execute = new QPushButton ("Execute");
113  layout->addWidget (execute);
114 
115  QWidget *w = new QWidget;
116  w->setLayout (layout);
117 
118  mainScene_ = new GraphicsScene (_ctx);
119  views_->addWidget(mainScene_->graphicsView ());
120 
121  splitter_->addWidget (w);
122  splitter_->addWidget (views_);
123  QList<int> sizes;
124  sizes << 275 << 10000;
125  splitter_->setSizes (sizes);
126 
127  setCentralWidget (splitter_);
128 
129  resize (1000, 700);
130 
131  setupUi ();
132 
133  connect (execute, SIGNAL (clicked (bool)), this, SLOT (executeCode ()));
134 
135  connect (mainScene_, SIGNAL (contentChanged()), this, SLOT (contentChanged()));
136 
137 
138  QMenu *menu = new QMenu (tr("&File"));
139  QIcon icon;
140 
141  icon.addFile(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"add-empty-object.png");
142  menu->addAction (icon, tr("New"), this, SLOT (newFile()), QKeySequence::New);
143 
144  icon.addFile(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"document-open.png");
145  menu->addAction (icon, tr("Open ..."), this, SLOT (load()), QKeySequence::Open);
146 
147  menu->addSeparator ();
148 
149  icon.addFile(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"document-save.png");
150  menu->addAction (icon, tr("Save"), this, SLOT (save()), QKeySequence::Save);
151 
152  icon.addFile(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"document-save-as.png");
153  menu->addAction (icon, tr("Save as ..."), this, SLOT (saveAs()), QKeySequence::SaveAs);
154  menu->addSeparator ();
155 
156  icon.addFile(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"window-close.png");
157  menu->addAction (icon, tr("Close"), this, SLOT (close()), QKeySequence::Close);
158 
159  menuBar()->addMenu (menu);
160 
161  menu = new QMenu (tr("&Script"));
162  icon.addFile(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"scriptEditor.png");
163  menu->addAction (icon, tr("Open in script editor"), this, SLOT (codeToScript()));
164 
165  menuBar()->addMenu (menu);
166 
167  base_ = this;
168 }
169 
170 //------------------------------------------------------------------------------
171 
173 BaseWidget::~BaseWidget ()
174 {
175  base_ = NULL;
176 }
177 
178 //------------------------------------------------------------------------------
179 
181 void BaseWidget::setupUi ()
182 {
183 
184  foreach (const QString &c, ctx_->categories ())
185  {
186  QWidget *widget = new QWidget (toolbox_);
187  QVBoxLayout *layout = new QVBoxLayout ();
188  foreach (Element *e, ctx_->elements (c))
189  {
190  if (!(e->flags () & ELEMENT_FLAG_SKIP_TOOLBOX))
191  {
192  ToolBoxElement *t = new ToolBoxElement (e, widget);
193  layout->addWidget (t);
194  }
195  }
196  layout->addStretch ();
197  widget->setLayout (layout);
198  toolbox_->addItem (widget, c);
199  }
200 }
201 
202 //------------------------------------------------------------------------------
203 
205 void BaseWidget::executeCode ()
206 {
207  QString errors = "";
208 
209  QString code = mainScene_->generateCode (errors);
210 
211  if (errors.isEmpty ())
212  {
213  ctx_->scriptEngine ()->pushContext ();
214  ctx_->scriptEngine ()->evaluate (code);
215  ctx_->scriptEngine ()->popContext ();
216  return;
217  }
218 
219  errors = "<h3> " + tr("Following Elements could not be processed:") + "</h3>" + errors;
220 
221  QMessageBox::warning (this, tr("Error during code generation"), errors,
222  QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
223 }
224 
225 //------------------------------------------------------------------------------
226 
228 void BaseWidget::codeToScript()
229 {
230 
231  QString errors = "";
232 
233  QString code = mainScene_->generateCode (errors);
234 
235  if (errors.isEmpty ())
236  {
237  emit codeToScriptEditor (code);
238  return;
239  }
240 
241  errors = "<qt> " + tr("Following Elements could not be processed:") + errors + "</qt>";
242 
243  QMessageBox::warning (this, tr("Error during code generation"), errors,
244  QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
245 }
246 
247 //------------------------------------------------------------------------------
248 
250 bool BaseWidget::saveAs()
251 {
252  return save (true);
253 }
254 
255 //------------------------------------------------------------------------------
256 
259 {
260  if (!saveIfChanged ())
261  return;
262 
263  QString filename = QFileDialog::getOpenFileName (this,
264  tr("Load Visual Script"),
265  OpenFlipperSettings().value("Core/CurrentDir").toString(),
266  tr("Visual Script File (*.ofvs)"));
267 
268  if (filename.isEmpty ())
269  return;
270 
271  // Get the chosen directory and remember it.
272  QFileInfo fileInfo(filename);
273  OpenFlipperSettings().setValue("Core/CurrentDir", fileInfo.absolutePath() );
274 
275  QFile f (filename);
276  if (!f.open (QFile::ReadOnly))
277  {
278  QMessageBox msgBox;
279  msgBox.setText(tr("Unable to open file."));
280  msgBox.setInformativeText(filename);
281  msgBox.exec();
282  return;
283  }
284 
285  while (!scenes_.isEmpty())
286  scenes_.pop ();
287  views_->setCurrentWidget (mainScene_->graphicsView());
288 
289  QXmlQuery query;
290  query.setFocus (&f);
291 
292  query.setQuery ("VisualScript");
293 
294  QXmlResultItems root;
295 
296  if (query.isValid ())
297  {
298  query.evaluateTo (&root);
299 
300  QXmlItem item (root.next ());
301  if (!item.isNull ())
302  {
303  QXmlQuery q (query);
304  q.setFocus (item);
305  mainScene_->loadFromXml (q);
306  }
307  }
308 
309  changedContent_ = false;
310  fileName_ = filename;
311  updateTitle ();
312 }
313 
314 //------------------------------------------------------------------------------
315 
317 bool BaseWidget::save(bool _newName)
318 {
319 
320  QString filename;
321 
322  if (fileName_.isEmpty () || _newName)
323  {
324  QFileDialog *d = new QFileDialog (this, tr("Save Visual Script"),
325  OpenFlipperSettings().value("Core/CurrentDir").toString(),
326  tr("Visual Script File (*.ofvs)"));
327 
328  d->setAcceptMode (QFileDialog::AcceptSave);
329  d->setDefaultSuffix ("ofvs");
330 
331  if (QDialog::Accepted == d->exec ())
332  filename = d->selectedFiles ()[0];
333  }
334  else
335  filename = fileName_;
336 
337  if (filename.isEmpty ())
338  return false;
339 
340  // Get the chosen directory and remember it.
341  QFileInfo fileInfo(filename);
342  OpenFlipperSettings().setValue("Core/CurrentDir", fileInfo.absolutePath() );
343 
344  QFile f (filename);
345  if (!f.open (QFile::WriteOnly))
346  {
347  QMessageBox msgBox;
348  msgBox.setText(tr("Unable to write file."));
349  msgBox.setInformativeText(filename);
350  msgBox.exec();
351  return false;
352  }
353 
354  QDomDocument doc("VisualScript");
355  QDomElement root = doc.createElement("VisualScript");
356  doc.appendChild(root);
357 
358  mainScene_->saveToXml (doc, root);
359 
360  f.write (doc.toString().toUtf8 ());
361  f.close ();
362 
363  changedContent_ = false;
364  fileName_ = filename;
365  updateTitle ();
366 
367  return true;
368 }
369 
370 //------------------------------------------------------------------------------
371 
373 void BaseWidget::newFile()
374 {
375  if (!saveIfChanged ())
376  return;
377 
378  while (!scenes_.isEmpty())
379  scenes_.pop ();
380  views_->setCurrentWidget (mainScene_->graphicsView());
381 
382  fileName_ = QString ();
383  mainScene_->clearScene ();
384  changedContent_ = false;
385  updateTitle ();
386 }
387 
388 //------------------------------------------------------------------------------
389 
391 void BaseWidget::updateTitle()
392 {
393  if (fileName_.isEmpty ())
394  setWindowTitle (tr("Untitled") + (changedContent_ ? " [" + tr("Modified") + "] " : "") + " - Visual Script Editor");
395  else
396  setWindowTitle (fileName_ + (changedContent_ ? " [" + tr("Modified") + "] " : "") + " - Visual Script Editor");
397 }
398 
399 //------------------------------------------------------------------------------
400 
402 void BaseWidget::contentChanged()
403 {
404  if (!changedContent_)
405  {
406  changedContent_ = true;
407  updateTitle ();
408  }
409 }
410 
411 //------------------------------------------------------------------------------
412 
414 bool BaseWidget::saveIfChanged()
415 {
416  if (changedContent_)
417  {
418  QMessageBox msgBox;
419  msgBox.setText(tr("The visual script has been modified."));
420  msgBox.setInformativeText(tr("Do you want to save your changes?"));
421  msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
422  msgBox.setDefaultButton(QMessageBox::Save);
423  int ret = msgBox.exec();
424  switch (ret) {
425  case QMessageBox::Save:
426  // Save was clicked
427  if (!save ())
428  return false;
429  break;
430  case QMessageBox::Discard:
431  // Don't Save was clicked
432  break;
433  case QMessageBox::Cancel:
434  // Cancel was clicked
435  return false;
436  break;
437  default:
438  // should never be reached
439  break;
440  }
441  return true;
442  }
443  else
444  return true;
445 }
446 
447 //------------------------------------------------------------------------------
448 
450 void BaseWidget::closeEvent(QCloseEvent *_event)
451 {
452  if (!saveIfChanged ())
453  _event->ignore ();
454  else
455  _event->accept();
456 }
457 
458 //------------------------------------------------------------------------------
459 
461 BaseWidget * BaseWidget::createBaseWidget (Context *_ctx, QWidget *_parent)
462 {
463  if (!base_)
464  base_ = new BaseWidget (_ctx, _parent);
465  return base_;
466 }
467 
468 //------------------------------------------------------------------------------
469 
471 BaseWidget * BaseWidget::getBaseWidget()
472 {
473  return base_;
474 }
475 
476 //------------------------------------------------------------------------------
477 
480 {
481  scenes_.push (_scene);
482  views_->setCurrentWidget (_scene->graphicsView());
483 }
484 
485 //------------------------------------------------------------------------------
486 
489 {
490  if (!scenes_.isEmpty())
491  scenes_.pop ();
492 
493  if (!scenes_.isEmpty())
494  views_->setCurrentWidget (scenes_.top ()->graphicsView());
495  else
496  views_->setCurrentWidget (mainScene_->graphicsView());
497 }
498 
499 //------------------------------------------------------------------------------
500 
503 {
504  views_->addWidget (_scene->graphicsView ());
505 }
506 
507 //------------------------------------------------------------------------------
508 
511 {
512  views_->removeWidget (_scene->graphicsView ());
513 }
514 
515 //------------------------------------------------------------------------------
516 }
517 
unsigned int flags() const
Flags.
Definition: element.hh:115
void pushScene(GraphicsScene *_scene)
show a new scene (function) in editor
Definition: baseWidget.cc:479
DLLEXPORT OpenFlipperQSettings & OpenFlipperSettings()
QSettings object containing all program settings of OpenFlipper.
GraphicsView * graphicsView()
Graphics view of the scene.
void removeScene(GraphicsScene *_scene)
remove a scene
Definition: baseWidget.cc:510
void setValue(const QString &key, const QVariant &value)
Wrapper function which makes it possible to enable Debugging output with -DOPENFLIPPER_SETTINGS_DEBUG...
void addScene(GraphicsScene *_scene)
add a new scene
Definition: baseWidget.cc:502
void load()
load file
Definition: baseWidget.cc:258
void popScene()
go back to last scene (function)
Definition: baseWidget.cc:488