Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /**************************************************************************** |
---|---|
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the tools applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #include <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 | // Check for presence of ImageMagick by launching its command line |
604 | // convert tool except on Windows, where convert.exe is a file system converter. |
605 | static bool senseImageMagick() |
606 | { |
607 | #ifdef Q_OS_WIN |
608 | return false; |
609 | #else |
610 | static int imageMagickFound = -1; |
611 | if (imageMagickFound == -1) { |
612 | QProcess proc; |
613 | proc.start(QLatin1String("convert"), QStringList(QLatin1String( "-h"))); |
614 | imageMagickFound = proc.waitForStarted() && proc.waitForFinished(2000) |
615 | && proc.readAllStandardOutput().contains("ImageMagick") ? |
616 | 1 : 0; |
617 | } |
618 | return imageMagickFound != 0; |
619 | #endif |
620 | } |
621 | |
622 | QDeclarativeViewer::QDeclarativeViewer(QWidget *parent, Qt::WindowFlags flags) |
623 | : QMainWindow(parent, flags) |
624 | , loggerWindow(new LoggerWidget(this)) |
625 | , frame_stream(0) |
626 | , convertAvailable(senseImageMagick()) |
627 | , rotateAction(0) |
628 | , orientation(0) |
629 | , showWarningsWindow(0) |
630 | , m_scriptOptions(0) |
631 | , tester(0) |
632 | , useQmlFileBrowser(true) |
633 | , translator(0) |
634 | { |
635 | QDeclarativeViewer::registerTypes(); |
636 | setWindowTitle(tr("Qt QML Viewer")); |
637 | #ifdef Q_WS_MAEMO_5 |
638 | setAttribute(Qt::WA_Maemo5StackedWindow); |
639 | // setPalette(QApplication::palette("QLabel")); |
640 | #endif |
641 | |
642 | devicemode = false; |
643 | canvas = 0; |
644 | record_autotime = 0; |
645 | record_rate = 50; |
646 | record_args += QLatin1String("-sameq"); |
647 | |
648 | recdlg = new RecordingDialog(this); |
649 | connect(recdlg->pickfile, SIGNAL(clicked()), this, SLOT(pickRecordingFile())); |
650 | senseFfmpeg(); |
651 | if (!ffmpegAvailable) |
652 | recdlg->showffmpegOptions(false); |
653 | if (!ffmpegAvailable && !convertAvailable) |
654 | recdlg->showRateOptions(false); |
655 | QString warn; |
656 | if (!ffmpegAvailable) { |
657 | if (!convertAvailable) |
658 | warn = tr("ffmpeg and ImageMagick not available - no video output"); |
659 | else |
660 | warn = tr("ffmpeg not available - GIF and PNG outputs only"); |
661 | recdlg->warning->setText(warn); |
662 | } else { |
663 | recdlg->warning->hide(); |
664 | } |
665 | |
666 | canvas = new DragAndDropView(this); |
667 | |
668 | canvas->setAttribute(Qt::WA_OpaquePaintEvent); |
669 | canvas->setAttribute(Qt::WA_NoSystemBackground); |
670 | |
671 | canvas->setFocus(); |
672 | |
673 | QObject::connect(canvas, SIGNAL(sceneResized(QSize)), this, SLOT(sceneResized(QSize))); |
674 | QObject::connect(canvas, SIGNAL(statusChanged(QDeclarativeView::Status)), this, SLOT(statusChanged())); |
675 | QObject::connect(canvas->engine(), SIGNAL(quit()), this, SLOT(close())); |
676 | |
677 | QObject::connect(warningsWidget(), SIGNAL(opened()), this, SLOT(warningsWidgetOpened())); |
678 | QObject::connect(warningsWidget(), SIGNAL(closed()), this, SLOT(warningsWidgetClosed())); |
679 | |
680 | if (!(flags & Qt::FramelessWindowHint)) { |
681 | createMenu(); |
682 | changeOrientation(orientation->actions().value(0)); |
683 | } else { |
684 | setMenuBar(0); |
685 | } |
686 | |
687 | setCentralWidget(canvas); |
688 | |
689 | namFactory = new NetworkAccessManagerFactory; |
690 | canvas->engine()->setNetworkAccessManagerFactory(namFactory); |
691 | |
692 | connect(&autoStartTimer, SIGNAL(timeout()), this, SLOT(autoStartRecording())); |
693 | connect(&autoStopTimer, SIGNAL(timeout()), this, SLOT(autoStopRecording())); |
694 | connect(&recordTimer, SIGNAL(timeout()), this, SLOT(recordFrame())); |
695 | connect(DeviceOrientation::instance(), SIGNAL(orientationChanged()), |
696 | this, SLOT(orientationChanged()), Qt::QueuedConnection); |
697 | autoStartTimer.setSingleShot(true); |
698 | autoStopTimer.setSingleShot(true); |
699 | recordTimer.setSingleShot(false); |
700 | |
701 | QObject::connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(appAboutToQuit())); |
702 | } |
703 | |
704 | QDeclarativeViewer::~QDeclarativeViewer() |
705 | { |
706 | delete loggerWindow; |
707 | canvas->engine()->setNetworkAccessManagerFactory(0); |
708 | delete namFactory; |
709 | } |
710 | |
711 | void QDeclarativeViewer::enableExperimentalGestures() |
712 | { |
713 | #ifndef QT_NO_GESTURES |
714 | canvas->viewport()->grabGesture(Qt::TapGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent); |
715 | canvas->viewport()->grabGesture(Qt::TapAndHoldGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent); |
716 | canvas->viewport()->grabGesture(Qt::PanGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent); |
717 | canvas->viewport()->grabGesture(Qt::PinchGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent); |
718 | canvas->viewport()->grabGesture(Qt::SwipeGesture,Qt::DontStartGestureOnChildren|Qt::ReceivePartialGestures|Qt::IgnoredGesturesPropagateToParent); |
719 | canvas->viewport()->setAttribute(Qt::WA_AcceptTouchEvents); |
720 | #endif |
721 | } |
722 | |
723 | QDeclarativeView *QDeclarativeViewer::view() const |
724 | { |
725 | return canvas; |
726 | } |
727 | |
728 | LoggerWidget *QDeclarativeViewer::warningsWidget() const |
729 | { |
730 | return loggerWindow; |
731 | } |
732 | |
733 | void QDeclarativeViewer::createMenu() |
734 | { |
735 | QAction *openAction = new QAction(tr("&Open..."), this); |
736 | openAction->setShortcuts(QKeySequence::Open); |
737 | connect(openAction, SIGNAL(triggered()), this, SLOT(openFile())); |
738 | |
739 | QAction *openUrlAction = new QAction(tr("Open &URL..."), this); |
740 | connect(openUrlAction, SIGNAL(triggered()), this, SLOT(openUrl())); |
741 | |
742 | QAction *reloadAction = new QAction(tr("&Reload"), this); |
743 | reloadAction->setShortcuts(QKeySequence::Refresh); |
744 | connect(reloadAction, SIGNAL(triggered()), this, SLOT(reload())); |
745 | |
746 | QAction *snapshotAction = new QAction(tr("&Take Snapshot"), this); |
747 | snapshotAction->setShortcut(QKeySequence(tr("F3"))); |
748 | connect(snapshotAction, SIGNAL(triggered()), this, SLOT(takeSnapShot())); |
749 | |
750 | recordAction = new QAction(tr("Start Recording &Video"), this); |
751 | recordAction->setShortcut(QKeySequence(tr("F9"))); |
752 | connect(recordAction, SIGNAL(triggered()), this, SLOT(toggleRecordingWithSelection())); |
753 | |
754 | QAction *recordOptions = new QAction(tr("Video &Options..."), this); |
755 | connect(recordOptions, SIGNAL(triggered()), this, SLOT(chooseRecordingOptions())); |
756 | |
757 | QAction *slowAction = new QAction(tr("&Slow Down Animations"), this); |
758 | slowAction->setShortcut(QKeySequence(tr("Ctrl+."))); |
759 | slowAction->setCheckable(true); |
760 | connect(slowAction, SIGNAL(triggered(bool)), this, SLOT(setSlowMode(bool))); |
761 | |
762 | showWarningsWindow = new QAction(tr("Show Warnings"), this); |
763 | #if !defined(Q_OS_SYMBIAN) |
764 | showWarningsWindow->setCheckable((true)); |
765 | showWarningsWindow->setChecked(loggerWindow->isVisible()); |
766 | #endif |
767 | connect(showWarningsWindow, SIGNAL(triggered(bool)), this, SLOT(showWarnings(bool))); |
768 | |
769 | QAction *proxyAction = new QAction(tr("HTTP &Proxy..."), this); |
770 | connect(proxyAction, SIGNAL(triggered()), this, SLOT(showProxySettings())); |
771 | |
772 | QAction *fullscreenAction = new QAction(tr("Full Screen"), this); |
773 | fullscreenAction->setCheckable(true); |
774 | connect(fullscreenAction, SIGNAL(triggered()), this, SLOT(toggleFullScreen())); |
775 | |
776 | rotateAction = new QAction(tr("Rotate orientation"), this); |
777 | rotateAction->setShortcut(QKeySequence(tr("Ctrl+T"))); |
778 | connect(rotateAction, SIGNAL(triggered()), this, SLOT(rotateOrientation())); |
779 | |
780 | orientation = new QActionGroup(this); |
781 | orientation->setExclusive(true); |
782 | connect(orientation, SIGNAL(triggered(QAction*)), this, SLOT(changeOrientation(QAction*))); |
783 | |
784 | #if defined(Q_OS_SYMBIAN) |
785 | QAction *autoOrientationAction = new QAction(tr("Auto-orientation"), this); |
786 | autoOrientationAction->setCheckable(true); |
787 | #endif |
788 | QAction *portraitAction = new QAction(tr("Portrait"), this); |
789 | portraitAction->setCheckable(true); |
790 | QAction *landscapeAction = new QAction(tr("Landscape"), this); |
791 | landscapeAction->setCheckable(true); |
792 | #if !defined(Q_OS_SYMBIAN) |
793 | QAction *portraitInvAction = new QAction(tr("Portrait (inverted)"), this); |
794 | portraitInvAction->setCheckable(true); |
795 | QAction *landscapeInvAction = new QAction(tr("Landscape (inverted)"), this); |
796 | landscapeInvAction->setCheckable(true); |
797 | #endif |
798 | |
799 | QAction *aboutAction = new QAction(tr("&About Qt..."), this); |
800 | aboutAction->setMenuRole(QAction::AboutQtRole); |
801 | connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); |
802 | |
803 | #if !defined(Q_OS_SYMBIAN) |
804 | QAction *closeAction = new QAction(tr("&Close"), this); |
805 | closeAction->setShortcuts(QKeySequence::Close); |
806 | connect(closeAction, SIGNAL(triggered()), this, SLOT(close())); |
807 | #endif |
808 | |
809 | QAction *quitAction = new QAction(tr("&Quit"), this); |
810 | quitAction->setMenuRole(QAction::QuitRole); |
811 | quitAction->setShortcuts(QKeySequence::Quit); |
812 | connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); |
813 | |
814 | QMenuBar *menu = menuBar(); |
815 | if (!menu) |
816 | return; |
817 | |
818 | #if defined(Q_WS_MAEMO_5) |
819 | menu->addAction(openAction); |
820 | menu->addAction(openUrlAction); |
821 | menu->addAction(reloadAction); |
822 | |
823 | menu->addAction(snapshotAction); |
824 | menu->addAction(recordAction); |
825 | |
826 | menu->addAction(recordOptions); |
827 | menu->addAction(proxyAction); |
828 | |
829 | menu->addAction(slowAction); |
830 | menu->addAction(showWarningsWindow); |
831 | |
832 | orientation->addAction(landscapeAction); |
833 | orientation->addAction(portraitAction); |
834 | menu->addAction(new Maemo5PickerAction(tr("Set orientation"), orientation, this)); |
835 | menu->addAction(fullscreenAction); |
836 | return; |
837 | #endif // Q_WS_MAEMO_5 |
838 | |
839 | QMenu *fileMenu = menu->addMenu(tr("&File")); |
840 | fileMenu->addAction(openAction); |
841 | fileMenu->addAction(openUrlAction); |
842 | fileMenu->addAction(reloadAction); |
843 | #if !defined(Q_OS_SYMBIAN) |
844 | fileMenu->addSeparator(); |
845 | fileMenu->addAction(closeAction); |
846 | fileMenu->addAction(quitAction); |
847 | |
848 | QMenu *recordMenu = menu->addMenu(tr("&Recording")); |
849 | recordMenu->addAction(snapshotAction); |
850 | recordMenu->addAction(recordAction); |
851 | #endif // ! Q_OS_SYMBIAN |
852 | |
853 | QMenu *debugMenu = menu->addMenu(tr("&Debugging")); |
854 | debugMenu->addAction(slowAction); |
855 | debugMenu->addAction(showWarningsWindow); |
856 | |
857 | QMenu *settingsMenu = menu->addMenu(tr("&Settings")); |
858 | settingsMenu->addAction(proxyAction); |
859 | #if defined(Q_OS_SYMBIAN) |
860 | settingsMenu->addAction(fullscreenAction); |
861 | #else |
862 | settingsMenu->addAction(recordOptions); |
863 | settingsMenu->addMenu(loggerWindow->preferencesMenu()); |
864 | #endif // !Q_OS_SYMBIAN |
865 | settingsMenu->addAction(rotateAction); |
866 | |
867 | QMenu *propertiesMenu = settingsMenu->addMenu(tr("Properties")); |
868 | |
869 | #if defined(Q_OS_SYMBIAN) |
870 | orientation->addAction(autoOrientationAction); |
871 | #endif |
872 | orientation->addAction(portraitAction); |
873 | orientation->addAction(landscapeAction); |
874 | #if !defined(Q_OS_SYMBIAN) |
875 | orientation->addAction(portraitInvAction); |
876 | orientation->addAction(landscapeInvAction); |
877 | #endif |
878 | propertiesMenu->addActions(orientation->actions()); |
879 | |
880 | QMenu *helpMenu = menu->addMenu(tr("&Help")); |
881 | helpMenu->addAction(aboutAction); |
882 | } |
883 | |
884 | void QDeclarativeViewer::showProxySettings() |
885 | { |
886 | ProxySettings settingsDlg (this); |
887 | |
888 | connect (&settingsDlg, SIGNAL (accepted()), this, SLOT (proxySettingsChanged ())); |
889 | |
890 | settingsDlg.exec(); |
891 | } |
892 | |
893 | void QDeclarativeViewer::proxySettingsChanged() |
894 | { |
895 | namFactory->proxyChanged(); |
896 | reload (); |
897 | } |
898 | |
899 | void QDeclarativeViewer::rotateOrientation() |
900 | { |
901 | #if defined(Q_WS_S60) |
902 | CAknAppUi *appUi = static_cast<CAknAppUi *>(CEikonEnv::Static()->AppUi()); |
903 | if (appUi) { |
904 | CAknAppUi::TAppUiOrientation oldOrientation = appUi->Orientation(); |
905 | QString newOrientation; |
906 | if (oldOrientation == CAknAppUi::EAppUiOrientationPortrait) { |
907 | newOrientation = QLatin1String("Landscape"); |
908 | } else { |
909 | newOrientation = QLatin1String("Portrait"); |
910 | } |
911 | foreach (QAction *action, orientation->actions()) { |
912 | if (action->text() == newOrientation) { |
913 | changeOrientation(action); |
914 | } |
915 | } |
916 | } |
917 | #else |
918 | QAction *current = orientation->checkedAction(); |
919 | QList<QAction *> actions = orientation->actions(); |
920 | int index = actions.indexOf(current); |
921 | if (index < 0) |
922 | return; |
923 | |
924 | QAction *newOrientation = actions[(index + 1) % actions.count()]; |
925 | changeOrientation(newOrientation); |
926 | #endif |
927 | } |
928 | |
929 | void QDeclarativeViewer::toggleFullScreen() |
930 | { |
931 | if (isFullScreen()) |
932 | showMaximized(); |
933 | else |
934 | showFullScreen(); |
935 | } |
936 | |
937 | void QDeclarativeViewer::showWarnings(bool show) |
938 | { |
939 | #if defined(Q_OS_SYMBIAN) |
940 | loggerWindow->showMaximized(); |
941 | #else |
942 | loggerWindow->setVisible(show); |
943 | #endif |
944 | } |
945 | |
946 | void QDeclarativeViewer::warningsWidgetOpened() |
947 | { |
948 | showWarningsWindow->setChecked(true); |
949 | } |
950 | |
951 | void QDeclarativeViewer::warningsWidgetClosed() |
952 | { |
953 | showWarningsWindow->setChecked(false); |
954 | } |
955 | |
956 | void QDeclarativeViewer::takeSnapShot() |
957 | { |
958 | static int snapshotcount = 1; |
959 | QString snapFileName = QString(QLatin1String("snapshot%1.png")).arg(snapshotcount); |
960 | QPixmap::grabWidget(canvas).save(snapFileName); |
961 | qDebug() << "Wrote"<< snapFileName; |
962 | ++snapshotcount; |
963 | } |
964 | |
965 | void QDeclarativeViewer::pickRecordingFile() |
966 | { |
967 | QString fileName = getVideoFileName(); |
968 | if (!fileName.isEmpty()) |
969 | recdlg->file->setText(fileName); |
970 | } |
971 | |
972 | void QDeclarativeViewer::chooseRecordingOptions() |
973 | { |
974 | // File |
975 | recdlg->file->setText(record_file); |
976 | |
977 | // Size |
978 | recdlg->setOriginalSize(canvas->size()); |
979 | |
980 | // Rate |
981 | recdlg->setVideoRate(record_rate); |
982 | |
983 | |
984 | // Profile |
985 | recdlg->setArguments(record_args.join(QLatin1String(" "))); |
986 | if (recdlg->exec()) { |
987 | // File |
988 | record_file = recdlg->file->text(); |
989 | // Size |
990 | record_outsize = recdlg->videoSize(); |
991 | // Rate |
992 | record_rate = recdlg->videoRate(); |
993 | // Profile |
994 | record_args = recdlg->arguments().split(QLatin1Char( |
995 | } |
996 | } |
997 | |
998 | void QDeclarativeViewer::toggleRecordingWithSelection() |
999 | { |
1000 | if (!recordTimer.isActive()) { |
1001 | if (record_file.isEmpty()) { |
1002 | QString fileName = getVideoFileName(); |
1003 | if (fileName.isEmpty()) |
1004 | return; |
1005 | if (!fileName.contains(QRegExp(QLatin1String(".[^\\/]*$")))) |
1006 | fileName += QLatin1String(".avi"); |
1007 | setRecordFile(fileName); |
1008 | } |
1009 | } |
1010 | toggleRecording(); |
1011 | } |
1012 | |
1013 | void QDeclarativeViewer::toggleRecording() |
1014 | { |
1015 | if (record_file.isEmpty()) { |
1016 | toggleRecordingWithSelection(); |
1017 | return; |
1018 | } |
1019 | bool recording = !recordTimer.isActive(); |
1020 | recordAction->setText(recording ? tr("&Stop Recording Video\tF9") : tr( "&Start Recording Video\tF9")); |
1021 | setRecording(recording); |
1022 | } |
1023 | |
1024 | void QDeclarativeViewer::setSlowMode(bool enable) |
1025 | { |
1026 | QUnifiedTimer::instance()->setSlowModeEnabled(enable); |
1027 | } |
1028 | |
1029 | void QDeclarativeViewer::addLibraryPath(const QString& lib) |
1030 | { |
1031 | canvas->engine()->addImportPath(lib); |
1032 | } |
1033 | |
1034 | void QDeclarativeViewer::addPluginPath(const QString& plugin) |
1035 | { |
1036 | canvas->engine()->addPluginPath(plugin); |
1037 | } |
1038 | |
1039 | void QDeclarativeViewer::reload() |
1040 | { |
1041 | launch(currentFileOrUrl); |
1042 | } |
1043 | |
1044 | void QDeclarativeViewer::openFile() |
1045 | { |
1046 | QString cur = canvas->source().toLocalFile(); |
1047 | if (useQmlFileBrowser) { |
1048 | open(QLatin1String("qrc:/browser/Browser.qml")); |
1049 | } else { |
1050 | QString fileName = QFileDialog::getOpenFileName(this, tr("Open QML file"), cur, tr( "QML Files (*.qml)")); |
1051 | if (!fileName.isEmpty()) { |
1052 | QFileInfo fi(fileName); |
1053 | open(fi.absoluteFilePath()); |
1054 | } |
1055 | } |
1056 | } |
1057 | |
1058 | void QDeclarativeViewer::openUrl() |
1059 | { |
1060 | QString cur = canvas->source().toLocalFile(); |
1061 | QString url= QInputDialog::getText(this, tr("Open QML file"), tr( "URL of main QML file:"), QLineEdit::Normal, cur); |
1062 | if (!url.isEmpty()) |
1063 | open(url); |
1064 | } |
1065 | |
1066 | void QDeclarativeViewer::statusChanged() |
1067 | { |
1068 | if (canvas->status() == QDeclarativeView::Error && tester) |
1069 | tester->executefailure(); |
1070 | |
1071 | if (canvas->status() == QDeclarativeView::Ready) { |
1072 | initialSize = canvas->initialSize(); |
1073 | updateSizeHints(true); |
1074 | } |
1075 | } |
1076 | |
1077 | void QDeclarativeViewer::launch(const QString& file_or_url) |
1078 | { |
1079 | QMetaObject::invokeMethod(this, "open", Qt::QueuedConnection, Q_ARG(QString, file_or_url)); |
1080 | } |
1081 | |
1082 | void QDeclarativeViewer::loadTranslationFile(const QString& directory) |
1083 | { |
1084 | if (!translator) { |
1085 | translator = new QTranslator(this); |
1086 | QApplication::installTranslator(translator); |
1087 | } |
1088 | |
1089 | translator->load(QLatin1String("qml_")+QLocale::system().name(), directory + QLatin1String( "/i18n")); |
1090 | } |
1091 | |
1092 | void QDeclarativeViewer::loadDummyDataFiles(const QString& directory) |
1093 | { |
1094 | QDir dir(directory + QLatin1String("/dummydata"), QLatin1String( "*.qml")); |
1095 | QStringList list = dir.entryList(); |
1096 | for (int i = 0; i < list.size(); ++i) { |
1097 | QString qml = list.at(i); |
1098 | QDeclarativeComponent comp(canvas->engine(), dir.filePath(qml)); |
1099 | QObject *dummyData = comp.create(); |
1100 | |
1101 | if(comp.isError()) { |
1102 | QList<QDeclarativeError> errors = comp.errors(); |
1103 | foreach (const QDeclarativeError &error, errors) { |
1104 | qWarning() << error; |
1105 | } |
1106 | if (tester) tester->executefailure(); |
1107 | } |
1108 | |
1109 | if (dummyData) { |
1110 | qWarning() << "Loaded dummy data:"<< dir.filePath(qml); |
1111 | qml.truncate(qml.length()-4); |
1112 | canvas->rootContext()->setContextProperty(qml, dummyData); |
1113 | dummyData->setParent(this); |
1114 | } |
1115 | } |
1116 | } |
1117 | |
1118 | bool QDeclarativeViewer::open(const QString& file_or_url) |
1119 | { |
1120 | currentFileOrUrl = file_or_url; |
1121 | |
1122 | QUrl url; |
1123 | QFileInfo fi(file_or_url); |
1124 | if (fi.exists()) |
1125 | url = QUrl::fromLocalFile(fi.absoluteFilePath()); |
1126 | else |
1127 | url = QUrl(file_or_url); |
1128 | setWindowTitle(tr("%1 - Qt QML Viewer").arg(file_or_url)); |
1129 | |
1130 | if (!m_script.isEmpty()) |
1131 | tester = new QDeclarativeTester(m_script, m_scriptOptions, canvas); |
1132 | |
1133 | delete canvas->rootObject(); |
1134 | canvas->engine()->clearComponentCache(); |
1135 | QDeclarativeContext *ctxt = canvas->rootContext(); |
1136 | ctxt->setContextProperty(QLatin1String("qmlViewer"), this); |
1137 | #ifdef Q_OS_SYMBIAN |
1138 | ctxt->setContextProperty(QLatin1String("qmlViewerFolder"), QLatin1String( "E:\\")); // Documents on your S60 phone |
1139 | #else |
1140 | ctxt->setContextProperty(QLatin1String("qmlViewerFolder"), QDir::currentPath()); |
1141 | #endif |
1142 | |
1143 | ctxt->setContextProperty(QLatin1String("runtime"), Runtime::instance()); |
1144 | |
1145 | QString fileName = url.toLocalFile(); |
1146 | if (!fileName.isEmpty()) { |
1147 | fi.setFile(fileName); |
1148 | if (fi.exists()) { |
1149 | if (fi.suffix().toLower() != QLatin1String("qml")) { |
1150 | qWarning() << "qml cannot open non-QML file"<< fileName; |
1151 | return false; |
1152 | } |
1153 | |
1154 | QFileInfo fi(fileName); |
1155 | loadTranslationFile(fi.path()); |
1156 | loadDummyDataFiles(fi.path()); |
1157 | } else { |
1158 | qWarning() << "qml cannot find file:"<< fileName; |
1159 | return false; |
1160 | } |
1161 | } |
1162 | |
1163 | QTime t; |
1164 | t.start(); |
1165 | |
1166 | canvas->setSource(url); |
1167 | |
1168 | return true; |
1169 | } |
1170 | |
1171 | void QDeclarativeViewer::setAutoRecord(int from, int to) |
1172 | { |
1173 | if (from==0) from=1; // ensure resized |
1174 | record_autotime = to-from; |
1175 | autoStartTimer.setInterval(from); |
1176 | autoStartTimer.start(); |
1177 | } |
1178 | |
1179 | void QDeclarativeViewer::setRecordArgs(const QStringList& a) |
1180 | { |
1181 | record_args = a; |
1182 | } |
1183 | |
1184 | void QDeclarativeViewer::setRecordFile(const QString& f) |
1185 | { |
1186 | record_file = f; |
1187 | } |
1188 | |
1189 | void QDeclarativeViewer::setRecordRate(int fps) |
1190 | { |
1191 | record_rate = fps; |
1192 | } |
1193 | |
1194 | void QDeclarativeViewer::sceneResized(QSize) |
1195 | { |
1196 | updateSizeHints(); |
1197 | } |
1198 | |
1199 | void QDeclarativeViewer::keyPressEvent(QKeyEvent *event) |
1200 | { |
1201 | if (event->key() == Qt::Key_0 && devicemode) |
1202 | exit(0); |
1203 | else if (event->key() == Qt::Key_F1 || (event->key() == Qt::Key_1 && devicemode)) { |
1204 | qDebug() << "F1 - help\n" |
1205 | << "F2 - save test script\n" |
1206 | << "F3 - take PNG snapshot\n" |
1207 | << "F4 - show items and state\n" |
1208 | << "F5 - reload QML\n" |
1209 | << "F6 - show object tree\n" |
1210 | << "F7 - show timing\n" |
1211 | << "F9 - toggle video recording\n" |
1212 | << "F10 - toggle orientation\n" |
1213 | << "device keys: 0=quit, 1..8=F1..F8" |
1214 | ; |
1215 | } else if (event->key() == Qt::Key_F2 || (event->key() == Qt::Key_2 && devicemode)) { |
1216 | if (tester && m_scriptOptions & Record) |
1217 | tester->save(); |
1218 | } else if (event->key() == Qt::Key_F3 || (event->key() == Qt::Key_3 && devicemode)) { |
1219 | takeSnapShot(); |
1220 | } else if (event->key() == Qt::Key_F5 || (event->key() == Qt::Key_5 && devicemode)) { |
1221 | reload(); |
1222 | } else if (event->key() == Qt::Key_F9 || (event->key() == Qt::Key_9 && devicemode)) { |
1223 | toggleRecording(); |
1224 | } else if (event->key() == Qt::Key_F10) { |
1225 | rotateOrientation(); |
1226 | } |
1227 | |
1228 | QWidget::keyPressEvent(event); |
1229 | } |
1230 | |
1231 | bool QDeclarativeViewer::event(QEvent *event) |
1232 | { |
1233 | if (event->type() == QEvent::WindowActivate) { |
1234 | Runtime::instance()->setActiveWindow(true); |
1235 | DeviceOrientation::instance()->resumeListening(); |
1236 | } else if (event->type() == QEvent::WindowDeactivate) { |
1237 | Runtime::instance()->setActiveWindow(false); |
1238 | DeviceOrientation::instance()->pauseListening(); |
1239 | } |
1240 | return QWidget::event(event); |
1241 | } |
1242 | |
1243 | // Detect ffmpeg, return its help string. |
1244 | static inline QString detectFfmpeg() |
1245 | { |
1246 | static QString ffmpegHelp; |
1247 | if (ffmpegHelp.isNull()) { |
1248 | QProcess proc; |
1249 | proc.start(QLatin1String("ffmpeg"), QStringList(QLatin1String( "-h"))); |
1250 | if (proc.waitForStarted() && proc.waitForFinished(2000)) { |
1251 | ffmpegHelp = QString::fromLocal8Bit(proc.readAllStandardOutput()); |
1252 | } else { |
1253 | ffmpegHelp = QLatin1String(""); |
1254 | } |
1255 | } |
1256 | return ffmpegHelp; |
1257 | } |
1258 | |
1259 | void QDeclarativeViewer::senseFfmpeg() |
1260 | { |
1261 | const QString ffmpegHelp = detectFfmpeg(); |
1262 | ffmpegAvailable = ffmpegHelp.contains(QLatin1String("-s ")); |
1263 | const QString text = tr("Video recording uses ffmpeg:") + QLatin1String( "\n\n") + ffmpegHelp; |
1264 | |
1265 | QDialog *d = new QDialog(recdlg); |
1266 | QVBoxLayout *l = new QVBoxLayout(d); |
1267 | QTextBrowser *b = new QTextBrowser(d); |
1268 | QFont f = b->font(); |
1269 | f.setFamily(QLatin1String("courier")); |
1270 | b->setFont(f); |
1271 | b->setText(text); |
1272 | l->addWidget(b); |
1273 | d->setLayout(l); |
1274 | ffmpegHelpWindow = d; |
1275 | connect(recdlg->ffmpegHelp,SIGNAL(clicked()), ffmpegHelpWindow, SLOT(show())); |
1276 | } |
1277 | |
1278 | void QDeclarativeViewer::setRecording(bool on) |
1279 | { |
1280 | if (on == recordTimer.isActive()) |
1281 | return; |
1282 | |
1283 | int period = int(1000/record_rate+0.5); |
1284 | QUnifiedTimer::instance()->setTimingInterval(on ? period:16); |
1285 | QUnifiedTimer::instance()->setConsistentTiming(on); |
1286 | if (on) { |
1287 | canvas->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); |
1288 | recordTimer.setInterval(period); |
1289 | recordTimer.start(); |
1290 | frame_fmt = record_file.right(4).toLower(); |
1291 | frame = QImage(canvas->width(),canvas->height(),QImage::Format_RGB32); |
1292 | if (frame_fmt != QLatin1String(".png") && (!convertAvailable || frame_fmt != QLatin1String( ".gif"))) { |
1293 | // Stream video to ffmpeg |
1294 | |
1295 | QProcess *proc = new QProcess(this); |
1296 | connect(proc, SIGNAL(finished(int)), this, SLOT(ffmpegFinished(int))); |
1297 | frame_stream = proc; |
1298 | |
1299 | QStringList args; |
1300 | args << QLatin1String("-y"); |
1301 | args << QLatin1String("-r") << QString::number(record_rate); |
1302 | args << QLatin1String("-f") << QLatin1String( "rawvideo"); |
1303 | args << QLatin1String("-pix_fmt") << (frame_fmt == QLatin1String( ".gif") ? QLatin1String( "rgb24") : QLatin1String( "rgb32")); |
1304 | args << QLatin1String("-s") << QString::fromAscii( "%1x%2").arg(canvas->width()).arg(canvas->height()); |
1305 | args << QLatin1String("-i") << QLatin1String( "-"); |
1306 | if (record_outsize.isValid()) { |
1307 | args << QLatin1String("-s") << QString::fromAscii( "%1x%2").arg(record_outsize.width()).arg(record_outsize.height()); |
1308 | args << QLatin1String("-aspect") << QString::number(double(canvas->width())/canvas->height()); |
1309 | } |
1310 | args += record_args; |
1311 | args << record_file; |
1312 | proc->start(QLatin1String("ffmpeg"), args); |
1313 | |
1314 | } else { |
1315 | // Store frames, save to GIF/PNG |
1316 | frame_stream = 0; |
1317 | } |
1318 | } else { |
1319 | canvas->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate); |
1320 | recordTimer.stop(); |
1321 | if (frame_stream) { |
1322 | qDebug() << "Saving video..."; |
1323 | frame_stream->close(); |
1324 | qDebug() << "Wrote"<< record_file; |
1325 | } else { |
1326 | QProgressDialog progress(tr("Saving frames..."), tr( "Cancel"), 0, frames.count()+10, this); |
1327 | progress.setWindowModality(Qt::WindowModal); |
1328 | |
1329 | int frame=0; |
1330 | QStringList inputs; |
1331 | qDebug() << "Saving frames..."; |
1332 | |
1333 | QString framename; |
1334 | bool png_output = false; |
1335 | if (record_file.right(4).toLower() == QLatin1String(".png")) { |
1336 | if (record_file.contains(QLatin1Char( |
1337 | framename = record_file; |
1338 | else |
1339 | framename = record_file.left(record_file.length()-4) + QLatin1String("%04d") + record_file.right(4); |
1340 | png_output = true; |
1341 | } else { |
1342 | framename = QLatin1String("tmp-frame%04d.png"); |
1343 | png_output = false; |
1344 | } |
1345 | foreach (QImage* img, frames) { |
1346 | progress.setValue(progress.value()+1); |
1347 | if (progress.wasCanceled()) |
1348 | break; |
1349 | QString name; |
1350 | name.sprintf(framename.toLocal8Bit(),frame++); |
1351 | if (record_outsize.isValid()) |
1352 | *img = img->scaled(record_outsize,Qt::IgnoreAspectRatio,Qt::SmoothTransformation); |
1353 | if (record_dither==QLatin1String("ordered")) |
1354 | img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither|Qt::OrderedDither).save(name); |
1355 | else if (record_dither==QLatin1String("threshold")) |
1356 | img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither|Qt::ThresholdDither).save(name); |
1357 | else if (record_dither==QLatin1String("floyd")) |
1358 | img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither).save(name); |
1359 | else |
1360 | img->save(name); |
1361 | inputs << name; |
1362 | delete img; |
1363 | } |
1364 | |
1365 | if (!progress.wasCanceled()) { |
1366 | if (png_output) { |
1367 | framename.replace(QRegExp(QLatin1String("%\\d*.")), QLatin1String( "*")); |
1368 | qDebug() << "Wrote frames"<< framename; |
1369 | inputs.clear(); // don't remove them |
1370 | } else { |
1371 | // ImageMagick and gifsicle for GIF encoding |
1372 | progress.setLabelText(tr("Converting frames to GIF file...")); |
1373 | QStringList args; |
1374 | args << QLatin1String("-delay") << QString::number(period/10); |
1375 | args << inputs; |
1376 | args << record_file; |
1377 | qDebug() << "Converting..."<< record_file << "(this may take a while)"; |
1378 | if (0!=QProcess::execute(QLatin1String("convert"), args)) { |
1379 | qWarning() << "Cannot run ImageMagick 'convert' - recorded frames not converted"; |
1380 | inputs.clear(); // don't remove them |
1381 | qDebug() << "Wrote frames tmp-frame*.png"; |
1382 | } else { |
1383 | if (record_file.right(4).toLower() == QLatin1String(".gif")) { |
1384 | qDebug() << "Compressing..."<< record_file; |
1385 | if (0!=QProcess::execute(QLatin1String("gifsicle"), QStringList() << QLatin1String( "-O2") |
1386 | << QLatin1String("-o") << record_file << record_file)) |
1387 | qWarning() << "Cannot run 'gifsicle' - not compressed"; |
1388 | } |
1389 | qDebug() << "Wrote"<< record_file; |
1390 | } |
1391 | } |
1392 | } |
1393 | |
1394 | progress.setValue(progress.maximum()-1); |
1395 | foreach (QString name, inputs) |
1396 | QFile::remove(name); |
1397 | |
1398 | frames.clear(); |
1399 | } |
1400 | } |
1401 | qDebug() << "Recording: "<< (recordTimer.isActive()? "ON": "OFF"); |
1402 | } |
1403 | |
1404 | void QDeclarativeViewer::ffmpegFinished(int code) |
1405 | { |
1406 | qDebug() << "ffmpeg returned"<< code << frame_stream->readAllStandardError(); |
1407 | } |
1408 | |
1409 | void QDeclarativeViewer::appAboutToQuit() |
1410 | { |
1411 | // avoid QGLContext errors about invalid contexts on exit |
1412 | canvas->setViewport(0); |
1413 | |
1414 | // avoid crashes if messages are received after app has closed |
1415 | delete loggerWindow; |
1416 | loggerWindow = 0; |
1417 | delete tester; |
1418 | tester = 0; |
1419 | close(); |
1420 | } |
1421 | |
1422 | void QDeclarativeViewer::autoStartRecording() |
1423 | { |
1424 | setRecording(true); |
1425 | autoStopTimer.setInterval(record_autotime); |
1426 | autoStopTimer.start(); |
1427 | } |
1428 | |
1429 | void QDeclarativeViewer::autoStopRecording() |
1430 | { |
1431 | setRecording(false); |
1432 | } |
1433 | |
1434 | void QDeclarativeViewer::recordFrame() |
1435 | { |
1436 | canvas->QWidget::render(&frame); |
1437 | if (frame_stream) { |
1438 | if (frame_fmt == QLatin1String(".gif")) { |
1439 | // ffmpeg can't do 32bpp with gif |
1440 | QImage rgb24 = frame.convertToFormat(QImage::Format_RGB888); |
1441 | frame_stream->write((char*)rgb24.bits(),rgb24.numBytes()); |
1442 | } else { |
1443 | frame_stream->write((char*)frame.bits(),frame.numBytes()); |
1444 | } |
1445 | } else { |
1446 | frames.append(new QImage(frame)); |
1447 | } |
1448 | } |
1449 | |
1450 | void QDeclarativeViewer::changeOrientation(QAction *action) |
1451 | { |
1452 | if (!action) |
1453 | return; |
1454 | QString o = action->text(); |
1455 | action->setChecked(true); |
1456 | #if defined(Q_WS_S60) |
1457 | CAknAppUi *appUi = static_cast<CAknAppUi *>(CEikonEnv::Static()->AppUi()); |
1458 | if (appUi) { |
1459 | CAknAppUi::TAppUiOrientation orientation = appUi->Orientation(); |
1460 | if (o == QLatin1String("Auto-orientation")) { |
1461 | appUi->SetOrientationL(CAknAppUi::EAppUiOrientationAutomatic); |
1462 | rotateAction->setVisible(false); |
1463 | } else if (o == QLatin1String("Portrait")) { |
1464 | appUi->SetOrientationL(CAknAppUi::EAppUiOrientationPortrait); |
1465 | rotateAction->setVisible(true); |
1466 | } else if (o == QLatin1String("Landscape")) { |
1467 | appUi->SetOrientationL(CAknAppUi::EAppUiOrientationLandscape); |
1468 | rotateAction->setVisible(true); |
1469 | } |
1470 | } |
1471 | #else |
1472 | if (o == QLatin1String("Portrait")) |
1473 | DeviceOrientation::instance()->setOrientation(DeviceOrientation::Portrait); |
1474 | else if (o == QLatin1String("Landscape")) |
1475 | DeviceOrientation::instance()->setOrientation(DeviceOrientation::Landscape); |
1476 | else if (o == QLatin1String("Portrait (inverted)")) |
1477 | DeviceOrientation::instance()->setOrientation(DeviceOrientation::PortraitInverted); |
1478 | else if (o == QLatin1String("Landscape (inverted)")) |
1479 | DeviceOrientation::instance()->setOrientation(DeviceOrientation::LandscapeInverted); |
1480 | #endif |
1481 | } |
1482 | |
1483 | void QDeclarativeViewer::orientationChanged() |
1484 | { |
1485 | updateSizeHints(); |
1486 | } |
1487 | |
1488 | void QDeclarativeViewer::setDeviceKeys(bool on) |
1489 | { |
1490 | devicemode = on; |
1491 | } |
1492 | |
1493 | void QDeclarativeViewer::setNetworkCacheSize(int size) |
1494 | { |
1495 | namFactory->setCacheSize(size); |
1496 | } |
1497 | |
1498 | void QDeclarativeViewer::setUseGL(bool useGL) |
1499 | { |
1500 | #ifdef GL_SUPPORTED |
1501 | if (useGL) { |
1502 | QGLFormat format = QGLFormat::defaultFormat(); |
1503 | #ifdef Q_WS_MAC |
1504 | format.setSampleBuffers(true); |
1505 | #else |
1506 | format.setSampleBuffers(false); |
1507 | #endif |
1508 | |
1509 | QGLWidget *glWidget = new QGLWidget(format); |
1510 | //### potentially faster, but causes junk to appear if top-level is Item, not Rectangle |
1511 | //glWidget->setAutoFillBackground(false); |
1512 | |
1513 | canvas->setViewport(glWidget); |
1514 | } |
1515 | #else |
1516 | Q_UNUSED(useGL) |
1517 | #endif |
1518 | } |
1519 | |
1520 | void QDeclarativeViewer::setUseNativeFileBrowser(bool use) |
1521 | { |
1522 | useQmlFileBrowser = !use; |
1523 | } |
1524 | |
1525 | void QDeclarativeViewer::setSizeToView(bool sizeToView) |
1526 | { |
1527 | QDeclarativeView::ResizeMode resizeMode = sizeToView ? QDeclarativeView::SizeRootObjectToView : QDeclarativeView::SizeViewToRootObject; |
1528 | if (resizeMode != canvas->resizeMode()) { |
1529 | canvas->setResizeMode(resizeMode); |
1530 | updateSizeHints(); |
1531 | } |
1532 | } |
1533 | |
1534 | void QDeclarativeViewer::updateSizeHints(bool initial) |
1535 | { |
1536 | static bool isRecursive = false; |
1537 | |
1538 | if (isRecursive) |
1539 | return; |
1540 | isRecursive = true; |
1541 | |
1542 | if (initial || (canvas->resizeMode() == QDeclarativeView::SizeViewToRootObject)) { |
1543 | QSize newWindowSize = initial ? initialSize : canvas->sizeHint(); |
1544 | //qWarning() << "USH:" << (initial ? "INIT:" : "V2R:") << "setting fixed size " << newWindowSize; |
1545 | if (!isFullScreen() && !isMaximized()) { |
1546 | canvas->setFixedSize(newWindowSize); |
1547 | resize(1, 1); |
1548 | layout()->setSizeConstraint(QLayout::SetFixedSize); |
1549 | layout()->activate(); |
1550 | } |
1551 | } |
1552 | //qWarning() << "USH: R2V: setting free size "; |
1553 | layout()->setSizeConstraint(QLayout::SetNoConstraint); |
1554 | layout()->activate(); |
1555 | setMinimumSize(minimumSizeHint()); |
1556 | setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); |
1557 | canvas->setMinimumSize(QSize(0,0)); |
1558 | canvas->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); |
1559 | |
1560 | isRecursive = false; |
1561 | } |
1562 | |
1563 | void QDeclarativeViewer::registerTypes() |
1564 | { |
1565 | static bool registered = false; |
1566 | |
1567 | if (!registered) { |
1568 | // registering only for exposing the DeviceOrientation::Orientation enum |
1569 | qmlRegisterUncreatableType<DeviceOrientation>("Qt", 4, 7, "Orientation", QString()); |
1570 | qmlRegisterUncreatableType<DeviceOrientation>("QtQuick", 1, 0, "Orientation", QString()); |
1571 | registered = true; |
1572 | } |
1573 | } |
1574 | |
1575 | QT_END_NAMESPACE |
1576 | |
1577 | #include "qmlruntime.moc" |
1578 |
Warning: That file was not part of the compilation database. It may have many parsing errors.