Ivan Andrianov avatar Ivan Andrianov committed 6c048e8

Main window.

Comments (0)

Files changed (14)

 find_package(FLAC)
 
 include_directories(${FFTRANSCODER_SOURCE_DIR}/src)
-include_directories(${FFTRANSCODER_BINARY_DIR}/src)
 
 if(WIN32)
   set(FFTRANSCODER_BIN_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
 add_subdirectory(src/codecs)
 add_subdirectory(src/ui)
 
-install(FILES fftranscoder.py DESTINATION ${FFTRANSCODER_BIN_INSTALL_DIR})
+add_executable(fftranscoder src/main.cpp)
+
+target_link_libraries(fftranscoder fftranscoder_ui ${QT_QTGUI_LIBRARY})
+
+install(TARGETS fftranscoder DESTINATION ${FFTRANSCODER_BIN_INSTALL_DIR})
 
 if(WIN32)
   install(FILES license.rtf DESTINATION ${FFTRANSCODER_DATA_INSTALL_DIR})

pysrc/fftranscoder/info.py

-# Copyright (C) 2011 by Ivan Andrianov
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-_name = 'FFTranscoder'
-
-_version = '2.0.0'
-
-_author = 'Ivan Andrianov'
-
-_description = 'Multimedia transcoder based on FFmpeg and other libraries'
-
-_license = 'MIT'
-
-def getName():
-    return _name
-
-def getVersion():
-    return _version
-
-def getAuthor():
-    return _author
-
-def getDescription():
-    return _description
-
-def getLicense():
-    return _license

pysrc/fftranscoder/ui/mainwindow.py

-# Copyright (C) 2012 by Ivan Andrianov
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-from PyQt4 import QtCore, QtGui
-
-from fftranscoder import codecs
-from fftranscoder import info
-from fftranscoder.ui import TranscodeModel
-from fftranscoder.ui import EncoderDialog
-
-class MainWindow(QtGui.QMainWindow):
-    def __init__(self):
-        QtGui.QMainWindow.__init__(self)
-
-        self.setWindowTitle(info.getName())
-        self.resize(900, 600)
-
-        self._prepareToolBar()
-
-        self.setCentralWidget(QtGui.QWidget())
-        self._mainLayout = QtGui.QVBoxLayout(self.centralWidget())
-        self._mainLayout.setContentsMargins(0, 0, 0, 0)
-        self._mainLayout.setSpacing(0)
-
-        self._transcodeModel = TranscodeModel()
-
-        self._prepareView()
-
-        self._prepareTagBlock()
-        
-        self._prepareWidgetMapper()
-
-    def _addFiles(self):
-        if len(codecs.PluginLoader.instance().decoderPlugins()) == 0:
-            QtGui.QMessageBox.critical(self, self.tr('Error'),
-                                       self.tr('No decoder plugins found.'))
-            return
-
-        fileNames = QtGui.QFileDialog.getOpenFileNames(self, 'Add files')
-        
-        if len(fileNames) > 0:
-            self._transcodeModel.addFiles(fileNames)
-
-            self._clearAction.setEnabled(True)
-
-            if self._transcodeModel.unprocessedFileCount > 0:
-                self._transcodeAction.setEnabled(True)
-
-    def _clear(self):
-        self._transcodeModel.clear()
-
-        self._clearAction.setEnabled(False)
-        self._transcodeAction.setEnabled(False)
-
-    def _transcode(self):
-        if len(codecs.PluginLoader.instance().encoderPlugins()) == 0:
-            QtGui.QMessageBox.critical(self, self.tr('Error'),
-                                       self.tr('No encoder plugins found.'))
-            return
-
-        dialog = EncoderDialog(self)
-        ok = dialog.exec_()
-
-        if ok:
-            progressDialog = QtGui.QProgressDialog(self)
-            progressDialog.setWindowModality(QtCore.Qt.WindowModal)
-            progressDialog.setWindowTitle(self.tr('Transcoding files') + '...')
-            progressDialog.setCancelButton(None)
-            progressDialog.setMinimumDuration(1000)
-            progressDialog.setMaximum(self._transcodeModel.unprocessedFileCount)
-            progressDialog.setValue(0)
-
-            self.setEnabled(False)
-            self._transcodeModel.filesTranscoded. \
-                connect(self._onTranscodedFiles)
-
-            self._transcodeModel.filesTranscoded. \
-                connect(progressDialog.setValue)
-            self._transcodeModel.transcode(dialog.encoder, dialog.quality)
-            self._transcodeAction.setEnabled(False)
-
-    def _about(self):
-        title = self.tr('About') + ' ' + info.getName()
-        text = '<big><big><b>' + info.getName() + ' '
-        text += info.getVersion() + '</b></big></big><br><br><br>'
-        text += self.tr(info.getDescription()) + '<br><br>'
-        text += 'Copyright (C) 2011-2012 ' + self.tr('by') + ' '
-        text += info.getAuthor() + '<br><br>'
-        text += self.tr('It\'s distributed under terms of the')
-        text += ' ' + info.getLicense() + ' ' + self.tr('license.')
-        QtGui.QMessageBox.about(self, title, text)
-
-    def _onTranscodedFiles(self, transcodedFileCount):
-        if self._transcodeModel.unprocessedFileCount == 0:
-            self.setEnabled(True)
-
-    def _prepareToolBar(self):
-        self._mainToolBar = QtGui.QToolBar()
-        self._mainToolBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
-        self._mainToolBar.setFloatable(False)
-        self._mainToolBar.setMovable(False)
-        self._mainToolBar.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)
-
-        self._addFilesAction = self._mainToolBar.addAction(self.tr('Add files'))
-        self._addFilesAction.setIcon(QtGui.QIcon.fromTheme('list-add'))
-        self._addFilesAction.triggered.connect(self._addFiles)
-
-        self._clearAction = self._mainToolBar.addAction(self.tr('Clear'))
-        self._clearAction.setIcon(QtGui.QIcon.fromTheme('edit-clear-list'))
-        self._clearAction.triggered.connect(self._clear)
-        self._clearAction.setEnabled(False)
-
-        self._transcodeAction = self._mainToolBar.addAction(self.tr('Transcode'))
-        self._transcodeAction.setIcon(QtGui.QIcon.fromTheme('system-run'))
-        self._transcodeAction.triggered.connect(self._transcode)
-        self._transcodeAction.setEnabled(False)
-
-        self._aboutAction = self._mainToolBar.addAction(self.tr('About'))
-        self._aboutAction.setIcon(QtGui.QIcon.fromTheme('help-about'))
-        self._aboutAction.triggered.connect(self._about)
-
-        self.addToolBar(self._mainToolBar)
-
-    def _prepareView(self):
-        self._view = QtGui.QTreeView()
-        self._view.setModel(self._transcodeModel)
-
-        header = self._view.header()
-        header.setResizeMode(QtGui.QHeaderView.ResizeToContents)
-
-        header.hideSection(TranscodeModel.ARTIST_NAME)
-        header.hideSection(TranscodeModel.ALBUM_NAME)
-        header.hideSection(TranscodeModel.TRACK_NAME)
-        header.hideSection(TranscodeModel.YEAR)
-        header.hideSection(TranscodeModel.TRACK_NUMBER)
-
-        self._mainLayout.addWidget(self._view, 1)
-
-    def _prepareTagBlock(self):
-        self._tagBlockWidget = QtGui.QWidget()
-        self._tagBlockLayout = QtGui.QGridLayout(self._tagBlockWidget)
-        self._tagBlockLayout.setColumnStretch(1, 1)
-
-        self._artistNameLabel = QtGui.QLabel(self.tr('Artist name'))
-        self._tagBlockLayout.addWidget(self._artistNameLabel, 0, 0)
-
-        self._artistNameLineEdit = QtGui.QLineEdit()
-        self._tagBlockLayout.addWidget(self._artistNameLineEdit, 0, 1)
-
-        self._albumNameLabel = QtGui.QLabel(self.tr('Album name'))
-        self._tagBlockLayout.addWidget(self._albumNameLabel, 1, 0)
-
-        self._albumNameLineEdit = QtGui.QLineEdit()
-        self._tagBlockLayout.addWidget(self._albumNameLineEdit, 1, 1)
-
-        self._trackNameLabel = QtGui.QLabel(self.tr('Track name'))
-        self._tagBlockLayout.addWidget(self._trackNameLabel, 2, 0)
-
-        self._trackNameLineEdit = QtGui.QLineEdit()
-        self._tagBlockLayout.addWidget(self._trackNameLineEdit, 2, 1)
-
-        self._yearLabel = QtGui.QLabel(self.tr('Year'))
-        self._tagBlockLayout.addWidget(self._yearLabel, 0, 2)
-
-        self._yearSpinBox = QtGui.QSpinBox()
-        self._yearSpinBox.setMaximum(9999)
-        self._tagBlockLayout.addWidget(self._yearSpinBox, 0, 3)
-
-        self._trackNameLabel = QtGui.QLabel(self.tr('Track number'))
-        self._tagBlockLayout.addWidget(self._trackNameLabel, 1, 2)
-
-        self._trackNumberSpinBox = QtGui.QSpinBox()
-        self._tagBlockLayout.addWidget(self._trackNumberSpinBox, 1, 3)
-
-        self._submitTagBlock = QtGui.QPushButton(self.tr('Submit tags'))
-        self._tagBlockLayout.addWidget(self._submitTagBlock, 2, 2, 1, -1)
-
-        self._mainLayout.addWidget(self._tagBlockWidget)
-
-    def _prepareWidgetMapper(self):
-        self._tagBlockWidgetMapper = QtGui.QDataWidgetMapper()
-        self._tagBlockWidgetMapper.setSubmitPolicy(
-                QtGui.QDataWidgetMapper.ManualSubmit)
-
-        self._tagBlockWidgetMapper.setModel(self._transcodeModel)
-        self._tagBlockWidgetMapper.addMapping(self._artistNameLineEdit,
-                TranscodeModel.ARTIST_NAME)
-        self._tagBlockWidgetMapper.addMapping(self._albumNameLineEdit,
-                TranscodeModel.ALBUM_NAME)
-        self._tagBlockWidgetMapper.addMapping(self._trackNameLineEdit,
-                TranscodeModel.TRACK_NAME)
-        self._tagBlockWidgetMapper.addMapping(self._yearSpinBox,
-                TranscodeModel.YEAR)
-        self._tagBlockWidgetMapper.addMapping(self._trackNumberSpinBox,
-                TranscodeModel.TRACK_NUMBER)
-
-        self._submitTagBlock.clicked.connect(
-                self._tagBlockWidgetMapper.submit)
-        self._view.selectionModel().currentRowChanged.connect(
-                lambda i, x: self._tagBlockWidgetMapper.setCurrentModelIndex(i))

pysrc/fftranscoder/ui/transcodemodel.py

-# Copyright (C) 2012 by Ivan Andrianov
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-from PyQt4 import QtCore, QtGui
-
-from fftranscoder import codecs
-
-class TranscodeModel(QtCore.QAbstractItemModel):
-    filesTranscoded = QtCore.pyqtSignal(int)
-
-    def __init__(self, parent = None):
-        self._threadPool = QtCore.QThreadPool()
-
-    @property
-    def statuses(self):
-        return self._statuses
-
-    @property
-    def unprocessedFileCount(self):
-        return len(filter(lambda s: s == TranscodeModel.NO, self._statuses))
-
-    def addFiles(self, fileNames):
-        self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(),
-                             self.rowCount() + len(fileNames))
-        self._fileNames.extend(fileNames)
-        for fileName in fileNames:
-            decoder = codecs.DecoderFinder.findDecoder(fileName)
-            if decoder is not None:
-                self._codecNames.append(decoder.codecName())
-                self._bitRates.append(decoder.bitRate())
-                self._streamInfos.append(decoder.streamInfo())
-                self._tagBlocks.append(decoder.tagBlock())
-                self._statuses.append(TranscodeModel.NO)
-            else:
-                self._codecNames.append(None)
-                self._bitRates.append(None)
-                self._streamInfos.append(None)
-                self._tagBlocks.append(None)
-                self._statuses.append(TranscodeModel.ERROR)
-        self.endInsertRows()
-
-    def clear(self):
-        self.beginRemoveRows(QtCore.QModelIndex(), 0, self.rowCount() - 1)
-        self._initValues()
-        self.endRemoveRows()
-
-    def transcode(self, encoderPlugin, quality = None):
-        self._transcodedFileCount = 0
-        for i in range(self.rowCount()):
-            status = self._statuses[i]
-            if status != TranscodeModel.NO:
-                continue
-            inputFileName = self._fileNames[i]
-            tagBlock = self._tagBlocks[i]
-            outputFileName = TranscodeModel._processFileName(inputFileName,
-                     encoderPlugin.fileNameExtension())
-            if quality is None:
-                quality = 0
-            transcoder = codecs.Transcoder(inputFileName,
-                   outputFileName, encoderPlugin, tagBlock, quality)
-            transcoder.setAutoDelete(False)
-            self._transcoders[transcoder] = i
-            transcoder.finished.connect(self._onTranscoderFinish)
-            self._threadPool.start(transcoder)
-
-    def _onTranscoderFinish(self, ok):
-        transcoder = self.sender()
-        index = self._transcoders[transcoder]
-        del self._transcoders[transcoder]
-        if ok:
-            self._statuses[index] = TranscodeModel.OK
-        else:
-            self._statuses[index] = TranscodeModel.ERROR
-        self.dataChanged.emit(self.index(index, 0), self.index(index, 5))
-        self._transcodedFileCount += 1
-        self.filesTranscoded.emit(self._transcodedFileCount)
-
-    @staticmethod
-    def _processFileName(fileName, fileNameExtension):
-        sections = fileName.split('.')[0:-1]
-        sections.append(fileNameExtension)
-        return '.'.join(sections)
+/*
+ * Copyright (C) 2012 by Ivan Andrianov
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <QtGui/qapplication.h>
+
+#include <ui/mainwindow.h>
+
+int main(int argc, char *argv[])
+{
+    QApplication app(argc, argv);
+    FFTranscoder::Ui::MainWindow mainWindow;
+    mainWindow.show();
+    return app.exec();
+}

src/ui/CMakeLists.txt

 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 # THE SOFTWARE.
 
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
 set(FFTRANSCODER_UI_SRCS_CXX
   encoderdialog.cpp
   global.cpp
+  mainwindow.cpp
   metadata.cpp
   transcodemodel.cpp
 )
 
 set(FFTRANSCODER_UI_SRCS_UI
   encoderdialog.ui
+  mainwindow.ui
 )
 
 qt4_wrap_ui(FFTRANSCODER_UI_SRCS_CXX ${FFTRANSCODER_UI_SRCS_UI})

src/ui/encoderdialog.cpp

 
 #include <codecs/encoderplugin.h>
 
-#include <ui/ui_encoderdialog.h>
+#include <ui_encoderdialog.h>
 
 namespace FFTranscoder
 {
 
 class EncoderDialog::Private : public QObject
 {
+    Q_OBJECT
+
 public:
     Private(EncoderDialog * dialog,
             const QList<Codecs::EncoderPlugin *> encoders);
 {
 }
 
-Codecs::EncoderPlugin * EncoderDialog::getEncoder() const
+Codecs::EncoderPlugin * EncoderDialog::encoder() const
 {
     return d->encoders[d->form.codecComboBox->currentIndex()];
 }
 
-int EncoderDialog::getQuality() const
+uint EncoderDialog::quality() const
 {
     if (d->form.qualitySlider->isEnabled())
     {
 
 }
 }
+
+#include <encoderdialog.moc>

src/ui/encoderdialog.h

             QWidget * parent = 0);
     virtual ~EncoderDialog();
 
-    Codecs::EncoderPlugin * getEncoder() const;
-    int getQuality() const;
+    Codecs::EncoderPlugin * encoder() const;
+    uint quality() const;
 
 private:
     class Private;

src/ui/mainwindow.cpp

+/*
+ * Copyright (C) 2012 by Ivan Andrianov
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "mainwindow.h"
+
+#include <QtCore/qthreadpool.h>
+#include <QtGui/qmessagebox.h>
+#include <QtGui/qfiledialog.h>
+#include <QtGui/qdatawidgetmapper.h>
+#include <QtGui/qprogressdialog.h>
+
+#include <codecs/decoder.h>
+#include <codecs/decoderfinder.h>
+#include <codecs/encoderplugin.h>
+#include <codecs/pluginloader.h>
+#include <codecs/tagblock.h>
+#include <codecs/transcoder.h>
+
+#include "transcodemodel.h"
+#include "encoderdialog.h"
+#include <ui_mainwindow.h>
+
+namespace FFTranscoder
+{
+namespace Ui
+{
+
+class MainWindow::Private : public QObject
+{
+    Q_OBJECT
+
+public:
+    Private(MainWindow * q);
+    ~Private();
+
+    void setupView();
+    void setupTagBlockWidgets();
+    void setupMapper();
+    void addFiles(QStringList fileNames);
+    QString outputFileName(QString inputFileName, QString fileNameExtension);
+    void initProgressDialog();
+    void submitTranscoders(Codecs::EncoderPlugin * encoderPlugin, uint quality);
+    bool isEditable(int row, TranscodeModel::Column column);
+
+    MainWindow * q;
+
+    QScopedPointer<TranscodeModel> model;
+    QScopedPointer<QDataWidgetMapper> mapper;
+    ::Ui::Form form;
+    QScopedPointer<QThreadPool> transcodersPool;
+
+    QProgressDialog * progressDialog;
+    QHash<Codecs::Transcoder *, int> transcoders;
+
+public slots:
+    void addFiles();
+    void clear();
+    void transcode();
+    void about();
+    void onTranscoderFinish(bool ok);
+    void updateTagBlockWidgets(const QModelIndex & index);
+};
+
+MainWindow::MainWindow(QWidget * parent)
+    : QMainWindow(parent), d(new Private(this))
+{
+}
+
+MainWindow::~MainWindow()
+{
+}
+
+MainWindow::Private::Private(MainWindow * q)
+    : q(q), model(new TranscodeModel()), mapper(new QDataWidgetMapper()),
+      transcodersPool(new QThreadPool())
+{
+    form.setupUi(q);
+    connect(form.addFilesAction, SIGNAL(triggered()), this, SLOT(addFiles()));
+    connect(form.clearAction, SIGNAL(triggered()), this, SLOT(clear()));
+    connect(form.transcodeAction, SIGNAL(triggered()), this, SLOT(transcode()));
+    connect(form.aboutAction, SIGNAL(triggered()), this, SLOT(about()));
+    setupView();
+    setupTagBlockWidgets();
+    setupMapper();
+}
+
+MainWindow::Private::~Private()
+{
+}
+
+void MainWindow::Private::setupView()
+{
+    form.treeView->setModel(model.data());
+    QHeaderView * header = form.treeView->header();
+    header->setResizeMode(QHeaderView::ResizeToContents);
+    header->hideSection(TranscodeModel::ARTIST_NAME);
+    header->hideSection(TranscodeModel::ALBUM_NAME);
+    header->hideSection(TranscodeModel::TRACK_NAME);
+    header->hideSection(TranscodeModel::YEAR);
+    header->hideSection(TranscodeModel::TRACK_NUMBER);
+}
+
+void MainWindow::Private::setupTagBlockWidgets()
+{
+    form.artistNameEdit->setEnabled(false);
+    form.albumNameEdit->setEnabled(false);
+    form.trackNameEdit->setEnabled(false);
+    form.yearSpin->setEnabled(false);
+    form.trackNumberSpin->setEnabled(false);
+    form.submitButton->setEnabled(false);
+}
+
+void MainWindow::Private::setupMapper()
+{
+    mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
+    mapper->setModel(model.data());
+    mapper->addMapping(form.artistNameEdit, TranscodeModel::ARTIST_NAME);
+    mapper->addMapping(form.albumNameEdit, TranscodeModel::ALBUM_NAME);
+    mapper->addMapping(form.trackNameEdit, TranscodeModel::TRACK_NAME);
+    mapper->addMapping(form.yearSpin, TranscodeModel::YEAR);
+    mapper->addMapping(form.trackNumberSpin, TranscodeModel::TRACK_NUMBER);
+    connect(form.submitButton, SIGNAL(clicked()),
+            mapper.data(), SLOT(submit()));
+    connect(form.treeView->selectionModel(),
+            SIGNAL(currentRowChanged(QModelIndex, QModelIndex)),
+            mapper.data(), SLOT(setCurrentModelIndex(QModelIndex)));
+    connect(form.treeView->selectionModel(),
+            SIGNAL(currentRowChanged(QModelIndex, QModelIndex)),
+            this, SLOT(updateTagBlockWidgets(QModelIndex)));
+}
+
+void MainWindow::Private::addFiles(QStringList fileNames)
+{
+    if(!fileNames.isEmpty())
+    {
+        form.clearAction->setEnabled(true);
+        foreach(QString fileName, fileNames)
+        {
+            QScopedPointer<Codecs::Decoder> decoder(
+                    Codecs::DecoderFinder::findDecoder(fileName));
+            MetaData metaData(fileName);
+            if(decoder)
+            {
+                metaData.setCodecName(decoder->codecName());
+                metaData.setBitRate(decoder->bitRate());
+                metaData.streamInfo() = decoder->streamInfo();
+                metaData.tagBlock() = decoder->tagBlock();
+                form.transcodeAction->setEnabled(true);
+            }
+            model->addElement(metaData);
+        }
+    }
+}
+
+QString MainWindow::Private::outputFileName(
+        QString inputFileName, QString fileNameExtension)
+{
+    return inputFileName.section('.', 0, -2) + '.' + fileNameExtension;
+}
+
+void MainWindow::Private::initProgressDialog()
+{
+    progressDialog = new QProgressDialog(q);
+    progressDialog->setWindowModality(Qt::WindowModal);
+    progressDialog->setWindowTitle(trUtf8("Transcoding files") + "...");
+    progressDialog->setCancelButton(0);
+    progressDialog->setMinimumDuration(1000);
+    progressDialog->setMaximum(model->rowCount());
+}
+
+void MainWindow::Private::submitTranscoders(
+        Codecs::EncoderPlugin * encoderPlugin, uint quality)
+{
+    for(int row = 0; row < model->rowCount(); row++)
+    {
+        MetaData metaData = model->getElement(row);
+        QString inputFileName = metaData.fileName();
+        QString outputFileName =
+                this->outputFileName(inputFileName,
+                        encoderPlugin->fileNameExtension());
+        Codecs::TagBlock tagBlock =  metaData.tagBlock();
+        Codecs::Transcoder * transcoder =
+                new Codecs::Transcoder(inputFileName, outputFileName,
+                        encoderPlugin, tagBlock, quality);
+        transcoder->setAutoDelete(false);
+        transcoders[transcoder] = row;
+        connect(transcoder, SIGNAL(finished(bool)),
+                this, SLOT(onTranscoderFinish(bool)));
+        transcodersPool->start(transcoder);
+    }
+}
+
+bool MainWindow::Private::isEditable(int row, TranscodeModel::Column column)
+{
+    return model->flags(model->index(row, column)) & Qt::ItemIsEditable;
+}
+
+void MainWindow::Private::addFiles()
+{
+    if(Codecs::PluginLoader::instance()->decoderPlugins().isEmpty())
+    {
+        QMessageBox::critical(q, trUtf8("Error"),
+                trUtf8("No decoder plugins found."));
+        return;
+    }
+    QStringList fileNames = QFileDialog::getOpenFileNames(q, "Add files");
+    addFiles(fileNames);
+}
+
+void MainWindow::Private::clear()
+{
+    model->clear();
+    form.clearAction->setEnabled(false);
+    form.transcodeAction->setEnabled(false);
+}
+
+void MainWindow::Private::transcode()
+{
+    QList<Codecs::EncoderPlugin *> encoderPlugins =
+            Codecs::PluginLoader::instance()->encoderPlugins();
+    if(encoderPlugins.isEmpty())
+    {
+        QMessageBox::critical(q, trUtf8("Error"),
+                trUtf8("No encoder plugins found."));
+        return;
+    }
+    EncoderDialog dialog(encoderPlugins, q);
+    if(dialog.exec())
+    {
+        form.transcodeAction->setEnabled(false);
+        q->setEnabled(false);
+        initProgressDialog();
+        submitTranscoders(dialog.encoder(), dialog.quality());
+    }
+}
+
+void MainWindow::Private::about()
+{
+    QString text = QString("<big><big><b>FFTranscoder ") + version();
+    text += "</b></big></big><br><br><br>";
+    text += QObject::trUtf8(
+                "Multimedia transcoder based on FFmpeg and other libraries");
+    text += "<br><br>Copyright (C) 2011-2013 " + QObject::trUtf8("by");
+    text += " Ivan Andrianov<br><br>";
+    text += QObject::trUtf8("It\'s distributed under terms of the");
+    text += " MIT " + QObject::trUtf8("license") + ".";
+    QMessageBox::about(0, "About FFTranscoder", text);
+}
+
+void MainWindow::Private::onTranscoderFinish(bool ok)
+{
+    Codecs::Transcoder * transcoder = (Codecs::Transcoder *) sender();
+    int row = transcoders.take(transcoder);
+    transcoder->deleteLater();
+    model->setStatus(row, ok ? TranscodeModel::OK : TranscodeModel::ERROR);
+    progressDialog->setValue(progressDialog->value() + 1);
+    if(transcoders.isEmpty())
+    {
+        progressDialog->deleteLater();
+        q->setEnabled(true);
+    }
+}
+
+void MainWindow::Private::updateTagBlockWidgets(const QModelIndex & index)
+{
+    if(!index.isValid())
+    {
+        setupTagBlockWidgets();
+        return;
+    }
+    int row = index.row();
+    form.artistNameEdit->setEnabled(
+                isEditable(row, TranscodeModel::ARTIST_NAME));
+    form.albumNameEdit->setEnabled(
+                isEditable(row, TranscodeModel::ALBUM_NAME));
+    form.trackNameEdit->setEnabled(
+                isEditable(row, TranscodeModel::TRACK_NAME));
+    form.yearSpin->setEnabled(isEditable(row, TranscodeModel::YEAR));
+    form.trackNumberSpin->setEnabled(
+                isEditable(row, TranscodeModel::TRACK_NUMBER));
+    form.submitButton->setEnabled(
+            form.artistNameEdit->isEnabled() ||
+            form.albumNameEdit->isEnabled() ||
+            form.trackNameEdit->isEnabled() ||
+            form.yearSpin->isEnabled() ||
+            form.trackNumberSpin->isEnabled());
+
+}
+
+}
+}
+
+#include <mainwindow.moc>

src/ui/mainwindow.h

+/*
+ * Copyright (C) 2012 by Ivan Andrianov
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef FFTRANSCODER_UI_MAINWINDOW_H
+#define FFTRANSCODER_UI_MAINWINDOW_H
+
+#include <QtCore/qscopedpointer.h>
+#include <QtGui/qmainwindow.h>
+
+#include "global.h"
+
+namespace FFTranscoder
+{
+namespace Ui
+{
+
+class FFTRANSCODER_UI_API MainWindow : public QMainWindow
+{
+    Q_OBJECT
+    
+public:
+    MainWindow(QWidget * parent = 0);
+    virtual ~MainWindow();
+    
+private:
+    class Private;
+    QScopedPointer<Private> d;
+};
+
+}
+}
+
+#endif // FFTRANSCODER_UI_MAINWINDOW_H

src/ui/mainwindow.ui

+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QMainWindow" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1000</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>FFTranscoder</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <layout class="QVBoxLayout" name="verticalLayout">
+    <property name="spacing">
+     <number>0</number>
+    </property>
+    <property name="margin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QTreeView" name="treeView"/>
+    </item>
+    <item>
+     <layout class="QGridLayout" name="gridLayout">
+      <property name="margin">
+       <number>10</number>
+      </property>
+      <property name="spacing">
+       <number>10</number>
+      </property>
+      <item row="0" column="2">
+       <widget class="QLabel" name="yearLabel">
+        <property name="text">
+         <string>Year</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="2">
+       <widget class="QLabel" name="trackNumberLabel">
+        <property name="text">
+         <string>Track number</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QLineEdit" name="artistNameEdit"/>
+      </item>
+      <item row="1" column="1">
+       <widget class="QLineEdit" name="albumNameEdit"/>
+      </item>
+      <item row="2" column="1">
+       <widget class="QLineEdit" name="trackNameEdit"/>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="trackNameLabel">
+        <property name="text">
+         <string>Track name</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="0">
+       <widget class="QLabel" name="artistNameLabel">
+        <property name="text">
+         <string>Artist name</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="albumNameLabel">
+        <property name="text">
+         <string>Album name</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="3">
+       <widget class="QSpinBox" name="yearSpin">
+        <property name="maximum">
+         <number>9999</number>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="3">
+       <widget class="QSpinBox" name="trackNumberSpin"/>
+      </item>
+      <item row="2" column="2" colspan="2">
+       <widget class="QPushButton" name="submitButton">
+        <property name="text">
+         <string>Submit tags</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QToolBar" name="toolBar">
+   <property name="contextMenuPolicy">
+    <enum>Qt::PreventContextMenu</enum>
+   </property>
+   <property name="windowTitle">
+    <string>toolBar</string>
+   </property>
+   <property name="movable">
+    <bool>false</bool>
+   </property>
+   <property name="toolButtonStyle">
+    <enum>Qt::ToolButtonTextBesideIcon</enum>
+   </property>
+   <property name="floatable">
+    <bool>false</bool>
+   </property>
+   <attribute name="toolBarArea">
+    <enum>TopToolBarArea</enum>
+   </attribute>
+   <attribute name="toolBarBreak">
+    <bool>false</bool>
+   </attribute>
+   <addaction name="addFilesAction"/>
+   <addaction name="clearAction"/>
+   <addaction name="transcodeAction"/>
+   <addaction name="aboutAction"/>
+  </widget>
+  <action name="addFilesAction">
+   <property name="text">
+    <string>Add files</string>
+   </property>
+  </action>
+  <action name="clearAction">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Clear</string>
+   </property>
+  </action>
+  <action name="transcodeAction">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Transcode</string>
+   </property>
+  </action>
+  <action name="aboutAction">
+   <property name="text">
+    <string>About</string>
+   </property>
+  </action>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

src/ui/metadata.cpp

  */
 
 #include "metadata.h"
-#include <QSharedData>
+
+#include <QtCore/qshareddata.h>
 
 namespace FFTranscoder
 {
     Codecs::TagBlock tagBlock;
 };
 
-MetaData::MetaData()
+MetaData::MetaData(QString fileName)
     : d(new MetaData::Data())
 {
+    d->fileName = fileName;
     setBitRate(0);
 }
 
 
 bool MetaData::isEmpty() const
 {
-    return d->fileName.isEmpty() && d->codecName.isEmpty() && d->bitRate == 0
-            && d->streamInfo.isEmpty() && d->tagBlock.isEmpty();
+    return d->codecName.isEmpty() && d->bitRate == 0 &&
+            d->streamInfo.isEmpty() && d->tagBlock.isEmpty();
 }
 
 QString MetaData::fileName() const
     return d->fileName;
 }
 
-void MetaData::setFileName(QString fileName)
-{
-    d->fileName = fileName;
-}
-
 QString MetaData::codecName() const
 {
     return d->codecName;

src/ui/metadata.h

 class FFTRANSCODER_UI_API MetaData
 {
 public:
-    MetaData();
+    MetaData(QString fileName);
     MetaData(const MetaData & other);
     MetaData & operator=(const MetaData & other);
     ~MetaData();
     bool isEmpty() const;
 
     QString fileName() const;
-    void setFileName(QString fileName);
 
     QString codecName() const;
     void setCodecName(QString codecName);

src/ui/transcodemodel.cpp

     }
     if(column == TranscodeModel::CODEC_NAME)
     {
-        return metaData.codecName();
+        if(!metaData.isEmpty())
+        {
+            return metaData.codecName();
+        }
+        return trUtf8("Unsupported");
     }
-    if(column == TranscodeModel::BIT_RATE)
+    if(column == TranscodeModel::BIT_RATE && !metaData.isEmpty())
     {
         int bitRate = metaData.bitRate();
         if(bitRate > 0)
 {
     const Codecs::StreamInfo & streamInfo = metaDatas[index.row()].streamInfo();
     int column = index.column();
-    if(column == TranscodeModel::BYTES_PER_SAMPLE)
+    if(!streamInfo.isEmpty())
     {
-        return streamInfo.bytesPerSample();
-    }
-    if(column == TranscodeModel::SAMPLE_RATE)
-    {
-        return QString::number(streamInfo.sampleRate() / 1000.0)
-                + " " + trUtf8("kHz");
-    }
-    if(column == TranscodeModel::CHANNEL_COUNT)
-    {
-        return streamInfo.channelCount();
+        if(column == TranscodeModel::BYTES_PER_SAMPLE)
+        {
+            return streamInfo.bytesPerSample();
+        }
+        if(column == TranscodeModel::SAMPLE_RATE)
+        {
+            return QString::number(streamInfo.sampleRate() / 1000.0)
+                    + " " + trUtf8("kHz");
+        }
+        if(column == TranscodeModel::CHANNEL_COUNT)
+        {
+            return streamInfo.channelCount();
+        }
     }
     return QVariant();
 }
 {
     const Codecs::TagBlock & tagBlock = metaDatas[index.row()].tagBlock();
     int column = index.column();
-    if(column == TranscodeModel::ARTIST_NAME)
+    if(!tagBlock.isEmpty())
     {
-        return tagBlock.artistName();
-    }
-    if(column == TranscodeModel::ALBUM_NAME)
-    {
-        return tagBlock.albumName();
-    }
-    if(column == TranscodeModel::TRACK_NAME)
-    {
-        return tagBlock.trackName();
-    }
-    if(column == TranscodeModel::YEAR)
-    {
-        return tagBlock.year();
-    }
-    if(column == TranscodeModel::TRACK_NUMBER)
-    {
-        return tagBlock.trackNumber();
+        if(column == TranscodeModel::ARTIST_NAME)
+        {
+            return tagBlock.artistName();
+        }
+        if(column == TranscodeModel::ALBUM_NAME)
+        {
+            return tagBlock.albumName();
+        }
+        if(column == TranscodeModel::TRACK_NAME)
+        {
+            return tagBlock.trackName();
+        }
+        if(column == TranscodeModel::YEAR)
+        {
+            return tagBlock.year();
+        }
+        if(column == TranscodeModel::TRACK_NUMBER)
+        {
+            return tagBlock.trackNumber();
+        }
     }
     return QVariant();
 }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.