1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the tools applications of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include <QtQuick1/qdeclarativeview.h>
48 # include <QMaemo5ValueButton>
49 # include <QMaemo5ListPickSelector>
50 # include <QWidgetAction>
51 # include <QStringListModel>
52 # include "ui_recopts_maemo5.h"
54 # include "ui_recopts.h"
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>
66 #include <QXmlStreamReader>
68 #include <QNetworkReply>
69 #include <QNetworkCookieJar>
70 #include <QNetworkDiskCache>
71 #include <QNetworkAccessManager>
72 #include <QSignalMapper>
73 #include <QDeclarativeComponent>
75 #include <QApplication>
76 #include <QTranslator>
78 #include <QTextBrowser>
81 #include <QVBoxLayout>
82 #include <QProgressDialog>
87 #include <QFileDialog>
88 #include <QInputDialog>
90 #include <QGraphicsObject>
91 #include <QNetworkProxyFactory>
95 #include <QMutexLocker>
96 #include "proxysettings.h"
97 #include "deviceorientation.h"
103 #if defined(Q_WS_S60)
104 #include <aknappui.h> // For locking app orientation
107 #include <qdeclarativetester.h>
111 class DragAndDropView : public QDeclarativeView
115 DragAndDropView(QDeclarativeViewer *parent = 0)
116 : QDeclarativeView(parent)
118 setAcceptDrops(true);
121 void dragEnterEvent(QDragEnterEvent *event)
123 const QMimeData *mimeData = event->mimeData();
124 if (mimeData->hasUrls())
125 event->acceptProposedAction();
128 void dragMoveEvent(QDragMoveEvent *event)
130 event->acceptProposedAction();
133 void dragLeaveEvent(QDragLeaveEvent *event)
138 void dropEvent(QDropEvent *event)
140 const QMimeData *mimeData = event->mimeData();
141 if (!mimeData->hasUrls())
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());
154 class Runtime : public QObject
158 Q_PROPERTY(bool isActiveWindow READ isActiveWindow NOTIFY isActiveWindowChanged)
159 Q_PROPERTY(DeviceOrientation::Orientation orientation READ orientation NOTIFY orientationChanged)
162 static Runtime* instance()
164 static Runtime *instance = 0;
166 instance = new Runtime;
170 bool isActiveWindow() const { return activeWindow; }
171 void setActiveWindow(bool active)
173 if (active == activeWindow)
175 activeWindow = active;
176 emit isActiveWindowChanged();
179 DeviceOrientation::Orientation orientation() const { return DeviceOrientation::instance()->orientation(); }
182 void isActiveWindowChanged();
183 void orientationChanged();
186 Runtime(QObject *parent=0) : QObject(parent), activeWindow(false)
188 connect(DeviceOrientation::instance(), SIGNAL(orientationChanged()),
189 this, SIGNAL(orientationChanged()));
197 #if defined(Q_WS_MAEMO_5)
199 class Maemo5PickerAction : public QWidgetAction {
202 Maemo5PickerAction(const QString &text, QActionGroup *actions, QObject *parent)
203 : QWidgetAction(parent), m_text(text), m_actions(actions)
206 QWidget *createWidget(QWidget *parent)
208 QMaemo5ValueButton *button = new QMaemo5ValueButton(m_text, parent);
209 button->setValueLayout(QMaemo5ValueButton::ValueUnderTextCentered);
210 QMaemo5ListPickSelector *pick = new QMaemo5ListPickSelector(button);
211 button->setPickSelector(pick);
214 int curIdx = -1, idx = 0;
215 foreach (QAction *a, m_actions->actions()) {
221 pick->setModel(new QStringListModel(sl));
222 pick->setCurrentIndex(curIdx);
224 button->setEnabled(false);
226 connect(pick, SIGNAL(selected(QString)), this, SLOT(emitTriggered()));
233 QMaemo5ListPickSelector *pick = qobject_cast<QMaemo5ListPickSelector *>(sender());
236 int idx = pick->currentIndex();
238 if (m_actions && idx >= 0 && idx < m_actions->actions().count())
239 m_actions->actions().at(idx)->trigger();
244 QPointer<QActionGroup> m_actions;
247 #endif // Q_WS_MAEMO_5
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", ""},
258 class RecordingDialog : public QDialog, public Ui::RecordingOptions {
262 RecordingDialog(QWidget *parent) : QDialog(parent)
266 hz->setValidator(new QDoubleValidator(hz));
268 for (int i=0; ffmpegprofiles[i].name; ++i) {
269 profile->addItem(QString::fromAscii(ffmpegprofiles[i].name));
273 void setArguments(QString a)
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));
285 profile->setCurrentIndex(i);
288 QString arguments() const
290 int i = profile->currentIndex();
291 return ffmpegprofiles[i].args[0] ? QLatin1String(ffmpegprofiles[i].args) : customargs;
294 void setOriginalSize(const QSize &s)
296 QString str = tr("Original (%1x%2)").arg(s.width()).arg(s.height());
299 sizeCombo->setItemText(0, str);
301 sizeOriginal->setText(str);
302 if (sizeWidth->value()<=1) {
303 sizeWidth->setValue(s.width());
304 sizeHeight->setValue(s.height());
309 void showffmpegOptions(bool b)
312 profileLabel->setVisible(b);
313 profile->setVisible(b);
314 ffmpegHelp->setVisible(b);
317 ffmpegOptions->setVisible(b);
321 void showRateOptions(bool b)
324 rateLabel->setVisible(b);
325 rateCombo->setVisible(b);
327 rateOptions->setVisible(b);
331 void setVideoRate(int rate)
349 rateCombo->setCurrentIndex(idx);
352 hz24->setChecked(true);
354 hz25->setChecked(true);
356 hz50->setChecked(true);
358 hz60->setChecked(true);
360 hzCustom->setChecked(true);
361 hz->setText(QString::number(rate));
366 int videoRate() const
369 switch (rateCombo->currentIndex()) {
380 if (hz24->isChecked())
382 else if (hz25->isChecked())
384 else if (hz50->isChecked())
386 else if (hz60->isChecked())
389 return hz->text().toInt();
394 QSize videoSize() const
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();
405 if (sizeOriginal->isChecked())
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);
414 return QSize(sizeWidth->value(), sizeHeight->value());
421 void pickProfile(int i)
423 if (ffmpegprofiles[i].args[0]) {
424 args->setText(QLatin1String(ffmpegprofiles[i].args));
426 args->setText(customargs);
430 void storeCustomArgs(QString s)
439 class PersistentCookieJar : public QNetworkCookieJar {
441 PersistentCookieJar(QObject *parent) : QNetworkCookieJar(parent) { load(); }
442 ~PersistentCookieJar() { save(); }
444 virtual QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const
446 QMutexLocker lock(&mutex);
447 return QNetworkCookieJar::cookiesForUrl(url);
450 virtual bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
452 QMutexLocker lock(&mutex);
453 return QNetworkCookieJar::setCookiesFromUrl(cookieList, url);
459 QMutexLocker lock(&mutex);
460 QList<QNetworkCookie> list = allCookies();
462 foreach (QNetworkCookie cookie, list) {
463 if (!cookie.isSessionCookie()) {
464 data.append(cookie.toRawForm());
469 settings.setValue(QLatin1String("Cookies"), data);
474 QMutexLocker lock(&mutex);
476 QByteArray data = settings.value(QLatin1String("Cookies")).toByteArray();
477 setAllCookies(QNetworkCookie::parseCookies(data));
480 mutable QMutex mutex;
483 class SystemProxyFactory : public QNetworkProxyFactory
486 SystemProxyFactory() : proxyDirty(true), httpProxyInUse(false) {
489 virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query)
493 QString protocolTag = query.protocolTag();
494 if (httpProxyInUse && (protocolTag == QLatin1String("http") || protocolTag == QLatin1String("https"))) {
495 QList<QNetworkProxy> ret;
500 // systemProxyForQuery can take insanely long on Windows (QTBUG-10106)
501 return QNetworkProxyFactory::proxyForQuery(query);
503 return QNetworkProxyFactory::systemProxyForQuery(query);
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.
512 httpProxyInUse = ProxySettings::httpProxyInUse();
514 httpProxy = ProxySettings::httpProxy();
517 void proxyChanged() {
522 volatile bool proxyDirty;
524 QNetworkProxy httpProxy;
527 class NetworkAccessManagerFactory : public QObject, public QDeclarativeNetworkAccessManagerFactory
531 NetworkAccessManagerFactory() : cacheSize(0) {}
532 ~NetworkAccessManagerFactory() {}
534 QNetworkAccessManager *create(QObject *parent);
536 void setCacheSize(int size) {
537 if (size != cacheSize) {
542 void proxyChanged() {
543 foreach (QNetworkAccessManager *nam, namList) {
544 static_cast<SystemProxyFactory*>(nam->proxyFactory())->proxyChanged();
548 static PersistentCookieJar *cookieJar;
551 void managerDestroyed(QObject *obj) {
552 namList.removeOne(static_cast<QNetworkAccessManager*>(obj));
558 QList<QNetworkAccessManager*> namList;
561 PersistentCookieJar *NetworkAccessManagerFactory::cookieJar = 0;
563 static void cleanup_cookieJar()
565 delete NetworkAccessManagerFactory::cookieJar;
566 NetworkAccessManagerFactory::cookieJar = 0;
569 QNetworkAccessManager *NetworkAccessManagerFactory::create(QObject *parent)
571 QMutexLocker lock(&mutex);
572 QNetworkAccessManager *manager = new QNetworkAccessManager(parent);
574 qAddPostRoutine(cleanup_cookieJar);
575 cookieJar = new PersistentCookieJar(0);
577 manager->setCookieJar(cookieJar);
578 cookieJar->setParent(0);
579 manager->setProxyFactory(new SystemProxyFactory);
581 QNetworkDiskCache *cache = new QNetworkDiskCache;
582 cache->setCacheDirectory(QDir::tempPath()+QLatin1String("/qml-viewer-network-cache"));
583 cache->setMaximumCacheSize(cacheSize);
584 manager->setCache(cache);
586 manager->setCache(0);
588 connect(manager, SIGNAL(destroyed(QObject*)), this, SLOT(managerDestroyed(QObject*)));
589 namList.append(manager);
593 QString QDeclarativeViewer::getVideoFileName()
595 QString title = convertAvailable || ffmpegAvailable ? tr("Save Video File") : tr("Save PNG Frames");
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(";; ")));
604 QDeclarativeViewer::QDeclarativeViewer(QWidget *parent, Qt::WindowFlags flags)
605 : QMainWindow(parent, flags)
606 , loggerWindow(new LoggerWidget(this))
610 , showWarningsWindow(0)
613 , useQmlFileBrowser(true)
616 QDeclarativeViewer::registerTypes();
617 setWindowTitle(tr("Qt QML Viewer"));
619 setAttribute(Qt::WA_Maemo5StackedWindow);
620 // setPalette(QApplication::palette("QLabel"));
627 record_args += QLatin1String("-sameq");
629 recdlg = new RecordingDialog(this);
630 connect(recdlg->pickfile, SIGNAL(clicked()), this, SLOT(pickRecordingFile()));
633 if (!ffmpegAvailable)
634 recdlg->showffmpegOptions(false);
635 if (!ffmpegAvailable && !convertAvailable)
636 recdlg->showRateOptions(false);
638 if (!ffmpegAvailable) {
639 if (!convertAvailable)
640 warn = tr("ffmpeg and ImageMagick not available - no video output");
642 warn = tr("ffmpeg not available - GIF and PNG outputs only");
643 recdlg->warning->setText(warn);
645 recdlg->warning->hide();
648 canvas = new DragAndDropView(this);
650 canvas->setAttribute(Qt::WA_OpaquePaintEvent);
651 canvas->setAttribute(Qt::WA_NoSystemBackground);
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()));
659 QObject::connect(warningsWidget(), SIGNAL(opened()), this, SLOT(warningsWidgetOpened()));
660 QObject::connect(warningsWidget(), SIGNAL(closed()), this, SLOT(warningsWidgetClosed()));
662 if (!(flags & Qt::FramelessWindowHint)) {
664 changeOrientation(orientation->actions().value(0));
669 setCentralWidget(canvas);
671 namFactory = new NetworkAccessManagerFactory;
672 canvas->engine()->setNetworkAccessManagerFactory(namFactory);
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);
683 QObject::connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(appAboutToQuit()));
686 QDeclarativeViewer::~QDeclarativeViewer()
689 canvas->engine()->setNetworkAccessManagerFactory(0);
693 void QDeclarativeViewer::enableExperimentalGestures()
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);
705 QDeclarativeView *QDeclarativeViewer::view() const
710 LoggerWidget *QDeclarativeViewer::warningsWidget() const
715 void QDeclarativeViewer::createMenu()
717 QAction *openAction = new QAction(tr("&Open..."), this);
718 openAction->setShortcuts(QKeySequence::Open);
719 connect(openAction, SIGNAL(triggered()), this, SLOT(openFile()));
721 QAction *openUrlAction = new QAction(tr("Open &URL..."), this);
722 connect(openUrlAction, SIGNAL(triggered()), this, SLOT(openUrl()));
724 QAction *reloadAction = new QAction(tr("&Reload"), this);
725 reloadAction->setShortcuts(QKeySequence::Refresh);
726 connect(reloadAction, SIGNAL(triggered()), this, SLOT(reload()));
728 QAction *snapshotAction = new QAction(tr("&Take Snapshot"), this);
729 snapshotAction->setShortcut(QKeySequence(tr("F3")));
730 connect(snapshotAction, SIGNAL(triggered()), this, SLOT(takeSnapShot()));
732 recordAction = new QAction(tr("Start Recording &Video"), this);
733 recordAction->setShortcut(QKeySequence(tr("F9")));
734 connect(recordAction, SIGNAL(triggered()), this, SLOT(toggleRecordingWithSelection()));
736 QAction *recordOptions = new QAction(tr("Video &Options..."), this);
737 connect(recordOptions, SIGNAL(triggered()), this, SLOT(chooseRecordingOptions()));
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)));
744 showWarningsWindow = new QAction(tr("Show Warnings"), this);
745 #if !defined(Q_OS_SYMBIAN)
746 showWarningsWindow->setCheckable((true));
747 showWarningsWindow->setChecked(loggerWindow->isVisible());
749 connect(showWarningsWindow, SIGNAL(triggered(bool)), this, SLOT(showWarnings(bool)));
751 QAction *proxyAction = new QAction(tr("HTTP &Proxy..."), this);
752 connect(proxyAction, SIGNAL(triggered()), this, SLOT(showProxySettings()));
754 QAction *fullscreenAction = new QAction(tr("Full Screen"), this);
755 fullscreenAction->setCheckable(true);
756 connect(fullscreenAction, SIGNAL(triggered()), this, SLOT(toggleFullScreen()));
758 rotateAction = new QAction(tr("Rotate orientation"), this);
759 rotateAction->setShortcut(QKeySequence(tr("Ctrl+T")));
760 connect(rotateAction, SIGNAL(triggered()), this, SLOT(rotateOrientation()));
762 orientation = new QActionGroup(this);
763 orientation->setExclusive(true);
764 connect(orientation, SIGNAL(triggered(QAction*)), this, SLOT(changeOrientation(QAction*)));
766 #if defined(Q_OS_SYMBIAN)
767 QAction *autoOrientationAction = new QAction(tr("Auto-orientation"), this);
768 autoOrientationAction->setCheckable(true);
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);
781 QAction *aboutAction = new QAction(tr("&About Qt..."), this);
782 aboutAction->setMenuRole(QAction::AboutQtRole);
783 connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
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()));
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()));
796 QMenuBar *menu = menuBar();
800 #if defined(Q_WS_MAEMO_5)
801 menu->addAction(openAction);
802 menu->addAction(openUrlAction);
803 menu->addAction(reloadAction);
805 menu->addAction(snapshotAction);
806 menu->addAction(recordAction);
808 menu->addAction(recordOptions);
809 menu->addAction(proxyAction);
811 menu->addAction(slowAction);
812 menu->addAction(showWarningsWindow);
814 orientation->addAction(landscapeAction);
815 orientation->addAction(portraitAction);
816 menu->addAction(new Maemo5PickerAction(tr("Set orientation"), orientation, this));
817 menu->addAction(fullscreenAction);
819 #endif // Q_WS_MAEMO_5
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);
830 QMenu *recordMenu = menu->addMenu(tr("&Recording"));
831 recordMenu->addAction(snapshotAction);
832 recordMenu->addAction(recordAction);
833 #endif // ! Q_OS_SYMBIAN
835 QMenu *debugMenu = menu->addMenu(tr("&Debugging"));
836 debugMenu->addAction(slowAction);
837 debugMenu->addAction(showWarningsWindow);
839 QMenu *settingsMenu = menu->addMenu(tr("&Settings"));
840 settingsMenu->addAction(proxyAction);
841 #if defined(Q_OS_SYMBIAN)
842 settingsMenu->addAction(fullscreenAction);
844 settingsMenu->addAction(recordOptions);
845 settingsMenu->addMenu(loggerWindow->preferencesMenu());
846 #endif // !Q_OS_SYMBIAN
847 settingsMenu->addAction(rotateAction);
849 QMenu *propertiesMenu = settingsMenu->addMenu(tr("Properties"));
851 #if defined(Q_OS_SYMBIAN)
852 orientation->addAction(autoOrientationAction);
854 orientation->addAction(portraitAction);
855 orientation->addAction(landscapeAction);
856 #if !defined(Q_OS_SYMBIAN)
857 orientation->addAction(portraitInvAction);
858 orientation->addAction(landscapeInvAction);
860 propertiesMenu->addActions(orientation->actions());
862 QMenu *helpMenu = menu->addMenu(tr("&Help"));
863 helpMenu->addAction(aboutAction);
866 void QDeclarativeViewer::showProxySettings()
868 ProxySettings settingsDlg (this);
870 connect (&settingsDlg, SIGNAL (accepted()), this, SLOT (proxySettingsChanged ()));
875 void QDeclarativeViewer::proxySettingsChanged()
877 namFactory->proxyChanged();
881 void QDeclarativeViewer::rotateOrientation()
883 #if defined(Q_WS_S60)
884 CAknAppUi *appUi = static_cast<CAknAppUi *>(CEikonEnv::Static()->AppUi());
886 CAknAppUi::TAppUiOrientation oldOrientation = appUi->Orientation();
887 QString newOrientation;
888 if (oldOrientation == CAknAppUi::EAppUiOrientationPortrait) {
889 newOrientation = QLatin1String("Landscape");
891 newOrientation = QLatin1String("Portrait");
893 foreach (QAction *action, orientation->actions()) {
894 if (action->text() == newOrientation) {
895 changeOrientation(action);
900 QAction *current = orientation->checkedAction();
901 QList<QAction *> actions = orientation->actions();
902 int index = actions.indexOf(current);
906 QAction *newOrientation = actions[(index + 1) % actions.count()];
907 changeOrientation(newOrientation);
911 void QDeclarativeViewer::toggleFullScreen()
919 void QDeclarativeViewer::showWarnings(bool show)
921 #if defined(Q_OS_SYMBIAN)
922 loggerWindow->showMaximized();
924 loggerWindow->setVisible(show);
928 void QDeclarativeViewer::warningsWidgetOpened()
930 showWarningsWindow->setChecked(true);
933 void QDeclarativeViewer::warningsWidgetClosed()
935 showWarningsWindow->setChecked(false);
938 void QDeclarativeViewer::takeSnapShot()
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;
947 void QDeclarativeViewer::pickRecordingFile()
949 QString fileName = getVideoFileName();
950 if (!fileName.isEmpty())
951 recdlg->file->setText(fileName);
954 void QDeclarativeViewer::chooseRecordingOptions()
957 recdlg->file->setText(record_file);
960 recdlg->setOriginalSize(canvas->size());
963 recdlg->setVideoRate(record_rate);
967 recdlg->setArguments(record_args.join(QLatin1String(" ")));
968 if (recdlg->exec()) {
970 record_file = recdlg->file->text();
972 record_outsize = recdlg->videoSize();
974 record_rate = recdlg->videoRate();
976 record_args = recdlg->arguments().split(QLatin1Char(' '),QString::SkipEmptyParts);
980 void QDeclarativeViewer::toggleRecordingWithSelection()
982 if (!recordTimer.isActive()) {
983 if (record_file.isEmpty()) {
984 QString fileName = getVideoFileName();
985 if (fileName.isEmpty())
987 if (!fileName.contains(QRegExp(QLatin1String(".[^\\/]*$"))))
988 fileName += QLatin1String(".avi");
989 setRecordFile(fileName);
995 void QDeclarativeViewer::toggleRecording()
997 if (record_file.isEmpty()) {
998 toggleRecordingWithSelection();
1001 bool recording = !recordTimer.isActive();
1002 recordAction->setText(recording ? tr("&Stop Recording Video\tF9") : tr("&Start Recording Video\tF9"));
1003 setRecording(recording);
1006 void QDeclarativeViewer::setSlowMode(bool enable)
1008 QUnifiedTimer::instance()->setSlowModeEnabled(enable);
1011 void QDeclarativeViewer::addLibraryPath(const QString& lib)
1013 canvas->engine()->addImportPath(lib);
1016 void QDeclarativeViewer::addPluginPath(const QString& plugin)
1018 canvas->engine()->addPluginPath(plugin);
1021 void QDeclarativeViewer::reload()
1023 launch(currentFileOrUrl);
1026 void QDeclarativeViewer::openFile()
1028 QString cur = canvas->source().toLocalFile();
1029 if (useQmlFileBrowser) {
1030 open(QLatin1String("qrc:/browser/Browser.qml"));
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());
1040 void QDeclarativeViewer::openUrl()
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);
1048 void QDeclarativeViewer::statusChanged()
1050 if (canvas->status() == QDeclarativeView::Error && tester)
1051 tester->executefailure();
1053 if (canvas->status() == QDeclarativeView::Ready) {
1054 initialSize = canvas->initialSize();
1055 updateSizeHints(true);
1056 QObject::connect(canvas, SIGNAL(sceneResized(QSize)), this, SLOT(sceneResized(QSize)));
1060 void QDeclarativeViewer::launch(const QString& file_or_url)
1062 QMetaObject::invokeMethod(this, "open", Qt::QueuedConnection, Q_ARG(QString, file_or_url));
1065 void QDeclarativeViewer::loadTranslationFile(const QString& directory)
1068 translator = new QTranslator(this);
1069 QApplication::installTranslator(translator);
1072 translator->load(QLatin1String("qml_" )+QLocale::system().name(), directory + QLatin1String("/i18n"));
1075 void QDeclarativeViewer::loadDummyDataFiles(const QString& directory)
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();
1084 if(comp.isError()) {
1085 QList<QDeclarativeError> errors = comp.errors();
1086 foreach (const QDeclarativeError &error, errors) {
1087 qWarning() << error;
1089 if (tester) tester->executefailure();
1093 qWarning() << "Loaded dummy data:" << dir.filePath(qml);
1094 qml.truncate(qml.length()-4);
1095 canvas->rootContext()->setContextProperty(qml, dummyData);
1096 dummyData->setParent(this);
1101 bool QDeclarativeViewer::open(const QString& file_or_url)
1103 currentFileOrUrl = file_or_url;
1106 QFileInfo fi(file_or_url);
1108 url = QUrl::fromLocalFile(fi.absoluteFilePath());
1110 url = QUrl(file_or_url);
1111 setWindowTitle(tr("%1 - Qt QML Viewer").arg(file_or_url));
1113 if (!m_script.isEmpty())
1114 tester = new QDeclarativeTester(m_script, m_scriptOptions, canvas);
1116 delete canvas->rootObject();
1117 canvas->engine()->clearComponentCache();
1118 QDeclarativeContext *ctxt = canvas->rootContext();
1119 ctxt->setContextProperty(QLatin1String("qmlViewer"), this);
1121 ctxt->setContextProperty(QLatin1String("qmlViewerFolder"), QLatin1String("E:\\")); // Documents on your S60 phone
1123 ctxt->setContextProperty(QLatin1String("qmlViewerFolder"), QDir::currentPath());
1126 ctxt->setContextProperty(QLatin1String("runtime"), Runtime::instance());
1128 QString fileName = url.toLocalFile();
1129 if (!fileName.isEmpty()) {
1130 fi.setFile(fileName);
1132 if (fi.suffix().toLower() != QLatin1String("qml")) {
1133 qWarning() << "qml cannot open non-QML file" << fileName;
1137 QFileInfo fi(fileName);
1138 loadTranslationFile(fi.path());
1139 loadDummyDataFiles(fi.path());
1141 qWarning() << "qml cannot find file:" << fileName;
1149 QObject::disconnect(canvas, SIGNAL(sceneResized(QSize)), this, SLOT(sceneResized(QSize)));
1150 canvas->setSource(url);
1155 void QDeclarativeViewer::setAutoRecord(int from, int to)
1157 if (from==0) from=1; // ensure resized
1158 record_autotime = to-from;
1159 autoStartTimer.setInterval(from);
1160 autoStartTimer.start();
1163 void QDeclarativeViewer::setRecordArgs(const QStringList& a)
1168 void QDeclarativeViewer::setRecordFile(const QString& f)
1173 void QDeclarativeViewer::setRecordRate(int fps)
1178 void QDeclarativeViewer::sceneResized(QSize)
1183 void QDeclarativeViewer::initialSizeChanged(QSize size)
1185 if (!isFullScreen() && !isMaximized()) {
1186 canvas->setGeometry(0,0,size.width(),size.height());
1187 layout()->setSizeConstraint(QLayout::SetFixedSize);
1188 layout()->activate();
1192 void QDeclarativeViewer::keyPressEvent(QKeyEvent *event)
1194 if (event->key() == Qt::Key_0 && devicemode)
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"
1208 } else if (event->key() == Qt::Key_F2 || (event->key() == Qt::Key_2 && devicemode)) {
1209 if (tester && m_scriptOptions & Record)
1211 } else if (event->key() == Qt::Key_F3 || (event->key() == Qt::Key_3 && devicemode)) {
1213 } else if (event->key() == Qt::Key_F5 || (event->key() == Qt::Key_5 && devicemode)) {
1215 } else if (event->key() == Qt::Key_F9 || (event->key() == Qt::Key_9 && devicemode)) {
1217 } else if (event->key() == Qt::Key_F10) {
1218 rotateOrientation();
1221 QWidget::keyPressEvent(event);
1224 bool QDeclarativeViewer::event(QEvent *event)
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();
1233 return QWidget::event(event);
1236 void QDeclarativeViewer::senseImageMagick()
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"));
1245 void QDeclarativeViewer::senseFfmpeg()
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;
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"));
1260 b->setText(ffmpegHelp);
1263 ffmpegHelpWindow = d;
1264 connect(recdlg->ffmpegHelp,SIGNAL(clicked()), ffmpegHelpWindow, SLOT(show()));
1267 void QDeclarativeViewer::setRecording(bool on)
1269 if (on == recordTimer.isActive())
1272 int period = int(1000/record_rate+0.5);
1273 QUnifiedTimer::instance()->setTimingInterval(on ? period:16);
1274 QUnifiedTimer::instance()->setConsistentTiming(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
1284 QProcess *proc = new QProcess(this);
1285 connect(proc, SIGNAL(finished(int)), this, SLOT(ffmpegFinished(int)));
1286 frame_stream = proc;
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());
1299 args += record_args;
1300 args << record_file;
1301 proc->start(QLatin1String("ffmpeg"), args);
1304 // Store frames, save to GIF/PNG
1308 canvas->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
1311 qDebug() << "Saving video...";
1312 frame_stream->close();
1313 qDebug() << "Wrote" << record_file;
1315 QProgressDialog progress(tr("Saving frames..."), tr("Cancel"), 0, frames.count()+10, this);
1316 progress.setWindowModality(Qt::WindowModal);
1320 qDebug() << "Saving frames...";
1323 bool png_output = false;
1324 if (record_file.right(4).toLower() == QLatin1String(".png")) {
1325 if (record_file.contains(QLatin1Char('%')))
1326 framename = record_file;
1328 framename = record_file.left(record_file.length()-4) + QLatin1String("%04d") + record_file.right(4);
1331 framename = QLatin1String("tmp-frame%04d.png");
1334 foreach (QImage* img, frames) {
1335 progress.setValue(progress.value()+1);
1336 if (progress.wasCanceled())
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);
1354 if (!progress.wasCanceled()) {
1356 framename.replace(QRegExp(QLatin1String("%\\d*.")), QLatin1String("*"));
1357 qDebug() << "Wrote frames" << framename;
1358 inputs.clear(); // don't remove them
1360 // ImageMagick and gifsicle for GIF encoding
1361 progress.setLabelText(tr("Converting frames to GIF file..."));
1363 args << QLatin1String("-delay") << QString::number(period/10);
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";
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";
1378 qDebug() << "Wrote" << record_file;
1383 progress.setValue(progress.maximum()-1);
1384 foreach (QString name, inputs)
1385 QFile::remove(name);
1390 qDebug() << "Recording: " << (recordTimer.isActive()?"ON":"OFF");
1393 void QDeclarativeViewer::ffmpegFinished(int code)
1395 qDebug() << "ffmpeg returned" << code << frame_stream->readAllStandardError();
1398 void QDeclarativeViewer::appAboutToQuit()
1400 // avoid QGLContext errors about invalid contexts on exit
1401 canvas->setViewport(0);
1403 // avoid crashes if messages are received after app has closed
1404 delete loggerWindow;
1411 void QDeclarativeViewer::autoStartRecording()
1414 autoStopTimer.setInterval(record_autotime);
1415 autoStopTimer.start();
1418 void QDeclarativeViewer::autoStopRecording()
1420 setRecording(false);
1423 void QDeclarativeViewer::recordFrame()
1425 canvas->QWidget::render(&frame);
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());
1432 frame_stream->write((char*)frame.bits(),frame.byteCount());
1435 frames.append(new QImage(frame));
1439 void QDeclarativeViewer::changeOrientation(QAction *action)
1443 QString o = action->text();
1444 action->setChecked(true);
1445 #if defined(Q_WS_S60)
1446 CAknAppUi *appUi = static_cast<CAknAppUi *>(CEikonEnv::Static()->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);
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);
1472 void QDeclarativeViewer::orientationChanged()
1477 void QDeclarativeViewer::setDeviceKeys(bool on)
1482 void QDeclarativeViewer::setNetworkCacheSize(int size)
1484 namFactory->setCacheSize(size);
1487 void QDeclarativeViewer::setUseGL(bool useGL)
1491 QGLFormat format = QGLFormat::defaultFormat();
1493 format.setSampleBuffers(true);
1494 format.setSwapInterval(1);
1496 format.setSampleBuffers(false);
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);
1503 canvas->setViewport(glWidget);
1510 void QDeclarativeViewer::setUseNativeFileBrowser(bool use)
1512 useQmlFileBrowser = !use;
1515 void QDeclarativeViewer::setSizeToView(bool sizeToView)
1517 QDeclarativeView::ResizeMode resizeMode = sizeToView ? QDeclarativeView::SizeRootObjectToView : QDeclarativeView::SizeViewToRootObject;
1518 if (resizeMode != canvas->resizeMode()) {
1519 canvas->setResizeMode(resizeMode);
1524 void QDeclarativeViewer::updateSizeHints(bool initial)
1526 static bool isRecursive = false;
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());
1538 layout()->setSizeConstraint(QLayout::SetFixedSize);
1539 layout()->activate();
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));
1550 isRecursive = false;
1553 void QDeclarativeViewer::registerTypes()
1555 static bool registered = false;
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());
1567 #include "qmlruntime.moc"