QTfrontend/ui/page/pagevideos.cpp
changeset 7687 c73fd8cfa7c0
parent 7680 46a91cbed8db
child 7794 ab7b94c03bc9
equal deleted inserted replaced
7613:ce6ead3327b2 7687:c73fd8cfa7c0
       
     1 /*
       
     2  * Hedgewars, a free turn based strategy game
       
     3  * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
       
     4  *
       
     5  * This program is free software; you can redistribute it and/or modify
       
     6  * it under the terms of the GNU General Public License as published by
       
     7  * the Free Software Foundation; version 2 of the License
       
     8  *
       
     9  * This program is distributed in the hope that it will be useful,
       
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    12  * GNU General Public License for more details.
       
    13  *
       
    14  * You should have received a copy of the GNU General Public License
       
    15  * along with this program; if not, write to the Free Software
       
    16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
       
    17  */
       
    18 
       
    19 #include <QGridLayout>
       
    20 #include <QPushButton>
       
    21 #include <QGroupBox>
       
    22 #include <QComboBox>
       
    23 #include <QCheckBox>
       
    24 #include <QLabel>
       
    25 #include <QLineEdit>
       
    26 #include <QSpinBox>
       
    27 #include <QTableWidget>
       
    28 #include <QDir>
       
    29 #include <QProgressBar>
       
    30 #include <QStringList>
       
    31 #include <QDesktopServices>
       
    32 #include <QUrl>
       
    33 #include <QList>
       
    34 #include <QMessageBox>
       
    35 #include <QHeaderView>
       
    36 #include <QKeyEvent>
       
    37 #include <QVBoxLayout>
       
    38 #include <QHBoxLayout>
       
    39 #include <QFileSystemWatcher>
       
    40 #include <QDateTime>
       
    41 #include <QRegExp>
       
    42 #include <QNetworkAccessManager>
       
    43 #include <QNetworkRequest>
       
    44 #include <QNetworkReply>
       
    45 #include <QXmlStreamReader>
       
    46 
       
    47 #include "hwconsts.h"
       
    48 #include "pagevideos.h"
       
    49 #include "igbox.h"
       
    50 #include "libav_iteraction.h"
       
    51 #include "gameuiconfig.h"
       
    52 #include "recorder.h"
       
    53 #include "ask_quit.h"
       
    54 #include "upload_video.h"
       
    55 
       
    56 static const QSize ThumbnailSize(350, 350*3/5);
       
    57 
       
    58 // columns in table with list of video files
       
    59 enum VideosColumns
       
    60 {
       
    61     vcName,
       
    62     vcSize,
       
    63     vcProgress, // either encoding or uploading
       
    64 
       
    65     vcNumColumns,
       
    66 };
       
    67 
       
    68 // this class is used for items in first column in file-table
       
    69 class VideoItem : public QTableWidgetItem
       
    70 {
       
    71     // note: QTableWidgetItem is not Q_OBJECT
       
    72 
       
    73     public:
       
    74         VideoItem(const QString& name);
       
    75         ~VideoItem();
       
    76 
       
    77         QString name;
       
    78         QString prefix; // original filename without extension
       
    79         QString desc;   // description (duration, resolution, etc...)
       
    80         QString uploadUrl; // http://youtu.be/???????
       
    81         HWRecorder    * pRecorder; // non NULL if file is being encoded
       
    82         QNetworkReply * pUploading; // non NULL if file is being uploaded
       
    83         bool seen; // used when updating directory
       
    84         float lastSizeUpdate;
       
    85         float progress;
       
    86 
       
    87         bool ready()
       
    88         { return !pRecorder; }
       
    89 
       
    90         QString path()
       
    91         { return cfgdir->absoluteFilePath("Videos/" + name);  }
       
    92 };
       
    93 
       
    94 VideoItem::VideoItem(const QString& name)
       
    95     : QTableWidgetItem(name, UserType)
       
    96 {
       
    97     this->name = name;
       
    98     pRecorder = NULL;
       
    99     pUploading = NULL;
       
   100     lastSizeUpdate = 0;
       
   101     progress = 0;
       
   102 }
       
   103 
       
   104 VideoItem::~VideoItem()
       
   105 {}
       
   106 
       
   107 QLayout * PageVideos::bodyLayoutDefinition()
       
   108 {
       
   109     QGridLayout * pPageLayout = new QGridLayout();
       
   110     pPageLayout->setColumnStretch(0, 1);
       
   111     pPageLayout->setColumnStretch(1, 2);
       
   112     pPageLayout->setRowStretch(0, 1);
       
   113     pPageLayout->setRowStretch(1, 1);
       
   114 
       
   115     // options
       
   116     {
       
   117         IconedGroupBox* pOptionsGroup = new IconedGroupBox(this);
       
   118         pOptionsGroup->setIcon(QIcon(":/res/Settings.png")); // FIXME
       
   119         pOptionsGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
       
   120         pOptionsGroup->setTitle(QGroupBox::tr("Video recording options"));
       
   121         QGridLayout * pOptLayout = new QGridLayout(pOptionsGroup);
       
   122 
       
   123         // label for format
       
   124         QLabel *labelFormat = new QLabel(pOptionsGroup);
       
   125         labelFormat->setText(QLabel::tr("Format"));
       
   126         pOptLayout->addWidget(labelFormat, 0, 0);
       
   127 
       
   128         // list of supported formats
       
   129         comboAVFormats = new QComboBox(pOptionsGroup);
       
   130         pOptLayout->addWidget(comboAVFormats, 0, 1, 1, 4);
       
   131         LibavIteraction::instance().fillFormats(comboAVFormats);
       
   132 
       
   133         // separator
       
   134         QFrame * hr = new QFrame(pOptionsGroup);
       
   135         hr->setFrameStyle(QFrame::HLine);
       
   136         hr->setLineWidth(3);
       
   137         hr->setFixedHeight(10);
       
   138         pOptLayout->addWidget(hr, 1, 0, 1, 5);
       
   139 
       
   140         // label for audio codec
       
   141         QLabel *labelACodec = new QLabel(pOptionsGroup);
       
   142         labelACodec->setText(QLabel::tr("Audio codec"));
       
   143         pOptLayout->addWidget(labelACodec, 2, 0);
       
   144 
       
   145         // list of supported audio codecs
       
   146         comboAudioCodecs = new QComboBox(pOptionsGroup);
       
   147         pOptLayout->addWidget(comboAudioCodecs, 2, 1, 1, 3);
       
   148 
       
   149         // checkbox 'record audio'
       
   150         checkRecordAudio = new QCheckBox(pOptionsGroup);
       
   151         checkRecordAudio->setText(QCheckBox::tr("Record audio"));
       
   152         pOptLayout->addWidget(checkRecordAudio, 2, 4);
       
   153 
       
   154         // separator
       
   155         hr = new QFrame(pOptionsGroup);
       
   156         hr->setFrameStyle(QFrame::HLine);
       
   157         hr->setLineWidth(3);
       
   158         hr->setFixedHeight(10);
       
   159         pOptLayout->addWidget(hr, 3, 0, 1, 5);
       
   160 
       
   161         // label for video codec
       
   162         QLabel *labelVCodec = new QLabel(pOptionsGroup);
       
   163         labelVCodec->setText(QLabel::tr("Video codec"));
       
   164         pOptLayout->addWidget(labelVCodec, 4, 0);
       
   165 
       
   166         // list of supported video codecs
       
   167         comboVideoCodecs = new QComboBox(pOptionsGroup);
       
   168         pOptLayout->addWidget(comboVideoCodecs, 4, 1, 1, 4);
       
   169 
       
   170         // label for resolution
       
   171         QLabel *labelRes = new QLabel(pOptionsGroup);
       
   172         labelRes->setText(QLabel::tr("Resolution"));
       
   173         pOptLayout->addWidget(labelRes, 5, 0);
       
   174 
       
   175         // width
       
   176         widthEdit = new QLineEdit(pOptionsGroup);
       
   177         widthEdit->setValidator(new QIntValidator(this));
       
   178         pOptLayout->addWidget(widthEdit, 5, 1);
       
   179 
       
   180         // x
       
   181         QLabel *labelX = new QLabel(pOptionsGroup);
       
   182         labelX->setText("X");
       
   183         pOptLayout->addWidget(labelX, 5, 2);
       
   184 
       
   185         // height
       
   186         heightEdit = new QLineEdit(pOptionsGroup);
       
   187         heightEdit->setValidator(new QIntValidator(pOptionsGroup));
       
   188         pOptLayout->addWidget(heightEdit, 5, 3);
       
   189 
       
   190         // checkbox 'use game resolution'
       
   191         checkUseGameRes = new QCheckBox(pOptionsGroup);
       
   192         checkUseGameRes->setText(QCheckBox::tr("Use game resolution"));
       
   193         pOptLayout->addWidget(checkUseGameRes, 5, 4);
       
   194 
       
   195         // label for framerate
       
   196         QLabel *labelFramerate = new QLabel(pOptionsGroup);
       
   197         labelFramerate->setText(QLabel::tr("Framerate"));
       
   198         pOptLayout->addWidget(labelFramerate, 6, 0);
       
   199 
       
   200         // framerate
       
   201         framerateBox = new QSpinBox(pOptionsGroup);
       
   202         framerateBox->setRange(1, 200);
       
   203         framerateBox->setSingleStep(1);
       
   204         pOptLayout->addWidget(framerateBox, 6, 1);
       
   205 
       
   206         // label for Bitrate
       
   207         QLabel *labelBitrate = new QLabel(pOptionsGroup);
       
   208         labelBitrate->setText(QLabel::tr("Bitrate (Kbps)"));
       
   209         pOptLayout->addWidget(labelBitrate, 6, 2);
       
   210 
       
   211         // bitrate
       
   212         bitrateBox = new QSpinBox(pOptionsGroup);
       
   213         bitrateBox->setRange(100, 5000);
       
   214         bitrateBox->setSingleStep(100);
       
   215         pOptLayout->addWidget(bitrateBox, 6, 3);
       
   216 
       
   217         // button 'set default options'
       
   218         btnDefaults = new QPushButton(pOptionsGroup);
       
   219         btnDefaults->setText(QPushButton::tr("Set default options"));
       
   220         pOptLayout->addWidget(btnDefaults, 7, 0, 1, 5);
       
   221 
       
   222         pPageLayout->addWidget(pOptionsGroup, 1, 0);
       
   223     }
       
   224 
       
   225     // list of videos
       
   226     {
       
   227         IconedGroupBox* pTableGroup = new IconedGroupBox(this);
       
   228         pTableGroup->setIcon(QIcon(":/res/graphicsicon.png")); // FIXME
       
   229         pTableGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
       
   230         pTableGroup->setTitle(QGroupBox::tr("Videos"));
       
   231 
       
   232         QStringList columns;
       
   233         columns << tr("Name");
       
   234         columns << tr("Size");
       
   235         columns << "";
       
   236 
       
   237         filesTable = new QTableWidget(pTableGroup);
       
   238         filesTable->setColumnCount(vcNumColumns);
       
   239         filesTable->setHorizontalHeaderLabels(columns);
       
   240         filesTable->setSelectionBehavior(QAbstractItemView::SelectRows);
       
   241         filesTable->setSelectionMode(QAbstractItemView::SingleSelection);
       
   242         filesTable->setEditTriggers(QAbstractItemView::SelectedClicked);
       
   243         filesTable->verticalHeader()->hide();
       
   244         filesTable->setMinimumWidth(400);
       
   245 
       
   246         QHeaderView * header = filesTable->horizontalHeader();
       
   247         header->setResizeMode(vcName, QHeaderView::ResizeToContents);
       
   248         header->setResizeMode(vcSize, QHeaderView::Fixed);
       
   249         header->resizeSection(vcSize, 100);
       
   250         header->setStretchLastSection(true);
       
   251 
       
   252         btnOpenDir = new QPushButton(QPushButton::tr("Open videos directory"), pTableGroup);
       
   253 
       
   254         QVBoxLayout *box = new QVBoxLayout(pTableGroup);
       
   255         box->addWidget(filesTable);
       
   256         box->addWidget(btnOpenDir);
       
   257 
       
   258         pPageLayout->addWidget(pTableGroup, 0, 1, 2, 1);
       
   259     }
       
   260 
       
   261     // description
       
   262     {
       
   263         IconedGroupBox* pDescGroup = new IconedGroupBox(this);
       
   264         pDescGroup->setIcon(QIcon(":/res/graphicsicon.png")); // FIXME
       
   265         pDescGroup->setTitle(QGroupBox::tr("Description"));
       
   266 
       
   267         QVBoxLayout* pDescLayout = new QVBoxLayout(pDescGroup);
       
   268         QHBoxLayout* pTopDescLayout = new QHBoxLayout(0);    // picture and text
       
   269         QHBoxLayout* pBottomDescLayout = new QHBoxLayout(0); // buttons
       
   270 
       
   271         // label with thumbnail picture
       
   272         labelThumbnail = new QLabel(pDescGroup);
       
   273         labelThumbnail->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
       
   274         labelThumbnail->setMaximumSize(ThumbnailSize);
       
   275         labelThumbnail->setStyleSheet(
       
   276                     "QFrame {"
       
   277                     "border: solid;"
       
   278                     "border-width: 3px;"
       
   279                     "border-color: #ffcc00;"
       
   280                     "border-radius: 4px;"
       
   281                     "}" );
       
   282         clearThumbnail();
       
   283         pTopDescLayout->addWidget(labelThumbnail, 2);
       
   284 
       
   285         // label with file description
       
   286         labelDesc = new QLabel(pDescGroup);
       
   287         labelDesc->setAlignment(Qt::AlignLeft | Qt::AlignTop);
       
   288         labelDesc->setTextInteractionFlags(Qt::TextSelectableByMouse |
       
   289                                            Qt::TextSelectableByKeyboard	|
       
   290                                            Qt::LinksAccessibleByMouse |
       
   291                                            Qt::LinksAccessibleByKeyboard);
       
   292         labelDesc->setTextFormat(Qt::RichText);
       
   293         labelDesc->setOpenExternalLinks(true);
       
   294         pTopDescLayout->addWidget(labelDesc, 1);
       
   295 
       
   296         // buttons: play and delete
       
   297         btnPlay = new QPushButton(QPushButton::tr("Play"), pDescGroup);
       
   298         btnPlay->setEnabled(false);
       
   299         pBottomDescLayout->addWidget(btnPlay);
       
   300         btnDelete = new QPushButton(QPushButton::tr("Delete"), pDescGroup);
       
   301         btnDelete->setEnabled(false);
       
   302         pBottomDescLayout->addWidget(btnDelete);
       
   303         btnToYouTube = new QPushButton(QPushButton::tr("Upload to YouTube"), pDescGroup);
       
   304         btnToYouTube->setEnabled(false);
       
   305         pBottomDescLayout->addWidget(btnToYouTube);
       
   306 
       
   307         pDescLayout->addStretch(1);
       
   308         pDescLayout->addLayout(pTopDescLayout, 0);
       
   309         pDescLayout->addStretch(1);
       
   310         pDescLayout->addLayout(pBottomDescLayout, 0);
       
   311 
       
   312         pPageLayout->addWidget(pDescGroup, 0, 0);
       
   313     }
       
   314 
       
   315     return pPageLayout;
       
   316 }
       
   317 
       
   318 QLayout * PageVideos::footerLayoutDefinition()
       
   319 {
       
   320     return NULL;
       
   321 }
       
   322 
       
   323 void PageVideos::connectSignals()
       
   324 {
       
   325     connect(checkUseGameRes, SIGNAL(stateChanged(int)), this, SLOT(changeUseGameRes(int)));
       
   326     connect(checkRecordAudio, SIGNAL(stateChanged(int)), this, SLOT(changeRecordAudio(int)));
       
   327     connect(comboAVFormats, SIGNAL(currentIndexChanged(int)), this, SLOT(changeAVFormat(int)));
       
   328     connect(btnDefaults, SIGNAL(clicked()), this, SLOT(setDefaultOptions()));
       
   329     connect(filesTable, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(cellDoubleClicked(int, int)));
       
   330     connect(filesTable, SIGNAL(cellChanged(int,int)), this, SLOT(cellChanged(int, int)));
       
   331     connect(filesTable, SIGNAL(currentCellChanged(int,int,int,int)), this, SLOT(currentCellChanged()));
       
   332     connect(btnPlay,   SIGNAL(clicked()), this, SLOT(playSelectedFile()));
       
   333     connect(btnDelete, SIGNAL(clicked()), this, SLOT(deleteSelectedFiles()));
       
   334     connect(btnToYouTube, SIGNAL(clicked()), this, SLOT(uploadToYouTube()));
       
   335     connect(btnOpenDir, SIGNAL(clicked()), this, SLOT(openVideosDirectory()));
       
   336 }
       
   337 
       
   338 PageVideos::PageVideos(QWidget* parent) : AbstractPage(parent),
       
   339     config(0), netManager(0)
       
   340 {
       
   341     nameChangedFromCode = false;
       
   342     numRecorders = 0;
       
   343     numUploads = 0;
       
   344     initPage();
       
   345 }
       
   346 
       
   347 void PageVideos::init(GameUIConfig * config)
       
   348 {
       
   349     this->config = config;
       
   350 
       
   351     QString path = cfgdir->absolutePath() + "/Videos";
       
   352     QFileSystemWatcher * pWatcher = new QFileSystemWatcher(this);
       
   353     pWatcher->addPath(path);
       
   354     connect(pWatcher, SIGNAL(directoryChanged(const QString &)), this, SLOT(updateFileList(const QString &)));
       
   355     updateFileList(path);
       
   356 
       
   357     startEncoding(); // this is for videos recorded from demos which were executed directly (without frontend)
       
   358 }
       
   359 
       
   360 // user changed file format, we need to update list of codecs
       
   361 void PageVideos::changeAVFormat(int index)
       
   362 {
       
   363     // remember selected codecs
       
   364     QString prevVCodec = videoCodec();
       
   365     QString prevACodec = audioCodec();
       
   366 
       
   367     // clear lists of codecs
       
   368     comboVideoCodecs->clear();
       
   369     comboAudioCodecs->clear();
       
   370 
       
   371     // get list of codecs for specified format
       
   372     LibavIteraction::instance().fillCodecs(comboAVFormats->itemData(index).toString(), comboVideoCodecs, comboAudioCodecs);
       
   373 
       
   374     // disable audio if there is no audio codec
       
   375     if (comboAudioCodecs->count() == 0)
       
   376     {
       
   377         checkRecordAudio->setChecked(false);
       
   378         checkRecordAudio->setEnabled(false);
       
   379     }
       
   380     else
       
   381         checkRecordAudio->setEnabled(true);
       
   382 
       
   383     // restore selected codecs if possible
       
   384     int iVCodec = comboVideoCodecs->findData(prevVCodec);
       
   385     if (iVCodec != -1)
       
   386         comboVideoCodecs->setCurrentIndex(iVCodec);
       
   387     int iACodec = comboAudioCodecs->findData(prevACodec);
       
   388     if (iACodec != -1)
       
   389         comboAudioCodecs->setCurrentIndex(iACodec);
       
   390 }
       
   391 
       
   392 // user switched checkbox 'use game resolution'
       
   393 void PageVideos::changeUseGameRes(int state)
       
   394 {
       
   395     if (state && config)
       
   396     {
       
   397         // set resolution to game resolution
       
   398         QRect resolution = config->vid_Resolution();
       
   399         widthEdit->setText(QString::number(resolution.width()));
       
   400         heightEdit->setText(QString::number(resolution.height()));
       
   401     }
       
   402     widthEdit->setEnabled(!state);
       
   403     heightEdit->setEnabled(!state);
       
   404 }
       
   405 
       
   406 // user switched checkbox 'record audio'
       
   407 void PageVideos::changeRecordAudio(int state)
       
   408 {
       
   409     comboAudioCodecs->setEnabled(!!state);
       
   410 }
       
   411 
       
   412 void PageVideos::setDefaultCodecs()
       
   413 {
       
   414     if (tryCodecs("mp4", "libx264", "libmp3lame"))
       
   415         return;
       
   416     if (tryCodecs("mp4", "libx264", "libfaac"))
       
   417         return;
       
   418     if (tryCodecs("mp4", "libx264", "libvo_aacenc"))
       
   419         return;
       
   420     if (tryCodecs("mp4", "libx264", "aac"))
       
   421         return;
       
   422     if (tryCodecs("mp4", "libx264", "mp2"))
       
   423         return;
       
   424     if (tryCodecs("avi", "libxvid", "libmp3lame"))
       
   425         return;
       
   426     if (tryCodecs("avi", "libxvid", "ac3_fixed"))
       
   427         return;
       
   428     if (tryCodecs("avi", "libxvid", "mp2"))
       
   429         return;
       
   430     if (tryCodecs("avi", "mpeg4", "libmp3lame"))
       
   431         return;
       
   432     if (tryCodecs("avi", "mpeg4", "ac3_fixed"))
       
   433         return;
       
   434     if (tryCodecs("avi", "mpeg4", "mp2"))
       
   435         return;
       
   436 
       
   437     // this shouldn't happen, just in case
       
   438     if (tryCodecs("ogg", "libtheora", "libvorbis"))
       
   439         return;
       
   440     tryCodecs("ogg", "libtheora", "flac");
       
   441 }
       
   442 
       
   443 void PageVideos::setDefaultOptions()
       
   444 {
       
   445     framerateBox->setValue(25);
       
   446     bitrateBox->setValue(400);
       
   447     checkRecordAudio->setChecked(true);
       
   448     checkUseGameRes->setChecked(true);
       
   449     setDefaultCodecs();
       
   450 }
       
   451 
       
   452 bool PageVideos::tryCodecs(const QString & format, const QString & vcodec, const QString & acodec)
       
   453 {
       
   454     // first we should change format
       
   455     int iFormat = comboAVFormats->findData(format);
       
   456     if (iFormat == -1)
       
   457         return false;
       
   458     comboAVFormats->setCurrentIndex(iFormat);
       
   459     // format was changed, so lists of codecs were automatically updated to codecs supported by this format
       
   460 
       
   461     // try to find video codec
       
   462     int iVCodec = comboVideoCodecs->findData(vcodec);
       
   463     if (iVCodec == -1)
       
   464         return false;
       
   465     comboVideoCodecs->setCurrentIndex(iVCodec);
       
   466 
       
   467     // try to find audio codec
       
   468     int iACodec = comboAudioCodecs->findData(acodec);
       
   469     if (iACodec == -1 && checkRecordAudio->isChecked())
       
   470         return false;
       
   471     if (iACodec != -1)
       
   472         comboAudioCodecs->setCurrentIndex(iACodec);
       
   473 
       
   474     return true;
       
   475 }
       
   476 
       
   477 // get file size as string
       
   478 static QString FileSizeStr(const QString & path)
       
   479 {
       
   480     quint64 size = QFileInfo(path).size();
       
   481 
       
   482     quint64 KiB = 1024;
       
   483     quint64 MiB = 1024*KiB;
       
   484     quint64 GiB = 1024*MiB;
       
   485     QString sizeStr;
       
   486     if (size >= GiB)
       
   487         return QString("%1 GiB").arg(QString::number(float(size)/GiB, 'f', 2));
       
   488     if (size >= MiB)
       
   489         return QString("%1 MiB").arg(QString::number(float(size)/MiB, 'f', 2));
       
   490      if (size >= KiB)
       
   491         return QString("%1 KiB").arg(QString::number(float(size)/KiB, 'f', 2));
       
   492     return PageVideos::tr("%1 bytes", "", size).arg(QString::number(size));
       
   493 }
       
   494 
       
   495 // set file size in file list in specified row
       
   496 void PageVideos::updateSize(int row)
       
   497 {
       
   498     VideoItem * item = nameItem(row);
       
   499     QString path = item->ready()? item->path() : cfgdir->absoluteFilePath("VideoTemp/" + item->pRecorder->name);
       
   500     filesTable->item(row, vcSize)->setText(FileSizeStr(path));
       
   501 }
       
   502 
       
   503 // There is a button 'Open videos dir', so it is possible that user will open
       
   504 // this dir and rename/delete some files there, so we should handle this.
       
   505 void PageVideos::updateFileList(const QString & path)
       
   506 {
       
   507     // mark all files as non seen
       
   508     int numRows = filesTable->rowCount();
       
   509     for (int i = 0; i < numRows; i++)
       
   510         nameItem(i)->seen = false;
       
   511 
       
   512     QStringList files = QDir(path).entryList(QDir::Files);
       
   513     foreach (const QString & name, files)
       
   514     {
       
   515         int row = -1;
       
   516         foreach (QTableWidgetItem * item, filesTable->findItems(name, Qt::MatchExactly))
       
   517         {
       
   518             if (item->type() != QTableWidgetItem::UserType || !((VideoItem*)item)->ready())
       
   519                 continue;
       
   520             row = item->row();
       
   521             break;
       
   522         }
       
   523         if (row == -1)
       
   524             row = appendRow(name);
       
   525         VideoItem * item = nameItem(row);
       
   526         item->seen = true;
       
   527         item->desc = "";
       
   528         updateSize(row);
       
   529     }
       
   530 
       
   531     // remove all non seen files
       
   532     for (int i = 0; i < filesTable->rowCount();)
       
   533     {
       
   534         VideoItem * item = nameItem(i);
       
   535         if (item->ready() && !item->seen)
       
   536             filesTable->removeRow(i);
       
   537         else
       
   538             i++;
       
   539     }
       
   540 }
       
   541 
       
   542 void PageVideos::addRecorder(HWRecorder* pRecorder)
       
   543 {
       
   544     int row = appendRow(pRecorder->name);
       
   545     VideoItem * item = nameItem(row);
       
   546     item->pRecorder = pRecorder;
       
   547     pRecorder->item = item;
       
   548 
       
   549     // add progress bar
       
   550     QProgressBar * progressBar = new QProgressBar(filesTable);
       
   551     progressBar->setMinimum(0);
       
   552     progressBar->setMaximum(10000);
       
   553     progressBar->setValue(0);
       
   554     connect(pRecorder, SIGNAL(onProgress(float)), this, SLOT(updateProgress(float)));
       
   555     connect(pRecorder, SIGNAL(encodingFinished(bool)), this, SLOT(encodingFinished(bool)));
       
   556     filesTable->setCellWidget(row, vcProgress, progressBar);
       
   557 
       
   558     numRecorders++;
       
   559 }
       
   560 
       
   561 void PageVideos::setProgress(int row, VideoItem* item, float value)
       
   562 {
       
   563     QProgressBar * progressBar = (QProgressBar*)filesTable->cellWidget(row, vcProgress);
       
   564     progressBar->setValue(value*10000);
       
   565     progressBar->setFormat(QString("%1%").arg(value*100, 0, 'f', 2));
       
   566     item->progress = value;
       
   567 }
       
   568 
       
   569 void PageVideos::updateProgress(float value)
       
   570 {
       
   571     HWRecorder * pRecorder = (HWRecorder*)sender();
       
   572     VideoItem * item = pRecorder->item;
       
   573     int row = filesTable->row(item);
       
   574 
       
   575     // update file size every percent
       
   576     if (value - item->lastSizeUpdate > 0.01)
       
   577     {
       
   578         updateSize(row);
       
   579         item->lastSizeUpdate = value;
       
   580     }
       
   581 
       
   582     setProgress(row, item, value);
       
   583 }
       
   584 
       
   585 void PageVideos::encodingFinished(bool success)
       
   586 {
       
   587     numRecorders--;
       
   588 
       
   589     HWRecorder * pRecorder = (HWRecorder*)sender();
       
   590     VideoItem * item = (VideoItem*)pRecorder->item;
       
   591     int row = filesTable->row(item);
       
   592 
       
   593     if (success)
       
   594     {
       
   595         // move file to destination
       
   596         success = cfgdir->rename("VideoTemp/" + pRecorder->name, "Videos/" + item->name);
       
   597         if (!success)
       
   598         {
       
   599             // unable to rename for some reason (maybe user entered incorrect name);
       
   600             // try to use temp name instead.
       
   601             success = cfgdir->rename("VideoTemp/" + pRecorder->name, "Videos/" + pRecorder->name);
       
   602             if (success)
       
   603                 setName(item, pRecorder->name);
       
   604         }
       
   605     }
       
   606 
       
   607     if (!success)
       
   608     {
       
   609         filesTable->removeRow(row);
       
   610         return;
       
   611     }
       
   612 
       
   613     filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar
       
   614     item->pRecorder = NULL;
       
   615     updateSize(row);
       
   616     updateDescription();
       
   617 }
       
   618 
       
   619 void PageVideos::cellDoubleClicked(int row, int column)
       
   620 {
       
   621     Q_UNUSED(column);
       
   622 
       
   623     play(row);
       
   624 }
       
   625 
       
   626 void PageVideos::cellChanged(int row, int column)
       
   627 {
       
   628     // user can only edit name
       
   629     if (column != vcName || nameChangedFromCode)
       
   630         return;
       
   631 
       
   632     // user has edited filename, so we should rename the file
       
   633     VideoItem * item = nameItem(row);
       
   634     QString oldName = item->name;
       
   635     QString newName = item->text();
       
   636     if (!newName.contains('.')) // user forgot an extension
       
   637     {
       
   638         // restore old extension
       
   639         int pt = oldName.lastIndexOf('.');
       
   640         if (pt != -1)
       
   641         {
       
   642             newName += oldName.right(oldName.length() - pt);
       
   643             setName(item, newName);
       
   644         }
       
   645     }
       
   646 #ifdef Q_WS_WIN
       
   647     // there is a bug in qt, QDir::rename() doesn't fail on such names but damages files
       
   648     if (newName.contains(QRegExp("[\"*:<>?\/|]")))
       
   649     {
       
   650         setName(item, oldName);
       
   651         return;
       
   652     }
       
   653 #endif
       
   654     if (item->ready() && !cfgdir->rename("Videos/" + oldName, "Videos/" + newName))
       
   655     {
       
   656         // unable to rename for some reason (maybe user entered incorrect name),
       
   657         // therefore restore old name in cell
       
   658         setName(item, oldName);
       
   659         return;
       
   660     }
       
   661     item->name = newName;
       
   662     updateDescription();
       
   663 }
       
   664 
       
   665 void PageVideos::setName(VideoItem * item, const QString & newName)
       
   666 {
       
   667     nameChangedFromCode = true;
       
   668     item->setText(newName);
       
   669     nameChangedFromCode = false;
       
   670     item->name = newName;
       
   671 }
       
   672 
       
   673 int PageVideos::appendRow(const QString & name)
       
   674 {
       
   675     int row = filesTable->rowCount();
       
   676     filesTable->setRowCount(row+1);
       
   677 
       
   678     // add 'name' item
       
   679     QTableWidgetItem * item = new VideoItem(name);
       
   680     item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
       
   681     nameChangedFromCode = true;
       
   682     filesTable->setItem(row, vcName, item);
       
   683     nameChangedFromCode = false;
       
   684 
       
   685     // add 'size' item
       
   686     item = new QTableWidgetItem();
       
   687     item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
       
   688     item->setTextAlignment(Qt::AlignRight);
       
   689     filesTable->setItem(row, vcSize, item);
       
   690 
       
   691     // add 'progress' item
       
   692     item = new QTableWidgetItem();
       
   693     item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
       
   694     filesTable->setItem(row, vcProgress, item);
       
   695 
       
   696     return row;
       
   697 }
       
   698 
       
   699 VideoItem* PageVideos::nameItem(int row)
       
   700 {
       
   701     return (VideoItem*)filesTable->item(row, vcName);
       
   702 }
       
   703 
       
   704 void PageVideos::clearThumbnail()
       
   705 {
       
   706     // add empty (transparent) image for proper sizing
       
   707     QPixmap pic(ThumbnailSize);
       
   708     pic.fill(QColor(0,0,0,0));
       
   709     labelThumbnail->setPixmap(pic);
       
   710 }
       
   711 
       
   712 void PageVideos::updateDescription()
       
   713 {
       
   714     VideoItem * item = nameItem(filesTable->currentRow());
       
   715     if (!item)
       
   716     {
       
   717         // nothing is selected => clear description and return
       
   718         labelDesc->clear();
       
   719         clearThumbnail();
       
   720         btnPlay->setEnabled(false);
       
   721         btnDelete->setEnabled(false);
       
   722         btnToYouTube->setEnabled(false);
       
   723         return;
       
   724     }
       
   725 
       
   726     btnPlay->setEnabled(item->ready());
       
   727     btnToYouTube->setEnabled(item->ready());
       
   728     btnDelete->setEnabled(true);
       
   729     btnDelete->setText(item->ready()? QPushButton::tr("Delete") :  QPushButton::tr("Cancel"));
       
   730     btnToYouTube->setText(item->pUploading? QPushButton::tr("Cancel uploading") :  QPushButton::tr("Upload to YouTube"));
       
   731 
       
   732     // construct string with desctiption of this file to display it
       
   733     QString desc = item->name + "\n\n";
       
   734 
       
   735     if (!item->ready())
       
   736         desc += tr("(in progress...)");
       
   737     else
       
   738     {
       
   739         QString path = item->path();
       
   740         desc += tr("Date: ") + QFileInfo(path).created().toString(Qt::DefaultLocaleLongDate) + '\n';
       
   741         desc += tr("Size: ") + FileSizeStr(path) + '\n';
       
   742         if (item->desc.isEmpty())
       
   743         {
       
   744             // Extract description from file;
       
   745             // It will contain duration, resolution, etc and also comment added by hwengine.
       
   746             item->desc = LibavIteraction::instance().getFileInfo(path);
       
   747 
       
   748             // extract prefix (original name) from description (it is enclosed in prefix[???]prefix)
       
   749             int prefixBegin = item->desc.indexOf("prefix[");
       
   750             int prefixEnd   = item->desc.indexOf("]prefix");
       
   751             if (prefixBegin != -1 && prefixEnd != -1)
       
   752             {
       
   753                 item->prefix = item->desc.mid(prefixBegin + 7, prefixEnd - (prefixBegin + 7));
       
   754                 item->desc.remove(prefixBegin, prefixEnd + 7 - prefixBegin);
       
   755             }
       
   756         }
       
   757         desc += item->desc + '\n';
       
   758     }
       
   759 
       
   760     if (item->prefix.isEmpty())
       
   761     {
       
   762         // try to extract prefix from file name instead
       
   763         if (item->ready())
       
   764             item->prefix = item->name;
       
   765         else
       
   766             item->prefix = item->pRecorder->name;
       
   767 
       
   768         // remove extension
       
   769         int pt = item->prefix.lastIndexOf('.');
       
   770         if (pt != -1)
       
   771             item->prefix.truncate(pt);
       
   772     }
       
   773 
       
   774     if (item->ready() && item->uploadUrl.isEmpty())
       
   775     {
       
   776         // try to load url from file
       
   777         QFile * file = new QFile(cfgdir->absoluteFilePath("VideoTemp/" + item->prefix + "-url.txt"), this);
       
   778         if (!file->open(QIODevice::ReadOnly))
       
   779             item->uploadUrl = "no";
       
   780         else
       
   781         {
       
   782             QByteArray data = file->readAll();
       
   783             file->close();
       
   784             item->uploadUrl = QString::fromUtf8(data.data());
       
   785         }
       
   786     }
       
   787     if (item->uploadUrl != "no")
       
   788         desc += QString("<a href=\"%1\" style=\"color: white;\">%1</a>").arg(item->uploadUrl);
       
   789     desc.replace("\n", "<br/>");
       
   790 
       
   791     labelDesc->setText(desc);
       
   792 
       
   793     if (!item->prefix.isEmpty())
       
   794     {
       
   795         QString thumbName = cfgdir->absoluteFilePath("VideoTemp/" + item->prefix);
       
   796         QPixmap pic;
       
   797         if (pic.load(thumbName + ".png") || pic.load(thumbName + ".bmp"))
       
   798         {
       
   799             if (pic.height()*ThumbnailSize.width() > pic.width()*ThumbnailSize.height())
       
   800                 pic = pic.scaledToWidth(ThumbnailSize.width());
       
   801             else
       
   802                 pic = pic.scaledToHeight(ThumbnailSize.height());
       
   803             labelThumbnail->setPixmap(pic);
       
   804         }
       
   805         else
       
   806             clearThumbnail();
       
   807     }
       
   808 }
       
   809 
       
   810 // user selected another cell, so we should change description
       
   811 void PageVideos::currentCellChanged()
       
   812 {
       
   813     updateDescription();
       
   814 }
       
   815 
       
   816 // open video file in external media player
       
   817 void PageVideos::play(int row)
       
   818 {
       
   819     VideoItem * item = nameItem(row);
       
   820     if (item && item->ready())
       
   821         QDesktopServices::openUrl(QUrl("file:///" + QDir::toNativeSeparators(item->path())));
       
   822 }
       
   823 
       
   824 void PageVideos::playSelectedFile()
       
   825 {
       
   826     int index = filesTable->currentRow();
       
   827     if (index != -1)
       
   828         play(index);
       
   829 }
       
   830 
       
   831 void PageVideos::deleteSelectedFiles()
       
   832 {
       
   833     int index = filesTable->currentRow();
       
   834     if (index == -1)
       
   835         return;
       
   836 
       
   837     VideoItem * item = nameItem(index);
       
   838     if (!item)
       
   839         return;
       
   840 
       
   841     // ask user if (s)he is serious
       
   842     if (QMessageBox::question(this,
       
   843                               tr("Are you sure?"),
       
   844                               tr("Do you really want do remove %1?").arg(item->name),
       
   845                               QMessageBox::Yes | QMessageBox::No)
       
   846             != QMessageBox::Yes)
       
   847         return;
       
   848 
       
   849     // remove
       
   850     if (!item->ready())
       
   851         item->pRecorder->deleteLater();
       
   852     else
       
   853         cfgdir->remove("Videos/" + item->name);
       
   854 
       
   855 // this code is for removing several files when multiple selection is enabled
       
   856 #if 0
       
   857     QList<QTableWidgetItem*> items = filesTable->selectedItems();
       
   858     int num = items.size() / vcNumColumns;
       
   859     if (num == 0)
       
   860         return;
       
   861 
       
   862     // ask user if (s)he is serious
       
   863     if (QMessageBox::question(this,
       
   864                               tr("Are you sure?"),
       
   865                               tr("Do you really want do remove %1 file(s)?", "", num).arg(num),
       
   866                               QMessageBox::Yes | QMessageBox::No)
       
   867             != QMessageBox::Yes)
       
   868         return;
       
   869 
       
   870     // remove
       
   871     foreach (QTableWidgetItem * witem, items)
       
   872     {
       
   873         if (witem->type() != QTableWidgetItem::UserType)
       
   874             continue;
       
   875         VideoItem * item = (VideoItem*)witem;
       
   876         if (!item->ready())
       
   877             item->pRecorder->deleteLater();
       
   878         else
       
   879             cfgdir->remove("Videos/" + item->name);
       
   880     }
       
   881 #endif
       
   882 }
       
   883 
       
   884 void PageVideos::keyPressEvent(QKeyEvent * pEvent)
       
   885 {
       
   886     if (filesTable->hasFocus())
       
   887     {
       
   888         if (pEvent->key() == Qt::Key_Delete)
       
   889         {
       
   890             deleteSelectedFiles();
       
   891             return;
       
   892         }
       
   893         if (pEvent->key() == Qt::Key_Enter) // doesn't work
       
   894         {
       
   895             playSelectedFile();
       
   896             return;
       
   897         }
       
   898     }
       
   899     AbstractPage::keyPressEvent(pEvent);
       
   900 }
       
   901 
       
   902 void PageVideos::openVideosDirectory()
       
   903 {
       
   904     QString path = QDir::toNativeSeparators(cfgdir->absolutePath() + "/Videos");
       
   905     QDesktopServices::openUrl(QUrl("file:///" + path));
       
   906 }
       
   907 
       
   908 // clear VideoTemp directory (except for thumbnails and upload links)
       
   909 void PageVideos::clearTemp()
       
   910 {
       
   911     QDir temp(cfgdir->absolutePath() + "/VideoTemp");
       
   912     QStringList files = temp.entryList(QDir::Files);
       
   913     foreach (const QString& file, files)
       
   914     {
       
   915         if (!file.endsWith(".bmp") && !file.endsWith(".png") && !file.endsWith("-url.txt"))
       
   916             temp.remove(file);
       
   917     }
       
   918 }
       
   919 
       
   920 bool PageVideos::tryQuit(HWForm * form)
       
   921 {
       
   922     bool quit = true;
       
   923     if (numRecorders != 0 || numUploads != 0)
       
   924     {
       
   925         // ask user what to do - abort or wait
       
   926         HWAskQuitDialog * askd = new HWAskQuitDialog(this, form);
       
   927         askd->deleteLater();
       
   928         quit = askd->exec();
       
   929     }
       
   930     if (quit)
       
   931         clearTemp();
       
   932     return quit;
       
   933 }
       
   934 
       
   935 // returns multi-line string with list of videos in progress
       
   936 /* it will look like this:
       
   937 foo.avi (15.21% - encoding)
       
   938 bar.avi (18.21% - uploading)
       
   939 */
       
   940 QString PageVideos::getVideosInProgress()
       
   941 {
       
   942     QString list = "";
       
   943     int count = filesTable->rowCount();
       
   944     for (int i = 0; i < count; i++)
       
   945     {
       
   946         VideoItem * item = nameItem(i);
       
   947         QString process;
       
   948         if (!item->ready())
       
   949             process = tr("encoding");
       
   950         else if (item->pUploading)
       
   951             process = tr("uploading");
       
   952         else
       
   953             continue;
       
   954         float progress = 100*item->progress;
       
   955         if (progress > 99.99)
       
   956             progress = 99.99; // displaying 100% may be confusing
       
   957         list += item->name + " (" + QString::number(progress, 'f', 2) + "% - " + process + ")\n";
       
   958     }
       
   959     return list;
       
   960 }
       
   961 
       
   962 void PageVideos::startEncoding(const QByteArray & record)
       
   963 {
       
   964     QDir videoTempDir(cfgdir->absolutePath() + "/VideoTemp/");
       
   965     QStringList files = videoTempDir.entryList(QStringList("*.txtout"), QDir::Files);
       
   966     foreach (const QString & str, files)
       
   967     {
       
   968         QString prefix = str;
       
   969         prefix.chop(7); // remove ".txtout"
       
   970         videoTempDir.rename(prefix + ".txtout", prefix + ".txtin"); // rename this file to not open it twice
       
   971 
       
   972         HWRecorder* pRecorder = new HWRecorder(config, prefix);
       
   973 
       
   974         if (!record.isEmpty())
       
   975             pRecorder->EncodeVideo(record);
       
   976         else
       
   977         {
       
   978             // this is for videos recorded from demos which were executed directly (without frontend)
       
   979             QFile demofile(videoTempDir.absoluteFilePath(prefix + ".hwd"));
       
   980             if (!demofile.open(QIODevice::ReadOnly))
       
   981                 continue;
       
   982             QByteArray demo = demofile.readAll();
       
   983             if (demo.isEmpty())
       
   984                 continue;
       
   985             pRecorder->EncodeVideo(demo);
       
   986         }
       
   987         addRecorder(pRecorder);
       
   988     }
       
   989 }
       
   990 
       
   991 VideoItem * PageVideos::itemFromReply(QNetworkReply* reply, int & row)
       
   992 {
       
   993     VideoItem * item = NULL;
       
   994     int count = filesTable->rowCount();
       
   995     // find corresponding item (maybe there is a better way to implement this?)
       
   996     for (int i = 0; i < count; i++)
       
   997     {
       
   998         item = nameItem(i);
       
   999         if (item->pUploading == reply)
       
  1000         {
       
  1001             row = i;
       
  1002             break;
       
  1003         }
       
  1004     }
       
  1005     return item;
       
  1006 }
       
  1007 
       
  1008 void PageVideos::uploadProgress(qint64 bytesSent, qint64 bytesTotal)
       
  1009 {
       
  1010     QNetworkReply* reply = (QNetworkReply*)sender();
       
  1011     int row;
       
  1012     VideoItem * item = itemFromReply(reply, row);
       
  1013     setProgress(row, item, bytesSent*1.0/bytesTotal);
       
  1014 }
       
  1015 
       
  1016 void PageVideos::uploadFinished()
       
  1017 {
       
  1018     QNetworkReply* reply = (QNetworkReply*)sender();
       
  1019     reply->deleteLater();
       
  1020 
       
  1021     int row;
       
  1022     VideoItem * item = itemFromReply(reply, row);
       
  1023     if (!item)
       
  1024         return;
       
  1025 
       
  1026     item->pUploading = NULL;
       
  1027 
       
  1028     // extract video id from reply
       
  1029     QString videoid;
       
  1030     QXmlStreamReader xml(reply);
       
  1031     while (!xml.atEnd())
       
  1032     {
       
  1033         xml.readNext();
       
  1034         if (xml.qualifiedName() == "yt:videoid")
       
  1035         {
       
  1036             videoid = xml.readElementText();
       
  1037             break;
       
  1038         }
       
  1039     }
       
  1040 
       
  1041     if (!videoid.isEmpty())
       
  1042     {
       
  1043         item->uploadUrl = "http://youtu.be/" + videoid;
       
  1044         updateDescription();
       
  1045 
       
  1046         // save url in file
       
  1047         QFile * file = new QFile(cfgdir->absoluteFilePath("VideoTemp/" + item->prefix + "-url.txt"), this);
       
  1048         if (file->open(QIODevice::WriteOnly))
       
  1049         {
       
  1050             file->write(item->uploadUrl.toUtf8());
       
  1051             file->close();
       
  1052         }
       
  1053     }
       
  1054 
       
  1055     filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar
       
  1056     numUploads--;
       
  1057 }
       
  1058 
       
  1059 // this will protect saved youtube password from those who cannot read source code
       
  1060 static QString protectPass(QString str)
       
  1061 {
       
  1062     QByteArray array = str.toUtf8();
       
  1063     for (int i = 0; i < array.size(); i++)
       
  1064         array[i] = array[i] ^ 0xC4 ^ i;
       
  1065     array = array.toBase64();
       
  1066     return QString::fromAscii(array.data());
       
  1067 }
       
  1068 
       
  1069 static QString unprotectPass(QString str)
       
  1070 {
       
  1071     QByteArray array = QByteArray::fromBase64(str.toAscii());
       
  1072     for (int i = 0; i < array.size(); i++)
       
  1073         array[i] = array[i] ^ 0xC4 ^ i;
       
  1074     return QString::fromUtf8(array);
       
  1075 }
       
  1076 
       
  1077 void PageVideos::uploadToYouTube()
       
  1078 {
       
  1079     int row = filesTable->currentRow();
       
  1080     VideoItem * item = nameItem(row);
       
  1081 
       
  1082     if (item->pUploading)
       
  1083     {
       
  1084         if (QMessageBox::question(this,
       
  1085                                   tr("Are you sure?"),
       
  1086                                   tr("Do you really want do cancel uploading %1?").arg(item->name),
       
  1087                                   QMessageBox::Yes | QMessageBox::No)
       
  1088                 != QMessageBox::Yes)
       
  1089             return;
       
  1090         item->pUploading->deleteLater();
       
  1091         filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar
       
  1092         numUploads--;
       
  1093         return;
       
  1094     }
       
  1095 
       
  1096     if (!netManager)
       
  1097         netManager = new QNetworkAccessManager(this);
       
  1098 
       
  1099     HWUploadVideoDialog* dlg = new HWUploadVideoDialog(this, item->name, netManager);
       
  1100     dlg->deleteLater();
       
  1101     if (config->value("youtube/save").toBool())
       
  1102     {
       
  1103         dlg->cbSave->setChecked(true);
       
  1104         dlg->leAccount->setText(config->value("youtube/name").toString());
       
  1105         dlg->lePassword->setText(unprotectPass(config->value("youtube/pswd").toString()));
       
  1106     }
       
  1107 
       
  1108     bool result = dlg->exec();
       
  1109 
       
  1110     if (dlg->cbSave->isChecked())
       
  1111     {
       
  1112         config->setValue("youtube/save", true);
       
  1113         config->setValue("youtube/name", dlg->leAccount->text());
       
  1114         config->setValue("youtube/pswd", protectPass(dlg->lePassword->text()));
       
  1115     }
       
  1116     else
       
  1117     {
       
  1118         config->setValue("youtube/save", false);
       
  1119         config->setValue("youtube/name", "");
       
  1120         config->setValue("youtube/pswd", "");
       
  1121     }
       
  1122 
       
  1123     if (!result)
       
  1124         return;
       
  1125 
       
  1126     QNetworkRequest request(QUrl(dlg->location));
       
  1127     request.setRawHeader("Content-Type", "application/octet-stream");
       
  1128 
       
  1129     QFile * file = new QFile(item->path(), this);
       
  1130     if (!file->open(QIODevice::ReadOnly))
       
  1131         return;
       
  1132 
       
  1133     // add progress bar
       
  1134     QProgressBar * progressBar = new QProgressBar(filesTable);
       
  1135     progressBar->setMinimum(0);
       
  1136     progressBar->setMaximum(10000);
       
  1137     progressBar->setValue(0);
       
  1138     // make it different from progress-bar used during encoding (use blue color)
       
  1139     progressBar->setStyleSheet("* {color: #00ccff; selection-background-color: #00ccff;}" );
       
  1140     filesTable->setCellWidget(row, vcProgress, progressBar);
       
  1141 
       
  1142     QNetworkReply* reply = netManager->put(request, file);
       
  1143     file->setParent(reply); // automatically close file when needed
       
  1144     item->pUploading = reply;
       
  1145     connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64)));
       
  1146     connect(reply, SIGNAL(finished()), this, SLOT(uploadFinished()));
       
  1147     numUploads++;
       
  1148 
       
  1149     updateDescription();
       
  1150 }