1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the demonstration applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "menumanager.h"
43 #include "colors.h"
44 #include "menucontent.h"
45 #include "examplecontent.h"
46 
47 MenuManager *MenuManager::pInstance = 0;
48 
instance()49 MenuManager * MenuManager::instance()
50 {
51     if (!MenuManager::pInstance)
52         MenuManager::pInstance = new MenuManager();
53     return MenuManager::pInstance;
54 }
55 
MenuManager()56 MenuManager::MenuManager()
57 {
58     this->ticker = 0;
59     this->tickerInAnim = 0;
60     this->upButton = 0;
61     this->downButton = 0;
62     this->helpEngine = 0;
63     this->score = new Score();
64     this->currentMenu = QLatin1String("[no menu visible]");
65     this->currentCategory = QLatin1String("[no category visible]");
66     this->currentMenuButtons = QLatin1String("[no menu buttons visible]");
67     this->currentInfo = QLatin1String("[no info visible]");
68     this->currentMenuCode = -1;
69     this->readXmlDocument();
70     this->initHelpEngine();
71 }
72 
~MenuManager()73 MenuManager::~MenuManager()
74 {
75     delete this->score;
76     delete this->contentsDoc;
77     delete this->helpEngine;
78 }
79 
getResource(const QString & name)80 QByteArray MenuManager::getResource(const QString &name)
81 {
82     QByteArray ba = this->helpEngine->fileData(name);
83     if (Colors::verbose && ba.isEmpty())
84         qDebug() << " - WARNING: Could not get " << name;
85     return ba;
86 }
87 
readXmlDocument()88 void MenuManager::readXmlDocument()
89 {
90     this->contentsDoc = new QDomDocument();
91     QString errorStr;
92     int errorLine;
93     int errorColumn;
94 
95     QFile file(":/xml/examples.xml");
96     bool statusOK = this->contentsDoc->setContent(&file, true, &errorStr, &errorLine, &errorColumn);
97     if (!statusOK){
98         QMessageBox::critical(0,
99                               QObject::tr("DOM Parser"),
100                               QObject::tr("Could not read or find the contents document. Error at line %1, column %2:\n%3")
101                               .arg(errorLine).arg(errorColumn).arg(errorStr)
102                               );
103         exit(-1);
104     }
105 }
106 
initHelpEngine()107 void MenuManager::initHelpEngine()
108 {
109     this->helpRootUrl = QString("qthelp://com.trolltech.qt.%1%2%3/qdoc/")
110         .arg(QT_VERSION >> 16).arg((QT_VERSION >> 8) & 0xFF)
111         .arg(QT_VERSION & 0xFF);
112 
113     // Store help collection file in cache dir of assistant
114     QString cacheDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation)
115                      + QLatin1String("/Trolltech/Assistant/");
116     QString helpDataFile = QString(QLatin1String("qtdemo_%1.qhc")).arg(QLatin1String(QT_VERSION_STR));
117 
118     QDir dir;
119     if (!dir.exists(cacheDir))
120         dir.mkpath(cacheDir);
121 
122     // Create help engine (and new
123     // helpDataFile if it does not exist):
124     this->helpEngine = new QHelpEngineCore(cacheDir + helpDataFile);
125     this->helpEngine->setupData();
126 
127     QString qtDocRoot = QLibraryInfo::location(QLibraryInfo::DocumentationPath) + QLatin1String("/qch");
128     qtDocRoot = QDir(qtDocRoot).absolutePath();
129 
130     QStringList qchFiles;
131     qchFiles << QLatin1String("/qt.qch")
132              << QLatin1String("/designer.qch")
133              << QLatin1String("/linguist.qch");
134 
135     QString oldDir = helpEngine->customValue(QLatin1String("docDir"), QString()).toString();
136     if (oldDir != qtDocRoot) {
137         foreach (const QString &qchFile, qchFiles)
138             helpEngine->unregisterDocumentation(QHelpEngineCore::namespaceName(qtDocRoot + qchFile));
139     }
140 
141     // If the data that the engine will work
142     // on is not yet registered, do it now:
143     foreach (const QString &qchFile, qchFiles)
144         helpEngine->registerDocumentation(qtDocRoot + qchFile);
145 
146     helpEngine->setCustomValue(QLatin1String("docDir"), qtDocRoot);
147 }
148 
itemSelected(int userCode,const QString & menuName)149 void MenuManager::itemSelected(int userCode, const QString &menuName)
150 {
151     switch (userCode){
152     case LAUNCH:
153         this->launchExample(this->currentInfo);
154         break;
155     case LAUNCH_QML:
156         this->launchQmlExample(this->currentInfo);
157         break;
158     case DOCUMENTATION:
159         this->showDocInAssistant(this->currentInfo);
160         break;
161     case QUIT:
162         this->window->loop = false;
163         QCoreApplication::quit();
164         break;
165     case FULLSCREEN:
166         this->window->toggleFullscreen();
167         break;
168     case ROOT:
169         // out:
170         this->score->queueMovie(this->currentMenu + " -out", Score::FROM_START, Score::LOCK_ITEMS);
171         this->score->queueMovie(this->currentMenuButtons + " -out", Score::FROM_START, Score::LOCK_ITEMS);
172         this->score->queueMovie(this->currentInfo + " -out");
173         this->score->queueMovie(this->currentInfo + " -buttons -out", Score::NEW_ANIMATION_ONLY);
174         this->score->queueMovie("back -out", Score::ONLY_IF_VISIBLE);
175         if(qmlRoot)
176             qmlRoot->setProperty("show", QVariant(false));
177         // book-keeping:
178         this->currentMenuCode = ROOT;
179         this->currentMenu = menuName + " -menu1";
180         this->currentMenuButtons = menuName + " -buttons";
181         this->currentInfo = menuName + " -info";
182         // in:
183         this->score->queueMovie("upndown -shake");
184         this->score->queueMovie(this->currentMenu, Score::FROM_START, Score::UNLOCK_ITEMS);
185         this->score->queueMovie(this->currentMenuButtons, Score::FROM_START, Score::UNLOCK_ITEMS);
186         this->score->queueMovie(this->currentInfo);
187         if (!Colors::noTicker){
188             this->ticker->doIntroTransitions = true;
189             this->tickerInAnim->startDelay = 2000;
190             this->ticker->useGuideQt();
191             this->score->queueMovie("ticker", Score::NEW_ANIMATION_ONLY);
192         }
193         break;
194     case MENU1:
195         // out:
196         this->score->queueMovie(this->currentMenu + " -out", Score::FROM_START, Score::LOCK_ITEMS);
197         this->score->queueMovie(this->currentMenuButtons + " -out", Score::FROM_START, Score::LOCK_ITEMS);
198         this->score->queueMovie(this->currentInfo + " -out");
199         if(qmlRoot)
200             qmlRoot->setProperty("show", QVariant(false));
201         // book-keeping:
202         this->currentMenuCode = MENU1;
203         this->currentCategory = menuName;
204         this->currentMenu = menuName + " -menu1";
205         this->currentInfo = menuName + " -info";
206         // in:
207         this->score->queueMovie("upndown -shake");
208         this->score->queueMovie("back -in");
209         this->score->queueMovie(this->currentMenu, Score::FROM_START, Score::UNLOCK_ITEMS);
210         this->score->queueMovie(this->currentInfo);
211         if (!Colors::noTicker)
212             this->ticker->useGuideTt();
213         break;
214     case MENU2:
215         // out:
216         this->score->queueMovie(this->currentInfo + " -out", Score::NEW_ANIMATION_ONLY);
217         this->score->queueMovie(this->currentInfo + " -buttons -out", Score::NEW_ANIMATION_ONLY);
218         if(qmlRoot)
219             qmlRoot->setProperty("show", QVariant(false));
220         // book-keeping:
221         this->currentMenuCode = MENU2;
222         this->currentInfo = menuName;
223         // in / shake:
224         this->score->queueMovie("upndown -shake");
225         this->score->queueMovie("back -shake");
226         this->score->queueMovie(this->currentMenu + " -shake");
227         this->score->queueMovie(this->currentInfo, Score::NEW_ANIMATION_ONLY);
228         this->score->queueMovie(this->currentInfo + " -buttons", Score::NEW_ANIMATION_ONLY);
229         if (!Colors::noTicker){
230             this->score->queueMovie("ticker -out", Score::NEW_ANIMATION_ONLY);
231         }
232         break;
233     case UP:{
234         QString backMenu = this->info[this->currentMenu]["back"];
235         if (!backMenu.isNull()){
236             this->score->queueMovie(this->currentMenu + " -top_out", Score::FROM_START, Score::LOCK_ITEMS);
237             this->score->queueMovie(backMenu + " -bottom_in", Score::FROM_START, Score::UNLOCK_ITEMS);
238             this->currentMenu = backMenu;
239         }
240         break; }
241     case DOWN:{
242         QString moreMenu = this->info[this->currentMenu]["more"];
243         if (!moreMenu.isNull()){
244             this->score->queueMovie(this->currentMenu + " -bottom_out", Score::FROM_START, Score::LOCK_ITEMS);
245             this->score->queueMovie(moreMenu + " -top_in", Score::FROM_START, Score::UNLOCK_ITEMS);
246             this->currentMenu = moreMenu;
247         }
248         break; }
249     case BACK:{
250         if (this->currentMenuCode == MENU2){
251             // out:
252             this->score->queueMovie(this->currentInfo + " -out", Score::NEW_ANIMATION_ONLY);
253             this->score->queueMovie(this->currentInfo + " -buttons -out", Score::NEW_ANIMATION_ONLY);
254             if(qmlRoot)
255                 qmlRoot->setProperty("show", QVariant(false));
256             // book-keeping:
257             this->currentMenuCode = MENU1;
258             this->currentMenuButtons = this->currentCategory + " -buttons";
259             this->currentInfo = this->currentCategory + " -info";
260             // in / shake:
261             this->score->queueMovie("upndown -shake");
262             this->score->queueMovie(this->currentMenu + " -shake");
263             this->score->queueMovie(this->currentInfo, Score::NEW_ANIMATION_ONLY);
264             this->score->queueMovie(this->currentInfo + " -buttons", Score::NEW_ANIMATION_ONLY);
265             if (!Colors::noTicker){
266                 this->ticker->doIntroTransitions = false;
267                 this->tickerInAnim->startDelay = 500;
268                 this->score->queueMovie("ticker", Score::NEW_ANIMATION_ONLY);
269             }
270         } else if (this->currentMenuCode != ROOT)
271             itemSelected(ROOT, Colors::rootMenuName);
272         break; }
273     }
274 
275     // update back- and more buttons
276     bool noBackMenu = this->info[this->currentMenu]["back"].isNull();
277     bool noMoreMenu = this->info[this->currentMenu]["more"].isNull();
278     this->upButton->setState(noBackMenu ? TextButton::DISABLED : TextButton::OFF);
279     this->downButton->setState(noMoreMenu ? TextButton::DISABLED : TextButton::OFF);
280 
281     if (this->score->hasQueuedMovies()){
282         this->score->playQue();
283         // Playing new movies might include
284         // loading etc. So ignore the FPS
285         // at this point
286         this->window->fpsHistory.clear();
287     }
288 }
289 
showDocInAssistant(const QString & name)290 void MenuManager::showDocInAssistant(const QString &name)
291 {
292     QString url = this->resolveDocUrl(name);
293     if (Colors::verbose)
294         qDebug() << "Sending URL to Assistant:" << url;
295 
296     // Start assistant if it's not already running:
297     if (this->assistantProcess.state() != QProcess::Running){
298         QString app = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QDir::separator();
299 #if !defined(Q_OS_MAC)
300         app += QLatin1String("assistant");
301 #else
302         app += QLatin1String("Assistant.app/Contents/MacOS/Assistant");
303 #endif
304         QStringList args;
305         args << QLatin1String("-enableRemoteControl");
306         this->assistantProcess.start(app, args);
307         if (!this->assistantProcess.waitForStarted()) {
308             QMessageBox::critical(0, tr("Qt Demo"), tr("Could not start Qt Assistant.").arg(app));
309             return;
310         }
311     }
312 
313     // Send command through remote control even if the process
314     // was started to activate assistant and bring it to front:
315     QTextStream str(&this->assistantProcess);
316     str << "SetSource " << url << QLatin1Char('\n') << endl;
317 }
318 
launchExample(const QString & name)319 void MenuManager::launchExample(const QString &name)
320 {
321     QString executable = this->resolveExeFile(name);
322 #ifdef Q_OS_MAC
323     if (Colors::verbose)
324         qDebug() << "Launching:" << executable;
325     bool success = QDesktopServices::openUrl(QUrl::fromLocalFile(executable));
326     if (!success){
327         QMessageBox::critical(0, tr("Failed to launch the example"),
328                           tr("Could not launch the example. Ensure that it has been built."),
329                           QMessageBox::Cancel);
330     }
331 #else // Not mac. To not break anything regarding dll's etc, keep it the way it was before:
332     QProcess *process = new QProcess(this);
333     connect(process, SIGNAL(finished(int)), this, SLOT(exampleFinished()));
334     connect(process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(exampleError(QProcess::ProcessError)));
335 
336 #ifdef Q_OS_WIN
337     //make sure it finds the dlls on windows
338     QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
339     env.insert(QLatin1String("PATH"), QLibraryInfo::location(QLibraryInfo::BinariesPath)
340                + QLatin1Char(';') + env.value(QLatin1String("Path")));
341     process->setProcessEnvironment(env);
342 #endif
343 
344     if (info[name]["changedirectory"] != "false"){
345         QString workingDirectory = resolveDataDir(name);
346         process->setWorkingDirectory(workingDirectory);
347         if (Colors::verbose)
348             qDebug() << "Setting working directory:" << workingDirectory;
349     }
350 
351     if (Colors::verbose)
352         qDebug() << "Launching:" << executable;
353     process->start(executable);
354 #endif
355 }
356 
launchQmlExample(const QString & name)357 void MenuManager::launchQmlExample(const QString &name)
358 {
359 #ifndef QT_NO_DECLARATIVE
360     if(!qmlRoot){
361         exampleError(QProcess::UnknownError);
362         return;
363     }
364     //resolveQmlFilename - refactor to separate fn?
365     QString dirName = this->info[name]["dirname"];
366     QString category = this->info[name]["category"];
367     QString fileName = this->info[name]["filename"];
368     QDir dir;
369     if (category == "demos")
370         dir = QDir(QLibraryInfo::location(QLibraryInfo::DemosPath));
371     else
372         dir = QDir(QLibraryInfo::location(QLibraryInfo::ExamplesPath));
373     QFile file(dir.path() + "/" + dirName + "/" + fileName + "/" + "main.qml");
374     if(!file.exists()){
375         //try dirname.qml as well
376         file.setFileName(dir.path() + "/" + dirName + "/" + fileName + "/" + fileName.split('/').last() + ".qml");
377         if(!file.exists()){
378             exampleError(QProcess::UnknownError);
379             return;
380         }
381     }
382 
383     qmlRoot->setProperty("qmlFile", QVariant(""));//unload component
384     qmlRoot->setProperty("show", QVariant(true));
385     qmlRoot->setProperty("qmlFile", QUrl::fromLocalFile(file.fileName()));
386 #else
387     exampleError(QProcess::UnknownError);
388 #endif
389 }
390 
quitQML()391 void MenuManager::quitQML()
392 {
393     if(qmlRoot)
394         qmlRoot->setProperty("show", QVariant(false));
395 }
396 
exampleFinished()397 void MenuManager::exampleFinished()
398 {
399 }
400 
exampleError(QProcess::ProcessError error)401 void MenuManager::exampleError(QProcess::ProcessError error)
402 {
403     if (error != QProcess::Crashed)
404         QMessageBox::critical(0, tr("Failed to launch the example"),
405                           tr("Could not launch the example. Ensure that it has been built."),
406                           QMessageBox::Cancel);
407 }
408 
init(MainWindow * window)409 void MenuManager::init(MainWindow *window)
410 {
411     this->window = window;
412 
413     // Create div:
414     this->createTicker();
415     this->createUpnDownButtons();
416     this->createBackButton();
417 
418     // Create first level menu:
419     QDomElement rootElement = this->contentsDoc->documentElement();
420     this->createRootMenu(rootElement);
421 
422     // Create second level menus:
423     QDomNode level2MenuNode = rootElement.firstChild();
424     while (!level2MenuNode.isNull()){
425         QDomElement level2MenuElement = level2MenuNode.toElement();
426         this->createSubMenu(level2MenuElement);
427 
428         // create leaf menu and example info:
429         QDomNode exampleNode = level2MenuElement.firstChild();
430         while (!exampleNode.isNull()){
431             QDomElement exampleElement = exampleNode.toElement();
432             this->readInfoAboutExample(exampleElement);
433             this->createLeafMenu(exampleElement);
434             exampleNode = exampleNode.nextSibling();
435         }
436 
437         level2MenuNode = level2MenuNode.nextSibling();
438     }
439 
440     qmlRoot = 0;
441 #ifndef QT_NO_DECLARATIVE
442     // Create QML Loader
443     declarativeEngine = new QDeclarativeEngine(this);
444     connect(declarativeEngine, SIGNAL(quit()),
445             this, SLOT(quitQML()));
446 
447     QDeclarativeComponent component(declarativeEngine, QUrl("qrc:qml/qmlShell.qml"), this);
448     QDeclarativeItem* qmlRootItem = 0;
449     if(component.isReady()){
450         qmlRoot = component.create();
451         qmlRootItem = qobject_cast<QDeclarativeItem*>(qmlRoot);
452     }else{
453         qDebug() << component.status() << component.errorString();
454     }
455 
456     if(qmlRootItem){
457         qmlRootItem->setHeight(this->window->scene->sceneRect().height());
458         qmlRootItem->setWidth(this->window->scene->sceneRect().width());
459         qmlRootItem->setZValue(101);//Above other items
460         qmlRootItem->setCursor(Qt::ArrowCursor);
461         window->scene->addItem(qmlRootItem);
462 
463         //Note that QML adds key handling to the app.
464         window->viewport()->setFocusPolicy(Qt::NoFocus);//Correct keyboard focus handling
465         window->setFocusPolicy(Qt::StrongFocus);
466         window->scene->setStickyFocus(true);
467         window->setFocus();
468     }else{
469         qDebug() << "Error initializing QML subsystem, Declarative examples will not work";
470     }
471 #endif
472 }
473 
readInfoAboutExample(const QDomElement & example)474 void MenuManager::readInfoAboutExample(const QDomElement &example)
475 {
476     QString name = example.attribute("name");
477     if (this->info.contains(name))
478         qWarning() << "__WARNING: MenuManager::readInfoAboutExample: Demo/example with name"
479                     << name << "appears twice in the xml-file!__";
480 
481     this->info[name]["filename"] = example.attribute("filename");
482     this->info[name]["category"] = example.parentNode().toElement().tagName();
483     this->info[name]["dirname"] = example.parentNode().toElement().attribute("dirname");
484     this->info[name]["changedirectory"] = example.attribute("changedirectory");
485     this->info[name]["image"] = example.attribute("image");
486     this->info[name]["qml"] = example.attribute("qml");
487 }
488 
resolveDataDir(const QString & name)489 QString MenuManager::resolveDataDir(const QString &name)
490 {
491     QString dirName = this->info[name]["dirname"];
492     QString category = this->info[name]["category"];
493     QString fileName = this->info[name]["filename"];
494 
495     QDir dir;
496     if (category == "demos")
497         dir = QDir(QLibraryInfo::location(QLibraryInfo::DemosPath));
498     else
499         dir = QDir(QLibraryInfo::location(QLibraryInfo::ExamplesPath));
500 
501     dir.cd(dirName);
502     dir.cd(fileName);
503     return dir.absolutePath();
504 }
505 
resolveExeFile(const QString & name)506 QString MenuManager::resolveExeFile(const QString &name)
507 {
508     QString dirName = this->info[name]["dirname"];
509     QString category = this->info[name]["category"];
510     QString fileName = this->info[name]["filename"];
511 
512     QDir dir;
513     if (category == "demos")
514         dir = QDir(QLibraryInfo::location(QLibraryInfo::DemosPath));
515     else
516         dir = QDir(QLibraryInfo::location(QLibraryInfo::ExamplesPath));
517 
518     dir.cd(dirName);
519     dir.cd(fileName);
520 
521     fileName = fileName.split(QLatin1Char('/')).last();
522 #ifdef Q_OS_WIN
523     fileName += QLatin1String(".exe");
524 #endif
525     // UNIX, Mac non-framework and Windows installed builds.
526     const QFile installedFile(dir.path() + QLatin1Char('/') + fileName);
527     if (installedFile.exists())
528         return installedFile.fileName();
529     // Windows in-source builds
530 #if defined(Q_OS_WIN)
531     const QFile winR(dir.path() + QLatin1String("/release/") + fileName);
532     if (winR.exists())
533         return winR.fileName();
534     const QFile winD(dir.path() + QLatin1String("/debug/") + fileName);
535     if (winD.exists())
536         return winD.fileName();
537 #elif defined(Q_OS_MAC)
538     // Mac frameworks
539     const QFile mac(dir.path() + QLatin1Char('/') + fileName + QLatin1String(".app"));
540     if (mac.exists())
541         return mac.fileName();
542 #endif
543     if (Colors::verbose)
544         qDebug() << "- WARNING: Could not resolve executable:" << dir.path() << fileName;
545     return "__executable not found__";
546 }
547 
resolveDocUrl(const QString & name)548 QString MenuManager::resolveDocUrl(const QString &name)
549 {
550     QString dirName = this->info[name]["dirname"];
551     QString category = this->info[name]["category"];
552     QString fileName = this->info[name]["filename"];
553 
554     if (category == "demos")
555         return this->helpRootUrl + "demos-" + fileName.replace("/", "-") + ".html";
556     else
557         return this->helpRootUrl + dirName.replace("/", "-") + "-" + fileName + ".html";
558 }
559 
resolveImageUrl(const QString & name)560 QString MenuManager::resolveImageUrl(const QString &name)
561 {
562     return this->helpRootUrl + "images/" + name;
563 }
564 
getHtml(const QString & name)565 QByteArray MenuManager::getHtml(const QString &name)
566 {
567     return getResource(this->resolveDocUrl(name));
568 }
569 
getImage(const QString & name)570 QByteArray MenuManager::getImage(const QString &name)
571 {
572     QString imageName = this->info[name]["image"];
573     QString category = this->info[name]["category"];
574     QString fileName = this->info[name]["filename"];
575     bool qml = (this->info[name]["qml"] == QLatin1String("true"));
576     if(qml)
577         fileName = QLatin1String("qml-") + fileName.split('/').last();
578 
579     if (imageName.isEmpty()){
580         if (category == "demos")
581             imageName = fileName + "-demo.png";
582         else
583             imageName = fileName + "-example.png";
584         if ((getResource(resolveImageUrl(imageName))).isEmpty())
585             imageName = fileName + ".png";
586         if ((getResource(resolveImageUrl(imageName))).isEmpty())
587             imageName = fileName + "example.png";
588     }
589     return getResource(resolveImageUrl(imageName));
590 }
591 
592 
createRootMenu(const QDomElement & el)593 void MenuManager::createRootMenu(const QDomElement &el)
594 {
595     QString name = el.attribute("name");
596     createMenu(el, MENU1);
597     createInfo(new MenuContentItem(el, this->window->scene, this->window->mainSceneRoot), name + " -info");
598 
599     Movie *menuButtonsIn = this->score->insertMovie(name + " -buttons");
600     Movie *menuButtonsOut = this->score->insertMovie(name + " -buttons -out");
601     createLowLeftButton(QLatin1String("Quit"), QUIT, menuButtonsIn, menuButtonsOut, 0);
602     createLowRightButton("Toggle fullscreen", FULLSCREEN, menuButtonsIn, menuButtonsOut, 0);
603 }
604 
createSubMenu(const QDomElement & el)605 void MenuManager::createSubMenu(const QDomElement &el)
606 {
607     QString name = el.attribute("name");
608     createMenu(el, MENU2);
609     createInfo(new MenuContentItem(el, this->window->scene, this->window->mainSceneRoot), name + " -info");
610 }
611 
createLeafMenu(const QDomElement & el)612 void MenuManager::createLeafMenu(const QDomElement &el)
613 {
614     QString name = el.attribute("name");
615     createInfo(new ExampleContent(name, this->window->scene, this->window->mainSceneRoot), name);
616 
617     Movie *infoButtonsIn = this->score->insertMovie(name + " -buttons");
618     Movie *infoButtonsOut = this->score->insertMovie(name + " -buttons -out");
619     createLowRightLeafButton("Documentation", 600, DOCUMENTATION, infoButtonsIn, infoButtonsOut, 0);
620     if (el.attribute("executable") != "false")
621         createLowRightLeafButton("Launch", 405, LAUNCH, infoButtonsIn, infoButtonsOut, 0);
622     else if(el.attribute("qml") == "true")
623         createLowRightLeafButton("Display", 405, LAUNCH_QML, infoButtonsIn, infoButtonsOut, 0);
624 }
625 
createMenu(const QDomElement & category,BUTTON_TYPE type)626 void MenuManager::createMenu(const QDomElement &category, BUTTON_TYPE type)
627 {
628     qreal sw = this->window->scene->sceneRect().width();
629     int xOffset = 15;
630     int yOffset = 10;
631     int maxExamples = Colors::menuCount;
632     int menuIndex = 1;
633     QString name = category.attribute("name");
634     QDomNode currentNode = category.firstChild();
635     QString currentMenu = name + QLatin1String(" -menu") + QString::number(menuIndex);
636 
637     while (!currentNode.isNull()){
638         Movie *movieIn = this->score->insertMovie(currentMenu);
639         Movie *movieOut = this->score->insertMovie(currentMenu + " -out");
640         Movie *movieNextTopOut = this->score->insertMovie(currentMenu + " -top_out");
641         Movie *movieNextBottomOut = this->score->insertMovie(currentMenu + " -bottom_out");
642         Movie *movieNextTopIn = this->score->insertMovie(currentMenu + " -top_in");
643         Movie *movieNextBottomIn = this->score->insertMovie(currentMenu + " -bottom_in");
644         Movie *movieShake = this->score->insertMovie(currentMenu + " -shake");
645 
646         int i = 0;
647         while (!currentNode.isNull() && i < maxExamples){
648             TextButton *item;
649 
650             // create normal menu button
651             QString label = currentNode.toElement().attribute("name");
652             item = new TextButton(label, TextButton::LEFT, type, this->window->scene, this->window->mainSceneRoot);
653             currentNode = currentNode.nextSibling();
654 
655 #ifndef QT_OPENGL_SUPPORT
656             if (currentNode.toElement().attribute("dirname") == "opengl")
657                 currentNode = currentNode.nextSibling();
658 #endif
659 
660             item->setRecursiveVisible(false);
661             item->setZValue(10);
662             qreal ih = item->sceneBoundingRect().height();
663             qreal iw = item->sceneBoundingRect().width();
664             qreal ihp = ih + 3;
665 
666             // create in-animation:
667             DemoItemAnimation *anim = new DemoItemAnimation(item, DemoItemAnimation::ANIM_IN);
668             anim->setDuration(float(1000 + (i * 20)) * Colors::animSpeedButtons);
669             anim->setStartPos(QPointF(xOffset, -ih));
670             anim->setPosAt(0.20, QPointF(xOffset, -ih));
671             anim->setPosAt(0.50, QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY + (10 * float(i / 4.0f))));
672             anim->setPosAt(0.60, QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY));
673             anim->setPosAt(0.70, QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY + (5 * float(i / 4.0f))));
674             anim->setPosAt(0.80, QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY));
675             anim->setPosAt(0.90, QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY + (2 * float(i / 4.0f))));
676             anim->setPosAt(1.00, QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY));
677             movieIn->append(anim);
678 
679             // create out-animation:
680             anim = new DemoItemAnimation(item, DemoItemAnimation::ANIM_OUT);
681             anim->hideOnFinished = true;
682             anim->setDuration((700 + (30 * i)) * Colors::animSpeedButtons);
683             anim->setStartPos(QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY));
684             anim->setPosAt(0.60, QPointF(xOffset, 600 - ih - ih));
685             anim->setPosAt(0.65, QPointF(xOffset + 20, 600 - ih));
686             anim->setPosAt(1.00, QPointF(sw + iw, 600 - ih));
687             movieOut->append(anim);
688 
689             // create shake-animation:
690             anim = new DemoItemAnimation(item);
691             anim->setDuration(700 * Colors::animSpeedButtons);
692             anim->setStartPos(QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY));
693             anim->setPosAt(0.55, QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY - i*2.0));
694             anim->setPosAt(0.70, QPointF(xOffset - 10, (i * ihp) + yOffset + Colors::contentStartY - i*1.5));
695             anim->setPosAt(0.80, QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY - i*1.0));
696             anim->setPosAt(0.90, QPointF(xOffset - 2, (i * ihp) + yOffset + Colors::contentStartY - i*0.5));
697             anim->setPosAt(1.00, QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY));
698             movieShake->append(anim);
699 
700             // create next-menu top-out-animation:
701             anim = new DemoItemAnimation(item, DemoItemAnimation::ANIM_OUT);
702             anim->hideOnFinished = true;
703             anim->setDuration((200 + (30 * i)) * Colors::animSpeedButtons);
704             anim->setStartPos(QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY));
705             anim->setPosAt(0.70, QPointF(xOffset, yOffset + Colors::contentStartY));
706             anim->setPosAt(1.00, QPointF(-iw, yOffset + Colors::contentStartY));
707             movieNextTopOut->append(anim);
708 
709             // create next-menu bottom-out-animation:
710             anim = new DemoItemAnimation(item, DemoItemAnimation::ANIM_OUT);
711             anim->hideOnFinished = true;
712             anim->setDuration((200 + (30 * i)) * Colors::animSpeedButtons);
713             anim->setStartPos(QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY));
714             anim->setPosAt(0.70, QPointF(xOffset, (maxExamples * ihp) + yOffset + Colors::contentStartY));
715             anim->setPosAt(1.00, QPointF(-iw, (maxExamples * ihp) + yOffset + Colors::contentStartY));
716             movieNextBottomOut->append(anim);
717 
718             // create next-menu top-in-animation:
719             anim = new DemoItemAnimation(item, DemoItemAnimation::ANIM_IN);
720             anim->setDuration((700 - (30 * i)) * Colors::animSpeedButtons);
721             anim->setStartPos(QPointF(-iw, yOffset + Colors::contentStartY));
722             anim->setPosAt(0.30, QPointF(xOffset, yOffset + Colors::contentStartY));
723             anim->setPosAt(1.00, QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY));
724             movieNextTopIn->append(anim);
725 
726             // create next-menu bottom-in-animation:
727             int reverse = maxExamples - i;
728             anim = new DemoItemAnimation(item, DemoItemAnimation::ANIM_IN);
729             anim->setDuration((1000 - (30 * reverse)) * Colors::animSpeedButtons);
730             anim->setStartPos(QPointF(-iw, (maxExamples * ihp) + yOffset + Colors::contentStartY));
731             anim->setPosAt(0.30, QPointF(xOffset, (maxExamples * ihp) + yOffset + Colors::contentStartY));
732             anim->setPosAt(1.00, QPointF(xOffset, (i * ihp) + yOffset + Colors::contentStartY));
733             movieNextBottomIn->append(anim);
734 
735             i++;
736         }
737 
738         if (!currentNode.isNull() && i == maxExamples){
739             // We need another menu, so register for 'more' and 'back' buttons
740             ++menuIndex;
741             this->info[currentMenu]["more"] = name + QLatin1String(" -menu") + QString::number(menuIndex);
742             currentMenu = name + QLatin1String(" -menu") + QString::number(menuIndex);
743             this->info[currentMenu]["back"] = name + QLatin1String(" -menu") + QString::number(menuIndex - 1);
744         }
745     }
746 }
747 
748 
createLowLeftButton(const QString & label,BUTTON_TYPE type,Movie * movieIn,Movie * movieOut,Movie * movieShake,const QString & menuString)749 void MenuManager::createLowLeftButton(const QString &label, BUTTON_TYPE type,
750     Movie *movieIn, Movie *movieOut, Movie *movieShake, const QString &menuString)
751 {
752     TextButton *button = new TextButton(label, TextButton::RIGHT, type, this->window->scene, this->window->mainSceneRoot, TextButton::PANEL);
753     if (!menuString.isNull())
754         button->setMenuString(menuString);
755     button->setRecursiveVisible(false);
756     button->setZValue(10);
757 
758     qreal iw = button->sceneBoundingRect().width();
759     int xOffset = 15;
760 
761     // create in-animation:
762     DemoItemAnimation *buttonIn = new DemoItemAnimation(button, DemoItemAnimation::ANIM_IN);
763     buttonIn->setDuration(1800 * Colors::animSpeedButtons);
764     buttonIn->setStartPos(QPointF(-iw, Colors::contentStartY + Colors::contentHeight - 35));
765     buttonIn->setPosAt(0.5, QPointF(-iw, Colors::contentStartY + Colors::contentHeight - 35));
766     buttonIn->setPosAt(0.7, QPointF(xOffset, Colors::contentStartY + Colors::contentHeight - 35));
767     buttonIn->setPosAt(1.0, QPointF(xOffset, Colors::contentStartY + Colors::contentHeight - 26));
768     movieIn->append(buttonIn);
769 
770     // create out-animation:
771     DemoItemAnimation *buttonOut = new DemoItemAnimation(button, DemoItemAnimation::ANIM_OUT);
772     buttonOut->hideOnFinished = true;
773     buttonOut->setDuration(400 * Colors::animSpeedButtons);
774     buttonOut->setStartPos(QPointF(xOffset, Colors::contentStartY + Colors::contentHeight - 26));
775     buttonOut->setPosAt(1.0, QPointF(-iw, Colors::contentStartY + Colors::contentHeight - 26));
776     movieOut->append(buttonOut);
777 
778     if (movieShake){
779         DemoItemAnimation *shakeAnim = new DemoItemAnimation(button, DemoItemAnimation::ANIM_UNSPECIFIED);
780         shakeAnim->timeline->setCurveShape(QTimeLine::LinearCurve);
781         shakeAnim->setDuration(650);
782         shakeAnim->setStartPos(buttonIn->posAt(1.0f));
783         shakeAnim->setPosAt(0.60, buttonIn->posAt(1.0f));
784         shakeAnim->setPosAt(0.70, buttonIn->posAt(1.0f) + QPointF(-3, 0));
785         shakeAnim->setPosAt(0.80, buttonIn->posAt(1.0f) + QPointF(2, 0));
786         shakeAnim->setPosAt(0.90, buttonIn->posAt(1.0f) + QPointF(-1, 0));
787         shakeAnim->setPosAt(1.00, buttonIn->posAt(1.0f));
788         movieShake->append(shakeAnim);
789     }
790 }
791 
createLowRightButton(const QString & label,BUTTON_TYPE type,Movie * movieIn,Movie * movieOut,Movie *)792 void MenuManager::createLowRightButton(const QString &label, BUTTON_TYPE type, Movie *movieIn, Movie *movieOut, Movie * /*movieShake*/)
793 {
794     TextButton *item = new TextButton(label, TextButton::RIGHT, type, this->window->scene, this->window->mainSceneRoot, TextButton::PANEL);
795     item->setRecursiveVisible(false);
796     item->setZValue(10);
797 
798     qreal sw = this->window->scene->sceneRect().width();
799     int xOffset = 70;
800 
801     // create in-animation:
802     DemoItemAnimation *anim = new DemoItemAnimation(item, DemoItemAnimation::ANIM_IN);
803     anim->setDuration(1800 * Colors::animSpeedButtons);
804     anim->setStartPos(QPointF(sw, Colors::contentStartY + Colors::contentHeight - 35));
805     anim->setPosAt(0.5, QPointF(sw, Colors::contentStartY + Colors::contentHeight - 35));
806     anim->setPosAt(0.7, QPointF(xOffset + 535, Colors::contentStartY + Colors::contentHeight - 35));
807     anim->setPosAt(1.0, QPointF(xOffset + 535, Colors::contentStartY + Colors::contentHeight - 26));
808     movieIn->append(anim);
809 
810     // create out-animation:
811     anim = new DemoItemAnimation(item, DemoItemAnimation::ANIM_OUT);
812     anim->hideOnFinished = true;
813     anim->setDuration(400 * Colors::animSpeedButtons);
814     anim->setStartPos(QPointF(xOffset + 535, Colors::contentStartY + Colors::contentHeight - 26));
815     anim->setPosAt(1.0, QPointF(sw, Colors::contentStartY + Colors::contentHeight - 26));
816     movieOut->append(anim);
817 }
818 
createLowRightLeafButton(const QString & label,int xOffset,BUTTON_TYPE type,Movie * movieIn,Movie * movieOut,Movie *)819 void MenuManager::createLowRightLeafButton(const QString &label, int xOffset, BUTTON_TYPE type, Movie *movieIn, Movie *movieOut, Movie * /*movieShake*/)
820 {
821     TextButton *item = new TextButton(label, TextButton::RIGHT, type, this->window->scene, this->window->mainSceneRoot, TextButton::PANEL);
822     item->setRecursiveVisible(false);
823     item->setZValue(10);
824 
825     qreal sw = this->window->scene->sceneRect().width();
826     qreal sh = this->window->scene->sceneRect().height();
827 
828     // create in-animation:
829     DemoItemAnimation *anim = new DemoItemAnimation(item, DemoItemAnimation::ANIM_IN);
830     anim->setDuration(1050 * Colors::animSpeedButtons);
831     anim->setStartPos(QPointF(sw, Colors::contentStartY + Colors::contentHeight - 35));
832     anim->setPosAt(0.10, QPointF(sw, Colors::contentStartY + Colors::contentHeight - 35));
833     anim->setPosAt(0.30, QPointF(xOffset, Colors::contentStartY + Colors::contentHeight - 35));
834     anim->setPosAt(0.35, QPointF(xOffset + 30, Colors::contentStartY + Colors::contentHeight - 35));
835     anim->setPosAt(0.40, QPointF(xOffset, Colors::contentStartY + Colors::contentHeight - 35));
836     anim->setPosAt(0.45, QPointF(xOffset + 5, Colors::contentStartY + Colors::contentHeight - 35));
837     anim->setPosAt(0.50, QPointF(xOffset, Colors::contentStartY + Colors::contentHeight - 35));
838     anim->setPosAt(1.00, QPointF(xOffset, Colors::contentStartY + Colors::contentHeight - 26));
839     movieIn->append(anim);
840 
841     // create out-animation:
842     anim = new DemoItemAnimation(item, DemoItemAnimation::ANIM_OUT);
843     anim->hideOnFinished = true;
844     anim->setDuration(300 * Colors::animSpeedButtons);
845     anim->setStartPos(QPointF(xOffset, Colors::contentStartY + Colors::contentHeight - 26));
846     anim->setPosAt(1.0, QPointF(xOffset, sh));
847     movieOut->append(anim);
848 }
849 
createInfo(DemoItem * item,const QString & name)850 void MenuManager::createInfo(DemoItem *item, const QString &name)
851 {
852     Movie *movie_in = this->score->insertMovie(name);
853     Movie *movie_out = this->score->insertMovie(name + " -out");
854     item->setZValue(8);
855     item->setRecursiveVisible(false);
856 
857     float xOffset = 230.0f;
858     DemoItemAnimation *infoIn = new DemoItemAnimation(item, DemoItemAnimation::ANIM_IN);
859     infoIn->timeline->setCurveShape(QTimeLine::LinearCurve);
860     infoIn->setDuration(650);
861     infoIn->setStartPos(QPointF(this->window->scene->sceneRect().width(), Colors::contentStartY));
862     infoIn->setPosAt(0.60, QPointF(xOffset, Colors::contentStartY));
863     infoIn->setPosAt(0.70, QPointF(xOffset + 20, Colors::contentStartY));
864     infoIn->setPosAt(0.80, QPointF(xOffset, Colors::contentStartY));
865     infoIn->setPosAt(0.90, QPointF(xOffset + 7, Colors::contentStartY));
866     infoIn->setPosAt(1.00, QPointF(xOffset, Colors::contentStartY));
867     movie_in->append(infoIn);
868 
869     DemoItemAnimation *infoOut = new DemoItemAnimation(item, DemoItemAnimation::ANIM_OUT);
870     infoOut->timeline->setCurveShape(QTimeLine::EaseInCurve);
871     infoOut->setDuration(300);
872     infoOut->hideOnFinished = true;
873     infoOut->setStartPos(QPointF(xOffset, Colors::contentStartY));
874     infoOut->setPosAt(1.0, QPointF(-600, Colors::contentStartY));
875     movie_out->append(infoOut);
876 }
877 
createTicker()878 void MenuManager::createTicker()
879 {
880     if (!Colors::noTicker){
881         Movie *movie_in = this->score->insertMovie("ticker");
882         Movie *movie_out = this->score->insertMovie("ticker -out");
883         Movie *movie_activate = this->score->insertMovie("ticker -activate");
884         Movie *movie_deactivate = this->score->insertMovie("ticker -deactivate");
885 
886         this->ticker = new ItemCircleAnimation(this->window->scene, 0);
887         this->ticker->setZValue(50);
888         this->ticker->hide();
889 
890         // Move ticker in:
891         int qtendpos = 485;
892         int qtPosY = 120;
893         this->tickerInAnim = new DemoItemAnimation(this->ticker, DemoItemAnimation::ANIM_IN);
894         this->tickerInAnim->setDuration(500);
895         this->tickerInAnim->setStartPos(QPointF(this->window->scene->sceneRect().width(), Colors::contentStartY + qtPosY));
896         this->tickerInAnim->setPosAt(0.60, QPointF(qtendpos, Colors::contentStartY + qtPosY));
897         this->tickerInAnim->setPosAt(0.70, QPointF(qtendpos + 30, Colors::contentStartY + qtPosY));
898         this->tickerInAnim->setPosAt(0.80, QPointF(qtendpos, Colors::contentStartY + qtPosY));
899         this->tickerInAnim->setPosAt(0.90, QPointF(qtendpos + 5, Colors::contentStartY + qtPosY));
900         this->tickerInAnim->setPosAt(1.00, QPointF(qtendpos, Colors::contentStartY + qtPosY));
901         movie_in->append(this->tickerInAnim);
902 
903         // Move ticker out:
904         DemoItemAnimation *qtOut = new DemoItemAnimation(this->ticker, DemoItemAnimation::ANIM_OUT);
905         qtOut->hideOnFinished = true;
906         qtOut->setDuration(500);
907         qtOut->setStartPos(QPointF(qtendpos, Colors::contentStartY + qtPosY));
908         qtOut->setPosAt(1.00, QPointF(this->window->scene->sceneRect().width() + 700, Colors::contentStartY + qtPosY));
909         movie_out->append(qtOut);
910 
911         // Move ticker in on activate:
912         DemoItemAnimation *qtActivate = new DemoItemAnimation(this->ticker);
913         qtActivate->setDuration(400);
914         qtActivate->setStartPos(QPointF(this->window->scene->sceneRect().width(), Colors::contentStartY + qtPosY));
915         qtActivate->setPosAt(0.60, QPointF(qtendpos, Colors::contentStartY + qtPosY));
916         qtActivate->setPosAt(0.70, QPointF(qtendpos + 30, Colors::contentStartY + qtPosY));
917         qtActivate->setPosAt(0.80, QPointF(qtendpos, Colors::contentStartY + qtPosY));
918         qtActivate->setPosAt(0.90, QPointF(qtendpos + 5, Colors::contentStartY + qtPosY));
919         qtActivate->setPosAt(1.00, QPointF(qtendpos, Colors::contentStartY + qtPosY));
920         movie_activate->append(qtActivate);
921 
922         // Move ticker out on deactivate:
923         DemoItemAnimation *qtDeactivate = new DemoItemAnimation(this->ticker);
924         qtDeactivate->hideOnFinished = true;
925         qtDeactivate->setDuration(400);
926         qtDeactivate->setStartPos(QPointF(qtendpos, Colors::contentStartY + qtPosY));
927         qtDeactivate->setPosAt(1.00, QPointF(qtendpos, 800));
928         movie_deactivate->append(qtDeactivate);
929     }
930 }
931 
createUpnDownButtons()932 void MenuManager::createUpnDownButtons()
933 {
934     float xOffset = 15.0f;
935     float yOffset = 450.0f;
936 
937     this->upButton = new TextButton("", TextButton::LEFT, MenuManager::UP, this->window->scene, this->window->mainSceneRoot, TextButton::UP);
938     this->upButton->prepare();
939     this->upButton->setPos(xOffset, yOffset);
940     this->upButton->setState(TextButton::DISABLED);
941 
942     this->downButton = new TextButton("", TextButton::LEFT, MenuManager::DOWN, this->window->scene, this->window->mainSceneRoot, TextButton::DOWN);
943     this->downButton->prepare();
944     this->downButton->setPos(xOffset + 10 + this->downButton->sceneBoundingRect().width(), yOffset);
945 
946     Movie *movieShake = this->score->insertMovie("upndown -shake");
947 
948     DemoItemAnimation *shakeAnim = new DemoItemAnimation(this->upButton, DemoItemAnimation::ANIM_UNSPECIFIED);
949     shakeAnim->timeline->setCurveShape(QTimeLine::LinearCurve);
950     shakeAnim->setDuration(650);
951     shakeAnim->setStartPos(this->upButton->pos());
952     shakeAnim->setPosAt(0.60, this->upButton->pos());
953     shakeAnim->setPosAt(0.70, this->upButton->pos() + QPointF(-2, 0));
954     shakeAnim->setPosAt(0.80, this->upButton->pos() + QPointF(1, 0));
955     shakeAnim->setPosAt(0.90, this->upButton->pos() + QPointF(-1, 0));
956     shakeAnim->setPosAt(1.00, this->upButton->pos());
957     movieShake->append(shakeAnim);
958 
959     shakeAnim = new DemoItemAnimation(this->downButton, DemoItemAnimation::ANIM_UNSPECIFIED);
960     shakeAnim->timeline->setCurveShape(QTimeLine::LinearCurve);
961     shakeAnim->setDuration(650);
962     shakeAnim->setStartPos(this->downButton->pos());
963     shakeAnim->setPosAt(0.60, this->downButton->pos());
964     shakeAnim->setPosAt(0.70, this->downButton->pos() + QPointF(-5, 0));
965     shakeAnim->setPosAt(0.80, this->downButton->pos() + QPointF(-3, 0));
966     shakeAnim->setPosAt(0.90, this->downButton->pos() + QPointF(-1, 0));
967     shakeAnim->setPosAt(1.00, this->downButton->pos());
968     movieShake->append(shakeAnim);
969 }
970 
createBackButton()971 void MenuManager::createBackButton()
972 {
973     Movie *backIn = this->score->insertMovie("back -in");
974     Movie *backOut = this->score->insertMovie("back -out");
975     Movie *backShake = this->score->insertMovie("back -shake");
976     createLowLeftButton(QLatin1String("Back"), ROOT, backIn, backOut, backShake, Colors::rootMenuName);
977 }
978