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