707d7705b23b700eb4be956c642ceadeee17dc28
[profile/ivi/qtdeclarative.git] / tools / qmlviewer / main.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the tools applications of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarative.h"
43 #include "qmlruntime.h"
44 #include "qdeclarativeengine.h"
45 #include "loggerwidget.h"
46 #include <QWidget>
47 #include <QDir>
48 #include <QApplication>
49 #include <QTranslator>
50 #include <QDebug>
51 #include <QMessageBox>
52 #include <QAtomicInt>
53 #include <QLibraryInfo>
54 #include "qdeclarativetester.h"
55
56 QT_USE_NAMESPACE
57
58 QtMsgHandler systemMsgOutput = 0;
59
60 static QDeclarativeViewer *openFile(const QString &fileName);
61 static void showViewer(QDeclarativeViewer *viewer);
62
63 QString warnings;
64 void exitApp(int i)
65 {
66 #ifdef Q_OS_WIN
67     // Debugging output is not visible by default on Windows -
68     // therefore show modal dialog with errors instead.
69     if (!warnings.isEmpty()) {
70         QMessageBox::warning(0, QApplication::translate("QDeclarativeViewer", "Qt QML Viewer"), warnings);
71     }
72 #endif
73     exit(i);
74 }
75
76 QWeakPointer<LoggerWidget> logger;
77 static QAtomicInt recursiveLock(0);
78
79 void myMessageOutput(QtMsgType type, const char *msg)
80 {
81     QString strMsg = QString::fromLatin1(msg);
82
83     if (!QCoreApplication::closingDown()) {
84         if (!logger.isNull()) {
85             if (recursiveLock.testAndSetOrdered(0, 1)) {
86                 QMetaObject::invokeMethod(logger.data(), "append", Q_ARG(QString, strMsg));
87                 recursiveLock.store(0);
88             }
89         } else {
90             warnings += strMsg;
91             warnings += QLatin1Char('\n');
92         }
93     }
94
95     if (systemMsgOutput) {
96         systemMsgOutput(type, msg);
97     } else { // Unix
98         fprintf(stderr, "%s\n", msg);
99         fflush(stderr);
100     }
101 }
102
103 static QDeclarativeViewer* globalViewer = 0;
104
105 // The qml file that is shown if the user didn't specify a QML file
106 QString initialFile = QLatin1String("qrc:/startup/startup.qml");
107
108 void usage()
109 {
110     qWarning("Usage: qmlviewer [options] <filename>");
111     qWarning(" ");
112     qWarning(" options:");
113     qWarning("  -v, -version ............................. display version");
114     qWarning("  -frameless ............................... run with no window frame");
115     qWarning("  -maximized................................ run maximized");
116     qWarning("  -fullscreen............................... run fullscreen");
117     qWarning("  -stayontop................................ keep viewer window on top");
118     qWarning("  -sizeviewtorootobject .................... the view resizes to the changes in the content");
119     qWarning("  -sizerootobjecttoview .................... the content resizes to the changes in the view (default)");
120     qWarning("  -qmlbrowser .............................. use a QML-based file browser");
121     qWarning("  -warnings [show|hide]..................... show warnings in a separate log window");
122     qWarning("  -recordfile <output> ..................... set video recording file");
123     qWarning("                                              - ImageMagick 'convert' for GIF)");
124     qWarning("                                              - png file for raw frames");
125     qWarning("                                              - 'ffmpeg' for other formats");
126     qWarning("  -recorddither ordered|threshold|floyd .... set GIF dither recording mode");
127     qWarning("  -recordrate <fps> ........................ set recording frame rate");
128     qWarning("  -record arg .............................. add a recording process argument");
129     qWarning("  -autorecord [from-]<tomilliseconds> ...... set recording to start and stop");
130     qWarning("  -devicekeys .............................. use numeric keys (see F1)");
131     qWarning("  -dragthreshold <size> .................... set mouse drag threshold size");
132     qWarning("  -netcache <size> ......................... set disk cache to size bytes");
133     qWarning("  -translation <translationfile> ........... set the language to run in");
134     qWarning("  -I <directory> ........................... prepend to the module import search path,");
135     qWarning("                                             display path if <directory> is empty");
136     qWarning("  -P <directory> ........................... prepend to the plugin search path");
137 #if defined(Q_OS_MAC)
138     qWarning("  -no-opengl ............................... don't use a QGLWidget for the viewport");
139     qWarning("  -opengl .................................. use a QGLWidget for the viewport (default)");
140 #else
141     qWarning("  -no-opengl ............................... don't use a QGLWidget for the viewport (default)");
142     qWarning("  -opengl .................................. use a QGLWidget for the viewport");
143 #endif
144     qWarning("  -script <path> ........................... set the script to use");
145     qWarning("  -scriptopts <options>|help ............... set the script options to use");
146
147     qWarning(" ");
148     qWarning(" Press F1 for interactive help");
149
150     exitApp(1);
151 }
152
153 void scriptOptsUsage()
154 {
155     qWarning("Usage: qmlviewer -scriptopts <option>[,<option>...] ...");
156     qWarning(" options:");
157     qWarning("  record ................................... record a new script");
158     qWarning("  play ..................................... playback an existing script");
159     qWarning("  testimages ............................... record images or compare images on playback");
160     qWarning("  testerror ................................ test 'error' property of root item on playback");
161     qWarning("  testskip  ................................ test 'skip' property of root item on playback");
162     qWarning("  snapshot ................................. file being recorded is static,");
163     qWarning("                                             only one frame will be recorded or tested");
164     qWarning("  exitoncomplete ........................... cleanly exit the viewer on script completion");
165     qWarning("  exitonfailure ............................ immediately exit the viewer on script failure");
166     qWarning("  saveonexit ............................... save recording on viewer exit");
167     qWarning(" ");
168     qWarning(" One of record, play or both must be specified.");
169
170     exitApp(1);
171 }
172
173 enum WarningsConfig { ShowWarnings, HideWarnings, DefaultWarnings };
174
175 struct ViewerOptions
176 {
177     ViewerOptions()
178         : frameless(false),
179           fps(0.0),
180           autorecord_from(0),
181           autorecord_to(0),
182           dither(QLatin1String("none")),
183           runScript(false),
184           devkeys(false),
185           cache(0),
186           useGL(false),
187           fullScreen(false),
188           stayOnTop(false),
189           maximized(false),
190           useNativeFileBrowser(true),
191           experimentalGestures(false),
192           warningsConfig(DefaultWarnings),
193           sizeToView(true)
194     {
195
196 #if defined(Q_OS_MAC)
197         useGL = true;
198 #endif
199     }
200
201     bool frameless;
202     double fps;
203     int autorecord_from;
204     int autorecord_to;
205     QString dither;
206     QString recordfile;
207     QStringList recordargs;
208     QStringList imports;
209     QStringList plugins;
210     QString script;
211     QString scriptopts;
212     bool runScript;
213     bool devkeys;
214     int cache;
215     QString translationFile;
216     bool useGL;
217     bool fullScreen;
218     bool stayOnTop;
219     bool maximized;
220     bool useNativeFileBrowser;
221     bool experimentalGestures;
222
223     WarningsConfig warningsConfig;
224     bool sizeToView;
225
226     QDeclarativeViewer::ScriptOptions scriptOptions;
227 };
228
229 static ViewerOptions opts;
230 static QStringList fileNames;
231
232 class Application : public QApplication
233 {
234     Q_OBJECT
235 public:
236     Application(int &argc, char **&argv)
237         : QApplication(argc, argv)
238     {}
239
240 protected:
241     bool event(QEvent *ev)
242     {
243         if (ev->type() != QEvent::FileOpen)
244             return QApplication::event(ev);
245
246         QFileOpenEvent *fev = static_cast<QFileOpenEvent *>(ev);
247
248         globalViewer->open(fev->file());
249         if (!globalViewer->isVisible())
250             showViewer(globalViewer);
251
252         return true;
253     }
254
255 private Q_SLOTS:
256     void showInitialViewer()
257     {
258         QApplication::processEvents();
259
260         QDeclarativeViewer *viewer = globalViewer;
261         if (!viewer)
262             return;
263         if (viewer->currentFile().isEmpty()) {
264             if(opts.useNativeFileBrowser)
265                 viewer->open(initialFile);
266             else
267                 viewer->openFile();
268         }
269         if (!viewer->isVisible())
270             showViewer(viewer);
271     }
272 };
273
274 static void parseScriptOptions()
275 {
276     QStringList options =
277         opts.scriptopts.split(QLatin1Char(','), QString::SkipEmptyParts);
278
279     QDeclarativeViewer::ScriptOptions scriptOptions = 0;
280     for (int i = 0; i < options.count(); ++i) {
281         const QString &option = options.at(i);
282         if (option == QLatin1String("help")) {
283             scriptOptsUsage();
284         } else if (option == QLatin1String("play")) {
285             scriptOptions |= QDeclarativeViewer::Play;
286         } else if (option == QLatin1String("record")) {
287             scriptOptions |= QDeclarativeViewer::Record;
288         } else if (option == QLatin1String("testimages")) {
289             scriptOptions |= QDeclarativeViewer::TestImages;
290         } else if (option == QLatin1String("testerror")) {
291             scriptOptions |= QDeclarativeViewer::TestErrorProperty;
292         } else if (option == QLatin1String("testskip")) {
293             scriptOptions |= QDeclarativeViewer::TestSkipProperty;
294         } else if (option == QLatin1String("exitoncomplete")) {
295             scriptOptions |= QDeclarativeViewer::ExitOnComplete;
296         } else if (option == QLatin1String("exitonfailure")) {
297             scriptOptions |= QDeclarativeViewer::ExitOnFailure;
298         } else if (option == QLatin1String("saveonexit")) {
299             scriptOptions |= QDeclarativeViewer::SaveOnExit;
300         } else if (option == QLatin1String("snapshot")) {
301             scriptOptions |= QDeclarativeViewer::Snapshot;
302         } else {
303             scriptOptsUsage();
304         }
305     }
306
307     opts.scriptOptions = scriptOptions;
308 }
309
310 static void parseCommandLineOptions(const QStringList &arguments)
311 {
312     for (int i = 1; i < arguments.count(); ++i) {
313         bool lastArg = (i == arguments.count() - 1);
314         QString arg = arguments.at(i);
315         if (arg == QLatin1String("-frameless")) {
316             opts.frameless = true;
317         } else if (arg == QLatin1String("-maximized")) {
318             opts.maximized = true;
319         } else if (arg == QLatin1String("-fullscreen")) {
320             opts.fullScreen = true;
321         } else if (arg == QLatin1String("-stayontop")) {
322             opts.stayOnTop = true;
323         } else if (arg == QLatin1String("-netcache")) {
324             if (lastArg) usage();
325             opts.cache = arguments.at(++i).toInt();
326         } else if (arg == QLatin1String("-recordrate")) {
327             if (lastArg) usage();
328             opts.fps = arguments.at(++i).toDouble();
329         } else if (arg == QLatin1String("-recordfile")) {
330             if (lastArg) usage();
331             opts.recordfile = arguments.at(++i);
332         } else if (arg == QLatin1String("-record")) {
333             if (lastArg) usage();
334             opts.recordargs << arguments.at(++i);
335         } else if (arg == QLatin1String("-recorddither")) {
336             if (lastArg) usage();
337             opts.dither = arguments.at(++i);
338         } else if (arg == QLatin1String("-autorecord")) {
339             if (lastArg) usage();
340             QString range = arguments.at(++i);
341             int dash = range.indexOf(QLatin1Char('-'));
342             if (dash > 0)
343                 opts.autorecord_from = range.left(dash).toInt();
344             opts.autorecord_to = range.mid(dash+1).toInt();
345         } else if (arg == QLatin1String("-devicekeys")) {
346             opts.devkeys = true;
347         } else if (arg == QLatin1String("-dragthreshold")) {
348             if (lastArg) usage();
349             qApp->setStartDragDistance(arguments.at(++i).toInt());
350         } else if (arg == QLatin1String("-v") || arg == QLatin1String("-version")) {
351             qWarning("Qt QML Viewer version %s", QT_VERSION_STR);
352             exitApp(0);
353         } else if (arg == QLatin1String("-translation")) {
354             if (lastArg) usage();
355             opts.translationFile = arguments.at(++i);
356         } else if (arg == QLatin1String("-no-opengl")) {
357             opts.useGL = false;
358         } else if (arg == QLatin1String("-opengl")) {
359             opts.useGL = true;
360         } else if (arg == QLatin1String("-qmlbrowser")) {
361             opts.useNativeFileBrowser = false;
362         } else if (arg == QLatin1String("-warnings")) {
363             if (lastArg) usage();
364             QString warningsStr = arguments.at(++i);
365             if (warningsStr == QLatin1String("show")) {
366                 opts.warningsConfig = ShowWarnings;
367             } else if (warningsStr == QLatin1String("hide")) {
368                 opts.warningsConfig = HideWarnings;
369             } else {
370                 usage();
371             }
372         } else if (arg == QLatin1String("-I") || arg == QLatin1String("-L")) {
373             if (arg == QLatin1String("-L"))
374                 qWarning("-L option provided for compatibility only, use -I instead");
375             if (lastArg) {
376                 QDeclarativeEngine tmpEngine;
377                 QString paths = tmpEngine.importPathList().join(QLatin1String(":"));
378                 qWarning("Current search path: %s", paths.toLocal8Bit().constData());
379                 exitApp(0);
380             }
381             opts.imports << arguments.at(++i);
382         } else if (arg == QLatin1String("-P")) {
383             if (lastArg) usage();
384             opts.plugins << arguments.at(++i);
385         } else if (arg == QLatin1String("-script")) {
386             if (lastArg) usage();
387             opts.script = arguments.at(++i);
388         } else if (arg == QLatin1String("-scriptopts")) {
389             if (lastArg) usage();
390             opts.scriptopts = arguments.at(++i);
391         } else if (arg == QLatin1String("-savescript")) {
392             if (lastArg) usage();
393             opts.script = arguments.at(++i);
394             opts.runScript = false;
395         } else if (arg == QLatin1String("-playscript")) {
396             if (lastArg) usage();
397             opts.script = arguments.at(++i);
398             opts.runScript = true;
399         } else if (arg == QLatin1String("-sizeviewtorootobject")) {
400             opts.sizeToView = false;
401         } else if (arg == QLatin1String("-sizerootobjecttoview")) {
402             opts.sizeToView = true;
403         } else if (arg == QLatin1String("-experimentalgestures")) {
404             opts.experimentalGestures = true;
405         } else if (!arg.startsWith(QLatin1Char('-'))) {
406             fileNames.append(arg);
407         } else if (true || arg == QLatin1String("-help")) {
408             usage();
409         }
410     }
411
412     if (!opts.scriptopts.isEmpty()) {
413
414         parseScriptOptions();
415
416         if (opts.script.isEmpty())
417             usage();
418
419         if (!(opts.scriptOptions & QDeclarativeViewer::Record) && !(opts.scriptOptions & QDeclarativeViewer::Play))
420             scriptOptsUsage();
421     }  else if (!opts.script.isEmpty()) {
422         usage();
423     }
424
425 }
426
427 static QDeclarativeViewer *createViewer()
428 {
429     Qt::WFlags wflags = (opts.frameless ? Qt::FramelessWindowHint : Qt::Widget);
430     if (opts.stayOnTop)
431         wflags |= Qt::WindowStaysOnTopHint;
432
433     QDeclarativeViewer *viewer = new QDeclarativeViewer(0, wflags);
434     viewer->setAttribute(Qt::WA_DeleteOnClose, true);
435     viewer->setUseGL(opts.useGL);
436
437     if (!opts.scriptopts.isEmpty()) {
438         viewer->setScriptOptions(opts.scriptOptions);
439         viewer->setScript(opts.script);
440     }
441
442     logger = viewer->warningsWidget();
443     if (opts.warningsConfig == ShowWarnings) {
444         logger.data()->setDefaultVisibility(LoggerWidget::ShowWarnings);
445         logger.data()->show();
446     } else if (opts.warningsConfig == HideWarnings){
447         logger.data()->setDefaultVisibility(LoggerWidget::HideWarnings);
448     }
449
450     if (opts.experimentalGestures)
451         viewer->enableExperimentalGestures();
452
453     foreach (QString lib, opts.imports)
454         viewer->addLibraryPath(lib);
455
456     foreach (QString plugin, opts.plugins)
457         viewer->addPluginPath(plugin);
458
459     viewer->setNetworkCacheSize(opts.cache);
460     viewer->setRecordFile(opts.recordfile);
461     viewer->setSizeToView(opts.sizeToView);
462     if (opts.fps > 0)
463         viewer->setRecordRate(opts.fps);
464     if (opts.autorecord_to)
465         viewer->setAutoRecord(opts.autorecord_from, opts.autorecord_to);
466     if (opts.devkeys)
467         viewer->setDeviceKeys(true);
468     viewer->setRecordDither(opts.dither);
469     if (opts.recordargs.count())
470         viewer->setRecordArgs(opts.recordargs);
471
472     viewer->setUseNativeFileBrowser(opts.useNativeFileBrowser);
473
474     return viewer;
475 }
476
477 void showViewer(QDeclarativeViewer *viewer)
478 {
479     if (opts.fullScreen)
480         viewer->showFullScreen();
481     else if (opts.maximized)
482         viewer->showMaximized();
483     else
484         viewer->show();
485     viewer->raise();
486 }
487
488 QDeclarativeViewer *openFile(const QString &fileName)
489 {
490     QDeclarativeViewer *viewer = globalViewer;
491
492     viewer->open(fileName);
493     showViewer(viewer);
494
495     return viewer;
496 }
497
498 int main(int argc, char ** argv)
499 {
500     systemMsgOutput = qInstallMsgHandler(myMessageOutput);
501     Application app(argc, argv);
502     app.setApplicationName(QLatin1String("QtQmlViewer"));
503     app.setOrganizationName(QLatin1String("Nokia"));
504     app.setOrganizationDomain(QLatin1String("nokia.com"));
505
506     QDeclarativeViewer::registerTypes();
507     QDeclarativeTester::registerTypes();
508
509     parseCommandLineOptions(app.arguments());
510
511     QTranslator translator;
512     QTranslator qtTranslator;
513     QString sysLocale = QLocale::system().name();
514     if (translator.load(QLatin1String("qmlviewer_") + sysLocale, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
515         app.installTranslator(&translator);
516         if (qtTranslator.load(QLatin1String("qt_") + sysLocale, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
517             app.installTranslator(&qtTranslator);
518         } else {
519             app.removeTranslator(&translator);
520         }
521     }
522
523     QTranslator qmlTranslator;
524     if (!opts.translationFile.isEmpty()) {
525         if (qmlTranslator.load(opts.translationFile)) {
526             app.installTranslator(&qmlTranslator);
527         } else {
528             qWarning() << "Could not load the translation file" << opts.translationFile;
529         }
530     }
531
532     if (opts.fullScreen && opts.maximized)
533         qWarning() << "Both -fullscreen and -maximized specified. Using -fullscreen.";
534
535     if (fileNames.isEmpty()) {
536         QFile qmlapp(QLatin1String("qmlapp"));
537         if (qmlapp.exists() && qmlapp.open(QFile::ReadOnly)) {
538             QString content = QString::fromUtf8(qmlapp.readAll());
539             qmlapp.close();
540
541             int newline = content.indexOf(QLatin1Char('\n'));
542             if (newline >= 0)
543                 fileNames += content.left(newline);
544             else
545                 fileNames += content;
546         }
547     }
548
549     globalViewer = createViewer();
550
551     if (fileNames.isEmpty()) {
552         // show the initial viewer delayed.
553         // This prevents an initial viewer popping up while there
554         // are FileOpen events coming through the event queue
555         QTimer::singleShot(1, &app, SLOT(showInitialViewer()));
556     } else {
557         foreach (const QString &fileName, fileNames)
558             openFile(fileName);
559     }
560
561     QObject::connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
562
563     return app.exec();
564 }
565
566 #include "main.moc"