tools/qvfb/qvfb.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     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 &currentSkin)
       
   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"