|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the tools applications of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qvfb.h" |
|
43 #include "qvfbview.h" |
|
44 #include "qvfbhdr.h" |
|
45 #ifdef Q_WS_X11 |
|
46 #include "qvfbx11view.h" |
|
47 #endif |
|
48 #include "qvfbratedlg.h" |
|
49 #include "ui_config.h" |
|
50 #include "qanimationwriter.h" |
|
51 |
|
52 #include <deviceskin.h> |
|
53 |
|
54 #include <QMenuBar> |
|
55 #include <QMenu> |
|
56 #include <QApplication> |
|
57 #include <QMessageBox> |
|
58 #include <QComboBox> |
|
59 #include <QLabel> |
|
60 #include <QFileDialog> |
|
61 #include <QSlider> |
|
62 #include <QSpinBox> |
|
63 #include <QLayout> |
|
64 #include <QRadioButton> |
|
65 #include <QImage> |
|
66 #include <QPixmap> |
|
67 #include <QCheckBox> |
|
68 #include <QCursor> |
|
69 #include <QTime> |
|
70 #include <QScrollArea> |
|
71 #include <QProgressBar> |
|
72 #include <QPushButton> |
|
73 #include <QTextStream> |
|
74 #include <QFile> |
|
75 #include <QFileInfo> |
|
76 #include <QDebug> |
|
77 |
|
78 #include <unistd.h> |
|
79 #include <stdlib.h> |
|
80 #include <sys/types.h> |
|
81 |
|
82 QT_BEGIN_NAMESPACE |
|
83 |
|
84 // ===================================================================== |
|
85 |
|
86 static const char *red_on_led_xpm[] = { |
|
87 "11 11 10 1", |
|
88 " c None", |
|
89 ". c #FF0000", |
|
90 "+ c #FF4C4C", |
|
91 "@ c #FF7A7A", |
|
92 "# c #D30000", |
|
93 "$ c #FF9393", |
|
94 "% c #BA0000", |
|
95 "& c #FFFFFF", |
|
96 "* c #7F0000", |
|
97 "= c #000000", |
|
98 " ", |
|
99 " .++@@ ", |
|
100 " .....+@ ", |
|
101 " ##...$.+@ ", |
|
102 " %#..$&$.+ ", |
|
103 " *#...$..+ ", |
|
104 " *%#...... ", |
|
105 " =*%#..... ", |
|
106 " =*%###. ", |
|
107 " ===*. ", |
|
108 " "}; |
|
109 |
|
110 static const char *red_off_led_xpm[] = { |
|
111 "11 11 12 1", |
|
112 " c None", |
|
113 ". c #CDB7B4", |
|
114 "+ c #D2BFBD", |
|
115 "@ c #DBCBCA", |
|
116 "# c #E5D9D8", |
|
117 "$ c #BC9E9B", |
|
118 "% c #E2D6D5", |
|
119 "& c #AD8986", |
|
120 "* c #FFFFFF", |
|
121 "= c #A8817D", |
|
122 "- c #B2908D", |
|
123 "; c #6F4D4A", |
|
124 " ", |
|
125 " .++@# ", |
|
126 " .....@# ", |
|
127 " $$...%.@# ", |
|
128 " &$..%*%.@ ", |
|
129 " =-...%..+ ", |
|
130 " =&-...... ", |
|
131 " ;==-..... ", |
|
132 " ;=&-$$. ", |
|
133 " ;==&$ ", |
|
134 " "}; |
|
135 |
|
136 static bool copyButtonConfiguration(const QString &prefix, int displayId) |
|
137 { |
|
138 const QString destDir = QT_VFB_DATADIR(displayId).append("/"); |
|
139 const QFileInfo src(prefix + QLatin1String("defaultbuttons.conf")); |
|
140 const QFileInfo dst(destDir + QLatin1String("defaultbuttons.conf")); |
|
141 unlink(dst.absoluteFilePath().toLatin1().constData()); |
|
142 if (!src.exists()) |
|
143 return false; |
|
144 const bool rc = QFile::copy(src.absoluteFilePath(), dst.absoluteFilePath()); |
|
145 if (!rc) |
|
146 qWarning() << "Failed to copy the button configuration file " << src.absoluteFilePath() << " to " << dst.absoluteFilePath() << '.'; |
|
147 return rc; |
|
148 } |
|
149 |
|
150 // ===================================================================== |
|
151 |
|
152 class AnimationSaveWidget : public QWidget { |
|
153 Q_OBJECT |
|
154 public: |
|
155 AnimationSaveWidget(QVFbAbstractView *v); |
|
156 ~AnimationSaveWidget(); |
|
157 bool detectPpmtoMpegCommand(); |
|
158 void timerEvent(QTimerEvent *te); |
|
159 void convertToMpeg(QString filename); |
|
160 void removeTemporaryFiles(); |
|
161 protected slots: |
|
162 void toggleRecord(); |
|
163 void reset(); |
|
164 void save(); |
|
165 private: |
|
166 QVFbAbstractView *view; |
|
167 QProgressBar *progressBar; |
|
168 QLabel *statusText; |
|
169 bool haveMpeg, savingAsMpeg, recording; |
|
170 QCheckBox *mpegSave; |
|
171 QAnimationWriter *animation; |
|
172 QPushButton *recBt, *resetBt, *saveBt; |
|
173 QLabel *timeDpy, *recLED; |
|
174 int timerId, progressTimerId; |
|
175 QPixmap recOn, recOff; |
|
176 QTime tm; |
|
177 int elapsed, imageNum; |
|
178 }; |
|
179 |
|
180 // ===================================================================== |
|
181 |
|
182 Zoomer::Zoomer(QVFb* target) : |
|
183 qvfb(target) |
|
184 { |
|
185 QVBoxLayout *layout = new QVBoxLayout(this); |
|
186 QSlider *sl = new QSlider(Qt::Horizontal); |
|
187 sl->setMinimum(10); |
|
188 sl->setMaximum(64); |
|
189 sl->setPageStep(1); |
|
190 sl->setValue(32); |
|
191 layout->addWidget(sl); |
|
192 connect(sl,SIGNAL(valueChanged(int)),this,SLOT(zoom(int))); |
|
193 label = new QLabel(); |
|
194 layout->addWidget(label); |
|
195 } |
|
196 |
|
197 void Zoomer::zoom(int z) |
|
198 { |
|
199 double d = (double)z/32.0; |
|
200 qvfb->setZoom(d); |
|
201 label->setText(QString::number(d,'g',2)); |
|
202 } |
|
203 |
|
204 // ===================================================================== |
|
205 |
|
206 QVFb::QVFb( int display_id, int w, int h, int d, int r, const QString &skin, DisplayType displayType, QWidget *parent, Qt::WindowFlags flags ) |
|
207 : QMainWindow( parent, flags ) |
|
208 { |
|
209 this->displayType = displayType; |
|
210 view = 0; |
|
211 secondaryView = 0; |
|
212 scroller = 0; |
|
213 this->skin = 0; |
|
214 currentSkinIndex = -1; |
|
215 findSkins(skin); |
|
216 zoomer = 0; |
|
217 QPixmap pix(":/res/images/logo.png"); |
|
218 setWindowIcon( pix ); |
|
219 rateDlg = 0; |
|
220 refreshRate = 30; |
|
221 // Create the menu first to avoid scroll bars in the main window |
|
222 createMenu( menuBar() ); |
|
223 init( display_id, w, h, d, r, skin ); |
|
224 enableCursor( true ); |
|
225 } |
|
226 |
|
227 QVFb::~QVFb() |
|
228 { |
|
229 } |
|
230 |
|
231 void QVFb::popupMenu() |
|
232 { |
|
233 QMenu *pm = new QMenu( this ); |
|
234 createMenu( pm ); |
|
235 pm->exec(QCursor::pos()); |
|
236 } |
|
237 |
|
238 void QVFb::init( int display_id, int pw, int ph, int d, int r, const QString& skin_name ) |
|
239 { |
|
240 delete view; |
|
241 view = 0; |
|
242 delete secondaryView; |
|
243 secondaryView = 0; |
|
244 delete scroller; |
|
245 scroller = 0; |
|
246 delete skin; |
|
247 skin = 0; |
|
248 |
|
249 skinscaleH = skinscaleV = 1.0; |
|
250 QVFbView::Rotation rot = ((r == 90) ? QVFbView::Rot90 : |
|
251 ((r == 180) ? QVFbView::Rot180 : |
|
252 ((r == 270) ? QVFbView::Rot270 : |
|
253 QVFbView::Rot0 ))); |
|
254 if ( !skin_name.isEmpty() ) { |
|
255 const bool vis = isVisible(); |
|
256 DeviceSkinParameters parameters; |
|
257 QString readError; |
|
258 if (parameters.read(skin_name,DeviceSkinParameters::ReadAll, &readError)) { |
|
259 skin = new DeviceSkin(parameters, this); |
|
260 connect(skin, SIGNAL(popupMenu()), this, SLOT(popupMenu())); |
|
261 const int sw = parameters.screenSize().width(); |
|
262 const int sh = parameters.screenSize().height(); |
|
263 const int sd = parameters.screenDepth; |
|
264 if (!pw) pw = sw; |
|
265 if (!ph) ph = sh; |
|
266 if (d < 0) { |
|
267 if (sd) |
|
268 d = sd; |
|
269 else |
|
270 d = -d; |
|
271 } |
|
272 if (vis) |
|
273 hide(); |
|
274 menuBar()->hide(); |
|
275 scroller = 0; |
|
276 #ifdef Q_WS_X11 |
|
277 if (displayType == X11) |
|
278 view = new QVFbX11View( display_id, pw, ph, d, rot, skin ); |
|
279 else |
|
280 #endif |
|
281 view = new QVFbView( display_id, pw, ph, d, rot, skin ); |
|
282 skin->setView( view ); |
|
283 view->setContentsMargins( 0, 0, 0, 0 ); |
|
284 view->setTouchscreenEmulation(!parameters.hasMouseHover); |
|
285 connect(skin, SIGNAL(skinKeyPressEvent(int,QString,bool)), view, SLOT(skinKeyPressEvent(int,QString,bool))); |
|
286 connect(skin, SIGNAL(skinKeyReleaseEvent(int,QString,bool)), view, SLOT(skinKeyReleaseEvent(int,QString,bool))); |
|
287 |
|
288 copyButtonConfiguration(skin->prefix(), view->displayId()); |
|
289 |
|
290 setCentralWidget( skin ); |
|
291 adjustSize(); |
|
292 skinscaleH = (double)sw/pw; |
|
293 skinscaleV = (double)sh/ph; |
|
294 if ( skinscaleH != 1.0 || skinscaleH != 1.0 ) |
|
295 setZoom(skinscaleH); |
|
296 view->show(); |
|
297 |
|
298 if (parameters.hasSecondaryScreen()) { |
|
299 const QSize ssize = parameters.secondaryScreenSize(); |
|
300 // assumes same depth and rotation |
|
301 #ifdef Q_WS_X11 |
|
302 if (displayType == X11) |
|
303 secondaryView = new QVFbX11View( display_id+1, ssize.width(), ssize.height(), d, rot, skin ); |
|
304 else |
|
305 #endif |
|
306 secondaryView = new QVFbView( display_id+1, ssize.width(), ssize.height(), d, rot, skin ); |
|
307 skin->setSecondaryView(secondaryView); |
|
308 secondaryView->show(); |
|
309 } |
|
310 |
|
311 if ( vis ) show(); |
|
312 } else { |
|
313 qWarning("%s", qPrintable(readError)); |
|
314 } |
|
315 } |
|
316 |
|
317 // If we failed to get a skin or we were not supplied |
|
318 // with one then fallback to a framebuffer without |
|
319 // a skin |
|
320 if (!skin){ |
|
321 // Default values |
|
322 if (!pw) |
|
323 pw = 240; |
|
324 if (!ph) |
|
325 ph = 320; |
|
326 if (!d) |
|
327 d = 32; |
|
328 else if (d < 0) |
|
329 d = -d; |
|
330 |
|
331 if (currentSkinIndex != -1) { |
|
332 clearMask(); |
|
333 setParent( 0, 0 ); |
|
334 move( pos() ); |
|
335 show(); |
|
336 //unset fixed size: |
|
337 setMinimumSize(0,0); |
|
338 setMaximumSize(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX); |
|
339 } |
|
340 menuBar()->show(); |
|
341 scroller = new QScrollArea(this); |
|
342 scroller->setFocusPolicy(Qt::NoFocus); // don't steal key events from the embedded app |
|
343 #ifdef Q_WS_X11 |
|
344 if (displayType == X11) |
|
345 view = new QVFbX11View( display_id, pw, ph, d, rot, scroller ); |
|
346 else |
|
347 #endif |
|
348 view = new QVFbView( display_id, pw, ph, d, rot, scroller ); |
|
349 scroller->setWidget(view); |
|
350 view->setContentsMargins( 0, 0, 0, 0 ); |
|
351 setCentralWidget(scroller); |
|
352 ph += 2; // avoid scrollbar |
|
353 scroller->show(); |
|
354 // delete defaultbuttons.conf if it was left behind... |
|
355 unlink(QFileInfo(QT_VFB_DATADIR(view->displayId()).append("/defaultbuttons.conf")).absoluteFilePath().toLatin1().constData()); |
|
356 if (secondaryView) |
|
357 unlink(QFileInfo(QT_VFB_DATADIR(view->displayId() + 1).append("/defaultbuttons.conf")).absoluteFilePath().toLatin1().constData()); |
|
358 } |
|
359 view->setRate(refreshRate); |
|
360 if (secondaryView) { |
|
361 secondaryView->setRate(refreshRate); |
|
362 } |
|
363 // Resize QVFb to the new size |
|
364 QSize newSize = view->sizeHint(); |
|
365 |
|
366 // ... fudge factor |
|
367 newSize += QSize(20, 35); |
|
368 |
|
369 resize(newSize); |
|
370 |
|
371 setWindowTitle(QString("Virtual framebuffer %1x%2 %3bpp Display :%4 Rotate %5") |
|
372 .arg(view->displayWidth()).arg(view->displayHeight()) |
|
373 .arg(d).arg(display_id).arg(r)); |
|
374 } |
|
375 |
|
376 void QVFb::enableCursor( bool e ) |
|
377 { |
|
378 if ( skin && skin->hasCursor() ) { |
|
379 view->setCursor( Qt::BlankCursor ); |
|
380 if (secondaryView) |
|
381 secondaryView->setCursor( Qt::BlankCursor ); |
|
382 } else { |
|
383 view->setCursor( e ? Qt::ArrowCursor : Qt::BlankCursor ); |
|
384 if (secondaryView) |
|
385 secondaryView->setCursor( e ? Qt::ArrowCursor : Qt::BlankCursor ); |
|
386 } |
|
387 cursorAction->setChecked( e ); |
|
388 } |
|
389 |
|
390 template <typename T> |
|
391 void QVFb::createMenu(T *menu) |
|
392 { |
|
393 menu->addMenu( createFileMenu() ); |
|
394 menu->addMenu( createViewMenu() ); |
|
395 menu->addSeparator(); |
|
396 menu->addMenu( createHelpMenu() ); |
|
397 } |
|
398 |
|
399 QMenu* QVFb::createFileMenu() |
|
400 { |
|
401 QMenu *file = new QMenu( "File", this ); |
|
402 file->addAction( "Configure...", this, SLOT(configure()), 0 ); |
|
403 file->addSeparator(); |
|
404 file->addAction( "&Save image...", this, SLOT(saveImage()), 0 ); |
|
405 file->addAction( "&Animation...", this, SLOT(toggleAnimation()), 0 ); |
|
406 file->addSeparator(); |
|
407 file->addAction( "&Quit", qApp, SLOT(quit()) ); |
|
408 return file; |
|
409 } |
|
410 |
|
411 QMenu* QVFb::createViewMenu() |
|
412 { |
|
413 viewMenu = new QMenu( "View", this ); |
|
414 cursorAction = viewMenu->addAction( "Show &Cursor", this, |
|
415 SLOT(toggleCursor()) ); |
|
416 cursorAction->setCheckable(true); |
|
417 if ( view ) |
|
418 enableCursor(true); |
|
419 viewMenu->addAction( "&Refresh Rate...", this, SLOT(changeRate()) ); |
|
420 viewMenu->addSeparator(); |
|
421 viewMenu->addAction( "No rotation", this, SLOT(setRot0()) ); |
|
422 viewMenu->addAction( "90\260 rotation", this, SLOT(setRot90()) ); |
|
423 viewMenu->addAction( "180\260 rotation", this, SLOT(setRot180()) ); |
|
424 viewMenu->addAction( "270\260 rotation", this, SLOT(setRot270()) ); |
|
425 viewMenu->addSeparator(); |
|
426 viewMenu->addAction( "Zoom scale &0.5", this, SLOT(setZoomHalf()) ); |
|
427 viewMenu->addAction( "Zoom scale 0.75", this, SLOT(setZoom075()) ); |
|
428 viewMenu->addAction( "Zoom scale &1", this, SLOT(setZoom1()) ); |
|
429 viewMenu->addAction( "Zoom scale &2", this, SLOT(setZoom2()) ); |
|
430 viewMenu->addAction( "Zoom scale &3", this, SLOT(setZoom3()) ); |
|
431 viewMenu->addAction( "Zoom scale &4", this, SLOT(setZoom4()) ); |
|
432 viewMenu->addSeparator(); |
|
433 viewMenu->addAction( "Zoom scale...", this, SLOT(setZoom()) ); |
|
434 return viewMenu; |
|
435 } |
|
436 |
|
437 |
|
438 QMenu* QVFb::createHelpMenu() |
|
439 { |
|
440 QMenu *help = new QMenu( "Help", this ); |
|
441 help->addAction("About...", this, SLOT(about())); |
|
442 return help; |
|
443 } |
|
444 |
|
445 void QVFb::setZoom(double z) |
|
446 { |
|
447 view->setZoom(z,z*skinscaleV/skinscaleH); |
|
448 if (secondaryView) |
|
449 secondaryView->setZoom(z,z*skinscaleV/skinscaleH); |
|
450 |
|
451 if (skin) { |
|
452 skin->setTransform(QMatrix().scale(z/skinscaleH,z/skinscaleV).rotate(90*view->displayRotation())); |
|
453 if (secondaryView) |
|
454 secondaryView->setFixedSize( |
|
455 int(secondaryView->displayWidth()*z), |
|
456 int(secondaryView->displayHeight()*z*skinscaleV/skinscaleH)); |
|
457 } |
|
458 } |
|
459 |
|
460 void QVFb::setRotation(QVFbView::Rotation r) |
|
461 { |
|
462 view->setRotation(r); |
|
463 if (secondaryView) |
|
464 secondaryView->setRotation(r); |
|
465 setZoom(view->zoomH()); |
|
466 } |
|
467 |
|
468 void QVFb::setRot0() |
|
469 { |
|
470 setRotation(QVFbView::Rot0); |
|
471 } |
|
472 |
|
473 void QVFb::setRot90() |
|
474 { |
|
475 setRotation(QVFbView::Rot90); |
|
476 } |
|
477 |
|
478 void QVFb::setRot180() |
|
479 { |
|
480 setRotation(QVFbView::Rot180); |
|
481 } |
|
482 |
|
483 void QVFb::setRot270() |
|
484 { |
|
485 setRotation(QVFbView::Rot270); |
|
486 } |
|
487 |
|
488 void QVFb::setZoomHalf() |
|
489 { |
|
490 setZoom(0.5); |
|
491 } |
|
492 |
|
493 void QVFb::setZoom075() |
|
494 { |
|
495 setZoom(0.75); |
|
496 } |
|
497 |
|
498 void QVFb::setZoom1() |
|
499 { |
|
500 setZoom(1); |
|
501 } |
|
502 |
|
503 void QVFb::setZoom() |
|
504 { |
|
505 if ( !zoomer ) |
|
506 zoomer = new Zoomer(this); |
|
507 zoomer->show(); |
|
508 } |
|
509 |
|
510 void QVFb::setZoom2() |
|
511 { |
|
512 setZoom(2); |
|
513 } |
|
514 |
|
515 void QVFb::setZoom3() |
|
516 { |
|
517 setZoom(3); |
|
518 } |
|
519 |
|
520 void QVFb::setZoom4() |
|
521 { |
|
522 setZoom(4); |
|
523 } |
|
524 |
|
525 void QVFb::saveImage() |
|
526 { |
|
527 QImage img = view->image(); |
|
528 QString filename = QFileDialog::getSaveFileName(this, "Save Main Screen image", "snapshot.png", "Portable Network Graphics (*.png)"); |
|
529 if (!filename.isEmpty()){ |
|
530 if(!img.save(filename,"PNG")) |
|
531 QMessageBox::critical(this, "Save Main Screen Image", "Save failed. Check that you have permission to write to the target directory."); |
|
532 } |
|
533 if (secondaryView) { |
|
534 QImage img = view->image(); |
|
535 QString filename = QFileDialog::getSaveFileName(this, "Save Second Screen image", "snapshot.png", "Portable Network Graphics (*.png)"); |
|
536 if (!filename.isEmpty()) { |
|
537 if(!img.save(filename,"PNG")) |
|
538 QMessageBox::critical(this, "Save Second Screen Image", "Save failed. Check that you have permission to write to the target directory."); |
|
539 } |
|
540 } |
|
541 } |
|
542 |
|
543 void QVFb::toggleAnimation() |
|
544 { |
|
545 static AnimationSaveWidget *animWidget = 0; |
|
546 if ( !animWidget ) |
|
547 animWidget = new AnimationSaveWidget(view); |
|
548 if ( animWidget->isVisible() ) |
|
549 animWidget->hide(); |
|
550 else |
|
551 animWidget->show(); |
|
552 } |
|
553 |
|
554 void QVFb::toggleCursor() |
|
555 { |
|
556 enableCursor(cursorAction->isChecked()); |
|
557 } |
|
558 |
|
559 void QVFb::changeRate() |
|
560 { |
|
561 if ( !rateDlg ) { |
|
562 rateDlg = new QVFbRateDialog( refreshRate, this ); |
|
563 connect( rateDlg, SIGNAL(updateRate(int)), this, SLOT(setRate(int)) ); |
|
564 } |
|
565 |
|
566 rateDlg->show(); |
|
567 } |
|
568 |
|
569 void QVFb::setRate(int i) |
|
570 { |
|
571 refreshRate = i; |
|
572 view->setRate(i); |
|
573 if (secondaryView) |
|
574 secondaryView->setRate(i); |
|
575 } |
|
576 |
|
577 |
|
578 void QVFb::about() |
|
579 { |
|
580 QMessageBox::about(this, "About QVFB", |
|
581 "<h2>The Qt for Embedded Linux Virtual X11 Framebuffer</h2>" |
|
582 "<p>This application runs under Qt for X11, emulating a simple framebuffer, " |
|
583 "which the Qt for Embedded Linux server and clients can attach to just as if " |
|
584 "it was a hardware Linux framebuffer. " |
|
585 "<p>With the aid of this development tool, you can develop Qt for Embedded " |
|
586 "Linux applications under X11 without having to switch to a virtual console. " |
|
587 "This means you can comfortably use your other development tools such " |
|
588 "as GUI profilers and debuggers." |
|
589 ); |
|
590 } |
|
591 |
|
592 void QVFb::findSkins(const QString ¤tSkin) |
|
593 { |
|
594 skinnames.clear(); |
|
595 skinfiles.clear(); |
|
596 QDir dir(":/skins/","*.skin"); |
|
597 const QFileInfoList l = dir.entryInfoList(); |
|
598 int i = 1; // "None" is already in list at index 0 |
|
599 for (QFileInfoList::const_iterator it = l.begin(); it != l.end(); ++it) { |
|
600 skinnames.append((*it).baseName()); // should perhaps be in file |
|
601 skinfiles.append((*it).filePath()); |
|
602 if (((*it).baseName() + ".skin") == currentSkin) |
|
603 currentSkinIndex = i; |
|
604 i++; |
|
605 } |
|
606 } |
|
607 |
|
608 class Config : public QDialog, public Ui::Config |
|
609 { |
|
610 public: |
|
611 Config(QWidget *parent) |
|
612 : QDialog(parent) |
|
613 { |
|
614 setupUi(this); |
|
615 setModal(true); |
|
616 |
|
617 connect(buttonOk, SIGNAL(clicked()), this, SLOT(accept())); |
|
618 connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject())); |
|
619 } |
|
620 }; |
|
621 |
|
622 void QVFb::configure() |
|
623 { |
|
624 config = new Config(this); |
|
625 |
|
626 int w = view->displayWidth(); |
|
627 int h = view->displayHeight(); |
|
628 |
|
629 // Need to block signals, because we connect to animateClick(), |
|
630 // since QCheckBox doesn't have setChecked(bool) in 2.x. |
|
631 chooseSize(QSize(w,h)); |
|
632 config->skin->insertItems(config->skin->count(), skinnames); |
|
633 if (currentSkinIndex > 0) |
|
634 config->skin->setCurrentIndex(currentSkinIndex); |
|
635 config->skin->addItem(tr("Browse...")); |
|
636 config->touchScreen->setChecked(view->touchScreenEmulation()); |
|
637 config->lcdScreen->setChecked(view->lcdScreenEmulation()); |
|
638 chooseDepth(view->displayDepth(), view->displayFormat()); |
|
639 config->rgbSwapped->setChecked(view->rgbSwapped()); |
|
640 connect(config->skin, SIGNAL(activated(int)), this, SLOT(skinConfigChosen(int))); |
|
641 if ( view->gammaRed() == view->gammaGreen() && view->gammaGreen() == view->gammaBlue() ) { |
|
642 config->gammaslider->setValue(int(view->gammaRed()*400)); |
|
643 config->rslider->setValue(100); |
|
644 config->gslider->setValue(100); |
|
645 config->bslider->setValue(100); |
|
646 } else { |
|
647 config->gammaslider->setValue(100); |
|
648 config->rslider->setValue(int(view->gammaRed()*400)); |
|
649 config->gslider->setValue(int(view->gammaGreen()*400)); |
|
650 config->bslider->setValue(int(view->gammaBlue()*400)); |
|
651 } |
|
652 connect(config->gammaslider, SIGNAL(valueChanged(int)), this, SLOT(setGamma400(int))); |
|
653 connect(config->rslider, SIGNAL(valueChanged(int)), this, SLOT(setR400(int))); |
|
654 connect(config->gslider, SIGNAL(valueChanged(int)), this, SLOT(setG400(int))); |
|
655 connect(config->bslider, SIGNAL(valueChanged(int)), this, SLOT(setB400(int))); |
|
656 updateGammaLabels(); |
|
657 |
|
658 double ogr=view->gammaRed(), ogg=view->gammaGreen(), ogb=view->gammaBlue(); |
|
659 qApp->setQuitOnLastWindowClosed(false); |
|
660 |
|
661 hide(); |
|
662 if ( config->exec() ) { |
|
663 int id = view->displayId(); // not settable yet |
|
664 if ( config->size_176_220->isChecked() ) { |
|
665 w=176; h=220; |
|
666 } else if ( config->size_240_320->isChecked() ) { |
|
667 w=240; h=320; |
|
668 } else if ( config->size_320_240->isChecked() ) { |
|
669 w=320; h=240; |
|
670 } else if ( config->size_640_480->isChecked() ) { |
|
671 w=640; h=480; |
|
672 } else if ( config->size_800_600->isChecked() ) { |
|
673 w=800; h=600; |
|
674 } else if ( config->size_1024_768->isChecked() ) { |
|
675 w=1024; h=768; |
|
676 } else { |
|
677 w=config->size_width->value(); |
|
678 h=config->size_height->value(); |
|
679 } |
|
680 int d; |
|
681 if ( config->depth_1->isChecked() ) |
|
682 d=1; |
|
683 else if ( config->depth_2gray->isChecked() ) |
|
684 d=2; |
|
685 else if ( config->depth_4gray->isChecked() ) |
|
686 d=4; |
|
687 else if ( config->depth_8->isChecked() ) |
|
688 d=8; |
|
689 else if ( config->depth_12->isChecked() ) |
|
690 d=12; |
|
691 else if ( config->depth_15->isChecked() ) |
|
692 d = 15; |
|
693 else if ( config->depth_16->isChecked() ) |
|
694 d=16; |
|
695 else if ( config->depth_18->isChecked() ) |
|
696 d=18; |
|
697 else if ( config->depth_24->isChecked() ) |
|
698 d=24; |
|
699 else |
|
700 d=32; |
|
701 QVFbView::PixelFormat displayFormat = config->depth_32_argb->isChecked() |
|
702 ? QVFbView::ARGBFormat : QVFbView::DefaultFormat; |
|
703 int skinIndex = config->skin->currentIndex(); |
|
704 if ( w != view->displayWidth() || h != view->displayHeight() |
|
705 || d != view->displayDepth() || skinIndex != currentSkinIndex ) { |
|
706 QVFbView::Rotation rot = view->displayRotation(); |
|
707 int r = ((rot == QVFbView::Rot90) ? 90 : |
|
708 ((rot == QVFbView::Rot180) ? 180 : |
|
709 ((rot == QVFbView::Rot270) ? 270 : 0 ))); |
|
710 currentSkinIndex = skinIndex; |
|
711 init( id, w, h, d, r, skinIndex > 0 ? skinfiles[skinIndex-1] : QString::null ); |
|
712 } |
|
713 view->setViewFormat(displayFormat); |
|
714 view->setTouchscreenEmulation( config->touchScreen->isChecked() ); |
|
715 if (view->rgbSwapped() != config->rgbSwapped->isChecked()) { |
|
716 //### the windowTitle logic is inside init(), and init isn't always invoked |
|
717 QString caption = windowTitle(); |
|
718 if (!config->rgbSwapped->isChecked()) |
|
719 caption.replace(QLatin1String(" BGR"), QString()); |
|
720 else |
|
721 caption.append(QLatin1String(" BGR")); |
|
722 setWindowTitle(caption); |
|
723 view->setRgbSwapped(config->rgbSwapped->isChecked()); |
|
724 } |
|
725 bool lcdEmulation = config->lcdScreen->isChecked(); |
|
726 view->setLcdScreenEmulation( lcdEmulation ); |
|
727 if ( lcdEmulation ) |
|
728 setZoom3(); |
|
729 } else { |
|
730 view->setGamma(ogr, ogg, ogb); |
|
731 } |
|
732 show(); |
|
733 qApp->setQuitOnLastWindowClosed(true); |
|
734 delete config; |
|
735 config=0; |
|
736 } |
|
737 |
|
738 void QVFb::chooseSize(const QSize& sz) |
|
739 { |
|
740 config->size_width->blockSignals(true); |
|
741 config->size_height->blockSignals(true); |
|
742 config->size_width->setValue(sz.width()); |
|
743 config->size_height->setValue(sz.height()); |
|
744 config->size_width->blockSignals(false); |
|
745 config->size_height->blockSignals(false); |
|
746 config->size_custom->setChecked(true); // unless changed by settings below |
|
747 config->size_176_220->setChecked(sz == QSize(176,220)); |
|
748 config->size_240_320->setChecked(sz == QSize(240,320)); |
|
749 config->size_320_240->setChecked(sz == QSize(320,240)); |
|
750 config->size_640_480->setChecked(sz == QSize(640,480)); |
|
751 config->size_800_600->setChecked(sz == QSize(800,600)); |
|
752 config->size_1024_768->setChecked(sz == QSize(1024,768)); |
|
753 } |
|
754 |
|
755 void QVFb::chooseDepth(int depth, QVFbView::PixelFormat displayFormat) |
|
756 { |
|
757 config->depth_1->setChecked(depth==1); |
|
758 config->depth_2gray->setChecked(depth==2); |
|
759 config->depth_4gray->setChecked(depth==4); |
|
760 config->depth_8->setChecked(depth==8); |
|
761 config->depth_12->setChecked(depth==12); |
|
762 config->depth_15->setChecked(depth==15); |
|
763 config->depth_16->setChecked(depth==16); |
|
764 config->depth_18->setChecked(depth==18); |
|
765 config->depth_24->setChecked(depth==24); |
|
766 config->depth_32->setChecked(depth==32 && displayFormat != QVFbView::ARGBFormat); |
|
767 config->depth_32_argb->setChecked(depth==32 && displayFormat == QVFbView::ARGBFormat); |
|
768 } |
|
769 |
|
770 void QVFb::skinConfigChosen(int i) |
|
771 { |
|
772 if (i == config->skin->count() - 1) { // Browse... ? |
|
773 QFileDialog dlg(this); |
|
774 dlg.setFileMode(QFileDialog::DirectoryOnly); |
|
775 dlg.setWindowTitle(tr("Load Custom Skin...")); |
|
776 dlg.setFilter(tr("All QVFB Skins (*.skin)")); |
|
777 dlg.setDirectory(QDir::current()); |
|
778 if (dlg.exec() && dlg.selectedFiles().count() == 1) { |
|
779 skinfiles.append(dlg.selectedFiles().first()); |
|
780 i = skinfiles.count(); |
|
781 config->skin->insertItem(i, QFileInfo(skinfiles.last()).baseName()); |
|
782 config->skin->setCurrentIndex(i); |
|
783 } else { |
|
784 i = 0; |
|
785 } |
|
786 } |
|
787 if ( i ) { |
|
788 DeviceSkinParameters parameters; |
|
789 QString readError; |
|
790 if (parameters.read(skinfiles[i-1], DeviceSkinParameters::ReadSizeOnly, &readError)) { |
|
791 chooseSize(parameters.screenSize()); |
|
792 if (parameters.screenDepth) |
|
793 chooseDepth(parameters.screenDepth,QVFbView::ARGBFormat); |
|
794 config->touchScreen->setChecked(!parameters.hasMouseHover); |
|
795 } else { |
|
796 qWarning("%s", qPrintable(readError)); |
|
797 } |
|
798 } |
|
799 } |
|
800 |
|
801 void QVFb::setGamma400(int n) |
|
802 { |
|
803 double g = n/100.0; |
|
804 view->setGamma(config->rslider->value()/100.0*g, |
|
805 config->gslider->value()/100.0*g, |
|
806 config->bslider->value()/100.0*g); |
|
807 updateGammaLabels(); |
|
808 } |
|
809 |
|
810 void QVFb::setR400(int n) |
|
811 { |
|
812 double g = n/100.0; |
|
813 view->setGamma(config->rslider->value()/100.0*g, |
|
814 view->gammaGreen(), |
|
815 view->gammaBlue()); |
|
816 updateGammaLabels(); |
|
817 } |
|
818 |
|
819 void QVFb::setG400(int n) |
|
820 { |
|
821 double g = n/100.0; |
|
822 view->setGamma(view->gammaRed(), |
|
823 config->gslider->value()/100.0*g, |
|
824 view->gammaBlue()); |
|
825 updateGammaLabels(); |
|
826 } |
|
827 |
|
828 void QVFb::setB400(int n) |
|
829 { |
|
830 double g = n/100.0; |
|
831 view->setGamma(view->gammaRed(), |
|
832 view->gammaGreen(), |
|
833 config->bslider->value()/100.0*g); |
|
834 updateGammaLabels(); |
|
835 } |
|
836 |
|
837 void QVFb::updateGammaLabels() |
|
838 { |
|
839 config->rlabel->setText(QString::number(view->gammaRed(),'g',2)); |
|
840 config->glabel->setText(QString::number(view->gammaGreen(),'g',2)); |
|
841 config->blabel->setText(QString::number(view->gammaBlue(),'g',2)); |
|
842 } |
|
843 |
|
844 QSize QVFb::sizeHint() const |
|
845 { |
|
846 return QSize(int(view->displayWidth()*view->zoomH()), |
|
847 int(menuBar()->height()+view->displayHeight()*view->zoomV())); |
|
848 } |
|
849 |
|
850 // ===================================================================== |
|
851 |
|
852 AnimationSaveWidget::AnimationSaveWidget(QVFbAbstractView *v) : |
|
853 QWidget((QWidget*)0,0), |
|
854 view(v), recording(false), animation(0), |
|
855 timerId(-1), progressTimerId(-1), |
|
856 recOn(red_on_led_xpm), recOff(red_off_led_xpm), |
|
857 imageNum(0) |
|
858 { |
|
859 // Create the animation record UI dialog |
|
860 QVBoxLayout *vlayout = new QVBoxLayout( this ); |
|
861 |
|
862 QWidget *hbox = new QWidget( this ); |
|
863 vlayout->addWidget(hbox); |
|
864 QHBoxLayout *hlayout = new QHBoxLayout(hbox); |
|
865 recBt = new QPushButton( tr("Record"), hbox ); |
|
866 hlayout->addWidget(recBt); |
|
867 resetBt = new QPushButton( tr("Reset"), hbox ); |
|
868 hlayout->addWidget(resetBt); |
|
869 saveBt = new QPushButton( tr("Save"), hbox ); |
|
870 hlayout->addWidget(saveBt); |
|
871 recBt->setFixedWidth( 100 ); |
|
872 resetBt->setFixedWidth( 100 ); |
|
873 saveBt->setFixedWidth( 100 ); |
|
874 timeDpy = new QLabel( "00:00", hbox ); |
|
875 hlayout->addWidget(timeDpy); |
|
876 recLED = new QLabel( hbox ); |
|
877 hlayout->addWidget(recLED); |
|
878 recLED->setPixmap( recOff ); |
|
879 timeDpy->setMargin( 5 ); |
|
880 connect( recBt, SIGNAL(clicked()), this, SLOT(toggleRecord()) ); |
|
881 connect( resetBt, SIGNAL(clicked()), this, SLOT(reset()) ); |
|
882 connect( saveBt, SIGNAL(clicked()), this, SLOT(save()) ); |
|
883 elapsed = 0; |
|
884 vlayout->setMargin( 5 ); |
|
885 vlayout->setSpacing( 5 ); |
|
886 haveMpeg = detectPpmtoMpegCommand(); |
|
887 mpegSave = new QCheckBox( tr("Save in MPEG format (requires netpbm package installed)"), this ); |
|
888 vlayout->addWidget(mpegSave); |
|
889 mpegSave->setChecked( haveMpeg ); |
|
890 mpegSave->setEnabled( haveMpeg ); |
|
891 savingAsMpeg = haveMpeg; |
|
892 QWidget *hbox2 = new QWidget( this ); |
|
893 vlayout->addWidget(hbox2); |
|
894 QHBoxLayout *hlayout2 = new QHBoxLayout( hbox2 ); |
|
895 statusText = new QLabel( tr("Click record to begin recording."), hbox2 ); |
|
896 hlayout2->addWidget(statusText); |
|
897 progressBar = new QProgressBar( hbox2 ); |
|
898 progressBar->setValue( 0 ); |
|
899 hlayout2->addWidget(progressBar); |
|
900 progressBar->hide(); |
|
901 } |
|
902 |
|
903 AnimationSaveWidget::~AnimationSaveWidget() |
|
904 { |
|
905 // clean up |
|
906 removeTemporaryFiles(); |
|
907 delete animation; |
|
908 } |
|
909 |
|
910 // returns true if we have ppmtompeg command, else returns false |
|
911 bool AnimationSaveWidget::detectPpmtoMpegCommand() |
|
912 { |
|
913 // search the PATH for the ppmtompeg command to test we can record to mpeg |
|
914 QStringList paths = QString(::getenv("PATH")).split(":"); |
|
915 for ( int i = 0; i < paths.count(); i++ ) |
|
916 if ( QFile::exists( paths[i] + "/" + "ppmtompeg" ) ) |
|
917 return true; |
|
918 return false; |
|
919 } |
|
920 |
|
921 void AnimationSaveWidget::timerEvent( QTimerEvent *te ) |
|
922 { |
|
923 QString str; |
|
924 |
|
925 // Recording timer |
|
926 if ( te->timerId() == timerId ) { |
|
927 |
|
928 // Add a frame to the animation |
|
929 if ( savingAsMpeg && view ) |
|
930 view->image().save( str.sprintf("/tmp/qvfb_tmp_image_%04d.ppm", imageNum), "PPM"); |
|
931 else if ( animation && view ) |
|
932 animation->appendFrame(view->image());//QPoint(0,0)); |
|
933 imageNum++; |
|
934 |
|
935 // Update the display of number of seconds that have been recorded. |
|
936 int tmMsec = tm.elapsed(); |
|
937 timeDpy->setText( str.sprintf("%02d:%02d", tmMsec/60000, (tmMsec%60000)/1000) ); |
|
938 QObject::timerEvent( te ); |
|
939 |
|
940 // Make the recording LED blink |
|
941 static int tick = 0; |
|
942 static bool on = false; |
|
943 if ( tick > 10 ) { |
|
944 tick = 0; |
|
945 if ( on ) |
|
946 recLED->setPixmap( recOff ); |
|
947 else |
|
948 recLED->setPixmap( recOn ); |
|
949 on = !on; |
|
950 } |
|
951 tick++; |
|
952 } |
|
953 |
|
954 // Saving progress timer |
|
955 if ( te->timerId() == progressTimerId ) { |
|
956 // Parse output log file to work out the encoding progress. |
|
957 QFile f("/tmp/qvfb_tmp_output.log"); |
|
958 f.open(QIODevice::ReadOnly); |
|
959 int largestNum = 0; |
|
960 bool done = false; |
|
961 char buffer[1024]; |
|
962 while ( !f.atEnd() ) { |
|
963 // example of the output log entries |
|
964 // During each frame: |
|
965 // "FRAME 764 (B): I BLOCKS: 0...... |
|
966 // When complete: |
|
967 // "======FRAMES READ: 766" |
|
968 f.readLine(buffer, 1024); |
|
969 str = QString(buffer); |
|
970 if ( str.left(6) == "FRAME " ) { |
|
971 int num = str.mid(6, str.indexOf(QChar(' '), 6) - 6).toInt(); |
|
972 if ( num > largestNum ) |
|
973 largestNum = num; |
|
974 } else if ( str.left(18) == "======FRAMES READ:" ) { |
|
975 done = true; |
|
976 } |
|
977 } |
|
978 f.close(); |
|
979 |
|
980 // Update the progress bar with the frame we are up to |
|
981 progressBar->setValue( largestNum ); |
|
982 |
|
983 // Finished saving |
|
984 if ( done ) { |
|
985 progressBar->hide(); |
|
986 statusText->setText( tr("Finished saving.")); |
|
987 removeTemporaryFiles(); |
|
988 killTimer( progressTimerId ); |
|
989 progressTimerId = -1; |
|
990 reset(); |
|
991 } |
|
992 } |
|
993 } |
|
994 |
|
995 // Takes the saved ppm files and converts them to a mpeg file named filename |
|
996 void AnimationSaveWidget::convertToMpeg(QString filename) |
|
997 { |
|
998 recLED->setPixmap( recOff ); |
|
999 killTimer( timerId ); |
|
1000 |
|
1001 progressBar->show(); |
|
1002 progressBar->setRange( 0, imageNum ); |
|
1003 progressBar->setValue( 0 ); |
|
1004 |
|
1005 // Build parameter file required by ppmtompeg |
|
1006 QFile file("/tmp/qvfb_tmp_ppmtompeg.params"); |
|
1007 if ( file.open( QIODevice::WriteOnly ) ) { |
|
1008 QTextStream t( &file ); |
|
1009 t << "PATTERN IBBPBBPBBPBBPBB\n"; |
|
1010 t << "OUTPUT " << filename << "\n"; |
|
1011 t << "INPUT_DIR /tmp\n"; |
|
1012 t << "INPUT\n"; |
|
1013 QString str; |
|
1014 str = str.sprintf("%04d", imageNum - 1); |
|
1015 t << "qvfb_tmp_image_*.ppm [0000-" << str << "]\n"; |
|
1016 t << "END_INPUT\n"; |
|
1017 t << "BASE_FILE_FORMAT PPM\n"; |
|
1018 t << "INPUT_CONVERT *\n"; |
|
1019 t << "GOP_SIZE 15\n"; |
|
1020 t << "SLICES_PER_FRAME 1\n"; |
|
1021 t << "PIXEL HALF\n"; |
|
1022 t << "RANGE 5\n"; |
|
1023 t << "PSEARCH_ALG LOGARITHMIC\n"; |
|
1024 t << "BSEARCH_ALG SIMPLE\n"; |
|
1025 t << "IQSCALE 1\n"; |
|
1026 t << "PQSCALE 1\n"; |
|
1027 t << "BQSCALE 1\n"; |
|
1028 t << "REFERENCE_FRAME DECODED\n"; |
|
1029 t << "ASPECT_RATIO 1\n"; |
|
1030 t << "FRAME_RATE 24\n"; |
|
1031 t << "BIT_RATE 64000\n"; // Quality |
|
1032 t << "BUFFER_SIZE 2048\n"; |
|
1033 } |
|
1034 file.close(); |
|
1035 |
|
1036 // ### can't use QProcess, not in Qt 2.3 |
|
1037 // ### but it's certainly in Qt 4! use it? |
|
1038 // Execute the ppmtompeg command as a seperate process to do the encoding |
|
1039 pid_t pid = ::fork(); |
|
1040 if ( !pid ) { |
|
1041 // Child process |
|
1042 // redirect stdout to log file |
|
1043 freopen("/tmp/qvfb_tmp_output.log", "w", stdout); |
|
1044 // ppmtompeg tool is from the netpbm package |
|
1045 ::execlp("ppmtompeg", "ppmtompeg", "/tmp/qvfb_tmp_ppmtompeg.params", (void *)0); |
|
1046 exit(0); |
|
1047 } |
|
1048 |
|
1049 // Update the saving progress bar every 200ms |
|
1050 progressTimerId = startTimer( 200 ); |
|
1051 } |
|
1052 |
|
1053 // Cleanup temporary files created during creating a mpeg file |
|
1054 void AnimationSaveWidget::removeTemporaryFiles() |
|
1055 { |
|
1056 QString str; |
|
1057 for ( int i = 0; i < imageNum; i++ ) |
|
1058 QFile::remove( str.sprintf("/tmp/qvfb_tmp_image_%04d.ppm", i) ); |
|
1059 QFile::remove("/tmp/qvfb_tmp_ppmtompeg.params"); |
|
1060 QFile::remove("/tmp/qvfb_tmp_output.log"); |
|
1061 imageNum = 0; |
|
1062 } |
|
1063 |
|
1064 // toggles between recording and paused (usually when record button clicked) |
|
1065 void AnimationSaveWidget::toggleRecord() |
|
1066 { |
|
1067 if ( recording ) { |
|
1068 recLED->setPixmap( recOff ); |
|
1069 recBt->setText( tr("Record") ); |
|
1070 statusText->setText( tr("Paused. Click record to resume, or save if done.")); |
|
1071 killTimer( timerId ); |
|
1072 timerId = -1; |
|
1073 elapsed = tm.elapsed(); |
|
1074 } else { |
|
1075 recLED->setPixmap( recOn ); |
|
1076 recBt->setText( tr("Pause") ); |
|
1077 statusText->setText( tr("Recording...")); |
|
1078 tm.start(); |
|
1079 if ( elapsed == 0 ) { |
|
1080 savingAsMpeg = mpegSave->isChecked(); |
|
1081 if ( !savingAsMpeg ) { |
|
1082 delete animation; |
|
1083 animation = new QAnimationWriter("/tmp/qvfb_tmp_animation.mng","MNG"); |
|
1084 animation->setFrameRate(24); |
|
1085 if ( view ) |
|
1086 animation->appendFrame(view->image()); |
|
1087 } |
|
1088 } |
|
1089 tm = tm.addMSecs(-elapsed); |
|
1090 elapsed = 0; |
|
1091 timerId = startTimer(1000 / 24); |
|
1092 } |
|
1093 recording = !recording; |
|
1094 } |
|
1095 |
|
1096 // Reset everything to initial state of not recording |
|
1097 void AnimationSaveWidget::reset() |
|
1098 { |
|
1099 if ( recording ) { |
|
1100 toggleRecord(); |
|
1101 statusText->setText( tr("Click record to begin recording.")); |
|
1102 removeTemporaryFiles(); |
|
1103 } |
|
1104 progressBar->setValue( 0 ); |
|
1105 timeDpy->setText( "00:00" ); |
|
1106 elapsed = 0; |
|
1107 imageNum = 0; |
|
1108 delete animation; |
|
1109 animation = 0; |
|
1110 } |
|
1111 |
|
1112 // Prompt for filename to save to and put animation in that file |
|
1113 void AnimationSaveWidget::save() |
|
1114 { |
|
1115 if ( recording ) |
|
1116 toggleRecord(); // pauses |
|
1117 statusText->setText( tr("Saving... ")); |
|
1118 |
|
1119 QString filename; |
|
1120 if ( savingAsMpeg ) { |
|
1121 filename = QFileDialog::getSaveFileName(this, tr("Save animation..."), "", "*.mpg"); |
|
1122 if ( !filename.isNull() ) |
|
1123 convertToMpeg(filename); |
|
1124 } else { |
|
1125 filename = QFileDialog::getSaveFileName(this, tr("Save animation..."), "", "*.mng"); |
|
1126 if (filename.isNull()) { |
|
1127 statusText->setText(tr("Save canceled.")); |
|
1128 } else { |
|
1129 QFile::remove(filename); |
|
1130 bool success = QFile::rename(QLatin1String("/tmp/qvfb_tmp_animation.mng"), |
|
1131 filename); |
|
1132 if (success) { |
|
1133 statusText->setText(tr("Finished saving.")); |
|
1134 reset(); |
|
1135 } else { |
|
1136 statusText->setText(tr("Save failed!")); |
|
1137 } |
|
1138 } |
|
1139 } |
|
1140 } |
|
1141 |
|
1142 QT_END_NAMESPACE |
|
1143 |
|
1144 #include "qvfb.moc" |