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
63 #include <OpenFlipper/common/GlobalOptions.hh>
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
252 void VSI::BaseWidget::load()
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
473 void VSI::BaseWidget::pushScene(GraphicsScene * _scene)
474 {
475 scenes_.push (_scene);
476 views_->setCurrentWidget (_scene->graphicsView());
477 }
478
479 //------------------------------------------------------------------------------
480
482 void VSI::BaseWidget::popScene()
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
496 void VSI::BaseWidget::addScene(GraphicsScene * _scene)
497 {
498 views_->addWidget (_scene->graphicsView ());
499 }
500
501 //------------------------------------------------------------------------------
502
504 void VSI::BaseWidget::removeScene(GraphicsScene * _scene)
505 {
506 views_->removeWidget (_scene->graphicsView ());
507 }
508
509 //------------------------------------------------------------------------------
510 }
511
OpenFlipperSettings
DLLEXPORT OpenFlipperQSettings & OpenFlipperSettings()
QSettings object containing all program settings of OpenFlipper.
Definition: GlobalOptions.cc:1038
VSI::GraphicsScene::graphicsView
GraphicsView * graphicsView()
Graphics view of the scene.
Definition: graphicsScene.hh:93
VSI::ToolBoxElement
Definition: toolBoxElement.hh:61
OpenFlipperQSettings::setValue
void setValue(const QString &key, const QVariant &value)
Wrapper function which makes it possible to enable Debugging output with -DOPENFLIPPER_SETTINGS_DEBUG...
Definition: OpenFlipperQSettings.cc:61
VSI::BaseWidget::popScene
void popScene()
go back to last scene (function)
Definition: baseWidget.cc:482
GlobalOptions.hh
VSI::BaseWidget::pushScene
void pushScene(GraphicsScene *_scene)
show a new scene (function) in editor
Definition: baseWidget.cc:473
VSI::Element
Definition: element.hh:68
VSI::BaseWidget
Definition: baseWidget.hh:69
VSI::BaseWidget::removeScene
void removeScene(GraphicsScene *_scene)
remove a scene
Definition: baseWidget.cc:504
VSI::Context
Definition: context.hh:70
VSI::BaseWidget::load
void load()
load file
Definition: baseWidget.cc:252
VSI
Definition: baseWidget.cc:72
VSI::GraphicsScene
Definition: graphicsScene.hh:80
VSI::Element::flags
unsigned int flags() const
Flags.
Definition: element.hh:109
VSI::BaseWidget::addScene
void addScene(GraphicsScene *_scene)
add a new scene
Definition: baseWidget.cc:496