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