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>
47 # include "ui_recopts.h"
49 #include "qmlruntime.h"
50 #include <qdeclarativecontext.h>
51 #include <qdeclarativeengine.h>
52 #include <qdeclarativenetworkaccessmanagerfactory.h>
53 #include "qdeclarative.h"
54 #include <QAbstractAnimation>
55 #include <private/qabstractanimation_p.h>
59 #include <QXmlStreamReader>
61 #include <QNetworkReply>
62 #include <QNetworkCookieJar>
63 #include <QNetworkDiskCache>
64 #include <QNetworkAccessManager>
65 #include <QSignalMapper>
66 #include <QDeclarativeComponent>
68 #include <QApplication>
69 #include <QTranslator>
71 #include <QTextBrowser>
74 #include <QVBoxLayout>
75 #include <QProgressDialog>
80 #include <QFileDialog>
81 #include <QInputDialog>
83 #include <QGraphicsObject>
84 #include <QNetworkProxyFactory>
88 #include <QMutexLocker>
89 #include "proxysettings.h"
90 #include "deviceorientation.h"
91 #include <qdeclarativetester.h>
99 class DragAndDropView : public QDeclarativeView
103 DragAndDropView(QDeclarativeViewer *parent = 0)
104 : QDeclarativeView(parent)
106 setAcceptDrops(true);
109 void dragEnterEvent(QDragEnterEvent *event)
111 const QMimeData *mimeData = event->mimeData();
112 if (mimeData->hasUrls())
113 event->acceptProposedAction();
116 void dragMoveEvent(QDragMoveEvent *event)
118 event->acceptProposedAction();
121 void dragLeaveEvent(QDragLeaveEvent *event)
126 void dropEvent(QDropEvent *event)
128 const QMimeData *mimeData = event->mimeData();
129 if (!mimeData->hasUrls())
131 const QList<QUrl> urlList = mimeData->urls();
132 foreach (const QUrl &url, urlList) {
133 if (url.scheme() == QLatin1String("file")) {
134 static_cast<QDeclarativeViewer *>(parent())->open(url.toLocalFile());
142 class Runtime : public QObject
146 Q_PROPERTY(bool isActiveWindow READ isActiveWindow NOTIFY isActiveWindowChanged)
147 Q_PROPERTY(DeviceOrientation::Orientation orientation READ orientation NOTIFY orientationChanged)
150 static Runtime* instance()
152 static Runtime *instance = 0;
154 instance = new Runtime;
158 bool isActiveWindow() const { return activeWindow; }
159 void setActiveWindow(bool active)
161 if (active == activeWindow)
163 activeWindow = active;
164 emit isActiveWindowChanged();
167 DeviceOrientation::Orientation orientation() const { return DeviceOrientation::instance()->orientation(); }
170 void isActiveWindowChanged();
171 void orientationChanged();
174 Runtime(QObject *parent=0) : QObject(parent), activeWindow(false)
176 connect(DeviceOrientation::instance(), SIGNAL(orientationChanged()),
177 this, SIGNAL(orientationChanged()));
183 static struct { const char *name, *args; } ffmpegprofiles[] = {
184 {"Maximum Quality", "-sameq"},
185 {"High Quality", "-qmax 2"},
186 {"Medium Quality", "-qmax 6"},
187 {"Low Quality", "-qmax 16"},
188 {"Custom ffmpeg arguments", ""},
192 class RecordingDialog : public QDialog, public Ui::RecordingOptions {
196 RecordingDialog(QWidget *parent) : QDialog(parent)
199 hz->setValidator(new QDoubleValidator(hz));
200 for (int i=0; ffmpegprofiles[i].name; ++i) {
201 profile->addItem(QString::fromAscii(ffmpegprofiles[i].name));
205 void setArguments(QString a)
208 for (i=0; ffmpegprofiles[i].args[0]; ++i) {
209 if (QString::fromAscii(ffmpegprofiles[i].args) == a) {
210 profile->setCurrentIndex(i);
211 args->setText(QString::fromAscii(ffmpegprofiles[i].args));
217 profile->setCurrentIndex(i);
220 QString arguments() const
222 int i = profile->currentIndex();
223 return ffmpegprofiles[i].args[0] ? QLatin1String(ffmpegprofiles[i].args) : customargs;
226 void setOriginalSize(const QSize &s)
228 QString str = tr("Original (%1x%2)").arg(s.width()).arg(s.height());
230 sizeOriginal->setText(str);
231 if (sizeWidth->value()<=1) {
232 sizeWidth->setValue(s.width());
233 sizeHeight->setValue(s.height());
237 void showffmpegOptions(bool b)
239 ffmpegOptions->setVisible(b);
242 void showRateOptions(bool b)
244 rateOptions->setVisible(b);
247 void setVideoRate(int rate)
250 hz24->setChecked(true);
252 hz25->setChecked(true);
254 hz50->setChecked(true);
256 hz60->setChecked(true);
258 hzCustom->setChecked(true);
259 hz->setText(QString::number(rate));
263 int videoRate() const
265 if (hz24->isChecked())
267 else if (hz25->isChecked())
269 else if (hz50->isChecked())
271 else if (hz60->isChecked())
274 return hz->text().toInt();
278 QSize videoSize() const
280 if (sizeOriginal->isChecked())
282 else if (size720p->isChecked())
283 return QSize(1280,720);
284 else if (sizeVGA->isChecked())
285 return QSize(640,480);
286 else if (sizeQVGA->isChecked())
287 return QSize(320,240);
289 return QSize(sizeWidth->value(), sizeHeight->value());
295 void pickProfile(int i)
297 if (ffmpegprofiles[i].args[0]) {
298 args->setText(QLatin1String(ffmpegprofiles[i].args));
300 args->setText(customargs);
304 void storeCustomArgs(QString s)
313 class PersistentCookieJar : public QNetworkCookieJar {
315 PersistentCookieJar(QObject *parent) : QNetworkCookieJar(parent) { load(); }
316 ~PersistentCookieJar() { save(); }
318 virtual QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const
320 QMutexLocker lock(&mutex);
321 return QNetworkCookieJar::cookiesForUrl(url);
324 virtual bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
326 QMutexLocker lock(&mutex);
327 return QNetworkCookieJar::setCookiesFromUrl(cookieList, url);
333 QMutexLocker lock(&mutex);
334 QList<QNetworkCookie> list = allCookies();
336 foreach (QNetworkCookie cookie, list) {
337 if (!cookie.isSessionCookie()) {
338 data.append(cookie.toRawForm());
343 settings.setValue(QLatin1String("Cookies"), data);
348 QMutexLocker lock(&mutex);
350 QByteArray data = settings.value(QLatin1String("Cookies")).toByteArray();
351 setAllCookies(QNetworkCookie::parseCookies(data));
354 mutable QMutex mutex;
357 class SystemProxyFactory : public QNetworkProxyFactory
360 SystemProxyFactory() : proxyDirty(true), httpProxyInUse(false) {
363 virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query)
367 QString protocolTag = query.protocolTag();
368 if (httpProxyInUse && (protocolTag == QLatin1String("http") || protocolTag == QLatin1String("https"))) {
369 QList<QNetworkProxy> ret;
374 // systemProxyForQuery can take insanely long on Windows (QTBUG-10106)
375 return QNetworkProxyFactory::proxyForQuery(query);
377 return QNetworkProxyFactory::systemProxyForQuery(query);
382 // Don't bother locking because we know that the proxy only
383 // changes in response to the settings dialog and that
384 // the view will be reloaded.
386 httpProxyInUse = ProxySettings::httpProxyInUse();
388 httpProxy = ProxySettings::httpProxy();
391 void proxyChanged() {
396 volatile bool proxyDirty;
398 QNetworkProxy httpProxy;
401 class NetworkAccessManagerFactory : public QObject, public QDeclarativeNetworkAccessManagerFactory
405 NetworkAccessManagerFactory() : cacheSize(0) {}
406 ~NetworkAccessManagerFactory() {}
408 QNetworkAccessManager *create(QObject *parent);
410 void setCacheSize(int size) {
411 if (size != cacheSize) {
416 void proxyChanged() {
417 foreach (QNetworkAccessManager *nam, namList) {
418 static_cast<SystemProxyFactory*>(nam->proxyFactory())->proxyChanged();
422 static PersistentCookieJar *cookieJar;
425 void managerDestroyed(QObject *obj) {
426 namList.removeOne(static_cast<QNetworkAccessManager*>(obj));
432 QList<QNetworkAccessManager*> namList;
435 PersistentCookieJar *NetworkAccessManagerFactory::cookieJar = 0;
437 static void cleanup_cookieJar()
439 delete NetworkAccessManagerFactory::cookieJar;
440 NetworkAccessManagerFactory::cookieJar = 0;
443 QNetworkAccessManager *NetworkAccessManagerFactory::create(QObject *parent)
445 QMutexLocker lock(&mutex);
446 QNetworkAccessManager *manager = new QNetworkAccessManager(parent);
448 qAddPostRoutine(cleanup_cookieJar);
449 cookieJar = new PersistentCookieJar(0);
451 manager->setCookieJar(cookieJar);
452 cookieJar->setParent(0);
453 manager->setProxyFactory(new SystemProxyFactory);
455 QNetworkDiskCache *cache = new QNetworkDiskCache;
456 cache->setCacheDirectory(QDir::tempPath()+QLatin1String("/qml-viewer-network-cache"));
457 cache->setMaximumCacheSize(cacheSize);
458 manager->setCache(cache);
460 manager->setCache(0);
462 connect(manager, SIGNAL(destroyed(QObject*)), this, SLOT(managerDestroyed(QObject*)));
463 namList.append(manager);
467 QString QDeclarativeViewer::getVideoFileName()
469 QString title = convertAvailable || ffmpegAvailable ? tr("Save Video File") : tr("Save PNG Frames");
471 if (ffmpegAvailable) types += tr("Common Video files")+QLatin1String(" (*.avi *.mpeg *.mov)");
472 if (convertAvailable) types += tr("GIF Animation")+QLatin1String(" (*.gif)");
473 types += tr("Individual PNG frames")+QLatin1String(" (*.png)");
474 if (ffmpegAvailable) types += tr("All ffmpeg formats (*.*)");
475 return QFileDialog::getSaveFileName(this, title, QString(), types.join(QLatin1String(";; ")));
478 QDeclarativeViewer::QDeclarativeViewer(QWidget *parent, Qt::WindowFlags flags)
479 : QMainWindow(parent, flags)
480 , loggerWindow(new LoggerWidget(this))
484 , showWarningsWindow(0)
487 , useQmlFileBrowser(true)
490 QDeclarativeViewer::registerTypes();
491 setWindowTitle(tr("Qt QML Viewer"));
496 record_args += QLatin1String("-sameq");
498 recdlg = new RecordingDialog(this);
499 connect(recdlg->pickfile, SIGNAL(clicked()), this, SLOT(pickRecordingFile()));
502 if (!ffmpegAvailable)
503 recdlg->showffmpegOptions(false);
504 if (!ffmpegAvailable && !convertAvailable)
505 recdlg->showRateOptions(false);
507 if (!ffmpegAvailable) {
508 if (!convertAvailable)
509 warn = tr("ffmpeg and ImageMagick not available - no video output");
511 warn = tr("ffmpeg not available - GIF and PNG outputs only");
512 recdlg->warning->setText(warn);
514 recdlg->warning->hide();
517 canvas = new DragAndDropView(this);
519 canvas->setAttribute(Qt::WA_OpaquePaintEvent);
520 canvas->setAttribute(Qt::WA_NoSystemBackground);
524 QObject::connect(canvas, SIGNAL(initialSizeChanged(QSize)), this, SLOT(initialSizeChanged(QSize)));
525 QObject::connect(canvas, SIGNAL(statusChanged(QDeclarativeView::Status)), this, SLOT(statusChanged()));
526 QObject::connect(canvas->engine(), SIGNAL(quit()), this, SLOT(close()));
528 QObject::connect(warningsWidget(), SIGNAL(opened()), this, SLOT(warningsWidgetOpened()));
529 QObject::connect(warningsWidget(), SIGNAL(closed()), this, SLOT(warningsWidgetClosed()));
531 if (!(flags & Qt::FramelessWindowHint)) {
533 changeOrientation(orientation->actions().value(0));
538 setCentralWidget(canvas);
540 namFactory = new NetworkAccessManagerFactory;
541 canvas->engine()->setNetworkAccessManagerFactory(namFactory);
543 connect(&autoStartTimer, SIGNAL(timeout()), this, SLOT(autoStartRecording()));
544 connect(&autoStopTimer, SIGNAL(timeout()), this, SLOT(autoStopRecording()));
545 connect(&recordTimer, SIGNAL(timeout()), this, SLOT(recordFrame()));
546 connect(DeviceOrientation::instance(), SIGNAL(orientationChanged()),
547 this, SLOT(orientationChanged()), Qt::QueuedConnection);
548 autoStartTimer.setSingleShot(true);
549 autoStopTimer.setSingleShot(true);
550 recordTimer.setSingleShot(false);
552 QObject::connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(appAboutToQuit()));
555 QDeclarativeViewer::~QDeclarativeViewer()
558 canvas->engine()->setNetworkAccessManagerFactory(0);
562 void QDeclarativeViewer::enableExperimentalGestures()
564 #ifndef QT_NO_GESTURES
565 canvas->viewport()->grabGesture(Qt::TapGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent);
566 canvas->viewport()->grabGesture(Qt::TapAndHoldGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent);
567 canvas->viewport()->grabGesture(Qt::PanGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent);
568 canvas->viewport()->grabGesture(Qt::PinchGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent);
569 canvas->viewport()->grabGesture(Qt::SwipeGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent);
570 canvas->viewport()->setAttribute(Qt::WA_AcceptTouchEvents);
574 QDeclarativeView *QDeclarativeViewer::view() const
579 LoggerWidget *QDeclarativeViewer::warningsWidget() const
584 void QDeclarativeViewer::createMenu()
586 QAction *openAction = new QAction(tr("&Open..."), this);
587 openAction->setShortcuts(QKeySequence::Open);
588 connect(openAction, SIGNAL(triggered()), this, SLOT(openFile()));
590 QAction *openUrlAction = new QAction(tr("Open &URL..."), this);
591 connect(openUrlAction, SIGNAL(triggered()), this, SLOT(openUrl()));
593 QAction *reloadAction = new QAction(tr("&Reload"), this);
594 reloadAction->setShortcuts(QKeySequence::Refresh);
595 connect(reloadAction, SIGNAL(triggered()), this, SLOT(reload()));
597 QAction *snapshotAction = new QAction(tr("&Take Snapshot"), this);
598 snapshotAction->setShortcut(QKeySequence(tr("F3")));
599 connect(snapshotAction, SIGNAL(triggered()), this, SLOT(takeSnapShot()));
601 recordAction = new QAction(tr("Start Recording &Video"), this);
602 recordAction->setShortcut(QKeySequence(tr("F9")));
603 connect(recordAction, SIGNAL(triggered()), this, SLOT(toggleRecordingWithSelection()));
605 QAction *recordOptions = new QAction(tr("Video &Options..."), this);
606 connect(recordOptions, SIGNAL(triggered()), this, SLOT(chooseRecordingOptions()));
608 QAction *slowAction = new QAction(tr("&Slow Down Animations"), this);
609 slowAction->setShortcut(QKeySequence(tr("Ctrl+.")));
610 slowAction->setCheckable(true);
611 connect(slowAction, SIGNAL(triggered(bool)), this, SLOT(setSlowMode(bool)));
613 showWarningsWindow = new QAction(tr("Show Warnings"), this);
614 showWarningsWindow->setCheckable((true));
615 showWarningsWindow->setChecked(loggerWindow->isVisible());
616 connect(showWarningsWindow, SIGNAL(triggered(bool)), this, SLOT(showWarnings(bool)));
618 QAction *proxyAction = new QAction(tr("HTTP &Proxy..."), this);
619 connect(proxyAction, SIGNAL(triggered()), this, SLOT(showProxySettings()));
621 QAction *fullscreenAction = new QAction(tr("Full Screen"), this);
622 fullscreenAction->setCheckable(true);
623 connect(fullscreenAction, SIGNAL(triggered()), this, SLOT(toggleFullScreen()));
625 rotateAction = new QAction(tr("Rotate orientation"), this);
626 rotateAction->setShortcut(QKeySequence(tr("Ctrl+T")));
627 connect(rotateAction, SIGNAL(triggered()), this, SLOT(rotateOrientation()));
629 orientation = new QActionGroup(this);
630 orientation->setExclusive(true);
631 connect(orientation, SIGNAL(triggered(QAction*)), this, SLOT(changeOrientation(QAction*)));
633 QAction *portraitAction = new QAction(tr("Portrait"), this);
634 portraitAction->setCheckable(true);
635 QAction *landscapeAction = new QAction(tr("Landscape"), this);
636 landscapeAction->setCheckable(true);
637 QAction *portraitInvAction = new QAction(tr("Portrait (inverted)"), this);
638 portraitInvAction->setCheckable(true);
639 QAction *landscapeInvAction = new QAction(tr("Landscape (inverted)"), this);
640 landscapeInvAction->setCheckable(true);
642 QAction *aboutAction = new QAction(tr("&About Qt..."), this);
643 aboutAction->setMenuRole(QAction::AboutQtRole);
644 connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
646 QAction *closeAction = new QAction(tr("&Close"), this);
647 closeAction->setShortcuts(QKeySequence::Close);
648 connect(closeAction, SIGNAL(triggered()), this, SLOT(close()));
650 QAction *quitAction = new QAction(tr("&Quit"), this);
651 quitAction->setMenuRole(QAction::QuitRole);
652 quitAction->setShortcuts(QKeySequence::Quit);
653 connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
655 QMenuBar *menu = menuBar();
659 QMenu *fileMenu = menu->addMenu(tr("&File"));
660 fileMenu->addAction(openAction);
661 fileMenu->addAction(openUrlAction);
662 fileMenu->addAction(reloadAction);
663 fileMenu->addSeparator();
664 fileMenu->addAction(closeAction);
665 fileMenu->addAction(quitAction);
667 QMenu *recordMenu = menu->addMenu(tr("&Recording"));
668 recordMenu->addAction(snapshotAction);
669 recordMenu->addAction(recordAction);
671 QMenu *debugMenu = menu->addMenu(tr("&Debugging"));
672 debugMenu->addAction(slowAction);
673 debugMenu->addAction(showWarningsWindow);
675 QMenu *settingsMenu = menu->addMenu(tr("&Settings"));
676 settingsMenu->addAction(proxyAction);
677 settingsMenu->addAction(recordOptions);
678 settingsMenu->addMenu(loggerWindow->preferencesMenu());
679 settingsMenu->addAction(rotateAction);
681 QMenu *propertiesMenu = settingsMenu->addMenu(tr("Properties"));
683 orientation->addAction(portraitAction);
684 orientation->addAction(landscapeAction);
685 orientation->addAction(portraitInvAction);
686 orientation->addAction(landscapeInvAction);
687 propertiesMenu->addActions(orientation->actions());
689 QMenu *helpMenu = menu->addMenu(tr("&Help"));
690 helpMenu->addAction(aboutAction);
693 void QDeclarativeViewer::showProxySettings()
695 ProxySettings settingsDlg (this);
697 connect (&settingsDlg, SIGNAL (accepted()), this, SLOT (proxySettingsChanged ()));
702 void QDeclarativeViewer::proxySettingsChanged()
704 namFactory->proxyChanged();
708 void QDeclarativeViewer::rotateOrientation()
710 QAction *current = orientation->checkedAction();
711 QList<QAction *> actions = orientation->actions();
712 int index = actions.indexOf(current);
716 QAction *newOrientation = actions[(index + 1) % actions.count()];
717 changeOrientation(newOrientation);
720 void QDeclarativeViewer::toggleFullScreen()
728 void QDeclarativeViewer::showWarnings(bool show)
730 loggerWindow->setVisible(show);
733 void QDeclarativeViewer::warningsWidgetOpened()
735 showWarningsWindow->setChecked(true);
738 void QDeclarativeViewer::warningsWidgetClosed()
740 showWarningsWindow->setChecked(false);
743 void QDeclarativeViewer::takeSnapShot()
745 static int snapshotcount = 1;
746 QString snapFileName = QString(QLatin1String("snapshot%1.png")).arg(snapshotcount);
747 QPixmap::grabWidget(canvas).save(snapFileName);
748 qDebug() << "Wrote" << snapFileName;
752 void QDeclarativeViewer::pickRecordingFile()
754 QString fileName = getVideoFileName();
755 if (!fileName.isEmpty())
756 recdlg->file->setText(fileName);
759 void QDeclarativeViewer::chooseRecordingOptions()
762 recdlg->file->setText(record_file);
765 recdlg->setOriginalSize(canvas->size());
768 recdlg->setVideoRate(record_rate);
772 recdlg->setArguments(record_args.join(QLatin1String(" ")));
773 if (recdlg->exec()) {
775 record_file = recdlg->file->text();
777 record_outsize = recdlg->videoSize();
779 record_rate = recdlg->videoRate();
781 record_args = recdlg->arguments().split(QLatin1Char(' '),QString::SkipEmptyParts);
785 void QDeclarativeViewer::toggleRecordingWithSelection()
787 if (!recordTimer.isActive()) {
788 if (record_file.isEmpty()) {
789 QString fileName = getVideoFileName();
790 if (fileName.isEmpty())
792 if (!fileName.contains(QRegExp(QLatin1String(".[^\\/]*$"))))
793 fileName += QLatin1String(".avi");
794 setRecordFile(fileName);
800 void QDeclarativeViewer::toggleRecording()
802 if (record_file.isEmpty()) {
803 toggleRecordingWithSelection();
806 bool recording = !recordTimer.isActive();
807 recordAction->setText(recording ? tr("&Stop Recording Video\tF9") : tr("&Start Recording Video\tF9"));
808 setRecording(recording);
811 void QDeclarativeViewer::setSlowMode(bool enable)
813 QUnifiedTimer::instance()->setSlowModeEnabled(enable);
816 void QDeclarativeViewer::addLibraryPath(const QString& lib)
818 canvas->engine()->addImportPath(lib);
821 void QDeclarativeViewer::addPluginPath(const QString& plugin)
823 canvas->engine()->addPluginPath(plugin);
826 void QDeclarativeViewer::reload()
828 launch(currentFileOrUrl);
831 void QDeclarativeViewer::openFile()
833 QString cur = canvas->source().toLocalFile();
834 if (useQmlFileBrowser) {
835 open(QLatin1String("qrc:/browser/Browser.qml"));
837 QString fileName = QFileDialog::getOpenFileName(this, tr("Open QML file"), cur, tr("QML Files (*.qml)"));
838 if (!fileName.isEmpty()) {
839 QFileInfo fi(fileName);
840 open(fi.absoluteFilePath());
845 void QDeclarativeViewer::openUrl()
847 QString cur = canvas->source().toLocalFile();
848 QString url= QInputDialog::getText(this, tr("Open QML file"), tr("URL of main QML file:"), QLineEdit::Normal, cur);
853 void QDeclarativeViewer::statusChanged()
855 if (canvas->status() == QDeclarativeView::Error && tester)
856 tester->executefailure();
858 if (canvas->status() == QDeclarativeView::Ready) {
859 initialSize = canvas->initialSize();
860 updateSizeHints(true);
861 QObject::connect(canvas, SIGNAL(sceneResized(QSize)), this, SLOT(sceneResized(QSize)));
865 void QDeclarativeViewer::launch(const QString& file_or_url)
867 QMetaObject::invokeMethod(this, "open", Qt::QueuedConnection, Q_ARG(QString, file_or_url));
870 void QDeclarativeViewer::loadTranslationFile(const QString& directory)
873 translator = new QTranslator(this);
874 QApplication::installTranslator(translator);
877 translator->load(QLatin1String("qml_" )+QLocale::system().name(), directory + QLatin1String("/i18n"));
880 void QDeclarativeViewer::loadDummyDataFiles(const QString& directory)
882 QDir dir(directory + QLatin1String("/dummydata"), QLatin1String("*.qml"));
883 QStringList list = dir.entryList();
884 for (int i = 0; i < list.size(); ++i) {
885 QString qml = list.at(i);
886 QDeclarativeComponent comp(canvas->engine(), dir.filePath(qml));
887 QObject *dummyData = comp.create();
890 QList<QDeclarativeError> errors = comp.errors();
891 foreach (const QDeclarativeError &error, errors) {
894 if (tester) tester->executefailure();
898 qWarning() << "Loaded dummy data:" << dir.filePath(qml);
899 qml.truncate(qml.length()-4);
900 canvas->rootContext()->setContextProperty(qml, dummyData);
901 dummyData->setParent(this);
906 bool QDeclarativeViewer::open(const QString& file_or_url)
908 currentFileOrUrl = file_or_url;
911 QFileInfo fi(file_or_url);
913 url = QUrl::fromLocalFile(fi.absoluteFilePath());
915 url = QUrl(file_or_url);
916 setWindowTitle(tr("%1 - Qt QML Viewer").arg(file_or_url));
918 if (!m_script.isEmpty())
919 tester = new QDeclarativeTester(m_script, m_scriptOptions, canvas);
921 delete canvas->rootObject();
922 canvas->engine()->clearComponentCache();
923 QDeclarativeContext *ctxt = canvas->rootContext();
924 ctxt->setContextProperty(QLatin1String("qmlViewer"), this);
925 ctxt->setContextProperty(QLatin1String("qmlViewerFolder"), QDir::currentPath());
926 ctxt->setContextProperty(QLatin1String("runtime"), Runtime::instance());
928 QString fileName = url.toLocalFile();
929 if (!fileName.isEmpty()) {
930 fi.setFile(fileName);
932 if (fi.suffix().toLower() != QLatin1String("qml")) {
933 qWarning() << "qml cannot open non-QML file" << fileName;
937 QFileInfo fi(fileName);
938 loadTranslationFile(fi.path());
939 loadDummyDataFiles(fi.path());
941 qWarning() << "qml cannot find file:" << fileName;
949 QObject::disconnect(canvas, SIGNAL(sceneResized(QSize)), this, SLOT(sceneResized(QSize)));
950 canvas->setSource(url);
955 void QDeclarativeViewer::setAutoRecord(int from, int to)
957 if (from==0) from=1; // ensure resized
958 record_autotime = to-from;
959 autoStartTimer.setInterval(from);
960 autoStartTimer.start();
963 void QDeclarativeViewer::setRecordArgs(const QStringList& a)
968 void QDeclarativeViewer::setRecordFile(const QString& f)
973 void QDeclarativeViewer::setRecordRate(int fps)
978 void QDeclarativeViewer::sceneResized(QSize)
983 void QDeclarativeViewer::initialSizeChanged(QSize size)
985 if (!isFullScreen() && !isMaximized()) {
986 canvas->setGeometry(0,0,size.width(),size.height());
987 layout()->setSizeConstraint(QLayout::SetFixedSize);
988 layout()->activate();
992 void QDeclarativeViewer::keyPressEvent(QKeyEvent *event)
994 if (event->key() == Qt::Key_0 && devicemode)
996 else if (event->key() == Qt::Key_F1 || (event->key() == Qt::Key_1 && devicemode)) {
997 qDebug() << "F1 - help\n"
998 << "F2 - save test script\n"
999 << "F3 - take PNG snapshot\n"
1000 << "F4 - show items and state\n"
1001 << "F5 - reload QML\n"
1002 << "F6 - show object tree\n"
1003 << "F7 - show timing\n"
1004 << "F9 - toggle video recording\n"
1005 << "F10 - toggle orientation\n"
1006 << "device keys: 0=quit, 1..8=F1..F8"
1008 } else if (event->key() == Qt::Key_F2 || (event->key() == Qt::Key_2 && devicemode)) {
1009 if (tester && m_scriptOptions & Record)
1011 } else if (event->key() == Qt::Key_F3 || (event->key() == Qt::Key_3 && devicemode)) {
1013 } else if (event->key() == Qt::Key_F5 || (event->key() == Qt::Key_5 && devicemode)) {
1015 } else if (event->key() == Qt::Key_F9 || (event->key() == Qt::Key_9 && devicemode)) {
1017 } else if (event->key() == Qt::Key_F10) {
1018 rotateOrientation();
1021 QWidget::keyPressEvent(event);
1024 bool QDeclarativeViewer::event(QEvent *event)
1026 if (event->type() == QEvent::WindowActivate) {
1027 Runtime::instance()->setActiveWindow(true);
1028 DeviceOrientation::instance()->resumeListening();
1029 } else if (event->type() == QEvent::WindowDeactivate) {
1030 Runtime::instance()->setActiveWindow(false);
1031 DeviceOrientation::instance()->pauseListening();
1033 return QWidget::event(event);
1036 void QDeclarativeViewer::senseImageMagick()
1039 proc.start(QLatin1String("convert"), QStringList() << QLatin1String("-h"));
1040 proc.waitForFinished(2000);
1041 QString help = QString::fromAscii(proc.readAllStandardOutput());
1042 convertAvailable = help.contains(QLatin1String("ImageMagick"));
1045 void QDeclarativeViewer::senseFfmpeg()
1048 proc.start(QLatin1String("ffmpeg"), QStringList() << QLatin1String("-h"));
1049 proc.waitForFinished(2000);
1050 QString ffmpegHelp = QString::fromAscii(proc.readAllStandardOutput());
1051 ffmpegAvailable = ffmpegHelp.contains(QLatin1String("-s "));
1052 ffmpegHelp = tr("Video recording uses ffmpeg:") + QLatin1String("\n\n") + ffmpegHelp;
1054 QDialog *d = new QDialog(recdlg);
1055 QVBoxLayout *l = new QVBoxLayout(d);
1056 QTextBrowser *b = new QTextBrowser(d);
1057 QFont f = b->font();
1058 f.setFamily(QLatin1String("courier"));
1060 b->setText(ffmpegHelp);
1063 ffmpegHelpWindow = d;
1064 connect(recdlg->ffmpegHelp,SIGNAL(clicked()), ffmpegHelpWindow, SLOT(show()));
1067 void QDeclarativeViewer::setRecording(bool on)
1069 if (on == recordTimer.isActive())
1072 int period = int(1000/record_rate+0.5);
1073 QUnifiedTimer::instance()->setTimingInterval(on ? period:16);
1074 QUnifiedTimer::instance()->setConsistentTiming(on);
1076 canvas->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
1077 recordTimer.setInterval(period);
1078 recordTimer.start();
1079 frame_fmt = record_file.right(4).toLower();
1080 frame = QImage(canvas->width(),canvas->height(),QImage::Format_RGB32);
1081 if (frame_fmt != QLatin1String(".png") && (!convertAvailable || frame_fmt != QLatin1String(".gif"))) {
1082 // Stream video to ffmpeg
1084 QProcess *proc = new QProcess(this);
1085 connect(proc, SIGNAL(finished(int)), this, SLOT(ffmpegFinished(int)));
1086 frame_stream = proc;
1089 args << QLatin1String("-y");
1090 args << QLatin1String("-r") << QString::number(record_rate);
1091 args << QLatin1String("-f") << QLatin1String("rawvideo");
1092 args << QLatin1String("-pix_fmt") << (frame_fmt == QLatin1String(".gif") ? QLatin1String("rgb24") : QLatin1String("rgb32"));
1093 args << QLatin1String("-s") << QString::fromAscii("%1x%2").arg(canvas->width()).arg(canvas->height());
1094 args << QLatin1String("-i") << QLatin1String("-");
1095 if (record_outsize.isValid()) {
1096 args << QLatin1String("-s") << QString::fromAscii("%1x%2").arg(record_outsize.width()).arg(record_outsize.height());
1097 args << QLatin1String("-aspect") << QString::number(double(canvas->width())/canvas->height());
1099 args += record_args;
1100 args << record_file;
1101 proc->start(QLatin1String("ffmpeg"), args);
1104 // Store frames, save to GIF/PNG
1108 canvas->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
1111 qDebug() << "Saving video...";
1112 frame_stream->close();
1113 qDebug() << "Wrote" << record_file;
1115 QProgressDialog progress(tr("Saving frames..."), tr("Cancel"), 0, frames.count()+10, this);
1116 progress.setWindowModality(Qt::WindowModal);
1120 qDebug() << "Saving frames...";
1123 bool png_output = false;
1124 if (record_file.right(4).toLower() == QLatin1String(".png")) {
1125 if (record_file.contains(QLatin1Char('%')))
1126 framename = record_file;
1128 framename = record_file.left(record_file.length()-4) + QLatin1String("%04d") + record_file.right(4);
1131 framename = QLatin1String("tmp-frame%04d.png");
1134 foreach (QImage* img, frames) {
1135 progress.setValue(progress.value()+1);
1136 if (progress.wasCanceled())
1139 name.sprintf(framename.toLocal8Bit(),frame++);
1140 if (record_outsize.isValid())
1141 *img = img->scaled(record_outsize,Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
1142 if (record_dither==QLatin1String("ordered"))
1143 img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither|Qt::OrderedDither).save(name);
1144 else if (record_dither==QLatin1String("threshold"))
1145 img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither|Qt::ThresholdDither).save(name);
1146 else if (record_dither==QLatin1String("floyd"))
1147 img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither).save(name);
1154 if (!progress.wasCanceled()) {
1156 framename.replace(QRegExp(QLatin1String("%\\d*.")), QLatin1String("*"));
1157 qDebug() << "Wrote frames" << framename;
1158 inputs.clear(); // don't remove them
1160 // ImageMagick and gifsicle for GIF encoding
1161 progress.setLabelText(tr("Converting frames to GIF file..."));
1163 args << QLatin1String("-delay") << QString::number(period/10);
1165 args << record_file;
1166 qDebug() << "Converting..." << record_file << "(this may take a while)";
1167 if (0!=QProcess::execute(QLatin1String("convert"), args)) {
1168 qWarning() << "Cannot run ImageMagick 'convert' - recorded frames not converted";
1169 inputs.clear(); // don't remove them
1170 qDebug() << "Wrote frames tmp-frame*.png";
1172 if (record_file.right(4).toLower() == QLatin1String(".gif")) {
1173 qDebug() << "Compressing..." << record_file;
1174 if (0!=QProcess::execute(QLatin1String("gifsicle"), QStringList() << QLatin1String("-O2")
1175 << QLatin1String("-o") << record_file << record_file))
1176 qWarning() << "Cannot run 'gifsicle' - not compressed";
1178 qDebug() << "Wrote" << record_file;
1183 progress.setValue(progress.maximum()-1);
1184 foreach (QString name, inputs)
1185 QFile::remove(name);
1190 qDebug() << "Recording: " << (recordTimer.isActive()?"ON":"OFF");
1193 void QDeclarativeViewer::ffmpegFinished(int code)
1195 qDebug() << "ffmpeg returned" << code << frame_stream->readAllStandardError();
1198 void QDeclarativeViewer::appAboutToQuit()
1200 // avoid QGLContext errors about invalid contexts on exit
1201 canvas->setViewport(0);
1203 // avoid crashes if messages are received after app has closed
1204 delete loggerWindow;
1211 void QDeclarativeViewer::autoStartRecording()
1214 autoStopTimer.setInterval(record_autotime);
1215 autoStopTimer.start();
1218 void QDeclarativeViewer::autoStopRecording()
1220 setRecording(false);
1223 void QDeclarativeViewer::recordFrame()
1225 canvas->QWidget::render(&frame);
1227 if (frame_fmt == QLatin1String(".gif")) {
1228 // ffmpeg can't do 32bpp with gif
1229 QImage rgb24 = frame.convertToFormat(QImage::Format_RGB888);
1230 frame_stream->write((char*)rgb24.bits(),rgb24.byteCount());
1232 frame_stream->write((char*)frame.bits(),frame.byteCount());
1235 frames.append(new QImage(frame));
1239 void QDeclarativeViewer::changeOrientation(QAction *action)
1243 QString o = action->text();
1244 action->setChecked(true);
1245 if (o == QLatin1String("Portrait"))
1246 DeviceOrientation::instance()->setOrientation(DeviceOrientation::Portrait);
1247 else if (o == QLatin1String("Landscape"))
1248 DeviceOrientation::instance()->setOrientation(DeviceOrientation::Landscape);
1249 else if (o == QLatin1String("Portrait (inverted)"))
1250 DeviceOrientation::instance()->setOrientation(DeviceOrientation::PortraitInverted);
1251 else if (o == QLatin1String("Landscape (inverted)"))
1252 DeviceOrientation::instance()->setOrientation(DeviceOrientation::LandscapeInverted);
1255 void QDeclarativeViewer::orientationChanged()
1260 void QDeclarativeViewer::setDeviceKeys(bool on)
1265 void QDeclarativeViewer::setNetworkCacheSize(int size)
1267 namFactory->setCacheSize(size);
1270 void QDeclarativeViewer::setUseGL(bool useGL)
1274 QGLFormat format = QGLFormat::defaultFormat();
1276 format.setSampleBuffers(true);
1277 format.setSwapInterval(1);
1279 format.setSampleBuffers(false);
1282 QGLWidget *glWidget = new QGLWidget(format);
1283 //### potentially faster, but causes junk to appear if top-level is Item, not Rectangle
1284 //glWidget->setAutoFillBackground(false);
1286 canvas->setViewport(glWidget);
1293 void QDeclarativeViewer::setUseNativeFileBrowser(bool use)
1295 useQmlFileBrowser = !use;
1298 void QDeclarativeViewer::setSizeToView(bool sizeToView)
1300 QDeclarativeView::ResizeMode resizeMode = sizeToView ? QDeclarativeView::SizeRootObjectToView : QDeclarativeView::SizeViewToRootObject;
1301 if (resizeMode != canvas->resizeMode()) {
1302 canvas->setResizeMode(resizeMode);
1307 void QDeclarativeViewer::updateSizeHints(bool initial)
1309 static bool isRecursive = false;
1315 if (initial || (canvas->resizeMode() == QDeclarativeView::SizeViewToRootObject)) {
1316 QSize newWindowSize = initial ? initialSize : canvas->sizeHint();
1317 //qWarning() << "USH:" << (initial ? "INIT:" : "V2R:") << "setting fixed size " << newWindowSize;
1318 if (!isFullScreen() && !isMaximized()) {
1319 canvas->setGeometry(0,0,newWindowSize.width(),newWindowSize.height());
1321 layout()->setSizeConstraint(QLayout::SetFixedSize);
1322 layout()->activate();
1325 //qWarning() << "USH: R2V: setting free size ";
1326 layout()->setSizeConstraint(QLayout::SetNoConstraint);
1327 layout()->activate();
1328 setMinimumSize(minimumSizeHint());
1329 setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
1330 canvas->setMinimumSize(QSize(0,0));
1331 canvas->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
1333 isRecursive = false;
1336 void QDeclarativeViewer::registerTypes()
1338 static bool registered = false;
1341 // registering only for exposing the DeviceOrientation::Orientation enum
1342 qmlRegisterUncreatableType<DeviceOrientation>("Qt", 4, 7, "Orientation", QString());
1343 qmlRegisterUncreatableType<DeviceOrientation>("QtQuick", 1, 0, "Orientation", QString());
1350 #include "qmlruntime.moc"