5ece654d0882ba244228cee351a8baedf55850d2
[profile/ivi/qtdeclarative.git] / tools / qmlviewer / qmlruntime.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 <QtQuick1/qdeclarativeview.h>
43
44 #ifdef hz
45 #undef hz
46 #endif
47 #ifdef Q_WS_MAEMO_5
48 #  include <QMaemo5ValueButton>
49 #  include <QMaemo5ListPickSelector>
50 #  include <QWidgetAction>
51 #  include <QStringListModel>
52 #  include "ui_recopts_maemo5.h"
53 #else
54 #  include "ui_recopts.h"
55 #endif
56
57 #include "qmlruntime.h"
58 #include <qdeclarativecontext.h>
59 #include <qdeclarativeengine.h>
60 #include <qdeclarativenetworkaccessmanagerfactory.h>
61 #include "qdeclarative.h"
62 #include <QAbstractAnimation>
63 #include <private/qabstractanimation_p.h>
64
65 #include <QSettings>
66 #include <QMimeData>
67 #include <QXmlStreamReader>
68 #include <QBuffer>
69 #include <QNetworkReply>
70 #include <QNetworkCookieJar>
71 #include <QNetworkDiskCache>
72 #include <QNetworkAccessManager>
73 #include <QSignalMapper>
74 #include <QDeclarativeComponent>
75 #include <QWidget>
76 #include <QApplication>
77 #include <QTranslator>
78 #include <QDir>
79 #include <QTextBrowser>
80 #include <QFile>
81 #include <QFileInfo>
82 #include <QVBoxLayout>
83 #include <QProgressDialog>
84 #include <QProcess>
85 #include <QMenuBar>
86 #include <QMenu>
87 #include <QAction>
88 #include <QFileDialog>
89 #include <QInputDialog>
90 #include <QTimer>
91 #include <QGraphicsObject>
92 #include <QNetworkProxyFactory>
93 #include <QKeyEvent>
94 #include <QMimeData>
95 #include <QMutex>
96 #include <QMutexLocker>
97 #include "proxysettings.h"
98 #include "deviceorientation.h"
99
100 #ifdef GL_SUPPORTED
101 #include <QGLWidget>
102 #endif
103
104 #if defined(Q_WS_S60)
105 #include <aknappui.h> // For locking app orientation
106 #endif
107
108 #include <qdeclarativetester.h>
109
110 QT_BEGIN_NAMESPACE
111
112 class DragAndDropView : public QDeclarativeView
113 {
114     Q_OBJECT
115 public:
116     DragAndDropView(QDeclarativeViewer *parent = 0)
117     : QDeclarativeView(parent)
118     {
119         setAcceptDrops(true);
120     }
121
122     void dragEnterEvent(QDragEnterEvent *event)
123     {
124         const QMimeData *mimeData = event->mimeData();
125         if (mimeData->hasUrls())
126             event->acceptProposedAction();
127     }
128
129     void dragMoveEvent(QDragMoveEvent *event)
130     {
131         event->acceptProposedAction();
132     }
133
134     void dragLeaveEvent(QDragLeaveEvent *event)
135     {
136         event->accept();
137     }
138
139     void dropEvent(QDropEvent *event)
140     {
141         const QMimeData *mimeData = event->mimeData();
142         if (!mimeData->hasUrls())
143             return;
144         const QList<QUrl> urlList = mimeData->urls();
145         foreach (const QUrl &url, urlList) {
146             if (url.scheme() == QLatin1String("file")) {
147                 static_cast<QDeclarativeViewer *>(parent())->open(url.toLocalFile());
148                 event->accept();
149                 return;
150             }
151         }
152     }
153 };
154
155 class Runtime : public QObject
156 {
157     Q_OBJECT
158
159     Q_PROPERTY(bool isActiveWindow READ isActiveWindow NOTIFY isActiveWindowChanged)
160     Q_PROPERTY(DeviceOrientation::Orientation orientation READ orientation NOTIFY orientationChanged)
161
162 public:
163     static Runtime* instance()
164     {
165         static Runtime *instance = 0;
166         if (!instance)
167             instance = new Runtime;
168         return instance;
169     }
170
171     bool isActiveWindow() const { return activeWindow; }
172     void setActiveWindow(bool active)
173     {
174         if (active == activeWindow)
175             return;
176         activeWindow = active;
177         emit isActiveWindowChanged();
178     }
179
180     DeviceOrientation::Orientation orientation() const { return DeviceOrientation::instance()->orientation(); }
181
182 Q_SIGNALS:
183     void isActiveWindowChanged();
184     void orientationChanged();
185
186 private:
187     Runtime(QObject *parent=0) : QObject(parent), activeWindow(false)
188     {
189         connect(DeviceOrientation::instance(), SIGNAL(orientationChanged()),
190                 this, SIGNAL(orientationChanged()));
191     }
192
193     bool activeWindow;
194 };
195
196
197
198 #if defined(Q_WS_MAEMO_5)
199
200 class Maemo5PickerAction : public QWidgetAction {
201     Q_OBJECT
202 public:
203     Maemo5PickerAction(const QString &text, QActionGroup *actions, QObject *parent)
204         : QWidgetAction(parent), m_text(text), m_actions(actions)
205     { }
206
207     QWidget *createWidget(QWidget *parent)
208     {
209         QMaemo5ValueButton *button = new QMaemo5ValueButton(m_text, parent);
210         button->setValueLayout(QMaemo5ValueButton::ValueUnderTextCentered);
211         QMaemo5ListPickSelector *pick = new QMaemo5ListPickSelector(button);
212         button->setPickSelector(pick);
213         if (m_actions) {
214             QStringList sl;
215             int curIdx = -1, idx = 0;
216             foreach (QAction *a, m_actions->actions()) {
217                 sl << a->text();
218                 if (a->isChecked())
219                     curIdx = idx;
220                 idx++;
221             }
222             pick->setModel(new QStringListModel(sl));
223             pick->setCurrentIndex(curIdx);
224         } else {
225             button->setEnabled(false);
226         }
227         connect(pick, SIGNAL(selected(QString)), this, SLOT(emitTriggered()));
228         return button;
229     }
230
231 private slots:
232     void emitTriggered()
233     {
234         QMaemo5ListPickSelector *pick = qobject_cast<QMaemo5ListPickSelector *>(sender());
235         if (!pick)
236             return;
237         int idx = pick->currentIndex();
238
239         if (m_actions && idx >= 0 && idx < m_actions->actions().count())
240             m_actions->actions().at(idx)->trigger();
241     }
242
243 private:
244     QString m_text;
245     QPointer<QActionGroup> m_actions;
246 };
247
248 #endif // Q_WS_MAEMO_5
249
250 static struct { const char *name, *args; } ffmpegprofiles[] = {
251     {"Maximum Quality", "-sameq"},
252     {"High Quality", "-qmax 2"},
253     {"Medium Quality", "-qmax 6"},
254     {"Low Quality", "-qmax 16"},
255     {"Custom ffmpeg arguments", ""},
256     {0,0}
257 };
258
259 class RecordingDialog : public QDialog, public Ui::RecordingOptions {
260     Q_OBJECT
261
262 public:
263     RecordingDialog(QWidget *parent) : QDialog(parent)
264     {
265         setupUi(this);
266 #ifndef Q_WS_MAEMO_5
267         hz->setValidator(new QDoubleValidator(hz));
268 #endif
269         for (int i=0; ffmpegprofiles[i].name; ++i) {
270             profile->addItem(QString::fromAscii(ffmpegprofiles[i].name));
271         }
272     }
273
274     void setArguments(QString a)
275     {
276         int i;
277         for (i=0; ffmpegprofiles[i].args[0]; ++i) {
278             if (QString::fromAscii(ffmpegprofiles[i].args) == a) {
279                 profile->setCurrentIndex(i);
280                 args->setText(QString::fromAscii(ffmpegprofiles[i].args));
281                 return;
282             }
283         }
284         customargs = a;
285         args->setText(a);
286         profile->setCurrentIndex(i);
287     }
288
289     QString arguments() const
290     {
291         int i = profile->currentIndex();
292         return ffmpegprofiles[i].args[0] ? QLatin1String(ffmpegprofiles[i].args) : customargs;
293     }
294
295     void setOriginalSize(const QSize &s)
296     {
297         QString str = tr("Original (%1x%2)").arg(s.width()).arg(s.height());
298
299 #ifdef Q_WS_MAEMO_5
300         sizeCombo->setItemText(0, str);
301 #else
302         sizeOriginal->setText(str);
303         if (sizeWidth->value()<=1) {
304             sizeWidth->setValue(s.width());
305             sizeHeight->setValue(s.height());
306         }
307 #endif
308     }
309
310     void showffmpegOptions(bool b)
311     {
312 #ifdef Q_WS_MAEMO_5
313         profileLabel->setVisible(b);
314         profile->setVisible(b);
315         ffmpegHelp->setVisible(b);
316         args->setVisible(b);
317 #else
318         ffmpegOptions->setVisible(b);
319 #endif
320     }
321
322     void showRateOptions(bool b)
323     {
324 #ifdef Q_WS_MAEMO_5
325         rateLabel->setVisible(b);
326         rateCombo->setVisible(b);
327 #else
328         rateOptions->setVisible(b);
329 #endif
330     }
331
332     void setVideoRate(int rate)
333     {
334 #ifdef Q_WS_MAEMO_5
335         int idx;
336         if (rate >= 60)
337             idx = 0;
338         else if (rate >= 50)
339             idx = 2;
340         else if (rate >= 25)
341             idx = 3;
342         else if (rate >= 24)
343             idx = 4;
344         else if (rate >= 20)
345             idx = 5;
346         else if (rate >= 15)
347             idx = 6;
348         else
349             idx = 7;
350         rateCombo->setCurrentIndex(idx);
351 #else
352         if (rate == 24)
353             hz24->setChecked(true);
354         else if (rate == 25)
355             hz25->setChecked(true);
356         else if (rate == 50)
357             hz50->setChecked(true);
358         else if (rate == 60)
359             hz60->setChecked(true);
360         else {
361             hzCustom->setChecked(true);
362             hz->setText(QString::number(rate));
363         }
364 #endif
365     }
366
367     int videoRate() const
368     {
369 #ifdef Q_WS_MAEMO_5
370         switch (rateCombo->currentIndex()) {
371             case 0: return 60;
372             case 1: return 50;
373             case 2: return 25;
374             case 3: return 24;
375             case 4: return 20;
376             case 5: return 15;
377             case 7: return 10;
378             default: return 60;
379         }
380 #else
381         if (hz24->isChecked())
382             return 24;
383         else if (hz25->isChecked())
384             return 25;
385         else if (hz50->isChecked())
386             return 50;
387         else if (hz60->isChecked())
388             return 60;
389         else {
390             return hz->text().toInt();
391         }
392 #endif
393     }
394
395     QSize videoSize() const
396     {
397 #ifdef Q_WS_MAEMO_5
398         switch (sizeCombo->currentIndex()) {
399             case 0: return QSize();
400             case 1: return QSize(640,480);
401             case 2: return QSize(320,240);
402             case 3: return QSize(1280,720);
403             default: return QSize();
404         }
405 #else
406         if (sizeOriginal->isChecked())
407             return QSize();
408         else if (size720p->isChecked())
409             return QSize(1280,720);
410         else if (sizeVGA->isChecked())
411             return QSize(640,480);
412         else if (sizeQVGA->isChecked())
413             return QSize(320,240);
414         else
415             return QSize(sizeWidth->value(), sizeHeight->value());
416 #endif
417     }
418
419
420
421 private slots:
422     void pickProfile(int i)
423     {
424         if (ffmpegprofiles[i].args[0]) {
425             args->setText(QLatin1String(ffmpegprofiles[i].args));
426         } else {
427             args->setText(customargs);
428         }
429     }
430
431     void storeCustomArgs(QString s)
432     {
433         setArguments(s);
434     }
435
436 private:
437     QString customargs;
438 };
439
440 class PersistentCookieJar : public QNetworkCookieJar {
441 public:
442     PersistentCookieJar(QObject *parent) : QNetworkCookieJar(parent) { load(); }
443     ~PersistentCookieJar() { save(); }
444
445     virtual QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const
446     {
447         QMutexLocker lock(&mutex);
448         return QNetworkCookieJar::cookiesForUrl(url);
449     }
450
451     virtual bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
452     {
453         QMutexLocker lock(&mutex);
454         return QNetworkCookieJar::setCookiesFromUrl(cookieList, url);
455     }
456
457 private:
458     void save()
459     {
460         QMutexLocker lock(&mutex);
461         QList<QNetworkCookie> list = allCookies();
462         QByteArray data;
463         foreach (QNetworkCookie cookie, list) {
464             if (!cookie.isSessionCookie()) {
465                 data.append(cookie.toRawForm());
466                 data.append("\n");
467             }
468         }
469         QSettings settings;
470         settings.setValue(QLatin1String("Cookies"), data);
471     }
472
473     void load()
474     {
475         QMutexLocker lock(&mutex);
476         QSettings settings;
477         QByteArray data = settings.value(QLatin1String("Cookies")).toByteArray();
478         setAllCookies(QNetworkCookie::parseCookies(data));
479     }
480
481     mutable QMutex mutex;
482 };
483
484 class SystemProxyFactory : public QNetworkProxyFactory
485 {
486 public:
487     SystemProxyFactory() : proxyDirty(true), httpProxyInUse(false) {
488     }
489
490     virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query)
491     {
492         if (proxyDirty)
493             setupProxy();
494         QString protocolTag = query.protocolTag();
495         if (httpProxyInUse && (protocolTag == QLatin1String("http") || protocolTag == QLatin1String("https"))) {
496             QList<QNetworkProxy> ret;
497             ret << httpProxy;
498             return ret;
499         }
500 #ifdef Q_OS_WIN
501         // systemProxyForQuery can take insanely long on Windows (QTBUG-10106)
502         return QNetworkProxyFactory::proxyForQuery(query);
503 #else
504         return QNetworkProxyFactory::systemProxyForQuery(query);
505 #endif
506     }
507
508     void setupProxy() {
509         // Don't bother locking because we know that the proxy only
510         // changes in response to the settings dialog and that
511         // the view will be reloaded.
512         proxyDirty = false;
513         httpProxyInUse = ProxySettings::httpProxyInUse();
514         if (httpProxyInUse)
515             httpProxy = ProxySettings::httpProxy();
516     }
517
518     void proxyChanged() {
519         proxyDirty = true;
520     }
521
522 private:
523     volatile bool proxyDirty;
524     bool httpProxyInUse;
525     QNetworkProxy httpProxy;
526 };
527
528 class NetworkAccessManagerFactory : public QObject, public QDeclarativeNetworkAccessManagerFactory
529 {
530     Q_OBJECT
531 public:
532     NetworkAccessManagerFactory() : cacheSize(0) {}
533     ~NetworkAccessManagerFactory() {}
534
535     QNetworkAccessManager *create(QObject *parent);
536
537     void setCacheSize(int size) {
538         if (size != cacheSize) {
539             cacheSize = size;
540         }
541     }
542
543     void proxyChanged() {
544         foreach (QNetworkAccessManager *nam, namList) {
545             static_cast<SystemProxyFactory*>(nam->proxyFactory())->proxyChanged();
546         }
547     }
548
549     static PersistentCookieJar *cookieJar;
550
551 private slots:
552     void managerDestroyed(QObject *obj) {
553         namList.removeOne(static_cast<QNetworkAccessManager*>(obj));
554     }
555
556 private:
557     QMutex mutex;
558     int cacheSize;
559     QList<QNetworkAccessManager*> namList;
560 };
561
562 PersistentCookieJar *NetworkAccessManagerFactory::cookieJar = 0;
563
564 static void cleanup_cookieJar()
565 {
566     delete NetworkAccessManagerFactory::cookieJar;
567     NetworkAccessManagerFactory::cookieJar = 0;
568 }
569
570 QNetworkAccessManager *NetworkAccessManagerFactory::create(QObject *parent)
571 {
572     QMutexLocker lock(&mutex);
573     QNetworkAccessManager *manager = new QNetworkAccessManager(parent);
574     if (!cookieJar) {
575         qAddPostRoutine(cleanup_cookieJar);
576         cookieJar = new PersistentCookieJar(0);
577     }
578     manager->setCookieJar(cookieJar);
579     cookieJar->setParent(0);
580     manager->setProxyFactory(new SystemProxyFactory);
581     if (cacheSize > 0) {
582         QNetworkDiskCache *cache = new QNetworkDiskCache;
583         cache->setCacheDirectory(QDir::tempPath()+QLatin1String("/qml-viewer-network-cache"));
584         cache->setMaximumCacheSize(cacheSize);
585         manager->setCache(cache);
586     } else {
587         manager->setCache(0);
588     }
589     connect(manager, SIGNAL(destroyed(QObject*)), this, SLOT(managerDestroyed(QObject*)));
590     namList.append(manager);
591     return manager;
592 }
593
594 QString QDeclarativeViewer::getVideoFileName()
595 {
596     QString title = convertAvailable || ffmpegAvailable ? tr("Save Video File") : tr("Save PNG Frames");
597     QStringList types;
598     if (ffmpegAvailable) types += tr("Common Video files")+QLatin1String(" (*.avi *.mpeg *.mov)");
599     if (convertAvailable) types += tr("GIF Animation")+QLatin1String(" (*.gif)");
600     types += tr("Individual PNG frames")+QLatin1String(" (*.png)");
601     if (ffmpegAvailable) types += tr("All ffmpeg formats (*.*)");
602     return QFileDialog::getSaveFileName(this, title, QString(), types.join(QLatin1String(";; ")));
603 }
604
605 QDeclarativeViewer::QDeclarativeViewer(QWidget *parent, Qt::WindowFlags flags)
606     : QMainWindow(parent, flags)
607       , loggerWindow(new LoggerWidget(this))
608       , frame_stream(0)
609       , rotateAction(0)
610       , orientation(0)
611       , showWarningsWindow(0)
612       , m_scriptOptions(0)
613       , tester(0)
614       , useQmlFileBrowser(true)
615       , translator(0)
616 {
617     QDeclarativeViewer::registerTypes();
618     setWindowTitle(tr("Qt QML Viewer"));
619 #ifdef Q_WS_MAEMO_5
620     setAttribute(Qt::WA_Maemo5StackedWindow);
621 //    setPalette(QApplication::palette("QLabel"));
622 #endif
623
624     devicemode = false;
625     canvas = 0;
626     record_autotime = 0;
627     record_rate = 50;
628     record_args += QLatin1String("-sameq");
629
630     recdlg = new RecordingDialog(this);
631     connect(recdlg->pickfile, SIGNAL(clicked()), this, SLOT(pickRecordingFile()));
632     senseFfmpeg();
633     senseImageMagick();
634     if (!ffmpegAvailable)
635         recdlg->showffmpegOptions(false);
636     if (!ffmpegAvailable && !convertAvailable)
637         recdlg->showRateOptions(false);
638     QString warn;
639     if (!ffmpegAvailable) {
640         if (!convertAvailable)
641             warn = tr("ffmpeg and ImageMagick not available - no video output");
642         else
643             warn = tr("ffmpeg not available - GIF and PNG outputs only");
644         recdlg->warning->setText(warn);
645     } else {
646         recdlg->warning->hide();
647     }
648
649     canvas = new DragAndDropView(this);
650
651     canvas->setAttribute(Qt::WA_OpaquePaintEvent);
652     canvas->setAttribute(Qt::WA_NoSystemBackground);
653
654     canvas->setFocus();
655
656     QObject::connect(canvas, SIGNAL(initialSizeChanged(QSize)), this, SLOT(initialSizeChanged(QSize)));
657     QObject::connect(canvas, SIGNAL(statusChanged(QDeclarativeView::Status)), this, SLOT(statusChanged()));
658     QObject::connect(canvas->engine(), SIGNAL(quit()), this, SLOT(close()));
659
660     QObject::connect(warningsWidget(), SIGNAL(opened()), this, SLOT(warningsWidgetOpened()));
661     QObject::connect(warningsWidget(), SIGNAL(closed()), this, SLOT(warningsWidgetClosed()));
662
663     if (!(flags & Qt::FramelessWindowHint)) {
664         createMenu();
665         changeOrientation(orientation->actions().value(0));
666     } else {
667         setMenuBar(0);
668     }
669
670     setCentralWidget(canvas);
671
672     namFactory = new NetworkAccessManagerFactory;
673     canvas->engine()->setNetworkAccessManagerFactory(namFactory);
674
675     connect(&autoStartTimer, SIGNAL(timeout()), this, SLOT(autoStartRecording()));
676     connect(&autoStopTimer, SIGNAL(timeout()), this, SLOT(autoStopRecording()));
677     connect(&recordTimer, SIGNAL(timeout()), this, SLOT(recordFrame()));
678     connect(DeviceOrientation::instance(), SIGNAL(orientationChanged()),
679             this, SLOT(orientationChanged()), Qt::QueuedConnection);
680     autoStartTimer.setSingleShot(true);
681     autoStopTimer.setSingleShot(true);
682     recordTimer.setSingleShot(false);
683
684     QObject::connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(appAboutToQuit()));
685 }
686
687 QDeclarativeViewer::~QDeclarativeViewer()
688 {
689     delete loggerWindow;
690     canvas->engine()->setNetworkAccessManagerFactory(0);
691     delete namFactory;
692 }
693
694 void QDeclarativeViewer::enableExperimentalGestures()
695 {
696 #ifndef QT_NO_GESTURES
697     canvas->viewport()->grabGesture(Qt::TapGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent);
698     canvas->viewport()->grabGesture(Qt::TapAndHoldGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent);
699     canvas->viewport()->grabGesture(Qt::PanGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent);
700     canvas->viewport()->grabGesture(Qt::PinchGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent);
701     canvas->viewport()->grabGesture(Qt::SwipeGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent);
702     canvas->viewport()->setAttribute(Qt::WA_AcceptTouchEvents);
703 #endif
704 }
705
706 QDeclarativeView *QDeclarativeViewer::view() const
707 {
708     return canvas;
709 }
710
711 LoggerWidget *QDeclarativeViewer::warningsWidget() const
712 {
713     return loggerWindow;
714 }
715
716 void QDeclarativeViewer::createMenu()
717 {
718     QAction *openAction = new QAction(tr("&Open..."), this);
719     openAction->setShortcuts(QKeySequence::Open);
720     connect(openAction, SIGNAL(triggered()), this, SLOT(openFile()));
721
722     QAction *openUrlAction = new QAction(tr("Open &URL..."), this);
723     connect(openUrlAction, SIGNAL(triggered()), this, SLOT(openUrl()));
724
725     QAction *reloadAction = new QAction(tr("&Reload"), this);
726     reloadAction->setShortcuts(QKeySequence::Refresh);
727     connect(reloadAction, SIGNAL(triggered()), this, SLOT(reload()));
728
729     QAction *snapshotAction = new QAction(tr("&Take Snapshot"), this);
730     snapshotAction->setShortcut(QKeySequence(tr("F3")));
731     connect(snapshotAction, SIGNAL(triggered()), this, SLOT(takeSnapShot()));
732
733     recordAction = new QAction(tr("Start Recording &Video"), this);
734     recordAction->setShortcut(QKeySequence(tr("F9")));
735     connect(recordAction, SIGNAL(triggered()), this, SLOT(toggleRecordingWithSelection()));
736
737     QAction *recordOptions = new QAction(tr("Video &Options..."), this);
738     connect(recordOptions, SIGNAL(triggered()), this, SLOT(chooseRecordingOptions()));
739
740     QAction *slowAction = new QAction(tr("&Slow Down Animations"), this);
741     slowAction->setShortcut(QKeySequence(tr("Ctrl+.")));
742     slowAction->setCheckable(true);
743     connect(slowAction, SIGNAL(triggered(bool)), this, SLOT(setSlowMode(bool)));
744
745     showWarningsWindow = new QAction(tr("Show Warnings"), this);
746 #if !defined(Q_OS_SYMBIAN)
747     showWarningsWindow->setCheckable((true));
748     showWarningsWindow->setChecked(loggerWindow->isVisible());
749 #endif
750     connect(showWarningsWindow, SIGNAL(triggered(bool)), this, SLOT(showWarnings(bool)));
751
752     QAction *proxyAction = new QAction(tr("HTTP &Proxy..."), this);
753     connect(proxyAction, SIGNAL(triggered()), this, SLOT(showProxySettings()));
754
755     QAction *fullscreenAction = new QAction(tr("Full Screen"), this);
756     fullscreenAction->setCheckable(true);
757     connect(fullscreenAction, SIGNAL(triggered()), this, SLOT(toggleFullScreen()));
758
759     rotateAction = new QAction(tr("Rotate orientation"), this);
760     rotateAction->setShortcut(QKeySequence(tr("Ctrl+T")));
761     connect(rotateAction, SIGNAL(triggered()), this, SLOT(rotateOrientation()));
762
763     orientation = new QActionGroup(this);
764     orientation->setExclusive(true);
765     connect(orientation, SIGNAL(triggered(QAction*)), this, SLOT(changeOrientation(QAction*)));
766
767 #if defined(Q_OS_SYMBIAN)
768     QAction *autoOrientationAction = new QAction(tr("Auto-orientation"), this);
769     autoOrientationAction->setCheckable(true);
770 #endif
771     QAction *portraitAction = new QAction(tr("Portrait"), this);
772     portraitAction->setCheckable(true);
773     QAction *landscapeAction = new QAction(tr("Landscape"), this);
774     landscapeAction->setCheckable(true);
775 #if !defined(Q_OS_SYMBIAN)
776     QAction *portraitInvAction = new QAction(tr("Portrait (inverted)"), this);
777     portraitInvAction->setCheckable(true);
778     QAction *landscapeInvAction = new QAction(tr("Landscape (inverted)"), this);
779     landscapeInvAction->setCheckable(true);
780 #endif
781
782     QAction *aboutAction = new QAction(tr("&About Qt..."), this);
783     aboutAction->setMenuRole(QAction::AboutQtRole);
784     connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
785
786 #if !defined(Q_OS_SYMBIAN)
787     QAction *closeAction = new QAction(tr("&Close"), this);
788     closeAction->setShortcuts(QKeySequence::Close);
789     connect(closeAction, SIGNAL(triggered()), this, SLOT(close()));
790 #endif
791
792     QAction *quitAction = new QAction(tr("&Quit"), this);
793     quitAction->setMenuRole(QAction::QuitRole);
794     quitAction->setShortcuts(QKeySequence::Quit);
795     connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
796
797     QMenuBar *menu = menuBar();
798     if (!menu)
799         return;
800
801 #if defined(Q_WS_MAEMO_5)
802     menu->addAction(openAction);
803     menu->addAction(openUrlAction);
804     menu->addAction(reloadAction);
805
806     menu->addAction(snapshotAction);
807     menu->addAction(recordAction);
808
809     menu->addAction(recordOptions);
810     menu->addAction(proxyAction);
811
812     menu->addAction(slowAction);
813     menu->addAction(showWarningsWindow);
814
815     orientation->addAction(landscapeAction);
816     orientation->addAction(portraitAction);
817     menu->addAction(new Maemo5PickerAction(tr("Set orientation"), orientation, this));
818     menu->addAction(fullscreenAction);
819     return;
820 #endif // Q_WS_MAEMO_5
821
822     QMenu *fileMenu = menu->addMenu(tr("&File"));
823     fileMenu->addAction(openAction);
824     fileMenu->addAction(openUrlAction);
825     fileMenu->addAction(reloadAction);
826 #if !defined(Q_OS_SYMBIAN)
827     fileMenu->addSeparator();
828     fileMenu->addAction(closeAction);
829     fileMenu->addAction(quitAction);
830
831     QMenu *recordMenu = menu->addMenu(tr("&Recording"));
832     recordMenu->addAction(snapshotAction);
833     recordMenu->addAction(recordAction);
834 #endif // ! Q_OS_SYMBIAN
835
836     QMenu *debugMenu = menu->addMenu(tr("&Debugging"));
837     debugMenu->addAction(slowAction);
838     debugMenu->addAction(showWarningsWindow);
839
840     QMenu *settingsMenu = menu->addMenu(tr("&Settings"));
841     settingsMenu->addAction(proxyAction);
842 #if defined(Q_OS_SYMBIAN)
843     settingsMenu->addAction(fullscreenAction);
844 #else
845     settingsMenu->addAction(recordOptions);
846     settingsMenu->addMenu(loggerWindow->preferencesMenu());
847 #endif // !Q_OS_SYMBIAN
848     settingsMenu->addAction(rotateAction);
849
850     QMenu *propertiesMenu = settingsMenu->addMenu(tr("Properties"));
851
852 #if defined(Q_OS_SYMBIAN)
853     orientation->addAction(autoOrientationAction);
854 #endif
855     orientation->addAction(portraitAction);
856     orientation->addAction(landscapeAction);
857 #if !defined(Q_OS_SYMBIAN)
858     orientation->addAction(portraitInvAction);
859     orientation->addAction(landscapeInvAction);
860 #endif
861     propertiesMenu->addActions(orientation->actions());
862
863     QMenu *helpMenu = menu->addMenu(tr("&Help"));
864     helpMenu->addAction(aboutAction);
865 }
866
867 void QDeclarativeViewer::showProxySettings()
868 {
869     ProxySettings settingsDlg (this);
870
871     connect (&settingsDlg, SIGNAL (accepted()), this, SLOT (proxySettingsChanged ()));
872
873     settingsDlg.exec();
874 }
875
876 void QDeclarativeViewer::proxySettingsChanged()
877 {
878     namFactory->proxyChanged();
879     reload ();
880 }
881
882 void QDeclarativeViewer::rotateOrientation()
883 {
884 #if defined(Q_WS_S60)
885     CAknAppUi *appUi = static_cast<CAknAppUi *>(CEikonEnv::Static()->AppUi());
886     if (appUi) {
887         CAknAppUi::TAppUiOrientation oldOrientation = appUi->Orientation();
888         QString newOrientation;
889         if (oldOrientation == CAknAppUi::EAppUiOrientationPortrait) {
890             newOrientation = QLatin1String("Landscape");
891         } else {
892             newOrientation = QLatin1String("Portrait");
893         }
894         foreach (QAction *action, orientation->actions()) {
895             if (action->text() == newOrientation) {
896                 changeOrientation(action);
897             }
898         }
899     }
900 #else
901     QAction *current = orientation->checkedAction();
902     QList<QAction *> actions = orientation->actions();
903     int index = actions.indexOf(current);
904     if (index < 0)
905         return;
906
907     QAction *newOrientation = actions[(index + 1) % actions.count()];
908     changeOrientation(newOrientation);
909 #endif
910 }
911
912 void QDeclarativeViewer::toggleFullScreen()
913 {
914     if (isFullScreen())
915         showMaximized();
916     else
917         showFullScreen();
918 }
919
920 void QDeclarativeViewer::showWarnings(bool show)
921 {
922 #if defined(Q_OS_SYMBIAN)
923     loggerWindow->showMaximized();
924 #else
925     loggerWindow->setVisible(show);
926 #endif
927 }
928
929 void QDeclarativeViewer::warningsWidgetOpened()
930 {
931     showWarningsWindow->setChecked(true);
932 }
933
934 void QDeclarativeViewer::warningsWidgetClosed()
935 {
936     showWarningsWindow->setChecked(false);
937 }
938
939 void QDeclarativeViewer::takeSnapShot()
940 {
941     static int snapshotcount = 1;
942     QString snapFileName = QString(QLatin1String("snapshot%1.png")).arg(snapshotcount);
943     QPixmap::grabWidget(canvas).save(snapFileName);
944     qDebug() << "Wrote" << snapFileName;
945     ++snapshotcount;
946 }
947
948 void QDeclarativeViewer::pickRecordingFile()
949 {
950     QString fileName = getVideoFileName();
951     if (!fileName.isEmpty())
952         recdlg->file->setText(fileName);
953 }
954
955 void QDeclarativeViewer::chooseRecordingOptions()
956 {
957     // File
958     recdlg->file->setText(record_file);
959
960     // Size
961     recdlg->setOriginalSize(canvas->size());
962
963     // Rate
964     recdlg->setVideoRate(record_rate);
965
966
967     // Profile
968     recdlg->setArguments(record_args.join(QLatin1String(" ")));
969     if (recdlg->exec()) {
970         // File
971         record_file = recdlg->file->text();
972         // Size
973         record_outsize = recdlg->videoSize();
974         // Rate
975         record_rate = recdlg->videoRate();
976         // Profile
977         record_args = recdlg->arguments().split(QLatin1Char(' '),QString::SkipEmptyParts);
978     }
979 }
980
981 void QDeclarativeViewer::toggleRecordingWithSelection()
982 {
983     if (!recordTimer.isActive()) {
984         if (record_file.isEmpty()) {
985             QString fileName = getVideoFileName();
986             if (fileName.isEmpty())
987                 return;
988             if (!fileName.contains(QRegExp(QLatin1String(".[^\\/]*$"))))
989                 fileName += QLatin1String(".avi");
990             setRecordFile(fileName);
991         }
992     }
993     toggleRecording();
994 }
995
996 void QDeclarativeViewer::toggleRecording()
997 {
998     if (record_file.isEmpty()) {
999         toggleRecordingWithSelection();
1000         return;
1001     }
1002     bool recording = !recordTimer.isActive();
1003     recordAction->setText(recording ? tr("&Stop Recording Video\tF9") : tr("&Start Recording Video\tF9"));
1004     setRecording(recording);
1005 }
1006
1007 void QDeclarativeViewer::setSlowMode(bool enable)
1008 {
1009     QUnifiedTimer::instance()->setSlowModeEnabled(enable);
1010 }
1011
1012 void QDeclarativeViewer::addLibraryPath(const QString& lib)
1013 {
1014     canvas->engine()->addImportPath(lib);
1015 }
1016
1017 void QDeclarativeViewer::addPluginPath(const QString& plugin)
1018 {
1019     canvas->engine()->addPluginPath(plugin);
1020 }
1021
1022 void QDeclarativeViewer::reload()
1023 {
1024     launch(currentFileOrUrl);
1025 }
1026
1027 void QDeclarativeViewer::openFile()
1028 {
1029     QString cur = canvas->source().toLocalFile();
1030     if (useQmlFileBrowser) {
1031         open(QLatin1String("qrc:/browser/Browser.qml"));
1032     } else {
1033         QString fileName = QFileDialog::getOpenFileName(this, tr("Open QML file"), cur, tr("QML Files (*.qml)"));
1034         if (!fileName.isEmpty()) {
1035             QFileInfo fi(fileName);
1036             open(fi.absoluteFilePath());
1037         }
1038     }
1039 }
1040
1041 void QDeclarativeViewer::openUrl()
1042 {
1043     QString cur = canvas->source().toLocalFile();
1044     QString url= QInputDialog::getText(this, tr("Open QML file"), tr("URL of main QML file:"), QLineEdit::Normal, cur);
1045     if (!url.isEmpty())
1046         open(url);
1047 }
1048
1049 void QDeclarativeViewer::statusChanged()
1050 {
1051     if (canvas->status() == QDeclarativeView::Error && tester)
1052         tester->executefailure();
1053
1054     if (canvas->status() == QDeclarativeView::Ready) {
1055         initialSize = canvas->initialSize();
1056         updateSizeHints(true);
1057         QObject::connect(canvas, SIGNAL(sceneResized(QSize)), this, SLOT(sceneResized(QSize)));
1058     }
1059 }
1060
1061 void QDeclarativeViewer::launch(const QString& file_or_url)
1062 {
1063     QMetaObject::invokeMethod(this, "open", Qt::QueuedConnection, Q_ARG(QString, file_or_url));
1064 }
1065
1066 void QDeclarativeViewer::loadTranslationFile(const QString& directory)
1067 {
1068     if (!translator) {
1069         translator = new QTranslator(this);
1070         QApplication::installTranslator(translator);
1071     }
1072
1073     translator->load(QLatin1String("qml_" )+QLocale::system().name(), directory + QLatin1String("/i18n"));
1074 }
1075
1076 void QDeclarativeViewer::loadDummyDataFiles(const QString& directory)
1077 {
1078     QDir dir(directory + QLatin1String("/dummydata"), QLatin1String("*.qml"));
1079     QStringList list = dir.entryList();
1080     for (int i = 0; i < list.size(); ++i) {
1081         QString qml = list.at(i);
1082         QDeclarativeComponent comp(canvas->engine(), dir.filePath(qml));
1083         QObject *dummyData = comp.create();
1084
1085         if(comp.isError()) {
1086             QList<QDeclarativeError> errors = comp.errors();
1087             foreach (const QDeclarativeError &error, errors) {
1088                 qWarning() << error;
1089             }
1090             if (tester) tester->executefailure();
1091         }
1092
1093         if (dummyData) {
1094             qWarning() << "Loaded dummy data:" << dir.filePath(qml);
1095             qml.truncate(qml.length()-4);
1096             canvas->rootContext()->setContextProperty(qml, dummyData);
1097             dummyData->setParent(this);
1098         }
1099     }
1100 }
1101
1102 bool QDeclarativeViewer::open(const QString& file_or_url)
1103 {
1104     currentFileOrUrl = file_or_url;
1105
1106     QUrl url;
1107     QFileInfo fi(file_or_url);
1108     if (fi.exists())
1109         url = QUrl::fromLocalFile(fi.absoluteFilePath());
1110     else
1111         url = QUrl(file_or_url);
1112     setWindowTitle(tr("%1 - Qt QML Viewer").arg(file_or_url));
1113
1114     if (!m_script.isEmpty())
1115         tester = new QDeclarativeTester(m_script, m_scriptOptions, canvas);
1116
1117     delete canvas->rootObject();
1118     canvas->engine()->clearComponentCache();
1119     QDeclarativeContext *ctxt = canvas->rootContext();
1120     ctxt->setContextProperty(QLatin1String("qmlViewer"), this);
1121 #ifdef Q_OS_SYMBIAN
1122     ctxt->setContextProperty(QLatin1String("qmlViewerFolder"), QLatin1String("E:\\")); // Documents on your S60 phone
1123 #else
1124     ctxt->setContextProperty(QLatin1String("qmlViewerFolder"), QDir::currentPath());
1125 #endif
1126
1127     ctxt->setContextProperty(QLatin1String("runtime"), Runtime::instance());
1128
1129     QString fileName = url.toLocalFile();
1130     if (!fileName.isEmpty()) {
1131         fi.setFile(fileName);
1132         if (fi.exists()) {
1133             if (fi.suffix().toLower() != QLatin1String("qml")) {
1134                 qWarning() << "qml cannot open non-QML file" << fileName;
1135                 return false;
1136             }
1137
1138             QFileInfo fi(fileName);
1139             loadTranslationFile(fi.path());
1140             loadDummyDataFiles(fi.path());
1141         } else {
1142             qWarning() << "qml cannot find file:" << fileName;
1143             return false;
1144         }
1145     }
1146
1147     QTime t;
1148     t.start();
1149
1150     QObject::disconnect(canvas, SIGNAL(sceneResized(QSize)), this, SLOT(sceneResized(QSize)));
1151     canvas->setSource(url);
1152
1153     return true;
1154 }
1155
1156 void QDeclarativeViewer::setAutoRecord(int from, int to)
1157 {
1158     if (from==0) from=1; // ensure resized
1159     record_autotime = to-from;
1160     autoStartTimer.setInterval(from);
1161     autoStartTimer.start();
1162 }
1163
1164 void QDeclarativeViewer::setRecordArgs(const QStringList& a)
1165 {
1166     record_args = a;
1167 }
1168
1169 void QDeclarativeViewer::setRecordFile(const QString& f)
1170 {
1171     record_file = f;
1172 }
1173
1174 void QDeclarativeViewer::setRecordRate(int fps)
1175 {
1176     record_rate = fps;
1177 }
1178
1179 void QDeclarativeViewer::sceneResized(QSize)
1180 {
1181     updateSizeHints();
1182 }
1183
1184 void QDeclarativeViewer::initialSizeChanged(QSize size)
1185 {
1186     if (!isFullScreen() && !isMaximized()) {
1187         canvas->setGeometry(0,0,size.width(),size.height());
1188         layout()->setSizeConstraint(QLayout::SetFixedSize);
1189         layout()->activate();
1190     }
1191 }
1192
1193 void QDeclarativeViewer::keyPressEvent(QKeyEvent *event)
1194 {
1195     if (event->key() == Qt::Key_0 && devicemode)
1196         exit(0);
1197     else if (event->key() == Qt::Key_F1 || (event->key() == Qt::Key_1 && devicemode)) {
1198         qDebug() << "F1 - help\n"
1199                  << "F2 - save test script\n"
1200                  << "F3 - take PNG snapshot\n"
1201                  << "F4 - show items and state\n"
1202                  << "F5 - reload QML\n"
1203                  << "F6 - show object tree\n"
1204                  << "F7 - show timing\n"
1205                  << "F9 - toggle video recording\n"
1206                  << "F10 - toggle orientation\n"
1207                  << "device keys: 0=quit, 1..8=F1..F8"
1208                 ;
1209     } else if (event->key() == Qt::Key_F2 || (event->key() == Qt::Key_2 && devicemode)) {
1210         if (tester && m_scriptOptions & Record)
1211             tester->save();
1212     } else if (event->key() == Qt::Key_F3 || (event->key() == Qt::Key_3 && devicemode)) {
1213         takeSnapShot();
1214     } else if (event->key() == Qt::Key_F5 || (event->key() == Qt::Key_5 && devicemode)) {
1215         reload();
1216     } else if (event->key() == Qt::Key_F9 || (event->key() == Qt::Key_9 && devicemode)) {
1217         toggleRecording();
1218     } else if (event->key() == Qt::Key_F10) {
1219         rotateOrientation();
1220     }
1221
1222     QWidget::keyPressEvent(event);
1223 }
1224
1225 bool QDeclarativeViewer::event(QEvent *event)
1226 {
1227     if (event->type() == QEvent::WindowActivate) {
1228         Runtime::instance()->setActiveWindow(true);
1229         DeviceOrientation::instance()->resumeListening();
1230     } else if (event->type() == QEvent::WindowDeactivate) {
1231         Runtime::instance()->setActiveWindow(false);
1232         DeviceOrientation::instance()->pauseListening();
1233     }
1234     return QWidget::event(event);
1235 }
1236
1237 void QDeclarativeViewer::senseImageMagick()
1238 {
1239     QProcess proc;
1240     proc.start(QLatin1String("convert"), QStringList() << QLatin1String("-h"));
1241     proc.waitForFinished(2000);
1242     QString help = QString::fromAscii(proc.readAllStandardOutput());
1243     convertAvailable = help.contains(QLatin1String("ImageMagick"));
1244 }
1245
1246 void QDeclarativeViewer::senseFfmpeg()
1247 {
1248     QProcess proc;
1249     proc.start(QLatin1String("ffmpeg"), QStringList() << QLatin1String("-h"));
1250     proc.waitForFinished(2000);
1251     QString ffmpegHelp = QString::fromAscii(proc.readAllStandardOutput());
1252     ffmpegAvailable = ffmpegHelp.contains(QLatin1String("-s "));
1253     ffmpegHelp = tr("Video recording uses ffmpeg:") + QLatin1String("\n\n") + ffmpegHelp;
1254
1255     QDialog *d = new QDialog(recdlg);
1256     QVBoxLayout *l = new QVBoxLayout(d);
1257     QTextBrowser *b = new QTextBrowser(d);
1258     QFont f = b->font();
1259     f.setFamily(QLatin1String("courier"));
1260     b->setFont(f);
1261     b->setText(ffmpegHelp);
1262     l->addWidget(b);
1263     d->setLayout(l);
1264     ffmpegHelpWindow = d;
1265     connect(recdlg->ffmpegHelp,SIGNAL(clicked()), ffmpegHelpWindow, SLOT(show()));
1266 }
1267
1268 void QDeclarativeViewer::setRecording(bool on)
1269 {
1270     if (on == recordTimer.isActive())
1271         return;
1272
1273     int period = int(1000/record_rate+0.5);
1274     QUnifiedTimer::instance()->setTimingInterval(on ? period:16);
1275     QUnifiedTimer::instance()->setConsistentTiming(on);
1276     if (on) {
1277         canvas->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
1278         recordTimer.setInterval(period);
1279         recordTimer.start();
1280         frame_fmt = record_file.right(4).toLower();
1281         frame = QImage(canvas->width(),canvas->height(),QImage::Format_RGB32);
1282         if (frame_fmt != QLatin1String(".png") && (!convertAvailable || frame_fmt != QLatin1String(".gif"))) {
1283             // Stream video to ffmpeg
1284
1285             QProcess *proc = new QProcess(this);
1286             connect(proc, SIGNAL(finished(int)), this, SLOT(ffmpegFinished(int)));
1287             frame_stream = proc;
1288
1289             QStringList args;
1290             args << QLatin1String("-y");
1291             args << QLatin1String("-r") << QString::number(record_rate);
1292             args << QLatin1String("-f") << QLatin1String("rawvideo");
1293             args << QLatin1String("-pix_fmt") << (frame_fmt == QLatin1String(".gif") ? QLatin1String("rgb24") : QLatin1String("rgb32"));
1294             args << QLatin1String("-s") << QString::fromAscii("%1x%2").arg(canvas->width()).arg(canvas->height());
1295             args << QLatin1String("-i") << QLatin1String("-");
1296             if (record_outsize.isValid()) {
1297                 args << QLatin1String("-s") << QString::fromAscii("%1x%2").arg(record_outsize.width()).arg(record_outsize.height());
1298                 args << QLatin1String("-aspect") << QString::number(double(canvas->width())/canvas->height());
1299             }
1300             args += record_args;
1301             args << record_file;
1302             proc->start(QLatin1String("ffmpeg"), args);
1303
1304         } else {
1305             // Store frames, save to GIF/PNG
1306             frame_stream = 0;
1307         }
1308     } else {
1309         canvas->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
1310         recordTimer.stop();
1311         if (frame_stream) {
1312             qDebug() << "Saving video...";
1313             frame_stream->close();
1314             qDebug() << "Wrote" << record_file;
1315         } else {
1316             QProgressDialog progress(tr("Saving frames..."), tr("Cancel"), 0, frames.count()+10, this);
1317             progress.setWindowModality(Qt::WindowModal);
1318
1319             int frame=0;
1320             QStringList inputs;
1321             qDebug() << "Saving frames...";
1322
1323             QString framename;
1324             bool png_output = false;
1325             if (record_file.right(4).toLower() == QLatin1String(".png")) {
1326                 if (record_file.contains(QLatin1Char('%')))
1327                     framename = record_file;
1328                 else
1329                     framename = record_file.left(record_file.length()-4) + QLatin1String("%04d") + record_file.right(4);
1330                 png_output = true;
1331             } else {
1332                 framename = QLatin1String("tmp-frame%04d.png");
1333                 png_output = false;
1334             }
1335             foreach (QImage* img, frames) {
1336                 progress.setValue(progress.value()+1);
1337                 if (progress.wasCanceled())
1338                     break;
1339                 QString name;
1340                 name.sprintf(framename.toLocal8Bit(),frame++);
1341                 if (record_outsize.isValid())
1342                     *img = img->scaled(record_outsize,Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
1343                 if (record_dither==QLatin1String("ordered"))
1344                     img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither|Qt::OrderedDither).save(name);
1345                 else if (record_dither==QLatin1String("threshold"))
1346                     img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither|Qt::ThresholdDither).save(name);
1347                 else if (record_dither==QLatin1String("floyd"))
1348                     img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither).save(name);
1349                 else
1350                     img->save(name);
1351                 inputs << name;
1352                 delete img;
1353             }
1354
1355             if (!progress.wasCanceled()) {
1356                 if (png_output) {
1357                     framename.replace(QRegExp(QLatin1String("%\\d*.")), QLatin1String("*"));
1358                     qDebug() << "Wrote frames" << framename;
1359                     inputs.clear(); // don't remove them
1360                 } else {
1361                     // ImageMagick and gifsicle for GIF encoding
1362                     progress.setLabelText(tr("Converting frames to GIF file..."));
1363                     QStringList args;
1364                     args << QLatin1String("-delay") << QString::number(period/10);
1365                     args << inputs;
1366                     args << record_file;
1367                     qDebug() << "Converting..." << record_file << "(this may take a while)";
1368                     if (0!=QProcess::execute(QLatin1String("convert"), args)) {
1369                         qWarning() << "Cannot run ImageMagick 'convert' - recorded frames not converted";
1370                         inputs.clear(); // don't remove them
1371                         qDebug() << "Wrote frames tmp-frame*.png";
1372                     } else {
1373                         if (record_file.right(4).toLower() == QLatin1String(".gif")) {
1374                             qDebug() << "Compressing..." << record_file;
1375                             if (0!=QProcess::execute(QLatin1String("gifsicle"), QStringList() << QLatin1String("-O2")
1376                                                      << QLatin1String("-o") << record_file << record_file))
1377                                 qWarning() << "Cannot run 'gifsicle' - not compressed";
1378                         }
1379                         qDebug() << "Wrote" << record_file;
1380                     }
1381                 }
1382             }
1383
1384             progress.setValue(progress.maximum()-1);
1385             foreach (QString name, inputs)
1386                 QFile::remove(name);
1387
1388             frames.clear();
1389         }
1390     }
1391     qDebug() << "Recording: " << (recordTimer.isActive()?"ON":"OFF");
1392 }
1393
1394 void QDeclarativeViewer::ffmpegFinished(int code)
1395 {
1396     qDebug() << "ffmpeg returned" << code << frame_stream->readAllStandardError();
1397 }
1398
1399 void QDeclarativeViewer::appAboutToQuit()
1400 {
1401     // avoid QGLContext errors about invalid contexts on exit
1402     canvas->setViewport(0);
1403
1404     // avoid crashes if messages are received after app has closed
1405     delete loggerWindow;
1406     loggerWindow = 0;
1407     delete tester;
1408     tester = 0;
1409     close();
1410 }
1411
1412 void QDeclarativeViewer::autoStartRecording()
1413 {
1414     setRecording(true);
1415     autoStopTimer.setInterval(record_autotime);
1416     autoStopTimer.start();
1417 }
1418
1419 void QDeclarativeViewer::autoStopRecording()
1420 {
1421     setRecording(false);
1422 }
1423
1424 void QDeclarativeViewer::recordFrame()
1425 {
1426     canvas->QWidget::render(&frame);
1427     if (frame_stream) {
1428         if (frame_fmt == QLatin1String(".gif")) {
1429             // ffmpeg can't do 32bpp with gif
1430             QImage rgb24 = frame.convertToFormat(QImage::Format_RGB888);
1431             frame_stream->write((char*)rgb24.bits(),rgb24.byteCount());
1432         } else {
1433             frame_stream->write((char*)frame.bits(),frame.byteCount());
1434         }
1435     } else {
1436         frames.append(new QImage(frame));
1437     }
1438 }
1439
1440 void QDeclarativeViewer::changeOrientation(QAction *action)
1441 {
1442     if (!action)
1443         return;
1444     QString o = action->text();
1445     action->setChecked(true);
1446 #if defined(Q_WS_S60)
1447     CAknAppUi *appUi = static_cast<CAknAppUi *>(CEikonEnv::Static()->AppUi());
1448     if (appUi) {
1449         CAknAppUi::TAppUiOrientation orientation = appUi->Orientation();
1450         if (o == QLatin1String("Auto-orientation")) {
1451             appUi->SetOrientationL(CAknAppUi::EAppUiOrientationAutomatic);
1452             rotateAction->setVisible(false);
1453         } else if (o == QLatin1String("Portrait")) {
1454             appUi->SetOrientationL(CAknAppUi::EAppUiOrientationPortrait);
1455             rotateAction->setVisible(true);
1456         } else if (o == QLatin1String("Landscape")) {
1457             appUi->SetOrientationL(CAknAppUi::EAppUiOrientationLandscape);
1458             rotateAction->setVisible(true);
1459         }
1460     }
1461 #else
1462     if (o == QLatin1String("Portrait"))
1463         DeviceOrientation::instance()->setOrientation(DeviceOrientation::Portrait);
1464     else if (o == QLatin1String("Landscape"))
1465         DeviceOrientation::instance()->setOrientation(DeviceOrientation::Landscape);
1466     else if (o == QLatin1String("Portrait (inverted)"))
1467         DeviceOrientation::instance()->setOrientation(DeviceOrientation::PortraitInverted);
1468     else if (o == QLatin1String("Landscape (inverted)"))
1469         DeviceOrientation::instance()->setOrientation(DeviceOrientation::LandscapeInverted);
1470 #endif
1471 }
1472
1473 void QDeclarativeViewer::orientationChanged()
1474 {
1475     updateSizeHints();
1476 }
1477
1478 void QDeclarativeViewer::setDeviceKeys(bool on)
1479 {
1480     devicemode = on;
1481 }
1482
1483 void QDeclarativeViewer::setNetworkCacheSize(int size)
1484 {
1485     namFactory->setCacheSize(size);
1486 }
1487
1488 void QDeclarativeViewer::setUseGL(bool useGL)
1489 {
1490 #ifdef GL_SUPPORTED
1491     if (useGL) {
1492         QGLFormat format = QGLFormat::defaultFormat();
1493 #ifdef Q_WS_MAC
1494         format.setSampleBuffers(true);
1495         format.setSwapInterval(1);
1496 #else
1497         format.setSampleBuffers(false);
1498 #endif
1499
1500         QGLWidget *glWidget = new QGLWidget(format);
1501         //### potentially faster, but causes junk to appear if top-level is Item, not Rectangle
1502         //glWidget->setAutoFillBackground(false);
1503
1504         canvas->setViewport(glWidget);
1505     }
1506 #else
1507     Q_UNUSED(useGL)
1508 #endif
1509 }
1510
1511 void QDeclarativeViewer::setUseNativeFileBrowser(bool use)
1512 {
1513     useQmlFileBrowser = !use;
1514 }
1515
1516 void QDeclarativeViewer::setSizeToView(bool sizeToView)
1517 {
1518     QDeclarativeView::ResizeMode resizeMode = sizeToView ? QDeclarativeView::SizeRootObjectToView : QDeclarativeView::SizeViewToRootObject;
1519     if (resizeMode != canvas->resizeMode()) {
1520         canvas->setResizeMode(resizeMode);
1521         updateSizeHints();
1522     }
1523 }
1524
1525 void QDeclarativeViewer::updateSizeHints(bool initial)
1526 {
1527     static bool isRecursive = false;
1528
1529     if (isRecursive)
1530         return;
1531     isRecursive = true;
1532
1533     if (initial || (canvas->resizeMode() == QDeclarativeView::SizeViewToRootObject)) {
1534         QSize newWindowSize = initial ? initialSize : canvas->sizeHint();
1535         //qWarning() << "USH:" << (initial ? "INIT:" : "V2R:") << "setting fixed size " << newWindowSize;
1536         if (!isFullScreen() && !isMaximized()) {
1537             canvas->setGeometry(0,0,newWindowSize.width(),newWindowSize.height());
1538             resize(1, 1);
1539             layout()->setSizeConstraint(QLayout::SetFixedSize);
1540             layout()->activate();
1541         }
1542     }
1543     //qWarning() << "USH: R2V: setting free size ";
1544     layout()->setSizeConstraint(QLayout::SetNoConstraint);
1545     layout()->activate();
1546     setMinimumSize(minimumSizeHint());
1547     setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
1548     canvas->setMinimumSize(QSize(0,0));
1549     canvas->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
1550
1551     isRecursive = false;
1552 }
1553
1554 void QDeclarativeViewer::registerTypes()
1555 {
1556     static bool registered = false;
1557
1558     if (!registered) {
1559         // registering only for exposing the DeviceOrientation::Orientation enum
1560         qmlRegisterUncreatableType<DeviceOrientation>("Qt", 4, 7, "Orientation", QString());
1561         qmlRegisterUncreatableType<DeviceOrientation>("QtQuick", 1, 0, "Orientation", QString());
1562         registered = true;
1563     }
1564 }
1565
1566 QT_END_NAMESPACE
1567
1568 #include "qmlruntime.moc"