/**
   \class CWebCamViewer
   
   A class for the usual webcam activity: take pictures, save them do disk
   and/or upload to a server.
 */

#define ENABLE_TRACING 1 
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef INCLUDE_UNISTD_H
#include <unistd.h>
#endif
#ifdef INCLUDE_STDLIB_H
#include <stdlib.h>
#endif

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include <qbitmap.h>
#include <qdir.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qlabel.h>
#include <qmessagebox.h>
#include <qmultilineedit.h>
#include <qpixmap.h>
#include <qpushbutton.h>
#include <qradiobutton.h>
#include <qspinbox.h>
#include <qstatusbar.h>
#include <qtoolbar.h>
#include <qtoolbutton.h>
#include <qtooltip.h>
#include <qurl.h>

#include "CamStreamApp.h"
#include "WebCamViewer.h"

CWebCamViewer::CWebCamViewer(CVideoDevice *video, const QSize &hint_size, QWidget *parent, const char *name)
	: CCamWindow(parent, name)
{
   int r;
   QWidget *pWidget;
   QToolBar *bar;
   QStatusBar *status;
   QString nullstring;
   CVideoAudioInput *vai;

qDebug("CWebCamViewer::CWebCamViewer(%p, %dx%d)", video, hint_size.width(), hint_size.height());

   /* Our central widget contains space for the view image, and optionally
      the last snapshot. The widget itself is empty (except for a background)
    */

   pVideo = video;
   pViewer = NULL;
   SnapInterval = -1;
   pLastSnapshot = NULL;
   Upload.pClient = NULL;
   Upload.ErrorCondition = FALSE;
   Upload.Commands.setAutoDelete(true);

   r = pVideo->Open(2);
   if (r < 0) {
     qWarning("Error opening video device: %d.", r);
     return;
   }
   setCaption(pVideo->GetIntfName());

   /* Create and initialize dialogs */
   pVOptions = CamApp->FindVideoOptions(pVideo->GetIntfName(), pVideo->GetNodeName(), TRUE);
   pConfiguration = new CSnapshotSettingsDlg(pVOptions);
   connect(pConfiguration->OKButton,     SIGNAL(clicked()), this, SLOT(ConfigurationDlgClosed()));
   connect(pConfiguration->CancelButton, SIGNAL(clicked()), this, SLOT(ConfigurationDlgClosed()));
   
   pSettings = new CVideoSettingsDlg(pVideo);
   connect(pSettings, SIGNAL(DialogClosed()), this, SLOT(SettingsDlgClosed()));

   pTimeSnapDlg = new CTimeSnapDlg();
   connect(pTimeSnapDlg->OkButton,     SIGNAL(clicked()), this, SLOT(StartTimeSnap()));
   connect(pTimeSnapDlg->CancelButton, SIGNAL(clicked()), this, SLOT(TimeSnapDlgClosed()));
   pSnapTimer = new QTimer(this);
   connect(pSnapTimer, SIGNAL(timeout()), this, SLOT(TimeSnapTick()));

   /* Connect the video device to us. You may wonder why the Notify and 
      Resized signal aren't connected to us. Well, Notify is handled
      in the Viewer widget (which updates itself), and Resized() indirectly
      through the same Viewer. Only the error condition matters to us.
    */
   connect(pVideo, SIGNAL(Error(int)), this, SLOT(DeviceError(int)));

   /* Add buttons to toolbar */
   bar = new QToolBar("main", this);

   ButtonPix[pbt_controls].load(SHARE_DIR "/icons/controls.png");
   ButtonPix[pbt_config  ].load(SHARE_DIR "/icons/configure.png");
   ButtonPix[pbt_showsnap].load(SHARE_DIR "/icons/showsnap.png");
   ButtonPix[pbt_snapshot].load(SHARE_DIR "/icons/filesave.png");
   ButtonPix[pbt_timesnap].load(SHARE_DIR "/icons/savetimer.png");
   ButtonPix[pbt_sound   ].load(SHARE_DIR "/icons/loudspeaker.png");

   pButton[pbt_controls] = new QToolButton(ButtonPix[pbt_controls], "various controls",                   nullstring, this, SLOT(ClickedSettingsDlg()),      bar);
   pButton[pbt_config  ] = new QToolButton(ButtonPix[pbt_config],   "configuration",                      nullstring, this, SLOT(ClickedConfigurationDlg()), bar); 
   pButton[pbt_showsnap] = new QToolButton(ButtonPix[pbt_showsnap], "show last snapshot",                 nullstring, this, SLOT(ClickedShowLastSnapshot()), bar); 
   pButton[pbt_snapshot] = new QToolButton(ButtonPix[pbt_snapshot], "take snapshot",                      nullstring, this, SLOT(TakeSnapshot()),            bar); 
   pButton[pbt_timesnap] = new QToolButton(ButtonPix[pbt_timesnap], "take snapshot at regular intervals", nullstring, this, SLOT(ClickedTimeSnapDlg()),      bar); 
   pButton[pbt_sound   ] = new QToolButton(ButtonPix[pbt_sound],    "sound on/off",                       nullstring, this, SLOT(ClickedSoundOnOff()),       bar);

   pButton[pbt_controls]->setToggleButton(TRUE);
   pButton[pbt_config  ]->setToggleButton(TRUE);
   pButton[pbt_showsnap]->setToggleButton(TRUE);
   pButton[pbt_timesnap]->setToggleButton(TRUE);
   pButton[pbt_sound   ]->setToggleButton(TRUE);
   addToolBar(bar);
   
   /* Create statusbar, add space for countdown timer */
   status = statusBar();
   pSnapLabel = new QLabel(status);
   pSnapLabel->setText("--:--");
   status->addWidget(pSnapLabel, 0, TRUE);

   /* Create auxilary timer & workaround for adjustSize() */
   SizeTimer = new QTimer(this);
   connect(SizeTimer, SIGNAL(timeout()), this, SLOT(CallAdjustSize()));
#if QT_VERSION < 300   
   connect(this, SIGNAL(endMovingToolBar(QToolBar *)), this, SLOT(CallAdjustSize()));
#else
   connect(this, SIGNAL(dockWindowPositionChanged(QDockWindow *)), this, SLOT(CallAdjustSize()));
#endif
   /* See if we can mute/unmute the device */
   vai = 0;
   r = pVideo->GetAudioInputs();
   if (r > 1) {
     /* Hrmpf. this is going to be interesting */
     qDebug("INFO: Video device has more than one audio input.");
     vai = pVideo->GetAudioInput();
   }
   if (vai == 0) {
     pVideo->SelectAudioInput(0);
     vai = pVideo->GetAudioInput();
   }

   if (vai != 0) {
     if (!vai->IsMutable())
       pButton[pbt_sound]->setEnabled(FALSE);
     else {
       vai->Mute(FALSE);
       pButton[pbt_sound]->setOn(TRUE);
     }
   }

   /* Create an empty widget upon we place our viewer and snapshot panels */
   pWidget = new QWidget(this, "empty widget");
   assert(pWidget != NULL);
   pWidget->setBackgroundMode(QWidget::PaletteBase);
   pWidget->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
   setCentralWidget(pWidget);

   pViewer = new CImagePanelRGB(pVideo, pWidget);
//   pViewer = new CImagePanelYUV(pVideo, pWidget);
   connect(pViewer, SIGNAL(ChangedVisibleSize(const QSize &)), this, SLOT(DeviceChangedSize(const QSize &)));
   pLastSnapshot = new CBasicPanel("snapshot", "Last snapshot", CCamPanel::RGB, pWidget);

   if (!hint_size.isNull()) { /* User supplied an image size; try that */
     if (!pVideo->SetSize(hint_size))
       qWarning("Video device did not accept hint_size.\n");
   }
   if (pViewer) {
     QPixmap blackimg;
     QSize viewsize;
     QPainter painter;
     
     viewsize = pVideo->GetSize();
     blackimg.resize(viewsize);
     painter.begin(&blackimg);
     painter.fillRect(0, 0, viewsize.width(), viewsize.height(), black);
     painter.setPen(yellow);
     painter.drawText(0, 0, viewsize.width(), viewsize.height(), AlignCenter, tr("Your last saved snapshot appears here"));
     painter.end();
     pLastSnapshot->SetSize(viewsize);
     pLastSnapshot->SetImage(0, blackimg.convertToImage());

     pViewer->show();
   }
   RecalcTotalViewSize();
}


CWebCamViewer::~CWebCamViewer()
{
   CVideoAudioInput *vai;

qDebug("CWebCamViewer::~CWebCamViewer()");
   StopTimeSnap();
   if (pVideo) {
     vai = pVideo->GetAudioInput();
     if (vai != 0)
       vai->Mute(TRUE); // Silence device
     //pVideo->EnableRGB(FALSE);
     pVideo->Close();
   }
   pSettings->hide();
   pConfiguration->hide();
   delete pViewer;
   delete pLastSnapshot;
   delete Upload.pClient;
}

// private


void CWebCamViewer::StartFTPUpload(const QString &filename, bool delafterwards)
{
   FTPCommandStruct *str;
   QString last;
   int LastSlash;

   if (Upload.ErrorCondition) {
     qWarning("CWebCamViewer::StartFTPUpload() Error condition was set");
     return;
   }
   if (!Upload.Commands.isEmpty()) { 
     qWarning("CWebCamViewer::StartFTPUpload() Still commands in queue, uploading previous image?");
     return;
   }

   if (Upload.pClient == NULL) {
     Upload.pClient = new CFTPClient();
     if (Upload.pClient == NULL) {
       qWarning("Failed to create FTP client process.");
       return;
     }
     connect(Upload.pClient, SIGNAL(StateChange(int, int, int, const QString &)), this, SLOT(FTPChangeState(int, int, int, const QString &)));

     str = new FTPCommandStruct;
     str->Command = CFTPClient::cmdLogin;
     str->Param[0] = pVOptions->GetFTPUser();
     str->Param[1] = pVOptions->GetFTPPass();
     str->Param[2] = pVOptions->GetFTPServer();
     // TODO: FTP port number
     Upload.Commands.append(str);

     str = new FTPCommandStruct;
     str->Command = CFTPClient::cmdSetType;
     str->Param[0] = "B";
     Upload.Commands.append(str);

     if (!pVOptions->GetFTPPath().isEmpty()) {
       str = new FTPCommandStruct;
       str->Command = CFTPClient::cmdChangeDir;
       str->Param[0] = pVOptions->GetFTPPath();
       Upload.Commands.append(str);
     }
     if (pVOptions->GetFTPPassive()) {
       str = new FTPCommandStruct;
       str->Command = CFTPClient::cmdPassive;
       Upload.Commands.append(str);
     }
   }
   
   str = new FTPCommandStruct;
   str->Command = CFTPClient::cmdUpload;
   str->Param[0] = filename;
   // FIXME: other name on server
   last = filename;
   LastSlash = last.findRev(QDir::separator());
   if (LastSlash > 0) {
     last = filename.mid(LastSlash + 1);
   }
   str->Param[1] = last;
   str->Param[2] = delafterwards ? "true" : "false";
   Upload.Commands.append(str);

   TriggerNextFTPCommand();
}

void CWebCamViewer::StopFTP()
{
   FTPCommandStruct *str;

qDebug("CWebCamViewer::StopFTP()");
   Upload.Commands.clear();
   str = new FTPCommandStruct;
   str->Command = CFTPClient::cmdDestroy;
   Upload.Commands.append(str);
   TriggerNextFTPCommand();
   statusBar()->clear();
}

void CWebCamViewer::TriggerNextFTPCommand()
{
   QTimer::singleShot(0, this, SLOT(NextFTPCommand()));
}


// private slots

void CWebCamViewer::ClickedConfigurationDlg()
{
   if (pButton[pbt_config]->isOn())
     pConfiguration->show();
   else
     pConfiguration->hide();
}

void CWebCamViewer::ConfigurationDlgClosed()
{
   pButton[pbt_config]->setOn(FALSE);
}

void CWebCamViewer::ClickedSettingsDlg()
{
   if (pButton[pbt_controls]->isOn())
     pSettings->show();
   else
     pSettings->hide();
}

void CWebCamViewer::SettingsDlgClosed()
{
   pButton[pbt_controls]->setOn(FALSE);
}

void CWebCamViewer::ClickedShowLastSnapshot()
{
   if (pLastSnapshot) {
     if (pButton[pbt_showsnap]->isOn())
       pLastSnapshot->show();
     else
       pLastSnapshot->hide();
     // Only enable RGB when we might need it (and then it's still a waste of CPU)
     pVideo->EnableRGB(pButton[pbt_showsnap]->isOn());
     RecalcTotalViewSize();
   }
}




void CWebCamViewer::ClickedTimeSnapDlg()
{
qDebug("CWebCamViewer::ClickedTimeSnapDlg()");
   if (pButton[pbt_timesnap]->isOn()) 
     pTimeSnapDlg->show();
   else {
     pTimeSnapDlg->hide();
     StopTimeSnap();
   }
}

void CWebCamViewer::TimeSnapDlgClosed()
{
qDebug("CWebCamViewer::TimeSnapDlgClosed()");
   pButton[pbt_timesnap]->setOn(FALSE);
}

void CWebCamViewer::ClickedSoundOnOff()
{
   CVideoAudioInput *vai;
   
qDebug("CWebCamViewer::ClickedSoundOnOff(): '%c'", pButton[pbt_sound]->isOn() ? 'T' : 'F');
   vai = pVideo->GetAudioInput();
   if (vai == NULL)
     return;
qDebug("setting sound");
   vai->Mute(!pButton[pbt_sound]->isOn());
}



void CWebCamViewer::FTPChangeState(int command, int new_state, int result, const QString &server_msg)
{
qDebug("CWebCamViewer::FTPChangeState(%d \"%s\", %d \"%s\", %d)", command, CFTPClient::CommandStr[command], new_state, CFTPClient::StateStr[new_state], result);

   switch(new_state) {
     case CFTPClient::stNOC:
       if (command != CFTPClient::cmdLogout) {
         Upload.ErrorCondition = true;
         // TODO: retry-button
         QMessageBox::warning(this, tr("FTP failure"), tr("FTP server closed connection"), QMessageBox::Ok, QMessageBox::NoButton);
         Upload.ErrorCondition = false;
       }
       StopFTP();
       break;
     
     case CFTPClient::stIdle:
       // success
       switch(command) {
         case CFTPClient::cmdUpload:
         case CFTPClient::cmdDownload:
           statusBar()->message(tr("Transfer completed in %1 seconds.").arg(Upload.StartTime.elapsed() / 1000.0, 3, 'f', 2));
           break;
       }
       TriggerNextFTPCommand();
       break;

     case CFTPClient::stWaitData:
       switch(command) {
         case CFTPClient::cmdUpload:
         case CFTPClient::cmdDownload:
           statusBar()->message(tr("Waiting for data connection."));
           break;
       }
       break;

     case CFTPClient::stTransfer:
       switch(command) {
         case CFTPClient::cmdUpload:
         case CFTPClient::cmdDownload:
           statusBar()->message(tr("Transfering file."));
           break;
       }
       break;

     case CFTPClient::stClosingData:
       switch(command) {
         case CFTPClient::cmdUpload:
         case CFTPClient::cmdDownload:
           statusBar()->message(tr("Closing data connection."));
           break;
       }
       break;

     case CFTPClient::stFailed:
       Upload.ErrorCondition = TRUE;
       switch(command) {
         case CFTPClient::cmdLogin:
           switch(result) {
             case 810:
               QMessageBox::warning(this, 
                            tr("Name lookup"), 
                            tr("Server name could not be resolved. Please check servername or IP address."), QMessageBox::Ok, QMessageBox::NoButton);
               break;

             default:
               QMessageBox::warning(this, tr("FTP login"), tr("Login failed. Please check username & password."), QMessageBox::Ok, QMessageBox::NoButton);
               break;
           }
           break;

         case CFTPClient::cmdChangeDir:
           QMessageBox::warning(this, tr("Changing directory"), tr("Failed to change to directory on FTP server.\nPlease check name and permissions on the server."), QMessageBox::Ok, QMessageBox::NoButton);
           break;

         case CFTPClient::cmdUpload: 
           QMessageBox::warning(this, tr("Upload failed"), tr("Uploading of image failed. Server said:\n") + server_msg, QMessageBox::Ok, QMessageBox::NoButton);
           break;

         default:
           QMessageBox::warning(this, tr("Other error"), tr("Some other (unexpected) error occured (command = '%1').\nPlease see debug messages for more info.").arg(CFTPClient::CommandStr[command]), QMessageBox::Ok, QMessageBox::NoButton);
           break;
       }
       Upload.ErrorCondition = FALSE;
       StopFTP();
       break;

     case CFTPClient::stUnknown:
       qDebug("Action resulted in unknown result code");
   }
}

void CWebCamViewer::NextFTPCommand()
{
   FTPCommandStruct *str;

   if (Upload.Commands.isEmpty())
     return;
     
   str = Upload.Commands.first();
qDebug("CWebCamViewer::NextFTPCommand(): next_cmd = %d \"%s\"", str->Command, CFTPClient::CommandStr[str->Command]);
   
   if (Upload.pClient == NULL) {
     qDebug("Uhm, our FTP connections seems to be gone?");
     Upload.Commands.clear();
     return;
   }
     
   switch(str->Command) {
     case CFTPClient::cmdLogin:
       statusBar()->message(tr("Connecting to FTP server."));
       Upload.pClient->Connect(str->Param[0], str->Param[1], str->Param[2]);
       break;
     
     case CFTPClient::cmdSetType:
       if (str->Param[0] == "B")
         Upload.pClient->SetTypeBinary();
       else
         Upload.pClient->SetTypeAscii();
       break;
     
     case CFTPClient::cmdChangeDir:
       Upload.pClient->ChangeDir(str->Param[0]);
       break;
       
     case CFTPClient::cmdUpload:
       Upload.StartTime.start();
       statusBar()->message(tr("Uploading..."));
       Upload.pClient->Upload(str->Param[0], str->Param[1]);
       break;
       
     case CFTPClient::cmdLogout:
       Upload.pClient->Logout();
       break;
       
     case CFTPClient::cmdListDir:
       Upload.pClient->ListDir();
       break;

     case CFTPClient::cmdPassive:
       Upload.pClient->SetPassive();
       TriggerNextFTPCommand();
       break;
       
     case CFTPClient::cmdRename:
       Upload.pClient->Rename(str->Param[0], str->Param[1]);
       break;
       
     case CFTPClient::cmdDestroy:
       delete Upload.pClient;
       Upload.pClient = NULL;
       break;

     default: 
       qDebug("Unknown command.... bleh");
       StopFTP();
       break;
   }
   Upload.Commands.remove();
}


/* When the user clicks 'Ok' in the TimeSnap dialog, this function gets
   called and we will start the timed snapshots 
 */
void CWebCamViewer::StartTimeSnap()
{
   SnapInterval = pTimeSnapDlg->Interval->value();
   if (SnapInterval < 1) {
     qWarning("Ignoring interval of %d seconds", SnapInterval);
     return;
    }
   if (pTimeSnapDlg->Minutes->isOn())
     SnapInterval *= 60;
qDebug("CWebCamViewer::StartTimeSnap(): %d", SnapInterval);
   SnapCounter = 0;
   pSnapTimer->start(1000);
   pVideo->EnableRGB(TRUE);
}


void CWebCamViewer::StopTimeSnap()
{
qDebug("CWebCamViewer::StopTimeSnap()");
   pSnapTimer->stop();
   pSnapLabel->setText("--:--");
   if (SnapInterval > 0) {
     SnapInterval = -1;
     SnapCounter = 0;
     pVideo->EnableRGB(FALSE);
   }
   StopFTP();
}

/**
  This function gets called once a second for a countdown to the next
  snapshot.
 */
void CWebCamViewer::TimeSnapTick()
{
   int rem;
   QString TimerText;

   if (SnapInterval < 0) {
     StopTimeSnap();
     return; // not active
   }
   SnapCounter++;

   if (SnapCounter >= SnapInterval) {
     TakeSnapshot(); 
   
     SnapCounter = 0;
   }
   // Update status bar, show remaining time
   rem = SnapInterval - SnapCounter;
   TimerText.sprintf("%02d:%02d", rem / 60 , rem % 60);
   pSnapLabel->setText(TimerText);
}


/** 
  \brief Takes an image from the video stream, saves it in the snapshot imagebuffer, starts FTP upload
*/
void CWebCamViewer::TakeSnapshot()
{
   time_t now;
   struct tm *pTM = 0;
   char strbuf[80];
   bool stampimage, stampfile;
   bool savetodisk, ftptoserver;
   bool delafterftp;
   int sequence;

   QString SnapExtension, SnapBase, SnapFmt;
   QString savename, ftpname;
   QFileInfo savefile;

   QImage DrawImage;
   QPainter p;
   QSize viewersize = pVideo->GetSize();
   QPixmap DrawArea(viewersize);

   stampimage = pVOptions->GetTimeInImage();
   stampfile = pVOptions->GetSaveOption() == 2;
   savetodisk = pVOptions->GetSaveToDisk();
   ftptoserver = pVOptions->GetFTPToServer();

qDebug("TakeSnapshot: stampimage = %c, stampfile %c, savetodisk = %c, ftptoserver = %c", 
       stampimage ? 'T' : 'F', 
       stampfile ? 'T' : 'F',
       savetodisk ? 'T' : 'F',
       ftptoserver ? 'T' : 'F');

   if (stampimage || stampfile) {
     time(&now);
     pTM = localtime(&now);
   }
   if (stampimage) {
     /* Paint time & text in image */
     DrawImage = pViewer->GetImage();
     DrawArea.convertFromImage(DrawImage, 0);

     strftime(strbuf, 80, "%a, %d %b %Y %H:%M:%S %z %Z", pTM);
     p.begin(&DrawArea);
     p.setPen(pVOptions->GetTextColor());
     p.setFont(pVOptions->GetTextFont());
     p.drawText(5, viewersize.height() - 5, strbuf);
     p.end();
     DrawImage = DrawArea.convertToImage();
   }
   else
     DrawImage = pViewer->GetImage().copy(); // Make deep copy of the current image 

   // Store image in LastSnapshot
   pLastSnapshot->SetSize(viewersize);
   pLastSnapshot->SetImage(0, DrawImage);
   RecalcTotalViewSize();

   /* Create filename to save to disk */
   SnapFmt = pVOptions->GetFileFormat();
   SnapExtension = CamApp->FormatStrToExtension(SnapFmt);
   SnapBase = pVOptions->GetBaseName();

   switch(pVOptions->GetSaveOption()) {
     case 1: // overwrite
       savename.sprintf("%s.%s", (const char *)SnapBase, (const char *)SnapExtension);
       break;

     case 2: // timestamp filename
       savename.sprintf("%s-%04d%02d%02d-%02d%02d%02d.%s",
                  (const char *)SnapBase,
                  1900 + pTM->tm_year, 1 + pTM->tm_mon, pTM->tm_mday,
                  pTM->tm_hour, pTM->tm_min, pTM->tm_sec,
                  (const char *)SnapExtension);
       break;
       
     case 3: // sequential numbering
       sequence = pVOptions->GetSequence();
       if (sequence > pVOptions->GetMaxSequence())
         sequence = 0;
       savename.sprintf("%s%03d.%s", (const char *)SnapBase, sequence, (const char *)SnapExtension);
       pVOptions->SetSequence(++sequence);
       break;
   }

   /* extract filename, withouth paths */
   savefile.setFile(savename);
   
   delafterftp = false;
   if (savetodisk) {
     qDebug("Saving image to " + savename);
     if (!DrawImage.save(savename, SnapFmt)) {
       // XXX Failed. pop up message box, or set message in status balk
       qWarning("Could not save image!");
     }
     else {
       if (ftptoserver)
         ftpname = savename;
     }
   }
   else {
     if (ftptoserver) {
       delafterftp = true;
       ftpname = CamApp->GetUploadTmpDir() + "/" + savefile.fileName();
       qDebug("Saving temp FTP image to " + ftpname);
       if (!DrawImage.save(ftpname, SnapFmt))
         qWarning("Could not save temp image for FTPing!");
     }
   }

   if (ftptoserver)
     StartFTPUpload(ftpname, delafterftp);
}


// protected

/** 
  \brief Calculate the total required viewing area, based on video image and 'last snapshot'
*/

void CWebCamViewer::RecalcTotalViewSize()
{
   QSize asize, osize;
   int w1 = 0, w2 = 0, h;

   if (pViewer && pLastSnapshot) {
     asize = pViewer->GetVisibleSize();
     w1 = asize.width();
     h = asize.height();
  
     if (pButton[pbt_showsnap]->isOn()) {
       asize = pLastSnapshot->GetVisibleSize();
       w2 = asize.width();
       if (asize.height() > h)
         h = asize.height();
       if (w2 == 0)
         w2 = w1;
     }
     
     asize.setWidth(w1 + w2);
     asize.setHeight(h);

     osize = centralWidget()->size();
     osize = size();
     if (asize != osize) { // Set new viewport only when necessary
qDebug("RecalcTotalViewSize: resize viewport(%dx%d)", asize.width(), asize.height());

       pViewer->move(0, 0);
       pLastSnapshot->move(w1, 0);

       centralWidget()->setFixedSize(asize);
       /* Bug or feature? At this point I cannot call adjustSize(), probably
          because we are in an event loop or something. So therefor we
          (ab)use a single shot timer to call adjustSize() for us, just
          after all current events have been processed (timeout = 0).
        */
       SizeTimer->start(0, TRUE);
     }
   }
}

// protected slots


/** Called from \ref CVideoDevice::Resized */
void CWebCamViewer::DeviceChangedSize(const QSize &new_size)
{
qDebug("CWebCamViewer::DeviceChangedSize(%dx%d)", new_size.width(), new_size.height());

   RecalcTotalViewSize();
}


void CWebCamViewer::DeviceError(int err_no)
{
   // Show pop-up box
   QString errstr;
   
   errstr.sprintf("The device experienced an error %d (%s). The window will be closed.", err_no, strerror(-err_no));
   QMessageBox::critical(this, "device error", errstr, QMessageBox::Ok, QMessageBox::NoButton);
   close(TRUE); // End!
}


/**
  \brief Work around for adjustSize()

  Because sometimes adjustSize() doesn't have the desired (or a delayed)
  effect, we call it through a single-shot timer. Unfortunately adjustSize()
  isn't a slot, so this is just a wrapper function.
*/
void CWebCamViewer::CallAdjustSize()
{
   adjustSize();
}

/// public slots



void CWebCamViewer::showMaximized()
{
  qDebug("void CWebCamViewer::showMaximized()");
}
