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