OSDN Git Service

Automatically use "1:1" mapping between the number of available CPU cores and the...
[lamexp/LameXP.git] / src / Dialog_MainWindow.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2017 LoRd_MuldeR <MuldeR2@GMX.de>
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; either version 2 of the License, or
8 // (at your option) any later version, but always including the *additional*
9 // restrictions defined in the "License.txt" file.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 //
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
22
23 #include "Dialog_MainWindow.h"
24
25 //UIC includes
26 #include "UIC_MainWindow.h"
27
28 //LameXP includes
29 #include "Global.h"
30 #include "Dialog_WorkingBanner.h"
31 #include "Dialog_MetaInfo.h"
32 #include "Dialog_About.h"
33 #include "Dialog_Update.h"
34 #include "Dialog_DropBox.h"
35 #include "Dialog_CueImport.h"
36 #include "Dialog_LogView.h"
37 #include "Thread_FileAnalyzer.h"
38 #include "Thread_MessageHandler.h"
39 #include "Model_MetaInfo.h"
40 #include "Model_Settings.h"
41 #include "Model_FileList.h"
42 #include "Model_FileSystem.h"
43 #include "Model_FileExts.h"
44 #include "Registry_Encoder.h"
45 #include "Registry_Decoder.h"
46 #include "Encoder_Abstract.h"
47 #include "ShellIntegration.h"
48 #include "CustomEventFilter.h"
49
50 //Mutils includes
51 #include <MUtils/Global.h>
52 #include <MUtils/OSSupport.h>
53 #include <MUtils/GUI.h>
54 #include <MUtils/Exception.h>
55 #include <MUtils/Sound.h>
56 #include <MUtils/Translation.h>
57 #include <MUtils/Version.h>
58
59 //Qt includes
60 #include <QMessageBox>
61 #include <QTimer>
62 #include <QDesktopWidget>
63 #include <QDate>
64 #include <QFileDialog>
65 #include <QInputDialog>
66 #include <QFileSystemModel>
67 #include <QDesktopServices>
68 #include <QUrl>
69 #include <QPlastiqueStyle>
70 #include <QCleanlooksStyle>
71 #include <QWindowsVistaStyle>
72 #include <QWindowsStyle>
73 #include <QSysInfo>
74 #include <QDragEnterEvent>
75 #include <QMimeData>
76 #include <QProcess>
77 #include <QUuid>
78 #include <QProcessEnvironment>
79 #include <QCryptographicHash>
80 #include <QTranslator>
81 #include <QResource>
82 #include <QScrollBar>
83
84 ////////////////////////////////////////////////////////////
85 // Constants
86 ////////////////////////////////////////////////////////////
87
88 static const unsigned int IDM_ABOUTBOX = 0xEFF0;
89 static const char *g_hydrogen_audio_url = "http://wiki.hydrogenaud.io/index.php?title=Main_Page";
90 static const char *g_documents_base_url = "http://lamexp.sourceforge.net/doc";
91
92 ////////////////////////////////////////////////////////////
93 // Helper Macros
94 ////////////////////////////////////////////////////////////
95
96 #define BANNER_VISIBLE ((!m_banner.isNull()) && m_banner->isVisible())
97
98 #define INIT_BANNER() do \
99 { \
100         if(m_banner.isNull()) \
101         { \
102                 m_banner.reset(new WorkingBanner(this)); \
103         } \
104 } \
105 while(0)
106
107 #define ABORT_IF_BUSY do \
108 { \
109         if(BANNER_VISIBLE || m_delayedFileTimer->isActive() || (QApplication::activeModalWidget() != NULL)) \
110         { \
111                 MUtils::Sound::beep(MUtils::Sound::BEEP_WRN); \
112                 return; \
113         } \
114 } \
115 while(0)
116
117 #define PLAY_SOUND_OPTIONAL(NAME, ASYNC) do \
118 { \
119         if(m_settings->soundsEnabled()) MUtils::Sound::play_sound((NAME), (ASYNC)); \
120 } \
121 while(0)
122
123 #define SHOW_CORNER_WIDGET(FLAG) do \
124 { \
125         if(QWidget *cornerWidget = ui->menubar->cornerWidget()) \
126         { \
127                 cornerWidget->setVisible((FLAG)); \
128         } \
129 } \
130 while(0)
131
132 #define LINK(URL) \
133         (QString("<a href=\"%1\">%2</a>").arg(URL).arg(QString(URL).replace("-", "&minus;")))
134
135 #define LINK_EX(URL, NAME) \
136         (QString("<a href=\"%1\">%2</a>").arg(URL).arg(QString(NAME).replace("-", "&minus;")))
137
138 #define FSLINK(PATH) \
139         (QString("<a href=\"file:///%1\">%2</a>").arg(PATH).arg(QString(PATH).replace("-", "&minus;")))
140
141 #define CENTER_CURRENT_OUTPUT_FOLDER_DELAYED() \
142         QTimer::singleShot(125, this, SLOT(centerOutputFolderModel()))
143
144 ////////////////////////////////////////////////////////////
145 // Static Functions
146 ////////////////////////////////////////////////////////////
147
148 static inline void SET_TEXT_COLOR(QWidget *const widget, const QColor &color)
149 {
150         QPalette _palette = widget->palette();
151         _palette.setColor(QPalette::WindowText, (color));
152         _palette.setColor(QPalette::Text, (color));
153         widget->setPalette(_palette);
154 }
155
156 static inline void SET_FONT_BOLD(QWidget *const widget, const bool &bold)
157
158         QFont _font = widget->font();
159         _font.setBold(bold);
160         widget->setFont(_font);
161 }
162
163 static inline void SET_FONT_BOLD(QAction *const widget, const bool &bold)
164
165         QFont _font = widget->font();
166         _font.setBold(bold);
167         widget->setFont(_font);
168 }
169
170 static inline void SET_MODEL(QAbstractItemView *const view, QAbstractItemModel *const model)
171 {
172         QItemSelectionModel *_tmp = view->selectionModel();
173         view->setModel(model);
174         MUTILS_DELETE(_tmp);
175 }
176
177 static inline void SET_CHECKBOX_STATE(QCheckBox *const chckbx, const bool &state)
178 {
179         const bool isDisabled = (!chckbx->isEnabled());
180         if(isDisabled)
181         {
182                 chckbx->setEnabled(true);
183         }
184         if(chckbx->isChecked() != state)
185         {
186                 chckbx->click();
187         }
188         if(chckbx->isChecked() != state)
189         {
190                 qWarning("Warning: Failed to set checkbox %p state!", chckbx);
191         }
192         if(isDisabled)
193         {
194                 chckbx->setEnabled(false);
195         }
196 }
197
198 static inline void TRIM_STRING_RIGHT(QString &str)
199 {
200         while((str.length() > 0) && str[str.length()-1].isSpace())
201         {
202                 str.chop(1);
203         }
204 }
205
206 static inline void MAKE_TRANSPARENT(QWidget *const widget, const bool &flag)
207 {
208         QPalette _p = widget->palette(); \
209         _p.setColor(QPalette::Background, Qt::transparent);
210         widget->setPalette(flag ? _p : QPalette());
211 }
212
213 template <typename T>
214 static QList<T>& INVERT_LIST(QList<T> &list)
215 {
216         if(!list.isEmpty())
217         {
218                 const int limit = list.size() / 2, maxIdx = list.size() - 1;
219                 for(int k = 0; k < limit; k++) list.swap(k, maxIdx - k);
220         }
221         return list;
222 }
223
224 ////////////////////////////////////////////////////////////
225 // Helper Classes
226 ////////////////////////////////////////////////////////////
227
228 class WidgetHideHelper
229 {
230 public:
231         WidgetHideHelper(QWidget *const widget) : m_widget(widget), m_visible(widget && widget->isVisible()) { if(m_widget && m_visible) m_widget->hide(); }
232         ~WidgetHideHelper(void)                                                                              { if(m_widget && m_visible) m_widget->show(); }
233 private:
234         QWidget *const m_widget;
235         const bool m_visible;
236 };
237
238 class SignalBlockHelper
239 {
240 public:
241         SignalBlockHelper(QObject *const object) : m_object(object), m_flag(object && object->blockSignals(true)) {}
242         ~SignalBlockHelper(void) { if(m_object && (!m_flag)) m_object->blockSignals(false); }
243 private:
244         QObject *const m_object;
245         const bool m_flag;
246 };
247
248 class FileListBlockHelper
249 {
250 public:
251         FileListBlockHelper(FileListModel *const fileList) : m_fileList(fileList) { if(m_fileList) m_fileList->setBlockUpdates(true);  }
252         ~FileListBlockHelper(void)                                                { if(m_fileList) m_fileList->setBlockUpdates(false); }
253 private:
254         FileListModel *const m_fileList;
255 };
256
257 ////////////////////////////////////////////////////////////
258 // Constructor
259 ////////////////////////////////////////////////////////////
260
261 MainWindow::MainWindow(MUtils::IPCChannel *const ipcChannel, FileListModel *const fileListModel, AudioFileModel_MetaInfo *const metaInfo, SettingsModel *const settingsModel, QWidget *const parent)
262 :
263         QMainWindow(parent),
264         ui(new Ui::MainWindow),
265         m_fileListModel(fileListModel),
266         m_metaData(metaInfo),
267         m_settings(settingsModel),
268         m_accepted(false),
269         m_firstTimeShown(true),
270         m_outputFolderViewCentering(false),
271         m_outputFolderViewInitCounter(0)
272 {
273         //Init the dialog, from the .ui file
274         ui->setupUi(this);
275         setWindowFlags(windowFlags() ^ Qt::WindowMaximizeButtonHint);
276         setMinimumSize(this->size());
277
278         //Create window icon
279         MUtils::GUI::set_window_icon(this, lamexp_app_icon(), true);
280
281         //Register meta types
282         qRegisterMetaType<AudioFileModel>("AudioFileModel");
283
284         //Enabled main buttons
285         connect(ui->buttonAbout, SIGNAL(clicked()), this, SLOT(aboutButtonClicked()));
286         connect(ui->buttonStart, SIGNAL(clicked()), this, SLOT(encodeButtonClicked()));
287         connect(ui->buttonQuit,  SIGNAL(clicked()), this, SLOT(closeButtonClicked()));
288
289         //Setup tab widget
290         ui->tabWidget->setCurrentIndex(0);
291         connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabPageChanged(int)));
292
293         //Add system menu
294         MUtils::GUI::sysmenu_append(this, IDM_ABOUTBOX, "About...");
295
296         //Setup corner widget
297         QLabel *cornerWidget = new QLabel(ui->menubar);
298         m_evenFilterCornerWidget.reset(new CustomEventFilter);
299         cornerWidget->setText("N/A");
300         cornerWidget->setFixedHeight(ui->menubar->height());
301         cornerWidget->setCursor(QCursor(Qt::PointingHandCursor));
302         cornerWidget->hide();
303         cornerWidget->installEventFilter(m_evenFilterCornerWidget.data());
304         connect(m_evenFilterCornerWidget.data(), SIGNAL(eventOccurred(QWidget*, QEvent*)), this, SLOT(cornerWidgetEventOccurred(QWidget*, QEvent*)));
305         ui->menubar->setCornerWidget(cornerWidget);
306
307         //--------------------------------
308         // Setup "Source" tab
309         //--------------------------------
310
311         ui->sourceFileView->setModel(m_fileListModel);
312         ui->sourceFileView->verticalHeader()  ->setResizeMode(QHeaderView::ResizeToContents);
313         ui->sourceFileView->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
314         ui->sourceFileView->setContextMenuPolicy(Qt::CustomContextMenu);
315         ui->sourceFileView->viewport()->installEventFilter(this);
316         m_dropNoteLabel.reset(new QLabel(ui->sourceFileView));
317         m_dropNoteLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
318         SET_FONT_BOLD(m_dropNoteLabel.data(), true);
319         SET_TEXT_COLOR(m_dropNoteLabel.data(), Qt::darkGray);
320         m_sourceFilesContextMenu.reset(new QMenu());
321         m_showDetailsContextAction = m_sourceFilesContextMenu->addAction(QIcon(":/icons/zoom.png"),         "N/A");
322         m_previewContextAction     = m_sourceFilesContextMenu->addAction(QIcon(":/icons/sound.png"),        "N/A");
323         m_findFileContextAction    = m_sourceFilesContextMenu->addAction(QIcon(":/icons/folder_go.png"),    "N/A");
324         m_sourceFilesContextMenu->addSeparator();
325         m_exportCsvContextAction   = m_sourceFilesContextMenu->addAction(QIcon(":/icons/table_save.png"),   "N/A");
326         m_importCsvContextAction   = m_sourceFilesContextMenu->addAction(QIcon(":/icons/folder_table.png"), "N/A");
327         SET_FONT_BOLD(m_showDetailsContextAction, true);
328
329         connect(ui->buttonAddFiles,                      SIGNAL(clicked()),                          this, SLOT(addFilesButtonClicked()));
330         connect(ui->buttonRemoveFile,                    SIGNAL(clicked()),                          this, SLOT(removeFileButtonClicked()));
331         connect(ui->buttonClearFiles,                    SIGNAL(clicked()),                          this, SLOT(clearFilesButtonClicked()));
332         connect(ui->buttonFileUp,                        SIGNAL(clicked()),                          this, SLOT(fileUpButtonClicked()));
333         connect(ui->buttonFileDown,                      SIGNAL(clicked()),                          this, SLOT(fileDownButtonClicked()));
334         connect(ui->buttonShowDetails,                   SIGNAL(clicked()),                          this, SLOT(showDetailsButtonClicked()));
335         connect(m_fileListModel,                         SIGNAL(rowsInserted(QModelIndex,int,int)),  this, SLOT(sourceModelChanged()));
336         connect(m_fileListModel,                         SIGNAL(rowsRemoved(QModelIndex,int,int)),   this, SLOT(sourceModelChanged()));
337         connect(m_fileListModel,                         SIGNAL(modelReset()),                       this, SLOT(sourceModelChanged()));
338         connect(ui->sourceFileView,                      SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(sourceFilesContextMenu(QPoint)));
339         connect(ui->sourceFileView->verticalScrollBar(), SIGNAL(sliderMoved(int)),                   this, SLOT(sourceFilesScrollbarMoved(int)));
340         connect(ui->sourceFileView->verticalScrollBar(), SIGNAL(valueChanged(int)),                  this, SLOT(sourceFilesScrollbarMoved(int)));
341         connect(m_showDetailsContextAction,              SIGNAL(triggered(bool)),                    this, SLOT(showDetailsButtonClicked()));
342         connect(m_previewContextAction,                  SIGNAL(triggered(bool)),                    this, SLOT(previewContextActionTriggered()));
343         connect(m_findFileContextAction,                 SIGNAL(triggered(bool)),                    this, SLOT(findFileContextActionTriggered()));
344         connect(m_exportCsvContextAction,                SIGNAL(triggered(bool)),                    this, SLOT(exportCsvContextActionTriggered()));
345         connect(m_importCsvContextAction,                SIGNAL(triggered(bool)),                    this, SLOT(importCsvContextActionTriggered()));
346
347         //--------------------------------
348         // Setup "Output" tab
349         //--------------------------------
350
351         ui->outputFolderView->setHeaderHidden(true);
352         ui->outputFolderView->setAnimated(false);
353         ui->outputFolderView->setMouseTracking(false);
354         ui->outputFolderView->setContextMenuPolicy(Qt::CustomContextMenu);
355         ui->outputFolderView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
356
357         m_evenFilterOutputFolderMouse.reset(new CustomEventFilter);
358         ui->outputFoldersGoUpLabel     ->installEventFilter(m_evenFilterOutputFolderMouse.data());
359         ui->outputFoldersEditorLabel   ->installEventFilter(m_evenFilterOutputFolderMouse.data());
360         ui->outputFoldersFovoritesLabel->installEventFilter(m_evenFilterOutputFolderMouse.data());
361         ui->outputFolderLabel          ->installEventFilter(m_evenFilterOutputFolderMouse.data());
362
363         m_evenFilterOutputFolderView.reset(new CustomEventFilter);
364         ui->outputFolderView->installEventFilter(m_evenFilterOutputFolderView.data());
365
366         SET_CHECKBOX_STATE(ui->saveToSourceFolderCheckBox, m_settings->outputToSourceDir());
367         ui->prependRelativePathCheckBox->setChecked(m_settings->prependRelativeSourcePath());
368         
369         connect(ui->outputFolderView,                 SIGNAL(clicked(QModelIndex)),             this, SLOT(outputFolderViewClicked(QModelIndex)));
370         connect(ui->outputFolderView,                 SIGNAL(activated(QModelIndex)),           this, SLOT(outputFolderViewClicked(QModelIndex)));
371         connect(ui->outputFolderView,                 SIGNAL(pressed(QModelIndex)),             this, SLOT(outputFolderViewClicked(QModelIndex)));
372         connect(ui->outputFolderView,                 SIGNAL(entered(QModelIndex)),             this, SLOT(outputFolderViewMoved(QModelIndex)));
373         connect(ui->outputFolderView,                 SIGNAL(expanded(QModelIndex)),            this, SLOT(outputFolderItemExpanded(QModelIndex)));
374         connect(ui->buttonMakeFolder,                 SIGNAL(clicked()),                        this, SLOT(makeFolderButtonClicked()));
375         connect(ui->buttonGotoHome,                   SIGNAL(clicked()),                        this, SLOT(gotoHomeFolderButtonClicked()));
376         connect(ui->buttonGotoDesktop,                SIGNAL(clicked()),                        this, SLOT(gotoDesktopButtonClicked()));
377         connect(ui->buttonGotoMusic,                  SIGNAL(clicked()),                        this, SLOT(gotoMusicFolderButtonClicked()));
378         connect(ui->saveToSourceFolderCheckBox,       SIGNAL(clicked()),                        this, SLOT(saveToSourceFolderChanged()));
379         connect(ui->prependRelativePathCheckBox,      SIGNAL(clicked()),                        this, SLOT(prependRelativePathChanged()));
380         connect(ui->outputFolderEdit,                 SIGNAL(editingFinished()),                this, SLOT(outputFolderEditFinished()));
381         connect(m_evenFilterOutputFolderMouse.data(), SIGNAL(eventOccurred(QWidget*, QEvent*)), this, SLOT(outputFolderMouseEventOccurred(QWidget*, QEvent*)));
382         connect(m_evenFilterOutputFolderView.data(),  SIGNAL(eventOccurred(QWidget*, QEvent*)), this, SLOT(outputFolderViewEventOccurred(QWidget*, QEvent*)));
383
384         m_outputFolderContextMenu.reset(new QMenu());
385         m_showFolderContextAction    = m_outputFolderContextMenu->addAction(QIcon(":/icons/zoom.png"),          "N/A");
386         m_goUpFolderContextAction    = m_outputFolderContextMenu->addAction(QIcon(":/icons/folder_up.png"),     "N/A");
387         m_outputFolderContextMenu->addSeparator();
388         m_refreshFolderContextAction = m_outputFolderContextMenu->addAction(QIcon(":/icons/arrow_refresh.png"), "N/A");
389         m_outputFolderContextMenu->setDefaultAction(m_showFolderContextAction);
390         connect(ui->outputFolderView,         SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(outputFolderContextMenu(QPoint)));
391         connect(m_showFolderContextAction,    SIGNAL(triggered(bool)),                    this, SLOT(showFolderContextActionTriggered()));
392         connect(m_refreshFolderContextAction, SIGNAL(triggered(bool)),                    this, SLOT(refreshFolderContextActionTriggered()));
393         connect(m_goUpFolderContextAction,    SIGNAL(triggered(bool)),                    this, SLOT(goUpFolderContextActionTriggered()));
394
395         m_outputFolderFavoritesMenu.reset(new QMenu());
396         m_addFavoriteFolderAction = m_outputFolderFavoritesMenu->addAction(QIcon(":/icons/add.png"), "N/A");
397         m_outputFolderFavoritesMenu->insertSeparator(m_addFavoriteFolderAction);
398         connect(m_addFavoriteFolderAction, SIGNAL(triggered(bool)), this, SLOT(addFavoriteFolderActionTriggered()));
399
400         ui->outputFolderEdit->setVisible(false);
401         m_outputFolderNoteBox.reset(new QLabel(ui->outputFolderView));
402         m_outputFolderNoteBox->setAutoFillBackground(true);
403         m_outputFolderNoteBox->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
404         m_outputFolderNoteBox->setFrameShape(QFrame::StyledPanel);
405         SET_FONT_BOLD(m_outputFolderNoteBox.data(), true);
406         m_outputFolderNoteBox->hide();
407
408         outputFolderViewClicked(QModelIndex());
409         refreshFavorites();
410         
411         //--------------------------------
412         // Setup "Meta Data" tab
413         //--------------------------------
414
415         m_metaInfoModel.reset(new MetaInfoModel(m_metaData));
416         m_metaInfoModel->clearData();
417         m_metaInfoModel->setData(m_metaInfoModel->index(4, 1), m_settings->metaInfoPosition());
418         ui->metaDataView->setModel(m_metaInfoModel.data());
419         ui->metaDataView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
420         ui->metaDataView->verticalHeader()->hide();
421         ui->metaDataView->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
422         SET_CHECKBOX_STATE(ui->writeMetaDataCheckBox, m_settings->writeMetaTags());
423         ui->generatePlaylistCheckBox->setChecked(m_settings->createPlaylist());
424         connect(ui->buttonEditMeta,           SIGNAL(clicked()), this, SLOT(editMetaButtonClicked()));
425         connect(ui->buttonClearMeta,          SIGNAL(clicked()), this, SLOT(clearMetaButtonClicked()));
426         connect(ui->writeMetaDataCheckBox,    SIGNAL(clicked()), this, SLOT(metaTagsEnabledChanged()));
427         connect(ui->generatePlaylistCheckBox, SIGNAL(clicked()), this, SLOT(playlistEnabledChanged()));
428
429         //--------------------------------
430         //Setup "Compression" tab
431         //--------------------------------
432
433         m_encoderButtonGroup.reset(new QButtonGroup(this));
434         m_encoderButtonGroup->addButton(ui->radioButtonEncoderMP3,    SettingsModel::MP3Encoder);
435         m_encoderButtonGroup->addButton(ui->radioButtonEncoderVorbis, SettingsModel::VorbisEncoder);
436         m_encoderButtonGroup->addButton(ui->radioButtonEncoderAAC,    SettingsModel::AACEncoder);
437         m_encoderButtonGroup->addButton(ui->radioButtonEncoderAC3,    SettingsModel::AC3Encoder);
438         m_encoderButtonGroup->addButton(ui->radioButtonEncoderFLAC,   SettingsModel::FLACEncoder);
439         m_encoderButtonGroup->addButton(ui->radioButtonEncoderAPE,    SettingsModel::MACEncoder);
440         m_encoderButtonGroup->addButton(ui->radioButtonEncoderOpus,   SettingsModel::OpusEncoder);
441         m_encoderButtonGroup->addButton(ui->radioButtonEncoderDCA,    SettingsModel::DCAEncoder);
442         m_encoderButtonGroup->addButton(ui->radioButtonEncoderPCM,    SettingsModel::PCMEncoder);
443
444         const int aacEncoder = EncoderRegistry::getAacEncoder();
445         ui->radioButtonEncoderAAC->setEnabled(aacEncoder > SettingsModel::AAC_ENCODER_NONE);
446
447         m_modeButtonGroup.reset(new QButtonGroup(this));
448         m_modeButtonGroup->addButton(ui->radioButtonModeQuality,        SettingsModel::VBRMode);
449         m_modeButtonGroup->addButton(ui->radioButtonModeAverageBitrate, SettingsModel::ABRMode);
450         m_modeButtonGroup->addButton(ui->radioButtonConstBitrate,       SettingsModel::CBRMode);
451
452         ui->radioButtonEncoderMP3->setChecked(true);
453         foreach(QAbstractButton *currentButton, m_encoderButtonGroup->buttons())
454         {
455                 if(currentButton->isEnabled() && (m_encoderButtonGroup->id(currentButton) == m_settings->compressionEncoder()))
456                 {
457                         currentButton->setChecked(true);
458                         break;
459                 }
460         }
461
462         m_evenFilterCompressionTab.reset(new CustomEventFilter());
463         ui->labelCompressionHelp->installEventFilter(m_evenFilterCompressionTab.data());
464         ui->labelResetEncoders  ->installEventFilter(m_evenFilterCompressionTab.data());
465
466         connect(m_encoderButtonGroup.data(),       SIGNAL(buttonClicked(int)),               this, SLOT(updateEncoder(int)));
467         connect(m_modeButtonGroup.data(),          SIGNAL(buttonClicked(int)),               this, SLOT(updateRCMode(int)));
468         connect(m_evenFilterCompressionTab.data(), SIGNAL(eventOccurred(QWidget*, QEvent*)), this, SLOT(compressionTabEventOccurred(QWidget*, QEvent*)));
469         connect(ui->sliderBitrate,                 SIGNAL(valueChanged(int)),                this, SLOT(updateBitrate(int)));
470
471         updateEncoder(m_encoderButtonGroup->checkedId());
472
473         //--------------------------------
474         //Setup "Advanced Options" tab
475         //--------------------------------
476
477         ui->sliderLameAlgoQuality->setValue(m_settings->lameAlgoQuality());
478         if(m_settings->maximumInstances() > 0) ui->sliderMaxInstances->setValue(m_settings->maximumInstances());
479
480         ui->spinBoxBitrateManagementMin   ->setValue(m_settings->bitrateManagementMinRate());
481         ui->spinBoxBitrateManagementMax   ->setValue(m_settings->bitrateManagementMaxRate());
482         ui->spinBoxNormalizationFilterPeak->setValue(static_cast<double>(m_settings->normalizationFilterMaxVolume()) / 100.0);
483         ui->spinBoxNormalizationFilterSize->setValue(m_settings->normalizationFilterSize());
484         ui->spinBoxToneAdjustBass         ->setValue(static_cast<double>(m_settings->toneAdjustBass()) / 100.0);
485         ui->spinBoxToneAdjustTreble       ->setValue(static_cast<double>(m_settings->toneAdjustTreble()) / 100.0);
486         ui->spinBoxAftenSearchSize        ->setValue(m_settings->aftenExponentSearchSize());
487         ui->spinBoxOpusComplexity         ->setValue(m_settings->opusComplexity());
488         
489         ui->comboBoxMP3ChannelMode   ->setCurrentIndex(m_settings->lameChannelMode());
490         ui->comboBoxSamplingRate     ->setCurrentIndex(m_settings->samplingRate());
491         ui->comboBoxAACProfile       ->setCurrentIndex(m_settings->aacEncProfile());
492         ui->comboBoxAftenCodingMode  ->setCurrentIndex(m_settings->aftenAudioCodingMode());
493         ui->comboBoxAftenDRCMode     ->setCurrentIndex(m_settings->aftenDynamicRangeCompression());
494         ui->comboBoxOpusFramesize    ->setCurrentIndex(m_settings->opusFramesize());
495         
496         SET_CHECKBOX_STATE(ui->checkBoxBitrateManagement,          m_settings->bitrateManagementEnabled());
497         SET_CHECKBOX_STATE(ui->checkBoxNeroAAC2PassMode,           m_settings->neroAACEnable2Pass());
498         SET_CHECKBOX_STATE(ui->checkBoxAftenFastAllocation,        m_settings->aftenFastBitAllocation());
499         SET_CHECKBOX_STATE(ui->checkBoxNormalizationFilterEnabled, m_settings->normalizationFilterEnabled());
500         SET_CHECKBOX_STATE(ui->checkBoxNormalizationFilterDynamic, m_settings->normalizationFilterDynamic());
501         SET_CHECKBOX_STATE(ui->checkBoxNormalizationFilterCoupled, m_settings->normalizationFilterCoupled());
502         SET_CHECKBOX_STATE(ui->checkBoxAutoDetectInstances,        (m_settings->maximumInstances() < 1));
503         SET_CHECKBOX_STATE(ui->checkBoxUseSystemTempFolder,        (!m_settings->customTempPathEnabled()));
504         SET_CHECKBOX_STATE(ui->checkBoxRename_Rename,              m_settings->renameFiles_renameEnabled());
505         SET_CHECKBOX_STATE(ui->checkBoxRename_RegExp,              m_settings->renameFiles_regExpEnabled());
506         SET_CHECKBOX_STATE(ui->checkBoxForceStereoDownmix,         m_settings->forceStereoDownmix());
507         SET_CHECKBOX_STATE(ui->checkBoxOpusDisableResample,        m_settings->opusDisableResample());
508         SET_CHECKBOX_STATE(ui->checkBoxKeepOriginalDateTime,       m_settings->keepOriginalDataTime());
509
510         ui->checkBoxNeroAAC2PassMode->setEnabled(aacEncoder == SettingsModel::AAC_ENCODER_NERO);
511         
512         ui->lineEditCustomParamLAME     ->setText(EncoderRegistry::loadEncoderCustomParams(m_settings, SettingsModel::MP3Encoder));
513         ui->lineEditCustomParamOggEnc   ->setText(EncoderRegistry::loadEncoderCustomParams(m_settings, SettingsModel::VorbisEncoder));
514         ui->lineEditCustomParamNeroAAC  ->setText(EncoderRegistry::loadEncoderCustomParams(m_settings, SettingsModel::AACEncoder));
515         ui->lineEditCustomParamFLAC     ->setText(EncoderRegistry::loadEncoderCustomParams(m_settings, SettingsModel::FLACEncoder));
516         ui->lineEditCustomParamAften    ->setText(EncoderRegistry::loadEncoderCustomParams(m_settings, SettingsModel::AC3Encoder));
517         ui->lineEditCustomParamOpus     ->setText(EncoderRegistry::loadEncoderCustomParams(m_settings, SettingsModel::OpusEncoder));
518         ui->lineEditCustomTempFolder    ->setText(QDir::toNativeSeparators(m_settings->customTempPath()));
519         ui->lineEditRenamePattern       ->setText(m_settings->renameFiles_renamePattern());
520         ui->lineEditRenameRegExp_Search ->setText(m_settings->renameFiles_regExpSearch ());
521         ui->lineEditRenameRegExp_Replace->setText(m_settings->renameFiles_regExpReplace());
522         
523         m_evenFilterCustumParamsHelp.reset(new CustomEventFilter());
524         ui->helpCustomParamLAME   ->installEventFilter(m_evenFilterCustumParamsHelp.data());
525         ui->helpCustomParamOggEnc ->installEventFilter(m_evenFilterCustumParamsHelp.data());
526         ui->helpCustomParamNeroAAC->installEventFilter(m_evenFilterCustumParamsHelp.data());
527         ui->helpCustomParamFLAC   ->installEventFilter(m_evenFilterCustumParamsHelp.data());
528         ui->helpCustomParamAften  ->installEventFilter(m_evenFilterCustumParamsHelp.data());
529         ui->helpCustomParamOpus   ->installEventFilter(m_evenFilterCustumParamsHelp.data());
530         
531         m_overwriteButtonGroup.reset(new QButtonGroup(this));
532         m_overwriteButtonGroup->addButton(ui->radioButtonOverwriteModeKeepBoth, SettingsModel::Overwrite_KeepBoth);
533         m_overwriteButtonGroup->addButton(ui->radioButtonOverwriteModeSkipFile, SettingsModel::Overwrite_SkipFile);
534         m_overwriteButtonGroup->addButton(ui->radioButtonOverwriteModeReplaces, SettingsModel::Overwrite_Replaces);
535
536         ui->radioButtonOverwriteModeKeepBoth->setChecked(m_settings->overwriteMode() == SettingsModel::Overwrite_KeepBoth);
537         ui->radioButtonOverwriteModeSkipFile->setChecked(m_settings->overwriteMode() == SettingsModel::Overwrite_SkipFile);
538         ui->radioButtonOverwriteModeReplaces->setChecked(m_settings->overwriteMode() == SettingsModel::Overwrite_Replaces);
539
540         FileExtsModel *fileExtModel = new FileExtsModel(this);
541         fileExtModel->importItems(m_settings->renameFiles_fileExtension());
542         ui->tableViewFileExts->setModel(fileExtModel);
543         ui->tableViewFileExts->verticalHeader()  ->setResizeMode(QHeaderView::ResizeToContents);
544         ui->tableViewFileExts->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
545
546         connect(ui->sliderLameAlgoQuality,              SIGNAL(valueChanged(int)),                this, SLOT(updateLameAlgoQuality(int)));
547         connect(ui->checkBoxBitrateManagement,          SIGNAL(clicked(bool)),                    this, SLOT(bitrateManagementEnabledChanged(bool)));
548         connect(ui->spinBoxBitrateManagementMin,        SIGNAL(valueChanged(int)),                this, SLOT(bitrateManagementMinChanged(int)));
549         connect(ui->spinBoxBitrateManagementMax,        SIGNAL(valueChanged(int)),                this, SLOT(bitrateManagementMaxChanged(int)));
550         connect(ui->comboBoxMP3ChannelMode,             SIGNAL(currentIndexChanged(int)),         this, SLOT(channelModeChanged(int)));
551         connect(ui->comboBoxSamplingRate,               SIGNAL(currentIndexChanged(int)),         this, SLOT(samplingRateChanged(int)));
552         connect(ui->checkBoxNeroAAC2PassMode,           SIGNAL(clicked(bool)),                    this, SLOT(neroAAC2PassChanged(bool)));
553         connect(ui->comboBoxAACProfile,                 SIGNAL(currentIndexChanged(int)),         this, SLOT(neroAACProfileChanged(int)));
554         connect(ui->checkBoxNormalizationFilterEnabled, SIGNAL(clicked(bool)),                    this, SLOT(normalizationEnabledChanged(bool)));
555         connect(ui->checkBoxNormalizationFilterDynamic, SIGNAL(clicked(bool)),                    this, SLOT(normalizationDynamicChanged(bool)));
556         connect(ui->checkBoxNormalizationFilterCoupled, SIGNAL(clicked(bool)),                    this, SLOT(normalizationCoupledChanged(bool)));
557         connect(ui->comboBoxAftenCodingMode,            SIGNAL(currentIndexChanged(int)),         this, SLOT(aftenCodingModeChanged(int)));
558         connect(ui->comboBoxAftenDRCMode,               SIGNAL(currentIndexChanged(int)),         this, SLOT(aftenDRCModeChanged(int)));
559         connect(ui->spinBoxAftenSearchSize,             SIGNAL(valueChanged(int)),                this, SLOT(aftenSearchSizeChanged(int)));
560         connect(ui->checkBoxAftenFastAllocation,        SIGNAL(clicked(bool)),                    this, SLOT(aftenFastAllocationChanged(bool)));
561         connect(ui->spinBoxNormalizationFilterPeak,     SIGNAL(valueChanged(double)),             this, SLOT(normalizationMaxVolumeChanged(double)));
562         connect(ui->spinBoxNormalizationFilterSize,     SIGNAL(valueChanged(int)),                this, SLOT(normalizationFilterSizeChanged(int)));
563         connect(ui->spinBoxNormalizationFilterSize,     SIGNAL(editingFinished()),                this, SLOT(normalizationFilterSizeFinished()));
564         connect(ui->spinBoxToneAdjustBass,              SIGNAL(valueChanged(double)),             this, SLOT(toneAdjustBassChanged(double)));
565         connect(ui->spinBoxToneAdjustTreble,            SIGNAL(valueChanged(double)),             this, SLOT(toneAdjustTrebleChanged(double)));
566         connect(ui->buttonToneAdjustReset,              SIGNAL(clicked()),                        this, SLOT(toneAdjustTrebleReset()));
567         connect(ui->lineEditCustomParamLAME,            SIGNAL(editingFinished()),                this, SLOT(customParamsChanged()));
568         connect(ui->lineEditCustomParamOggEnc,          SIGNAL(editingFinished()),                this, SLOT(customParamsChanged()));
569         connect(ui->lineEditCustomParamNeroAAC,         SIGNAL(editingFinished()),                this, SLOT(customParamsChanged()));
570         connect(ui->lineEditCustomParamFLAC,            SIGNAL(editingFinished()),                this, SLOT(customParamsChanged()));
571         connect(ui->lineEditCustomParamAften,           SIGNAL(editingFinished()),                this, SLOT(customParamsChanged()));
572         connect(ui->lineEditCustomParamOpus,            SIGNAL(editingFinished()),                this, SLOT(customParamsChanged()));
573         connect(ui->sliderMaxInstances,                 SIGNAL(valueChanged(int)),                this, SLOT(updateMaximumInstances(int)));
574         connect(ui->checkBoxAutoDetectInstances,        SIGNAL(clicked(bool)),                    this, SLOT(autoDetectInstancesChanged(bool)));
575         connect(ui->buttonBrowseCustomTempFolder,       SIGNAL(clicked()),                        this, SLOT(browseCustomTempFolderButtonClicked()));
576         connect(ui->lineEditCustomTempFolder,           SIGNAL(textChanged(QString)),             this, SLOT(customTempFolderChanged(QString)));
577         connect(ui->checkBoxUseSystemTempFolder,        SIGNAL(clicked(bool)),                    this, SLOT(useCustomTempFolderChanged(bool)));
578         connect(ui->buttonResetAdvancedOptions,         SIGNAL(clicked()),                        this, SLOT(resetAdvancedOptionsButtonClicked()));
579         connect(ui->checkBoxRename_Rename,              SIGNAL(clicked(bool)),                    this, SLOT(renameOutputEnabledChanged(bool)));
580         connect(ui->checkBoxRename_RegExp,              SIGNAL(clicked(bool)),                    this, SLOT(renameRegExpEnabledChanged(bool)));
581         connect(ui->lineEditRenamePattern,              SIGNAL(editingFinished()),                this, SLOT(renameOutputPatternChanged()));
582         connect(ui->lineEditRenamePattern,              SIGNAL(textChanged(QString)),             this, SLOT(renameOutputPatternChanged(QString)));
583         connect(ui->lineEditRenameRegExp_Search,        SIGNAL(editingFinished()),                this, SLOT(renameRegExpValueChanged()));
584         connect(ui->lineEditRenameRegExp_Search,        SIGNAL(textChanged(QString)),             this, SLOT(renameRegExpSearchChanged(QString)));
585         connect(ui->lineEditRenameRegExp_Replace,       SIGNAL(editingFinished()),                this, SLOT(renameRegExpValueChanged()));
586         connect(ui->lineEditRenameRegExp_Replace,       SIGNAL(textChanged(QString)),             this, SLOT(renameRegExpReplaceChanged(QString)));
587         connect(ui->labelShowRenameMacros,              SIGNAL(linkActivated(QString)),           this, SLOT(showRenameMacros(QString)));
588         connect(ui->labelShowRegExpHelp,                SIGNAL(linkActivated(QString)),           this, SLOT(showRenameMacros(QString)));
589         connect(ui->checkBoxForceStereoDownmix,         SIGNAL(clicked(bool)),                    this, SLOT(forceStereoDownmixEnabledChanged(bool)));
590         connect(ui->comboBoxOpusFramesize,              SIGNAL(currentIndexChanged(int)),         this, SLOT(opusSettingsChanged()));
591         connect(ui->spinBoxOpusComplexity,              SIGNAL(valueChanged(int)),                this, SLOT(opusSettingsChanged()));
592         connect(ui->checkBoxOpusDisableResample,        SIGNAL(clicked(bool)),                    this, SLOT(opusSettingsChanged()));
593         connect(ui->buttonRename_Rename,                SIGNAL(clicked(bool)),                    this, SLOT(renameButtonClicked(bool)));
594         connect(ui->buttonRename_RegExp,                SIGNAL(clicked(bool)),                    this, SLOT(renameButtonClicked(bool)));
595         connect(ui->buttonRename_FileEx,                SIGNAL(clicked(bool)),                    this, SLOT(renameButtonClicked(bool)));
596         connect(ui->buttonFileExts_Add,                 SIGNAL(clicked()),                        this, SLOT(fileExtAddButtonClicked()));
597         connect(ui->buttonFileExts_Remove,              SIGNAL(clicked()),                        this, SLOT(fileExtRemoveButtonClicked()));
598         connect(ui->checkBoxKeepOriginalDateTime,       SIGNAL(clicked(bool)),                    this, SLOT(keepOriginalDateTimeChanged(bool)));
599         connect(m_overwriteButtonGroup.data(),          SIGNAL(buttonClicked(int)),               this, SLOT(overwriteModeChanged(int)));
600         connect(m_evenFilterCustumParamsHelp.data(),    SIGNAL(eventOccurred(QWidget*, QEvent*)), this, SLOT(customParamsHelpRequested(QWidget*, QEvent*)));
601         connect(fileExtModel,                           SIGNAL(modelReset()),                     this, SLOT(fileExtModelChanged()));
602
603         //--------------------------------
604         // Force initial GUI update
605         //--------------------------------
606
607         updateLameAlgoQuality(ui->sliderLameAlgoQuality->value());
608         updateMaximumInstances(ui->sliderMaxInstances->value());
609         toneAdjustTrebleChanged(ui->spinBoxToneAdjustTreble->value());
610         toneAdjustBassChanged(ui->spinBoxToneAdjustBass->value());
611         normalizationEnabledChanged(ui->checkBoxNormalizationFilterEnabled->isChecked());
612         customParamsChanged();
613         
614         //--------------------------------
615         // Initialize actions
616         //--------------------------------
617
618         //Activate file menu actions
619         ui->actionOpenFolder           ->setData(QVariant::fromValue<bool>(false));
620         ui->actionOpenFolderRecursively->setData(QVariant::fromValue<bool>(true));
621         connect(ui->actionOpenFolder,            SIGNAL(triggered()), this, SLOT(openFolderActionActivated()));
622         connect(ui->actionOpenFolderRecursively, SIGNAL(triggered()), this, SLOT(openFolderActionActivated()));
623
624         //Activate view menu actions
625         m_tabActionGroup.reset(new QActionGroup(this));
626         m_tabActionGroup->addAction(ui->actionSourceFiles);
627         m_tabActionGroup->addAction(ui->actionOutputDirectory);
628         m_tabActionGroup->addAction(ui->actionCompression);
629         m_tabActionGroup->addAction(ui->actionMetaData);
630         m_tabActionGroup->addAction(ui->actionAdvancedOptions);
631         ui->actionSourceFiles->setData(0);
632         ui->actionOutputDirectory->setData(1);
633         ui->actionMetaData->setData(2);
634         ui->actionCompression->setData(3);
635         ui->actionAdvancedOptions->setData(4);
636         ui->actionSourceFiles->setChecked(true);
637         connect(m_tabActionGroup.data(), SIGNAL(triggered(QAction*)), this, SLOT(tabActionActivated(QAction*)));
638
639         //Activate style menu actions
640         m_styleActionGroup .reset(new QActionGroup(this));
641         m_styleActionGroup->addAction(ui->actionStylePlastique);
642         m_styleActionGroup->addAction(ui->actionStyleCleanlooks);
643         m_styleActionGroup->addAction(ui->actionStyleWindowsVista);
644         m_styleActionGroup->addAction(ui->actionStyleWindowsXP);
645         m_styleActionGroup->addAction(ui->actionStyleWindowsClassic);
646         ui->actionStylePlastique->setData(0);
647         ui->actionStyleCleanlooks->setData(1);
648         ui->actionStyleWindowsVista->setData(2);
649         ui->actionStyleWindowsXP->setData(3);
650         ui->actionStyleWindowsClassic->setData(4);
651         ui->actionStylePlastique->setChecked(true);
652         ui->actionStyleWindowsXP->setEnabled((QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) >= QSysInfo::WV_XP && MUtils::GUI::themes_enabled());
653         ui->actionStyleWindowsVista->setEnabled((QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) >= QSysInfo::WV_VISTA && MUtils::GUI::themes_enabled());
654         connect(m_styleActionGroup.data(), SIGNAL(triggered(QAction*)), this, SLOT(styleActionActivated(QAction*)));
655         styleActionActivated(NULL);
656
657         //Populate the language menu
658         m_languageActionGroup.reset(new QActionGroup(this));
659         QStringList translations;
660         if(MUtils::Translation::enumerate(translations) > 0)
661         {
662                 for(QStringList::ConstIterator iter = translations.constBegin(); iter != translations.constEnd(); iter++)
663                 {
664                         QAction *currentLanguage = new QAction(this);
665                         currentLanguage->setData(*iter);
666                         currentLanguage->setText(MUtils::Translation::get_name(*iter));
667                         currentLanguage->setIcon(QIcon(QString(":/flags/%1.png").arg(*iter)));
668                         currentLanguage->setCheckable(true);
669                         currentLanguage->setChecked(false);
670                         m_languageActionGroup->addAction(currentLanguage);
671                         ui->menuLanguage->insertAction(ui->actionLoadTranslationFromFile, currentLanguage);
672                 }
673         }
674         ui->menuLanguage->insertSeparator(ui->actionLoadTranslationFromFile);
675         connect(ui->actionLoadTranslationFromFile, SIGNAL(triggered(bool)),     this, SLOT(languageFromFileActionActivated(bool)));
676         connect(m_languageActionGroup.data(),      SIGNAL(triggered(QAction*)), this, SLOT(languageActionActivated(QAction*)));
677         ui->actionLoadTranslationFromFile->setChecked(false);
678
679         //Activate tools menu actions
680         ui->actionDisableUpdateReminder->setChecked(!m_settings->autoUpdateEnabled());
681         ui->actionDisableSounds->setChecked(!m_settings->soundsEnabled());
682         ui->actionDisableNeroAacNotifications->setChecked(!m_settings->neroAacNotificationsEnabled());
683         ui->actionDisableSlowStartupNotifications->setChecked(!m_settings->antivirNotificationsEnabled());
684         ui->actionDisableShellIntegration->setChecked(!m_settings->shellIntegrationEnabled());
685         ui->actionDisableShellIntegration->setDisabled(lamexp_version_portable() && ui->actionDisableShellIntegration->isChecked());
686         ui->actionCheckForBetaUpdates->setChecked(m_settings->autoUpdateCheckBeta() || lamexp_version_demo());
687         ui->actionCheckForBetaUpdates->setEnabled(!lamexp_version_demo());
688         ui->actionHibernateComputer->setChecked(m_settings->hibernateComputer());
689         ui->actionHibernateComputer->setEnabled(MUtils::OS::is_hibernation_supported());
690         connect(ui->actionDisableUpdateReminder, SIGNAL(triggered(bool)), this, SLOT(disableUpdateReminderActionTriggered(bool)));
691         connect(ui->actionDisableSounds, SIGNAL(triggered(bool)), this, SLOT(disableSoundsActionTriggered(bool)));
692         connect(ui->actionDisableNeroAacNotifications, SIGNAL(triggered(bool)), this, SLOT(disableNeroAacNotificationsActionTriggered(bool)));
693         connect(ui->actionDisableSlowStartupNotifications, SIGNAL(triggered(bool)), this, SLOT(disableSlowStartupNotificationsActionTriggered(bool)));
694         connect(ui->actionDisableShellIntegration, SIGNAL(triggered(bool)), this, SLOT(disableShellIntegrationActionTriggered(bool)));
695         connect(ui->actionShowDropBoxWidget, SIGNAL(triggered(bool)), this, SLOT(showDropBoxWidgetActionTriggered(bool)));
696         connect(ui->actionHibernateComputer, SIGNAL(triggered(bool)), this, SLOT(hibernateComputerActionTriggered(bool)));
697         connect(ui->actionCheckForBetaUpdates, SIGNAL(triggered(bool)), this, SLOT(checkForBetaUpdatesActionTriggered(bool)));
698         connect(ui->actionImportCueSheet, SIGNAL(triggered(bool)), this, SLOT(importCueSheetActionTriggered(bool)));
699                 
700         //Activate help menu actions
701         ui->actionVisitHomepage    ->setData(QString::fromLatin1(lamexp_website_url()));
702         ui->actionVisitSupport     ->setData(QString::fromLatin1(lamexp_support_url()));
703         ui->actionVisitMuldersSite ->setData(QString::fromLatin1(lamexp_mulders_url()));
704         ui->actionVisitTracker     ->setData(QString::fromLatin1(lamexp_tracker_url()));
705         ui->actionVisitHAK         ->setData(QString::fromLatin1(g_hydrogen_audio_url));
706         ui->actionDocumentManual   ->setData(QString("%1/Manual.html")   .arg(QApplication::applicationDirPath()));
707         ui->actionDocumentChangelog->setData(QString("%1/Changelog.html").arg(QApplication::applicationDirPath()));
708         ui->actionDocumentTranslate->setData(QString("%1/Translate.html").arg(QApplication::applicationDirPath()));
709         connect(ui->actionCheckUpdates,      SIGNAL(triggered()), this, SLOT(checkUpdatesActionActivated()));
710         connect(ui->actionVisitSupport,      SIGNAL(triggered()), this, SLOT(visitHomepageActionActivated()));
711         connect(ui->actionVisitTracker,      SIGNAL(triggered()), this, SLOT(visitHomepageActionActivated()));
712         connect(ui->actionVisitHomepage,     SIGNAL(triggered()), this, SLOT(visitHomepageActionActivated()));
713         connect(ui->actionVisitMuldersSite,  SIGNAL(triggered()), this, SLOT(visitHomepageActionActivated()));
714         connect(ui->actionVisitHAK,          SIGNAL(triggered()), this, SLOT(visitHomepageActionActivated()));
715         connect(ui->actionDocumentManual,    SIGNAL(triggered()), this, SLOT(documentActionActivated()));
716         connect(ui->actionDocumentChangelog, SIGNAL(triggered()), this, SLOT(documentActionActivated()));
717         connect(ui->actionDocumentTranslate, SIGNAL(triggered()), this, SLOT(documentActionActivated()));
718         
719         //--------------------------------
720         // Prepare to show window
721         //--------------------------------
722
723         //Adjust size to DPI settings and re-center
724         MUtils::GUI::scale_widget(this);
725         
726         //Create DropBox widget
727         m_dropBox.reset(new DropBox(this, m_fileListModel, m_settings));
728         connect(m_fileListModel, SIGNAL(modelReset()),                      m_dropBox.data(), SLOT(modelChanged()));
729         connect(m_fileListModel, SIGNAL(rowsInserted(QModelIndex,int,int)), m_dropBox.data(), SLOT(modelChanged()));
730         connect(m_fileListModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),  m_dropBox.data(), SLOT(modelChanged()));
731         connect(m_fileListModel, SIGNAL(rowAppended()),                     m_dropBox.data(), SLOT(modelChanged()));
732
733         //Create message handler thread
734         m_messageHandler.reset(new MessageHandlerThread(ipcChannel));
735         connect(m_messageHandler.data(), SIGNAL(otherInstanceDetected()),       this, SLOT(notifyOtherInstance()), Qt::QueuedConnection);
736         connect(m_messageHandler.data(), SIGNAL(fileReceived(QString)),         this, SLOT(addFileDelayed(QString)), Qt::QueuedConnection);
737         connect(m_messageHandler.data(), SIGNAL(folderReceived(QString, bool)), this, SLOT(addFolderDelayed(QString, bool)), Qt::QueuedConnection);
738         connect(m_messageHandler.data(), SIGNAL(killSignalReceived()),          this, SLOT(close()), Qt::QueuedConnection);
739         m_messageHandler->start();
740
741         //Init delayed file handling
742         m_delayedFileList .reset(new QStringList());
743         m_delayedFileTimer.reset(new QTimer());
744         m_delayedFileTimer->setSingleShot(true);
745         m_delayedFileTimer->setInterval(5000);
746         connect(m_delayedFileTimer.data(), SIGNAL(timeout()), this, SLOT(handleDelayedFiles()));
747
748         //Load translation
749         initializeTranslation();
750
751         //Re-translate (make sure we translate once)
752         QEvent languageChangeEvent(QEvent::LanguageChange);
753         changeEvent(&languageChangeEvent);
754
755         //Enable Drag & Drop
756         m_droppedFileList.reset(new QList<QUrl>());
757         this->setAcceptDrops(true);
758 }
759
760 ////////////////////////////////////////////////////////////
761 // Destructor
762 ////////////////////////////////////////////////////////////
763
764 MainWindow::~MainWindow(void)
765 {
766         //Stop message handler thread
767         if(m_messageHandler && m_messageHandler->isRunning())
768         {
769                 m_messageHandler->stop();
770                 if(!m_messageHandler->wait(2500))
771                 {
772                         m_messageHandler->terminate();
773                         m_messageHandler->wait();
774                 }
775         }
776
777         //Unset models
778         SET_MODEL(ui->sourceFileView,   NULL);
779         SET_MODEL(ui->outputFolderView, NULL);
780         SET_MODEL(ui->metaDataView,     NULL);
781
782         //Un-initialize the dialog
783         MUTILS_DELETE(ui);
784 }
785
786 ////////////////////////////////////////////////////////////
787 // PRIVATE FUNCTIONS
788 ////////////////////////////////////////////////////////////
789
790 /*
791  * Add file to source list
792  */
793 void MainWindow::addFiles(const QStringList &files)
794 {
795         if(files.isEmpty())
796         {
797                 return;
798         }
799
800         if(ui->tabWidget->currentIndex() != 0)
801         {
802                 SignalBlockHelper signalBlockHelper(ui->tabWidget);
803                 ui->tabWidget->setCurrentIndex(0);
804                 tabPageChanged(ui->tabWidget->currentIndex(), true);
805         }
806
807         INIT_BANNER();
808         QScopedPointer<FileAnalyzer> analyzer(new FileAnalyzer(files));
809
810         connect(analyzer.data(), SIGNAL(fileSelected(QString)),            m_banner.data(), SLOT(setText(QString)),             Qt::QueuedConnection);
811         connect(analyzer.data(), SIGNAL(progressValChanged(unsigned int)), m_banner.data(), SLOT(setProgressVal(unsigned int)), Qt::QueuedConnection);
812         connect(analyzer.data(), SIGNAL(progressMaxChanged(unsigned int)), m_banner.data(), SLOT(setProgressMax(unsigned int)), Qt::QueuedConnection);
813         connect(analyzer.data(), SIGNAL(fileAnalyzed(AudioFileModel)),     m_fileListModel, SLOT(addFile(AudioFileModel)),      Qt::QueuedConnection);
814         connect(m_banner.data(), SIGNAL(userAbort()),                      analyzer.data(), SLOT(abortProcess()),               Qt::DirectConnection);
815
816         if(!analyzer.isNull())
817         {
818                 FileListBlockHelper fileListBlocker(m_fileListModel);
819                 m_banner->show(tr("Adding file(s), please wait..."), analyzer.data());
820         }
821
822         qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
823         ui->sourceFileView->update();
824         qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
825         ui->sourceFileView->scrollToBottom();
826         qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
827
828         if(analyzer->filesDenied())
829         {
830                 QMessageBox::warning(this, tr("Access Denied"), QString("%1<br>%2").arg(NOBR(tr("%n file(s) have been rejected, because read access was not granted!", "", analyzer->filesDenied())), NOBR(tr("This usually means the file is locked by another process."))));
831         }
832         if(analyzer->filesDummyCDDA())
833         {
834                 QMessageBox::warning(this, tr("CDDA Files"), QString("%1<br><br>%2<br>%3").arg(NOBR(tr("%n file(s) have been rejected, because they are dummy CDDA files!", "", analyzer->filesDummyCDDA())), NOBR(tr("Sorry, LameXP cannot extract audio tracks from an Audio-CD at present.")), NOBR(tr("We recommend using %1 for that purpose.").arg("<a href=\"http://www.exactaudiocopy.de/\">Exact Audio Copy</a>"))));
835         }
836         if(analyzer->filesCueSheet())
837         {
838                 QMessageBox::warning(this, tr("Cue Sheet"), QString("%1<br>%2").arg(NOBR(tr("%n file(s) have been rejected, because they appear to be Cue Sheet images!", "",analyzer->filesCueSheet())), NOBR(tr("Please use LameXP's Cue Sheet wizard for importing Cue Sheet files."))));
839         }
840         if(analyzer->filesRejected())
841         {
842                 QMessageBox::warning(this, tr("Files Rejected"), QString("%1<br>%2").arg(NOBR(tr("%n file(s) have been rejected, because the file format could not be recognized!", "", analyzer->filesRejected())), NOBR(tr("This usually means the file is damaged or the file format is not supported."))));
843         }
844
845         m_banner->close();
846 }
847
848 /*
849  * Add folder to source list
850  */
851 void MainWindow::addFolder(const QString &path, bool recursive, bool delayed, QString filter)
852 {
853         QFileInfoList folderInfoList;
854         folderInfoList << QFileInfo(path);
855         QStringList fileList;
856         
857         showBanner(tr("Scanning folder(s) for files, please wait..."));
858         
859         QApplication::processEvents();
860         MUtils::OS::check_key_state_esc();
861
862         while(!folderInfoList.isEmpty())
863         {
864                 if(MUtils::OS::check_key_state_esc())
865                 {
866                         qWarning("Operation cancelled by user!");
867                         MUtils::Sound::beep(MUtils::Sound::BEEP_ERR);
868                         fileList.clear();
869                         break;
870                 }
871                 
872                 QDir currentDir(folderInfoList.takeFirst().canonicalFilePath());
873                 QFileInfoList fileInfoList = currentDir.entryInfoList(QDir::Files | QDir::NoSymLinks);
874
875                 for(QFileInfoList::ConstIterator iter = fileInfoList.constBegin(); iter != fileInfoList.constEnd(); iter++)
876                 {
877                         if(filter.isEmpty() || (iter->suffix().compare(filter, Qt::CaseInsensitive) == 0))
878                         {
879                                 fileList << iter->canonicalFilePath();
880                         }
881                 }
882
883                 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
884
885                 if(recursive)
886                 {
887                         folderInfoList.append(currentDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks));
888                         QApplication::processEvents();
889                 }
890         }
891         
892         m_banner->close();
893         QApplication::processEvents();
894
895         if(!fileList.isEmpty())
896         {
897                 if(delayed)
898                 {
899                         addFilesDelayed(fileList);
900                 }
901                 else
902                 {
903                         addFiles(fileList);
904                 }
905         }
906 }
907
908 /*
909  * Check for updates
910  */
911 bool MainWindow::checkForUpdates(void)
912 {
913         bool bReadyToInstall = false;
914         
915         UpdateDialog *updateDialog = new UpdateDialog(m_settings, this);
916         updateDialog->exec();
917
918         if(updateDialog->getSuccess())
919         {
920                 SHOW_CORNER_WIDGET(false);
921                 m_settings->autoUpdateLastCheck(QDate::currentDate().toString(Qt::ISODate));
922                 bReadyToInstall = updateDialog->updateReadyToInstall();
923         }
924
925         MUTILS_DELETE(updateDialog);
926         return bReadyToInstall;
927 }
928
929 /*
930  * Refresh list of favorites
931  */
932 void MainWindow::refreshFavorites(void)
933 {
934         QList<QAction*> folderList = m_outputFolderFavoritesMenu->actions();
935         QStringList favorites = m_settings->favoriteOutputFolders().split("|", QString::SkipEmptyParts);
936         while(favorites.count() > 6) favorites.removeFirst();
937
938         while(!folderList.isEmpty())
939         {
940                 QAction *currentItem = folderList.takeFirst();
941                 if(currentItem->isSeparator()) break;
942                 m_outputFolderFavoritesMenu->removeAction(currentItem);
943                 MUTILS_DELETE(currentItem);
944         }
945
946         QAction *lastItem = m_outputFolderFavoritesMenu->actions().first();
947
948         while(!favorites.isEmpty())
949         {
950                 QString path = favorites.takeLast();
951                 if(QDir(path).exists())
952                 {
953                         QAction *action = new QAction(QIcon(":/icons/folder_go.png"), QDir::toNativeSeparators(path), this);
954                         action->setData(path);
955                         m_outputFolderFavoritesMenu->insertAction(lastItem, action);
956                         connect(action, SIGNAL(triggered(bool)), this, SLOT(gotoFavoriteFolder()));
957                         lastItem = action;
958                 }
959         }
960 }
961
962 /*
963  * Initilaize translation
964  */
965 void MainWindow::initializeTranslation(void)
966 {
967         bool translationLoaded = false;
968
969         //Try to load "external" translation file
970         if(!m_settings->currentLanguageFile().isEmpty())
971         {
972                 const QString qmFilePath = QFileInfo(m_settings->currentLanguageFile()).canonicalFilePath();
973                 if((!qmFilePath.isEmpty()) && QFileInfo(qmFilePath).exists() && QFileInfo(qmFilePath).isFile() && (QFileInfo(qmFilePath).suffix().compare("qm", Qt::CaseInsensitive) == 0))
974                 {
975                         if(MUtils::Translation::install_translator_from_file(qmFilePath))
976                         {
977                                 QList<QAction*> actions = m_languageActionGroup->actions();
978                                 while(!actions.isEmpty()) actions.takeFirst()->setChecked(false);
979                                 ui->actionLoadTranslationFromFile->setChecked(true);
980                                 translationLoaded = true;
981                         }
982                 }
983         }
984
985         //Try to load "built-in" translation file
986         if(!translationLoaded)
987         {
988                 QList<QAction*> languageActions = m_languageActionGroup->actions();
989                 while(!languageActions.isEmpty())
990                 {
991                         QAction *currentLanguage = languageActions.takeFirst();
992                         if(currentLanguage->data().toString().compare(m_settings->currentLanguage(), Qt::CaseInsensitive) == 0)
993                         {
994                                 currentLanguage->setChecked(true);
995                                 languageActionActivated(currentLanguage);
996                                 translationLoaded = true;
997                         }
998                 }
999         }
1000
1001         //Fallback to default translation
1002         if(!translationLoaded)
1003         {
1004                 QList<QAction*> languageActions = m_languageActionGroup->actions();
1005                 while(!languageActions.isEmpty())
1006                 {
1007                         QAction *currentLanguage = languageActions.takeFirst();
1008                         if(currentLanguage->data().toString().compare(MUtils::Translation::DEFAULT_LANGID, Qt::CaseInsensitive) == 0)
1009                         {
1010                                 currentLanguage->setChecked(true);
1011                                 languageActionActivated(currentLanguage);
1012                                 translationLoaded = true;
1013                         }
1014                 }
1015         }
1016
1017         //Make sure we loaded some translation
1018         if(!translationLoaded)
1019         {
1020                 qFatal("Failed to load any translation, this is NOT supposed to happen!");
1021         }
1022 }
1023
1024 /*
1025  * Open a document link
1026  */
1027 void MainWindow::openDocumentLink(QAction *const action)
1028 {
1029         if(!(action->data().isValid() && (action->data().type() == QVariant::String)))
1030         {
1031                 qWarning("Cannot open document for this QAction!");
1032                 return;
1033         }
1034
1035         //Try to open exitsing document file
1036         const QFileInfo document(action->data().toString());
1037         if(document.exists() && document.isFile() && (document.size() >= 1024))
1038         {
1039                 QDesktopServices::openUrl(QUrl::fromLocalFile(document.canonicalFilePath()));
1040                 return;
1041         }
1042
1043         //Document not found -> fallback mode!
1044         qWarning("Document '%s' not found -> redirecting to the website!", MUTILS_UTF8(document.fileName()));
1045         const QUrl url(QString("%1/%2").arg(QString::fromLatin1(g_documents_base_url), document.fileName()));
1046         QDesktopServices::openUrl(url);
1047 }
1048
1049 /*
1050  * Move selected files up/down
1051  */
1052 void MainWindow::moveSelectedFiles(const bool &up)
1053 {
1054         QItemSelectionModel *const selection = ui->sourceFileView->selectionModel();
1055         if(selection && selection->hasSelection())
1056         {
1057                 const QModelIndexList selectedRows = up ? selection->selectedRows() : INVERT_LIST(selection->selectedRows());
1058                 if((up && (selectedRows.first().row() > 0)) || ((!up) && (selectedRows.first().row() < m_fileListModel->rowCount() - 1)))
1059                 {
1060                         const int delta = up ? (-1) : 1;
1061                         const int firstIndex = (up ? selectedRows.first() : selectedRows.last()).row() + delta;
1062                         const int selectionCount = selectedRows.count();
1063                         if(abs(delta) > 0)
1064                         {
1065                                 FileListBlockHelper fileListBlocker(m_fileListModel);
1066                                 for(QModelIndexList::ConstIterator iter = selectedRows.constBegin(); iter != selectedRows.constEnd(); iter++)
1067                                 {
1068                                         if(!m_fileListModel->moveFile((*iter), delta))
1069                                         {
1070                                                 break;
1071                                         }
1072                                 }
1073                         }
1074                         selection->clearSelection();
1075                         for(int i = 0; i < selectionCount; i++)
1076                         {
1077                                 const QModelIndex item = m_fileListModel->index(firstIndex + i, 0);
1078                                 selection->select(QItemSelection(item, item), QItemSelectionModel::Select | QItemSelectionModel::Rows);
1079                         }
1080                         ui->sourceFileView->scrollTo(m_fileListModel->index((up ? firstIndex : firstIndex + selectionCount - 1), 0), QAbstractItemView::PositionAtCenter);
1081                         return;
1082                 }
1083         }
1084         MUtils::Sound::beep(MUtils::Sound::BEEP_WRN);
1085 }
1086
1087 /*
1088  * Show banner popup dialog
1089  */
1090 void MainWindow::showBanner(const QString &text)
1091 {
1092         INIT_BANNER();
1093         m_banner->show(text);
1094 }
1095
1096 /*
1097  * Show banner popup dialog
1098  */
1099 void MainWindow::showBanner(const QString &text, QThread *const thread)
1100 {
1101         INIT_BANNER();
1102         m_banner->show(text, thread);
1103 }
1104
1105 /*
1106  * Show banner popup dialog
1107  */
1108 void MainWindow::showBanner(const QString &text, QEventLoop *const eventLoop)
1109 {
1110         INIT_BANNER();
1111         m_banner->show(text, eventLoop);
1112 }
1113
1114 /*
1115  * Show banner popup dialog
1116  */
1117 void MainWindow::showBanner(const QString &text, bool &flag, const bool &test)
1118 {
1119         if((!flag) && (test))
1120         {
1121                 INIT_BANNER();
1122                 m_banner->show(text);
1123                 flag = true;
1124         }
1125 }
1126
1127 ////////////////////////////////////////////////////////////
1128 // EVENTS
1129 ////////////////////////////////////////////////////////////
1130
1131 /*
1132  * Window is about to be shown
1133  */
1134 void MainWindow::showEvent(QShowEvent *event)
1135 {
1136         m_accepted = false;
1137         resizeEvent(NULL);
1138         sourceModelChanged();
1139
1140         if(!event->spontaneous())
1141         {
1142                 SignalBlockHelper signalBlockHelper(ui->tabWidget);
1143                 ui->tabWidget->setCurrentIndex(0);
1144                 tabPageChanged(ui->tabWidget->currentIndex(), true);
1145         }
1146
1147         if(m_firstTimeShown)
1148         {
1149                 m_firstTimeShown = false;
1150                 QTimer::singleShot(0, this, SLOT(windowShown()));
1151         }
1152         else
1153         {
1154                 if(m_settings->dropBoxWidgetEnabled())
1155                 {
1156                         m_dropBox->setVisible(true);
1157                 }
1158         }
1159 }
1160
1161 /*
1162  * Re-translate the UI
1163  */
1164 void MainWindow::changeEvent(QEvent *e)
1165 {
1166         QMainWindow::changeEvent(e);
1167         if(e->type() != QEvent::LanguageChange)
1168         {
1169                 return;
1170         }
1171
1172         int comboBoxIndex[6];
1173                 
1174         //Backup combobox indices, as retranslateUi() resets
1175         comboBoxIndex[0] = ui->comboBoxMP3ChannelMode->currentIndex();
1176         comboBoxIndex[1] = ui->comboBoxSamplingRate->currentIndex();
1177         comboBoxIndex[2] = ui->comboBoxAACProfile->currentIndex();
1178         comboBoxIndex[3] = ui->comboBoxAftenCodingMode->currentIndex();
1179         comboBoxIndex[4] = ui->comboBoxAftenDRCMode->currentIndex();
1180         comboBoxIndex[5] = ui->comboBoxOpusFramesize->currentIndex();
1181                 
1182         //Re-translate from UIC
1183         ui->retranslateUi(this);
1184
1185         //Restore combobox indices
1186         ui->comboBoxMP3ChannelMode->setCurrentIndex(comboBoxIndex[0]);
1187         ui->comboBoxSamplingRate->setCurrentIndex(comboBoxIndex[1]);
1188         ui->comboBoxAACProfile->setCurrentIndex(comboBoxIndex[2]);
1189         ui->comboBoxAftenCodingMode->setCurrentIndex(comboBoxIndex[3]);
1190         ui->comboBoxAftenDRCMode->setCurrentIndex(comboBoxIndex[4]);
1191         ui->comboBoxOpusFramesize->setCurrentIndex(comboBoxIndex[5]);
1192
1193         //Update the window title
1194         if(MUTILS_DEBUG)
1195         {
1196                 setWindowTitle(QString("%1 [!!! DEBUG BUILD !!!]").arg(windowTitle()));
1197         }
1198         else if(lamexp_version_demo())
1199         {
1200                 setWindowTitle(QString("%1 [%2]").arg(windowTitle(), tr("DEMO VERSION")));
1201         }
1202
1203         //Manually re-translate widgets that UIC doesn't handle
1204         m_outputFolderNoteBox->setText(tr("Initializing directory outline, please be patient..."));
1205         m_dropNoteLabel->setText(QString("<br><img src=\":/images/DropZone.png\"><br><br>%1").arg(tr("You can drop in audio files here!")));
1206         if(QLabel *cornerWidget = dynamic_cast<QLabel*>(ui->menubar->cornerWidget()))
1207         {
1208                 cornerWidget->setText(QString("<nobr><img src=\":/icons/exclamation_small.png\">&nbsp;<b style=\"color:darkred\">%1</b>&nbsp;&nbsp;&nbsp;</nobr>").arg(tr("Check for Updates")));
1209         }
1210         m_showDetailsContextAction->setText(tr("Show Details"));
1211         m_previewContextAction->setText(tr("Open File in External Application"));
1212         m_findFileContextAction->setText(tr("Browse File Location"));
1213         m_showFolderContextAction->setText(tr("Browse Selected Folder"));
1214         m_refreshFolderContextAction->setText(tr("Refresh Directory Outline"));
1215         m_goUpFolderContextAction->setText(tr("Go To Parent Directory"));
1216         m_addFavoriteFolderAction->setText(tr("Bookmark Current Output Folder"));
1217         m_exportCsvContextAction->setText(tr("Export Meta Tags to CSV File"));
1218         m_importCsvContextAction->setText(tr("Import Meta Tags from CSV File"));
1219
1220         //Force GUI update
1221         m_metaInfoModel->clearData();
1222         m_metaInfoModel->setData(m_metaInfoModel->index(4, 1), m_settings->metaInfoPosition());
1223         updateEncoder(m_settings->compressionEncoder());
1224         updateLameAlgoQuality(ui->sliderLameAlgoQuality->value());
1225         updateMaximumInstances(ui->sliderMaxInstances->value());
1226         renameOutputPatternChanged(ui->lineEditRenamePattern->text(), true);
1227         renameRegExpSearchChanged (ui->lineEditRenameRegExp_Search ->text(), true);
1228         renameRegExpReplaceChanged(ui->lineEditRenameRegExp_Replace->text(), true);
1229
1230         //Re-install shell integration
1231         if(m_settings->shellIntegrationEnabled())
1232         {
1233                 ShellIntegration::install();
1234         }
1235
1236         //Translate system menu
1237         MUtils::GUI::sysmenu_update(this, IDM_ABOUTBOX, ui->buttonAbout->text());
1238         
1239         //Force resize event
1240         QApplication::postEvent(this, new QResizeEvent(this->size(), QSize()));
1241         for(QObjectList::ConstIterator iter = this->children().constBegin(); iter != this->children().constEnd(); iter++)
1242         {
1243                 if(QWidget *child = dynamic_cast<QWidget*>(*iter))
1244                 {
1245                         QApplication::postEvent(child, new QResizeEvent(child->size(), QSize()));
1246                 }
1247         }
1248
1249         //Force tabe page change
1250         tabPageChanged(ui->tabWidget->currentIndex(), true);
1251 }
1252
1253 /*
1254  * File dragged over window
1255  */
1256 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
1257 {
1258         QStringList formats = event->mimeData()->formats();
1259         
1260         if(formats.contains("application/x-qt-windows-mime;value=\"FileNameW\"", Qt::CaseInsensitive) && formats.contains("text/uri-list", Qt::CaseInsensitive))
1261         {
1262                 event->acceptProposedAction();
1263         }
1264 }
1265
1266 /*
1267  * File dropped onto window
1268  */
1269 void MainWindow::dropEvent(QDropEvent *event)
1270 {
1271         m_droppedFileList->clear();
1272         (*m_droppedFileList) << event->mimeData()->urls();
1273         if(!m_droppedFileList->isEmpty())
1274         {
1275                 PLAY_SOUND_OPTIONAL("drop", true);
1276                 QTimer::singleShot(0, this, SLOT(handleDroppedFiles()));
1277         }
1278 }
1279
1280 /*
1281  * Window tries to close
1282  */
1283 void MainWindow::closeEvent(QCloseEvent *event)
1284 {
1285         if(BANNER_VISIBLE || m_delayedFileTimer->isActive())
1286         {
1287                 MUtils::Sound::beep(MUtils::Sound::BEEP_WRN);
1288                 event->ignore();
1289         }
1290         
1291         if(m_dropBox)
1292         {
1293                 m_dropBox->hide();
1294         }
1295 }
1296
1297 /*
1298  * Window was resized
1299  */
1300 void MainWindow::resizeEvent(QResizeEvent *event)
1301 {
1302         if(event) QMainWindow::resizeEvent(event);
1303
1304         if(QWidget *port = ui->sourceFileView->viewport())
1305         {
1306                 m_dropNoteLabel->setGeometry(port->geometry());
1307         }
1308
1309         if(QWidget *port = ui->outputFolderView->viewport())
1310         {
1311                 m_outputFolderNoteBox->setGeometry(16, (port->height() - 64) / 2, port->width() - 32, 64);
1312         }
1313 }
1314
1315 /*
1316  * Key press event filter
1317  */
1318 void MainWindow::keyPressEvent(QKeyEvent *e)
1319 {
1320         if(e->key() == Qt::Key_Delete)
1321         {
1322                 if(ui->sourceFileView->isVisible())
1323                 {
1324                         QTimer::singleShot(0, this, SLOT(removeFileButtonClicked()));
1325                         return;
1326                 }
1327         }
1328
1329         if(e->modifiers().testFlag(Qt::ControlModifier) && (e->key() == Qt::Key_F5))
1330         {
1331                 initializeTranslation();
1332                 MUtils::Sound::beep(MUtils::Sound::BEEP_NFO);
1333                 return;
1334         }
1335
1336         if(e->key() == Qt::Key_F5)
1337         {
1338                 if(ui->outputFolderView->isVisible())
1339                 {
1340                         QTimer::singleShot(0, this, SLOT(refreshFolderContextActionTriggered()));
1341                         return;
1342                 }
1343         }
1344
1345         QMainWindow::keyPressEvent(e);
1346 }
1347
1348 /*
1349  * Event filter
1350  */
1351 bool MainWindow::eventFilter(QObject *obj, QEvent *event)
1352 {
1353         if(obj == m_fileSystemModel.data())
1354         {
1355                 if(QApplication::overrideCursor() == NULL)
1356                 {
1357                         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1358                         QTimer::singleShot(250, this, SLOT(restoreCursor()));
1359                 }
1360         }
1361
1362         return QMainWindow::eventFilter(obj, event);
1363 }
1364
1365 bool MainWindow::event(QEvent *e)
1366 {
1367         switch(e->type())
1368         {
1369         case MUtils::GUI::USER_EVENT_QUERYENDSESSION:
1370                 qWarning("System is shutting down, main window prepares to close...");
1371                 if(BANNER_VISIBLE) m_banner->close();
1372                 if(m_delayedFileTimer->isActive()) m_delayedFileTimer->stop();
1373                 return true;
1374         case MUtils::GUI::USER_EVENT_ENDSESSION:
1375                 qWarning("System is shutting down, main window will close now...");
1376                 if(isVisible())
1377                 {
1378                         while(!close())
1379                         {
1380                                 QApplication::processEvents(QEventLoop::WaitForMoreEvents & QEventLoop::ExcludeUserInputEvents);
1381                         }
1382                 }
1383                 m_fileListModel->clearFiles();
1384                 return true;
1385         case QEvent::MouseButtonPress:
1386                 if(ui->outputFolderEdit->isVisible())
1387                 {
1388                         QTimer::singleShot(0, this, SLOT(outputFolderEditFinished()));
1389                 }
1390         default:
1391                 return QMainWindow::event(e);
1392         }
1393 }
1394
1395 bool MainWindow::winEvent(MSG *message, long *result)
1396 {
1397         if(MUtils::GUI::sysmenu_check_msg(message, IDM_ABOUTBOX))
1398         {
1399                 QTimer::singleShot(0, ui->buttonAbout, SLOT(click()));
1400                 *result = 0;
1401                 return true;
1402         }
1403         return false;
1404 }
1405
1406 ////////////////////////////////////////////////////////////
1407 // Slots
1408 ////////////////////////////////////////////////////////////
1409
1410 // =========================================================
1411 // Show window slots
1412 // =========================================================
1413
1414 /*
1415  * Window shown
1416  */
1417 void MainWindow::windowShown(void)
1418 {
1419         const MUtils::OS::ArgumentMap &arguments = MUtils::OS::arguments(); //QApplication::arguments();
1420
1421         //Force resize event
1422         resizeEvent(NULL);
1423
1424         //First run?
1425         const bool firstRun = arguments.contains("first-run");
1426
1427         //Check license
1428         if((m_settings->licenseAccepted() <= 0) || firstRun)
1429         {
1430                 int iAccepted = m_settings->licenseAccepted();
1431
1432                 if((iAccepted == 0) || firstRun)
1433                 {
1434                         AboutDialog *about = new AboutDialog(m_settings, this, true);
1435                         iAccepted = about->exec();
1436                         if(iAccepted <= 0) iAccepted = -2;
1437                         MUTILS_DELETE(about);
1438                 }
1439
1440                 if(iAccepted <= 0)
1441                 {
1442                         m_settings->licenseAccepted(++iAccepted);
1443                         m_settings->syncNow();
1444                         QApplication::processEvents();
1445                         MUtils::Sound::play_sound("whammy", false);
1446                         QMessageBox::critical(this, tr("License Declined"), tr("You have declined the license. Consequently the application will exit now!"), tr("Goodbye!"));
1447                         QFileInfo uninstallerInfo = QFileInfo(QString("%1/Uninstall.exe").arg(QApplication::applicationDirPath()));
1448                         if(uninstallerInfo.exists())
1449                         {
1450                                 QString uninstallerDir = uninstallerInfo.canonicalPath();
1451                                 QString uninstallerPath = uninstallerInfo.canonicalFilePath();
1452                                 for(int i = 0; i < 3; i++)
1453                                 {
1454                                         if(MUtils::OS::shell_open(this, QDir::toNativeSeparators(uninstallerPath), "/Force", QDir::toNativeSeparators(uninstallerDir))) break;
1455                                 }
1456                         }
1457                         QApplication::quit();
1458                         return;
1459                 }
1460                 
1461                 MUtils::Sound::play_sound("woohoo", false);
1462                 m_settings->licenseAccepted(1);
1463                 m_settings->syncNow();
1464                 if(lamexp_version_demo()) showAnnounceBox();
1465         }
1466         
1467         //Check for expiration
1468         if(lamexp_version_demo())
1469         {
1470                 if(MUtils::OS::current_date() >= lamexp_version_expires())
1471                 {
1472                         qWarning("Binary has expired !!!");
1473                         MUtils::Sound::play_sound("whammy", false);
1474                         if(QMessageBox::warning(this, tr("LameXP - Expired"), QString("%1<br>%2").arg(NOBR(tr("This demo (pre-release) version of LameXP has expired at %1.").arg(lamexp_version_expires().toString(Qt::ISODate))), NOBR(tr("LameXP is free software and release versions won't expire."))), tr("Check for Updates"), tr("Exit Program")) == 0)
1475                         {
1476                                 checkForUpdates();
1477                         }
1478                         QApplication::quit();
1479                         return;
1480                 }
1481         }
1482
1483         //Slow startup indicator
1484         if(m_settings->slowStartup() && m_settings->antivirNotificationsEnabled())
1485         {
1486                 QString message;
1487                 message += NOBR(tr("It seems that a bogus anti-virus software is slowing down the startup of LameXP.")).append("<br>");
1488                 message += NOBR(tr("Please refer to the %1 document for details and solutions!")).arg(LINK_EX(QString("%1/Manual.html#performance-issues").arg(g_documents_base_url), tr("Manual"))).append("<br>");
1489                 if(QMessageBox::warning(this, tr("Slow Startup"), message, tr("Discard"), tr("Don't Show Again")) == 1)
1490                 {
1491                         m_settings->antivirNotificationsEnabled(false);
1492                         ui->actionDisableSlowStartupNotifications->setChecked(!m_settings->antivirNotificationsEnabled());
1493                 }
1494         }
1495
1496         //Update reminder
1497         if(MUtils::OS::current_date() >= MUtils::Version::app_build_date().addYears(1))
1498         {
1499                 qWarning("Binary is more than a year old, time to update!");
1500                 SHOW_CORNER_WIDGET(true);
1501                 int ret = QMessageBox::warning(this, tr("Urgent Update"), NOBR(tr("Your version of LameXP is more than a year old. Time for an update!")), tr("Check for Updates"), tr("Exit Program"), tr("Ignore"));
1502                 switch(ret)
1503                 {
1504                 case 0:
1505                         if(checkForUpdates())
1506                         {
1507                                 QApplication::quit();
1508                                 return;
1509                         }
1510                         break;
1511                 case 1:
1512                         QApplication::quit();
1513                         return;
1514                 default:
1515                         QEventLoop loop; QTimer::singleShot(7000, &loop, SLOT(quit()));
1516                         MUtils::Sound::play_sound("waiting", true);
1517                         showBanner(tr("Skipping update check this time, please be patient..."), &loop);
1518                         break;
1519                 }
1520         }
1521         else
1522         {
1523                 QDate lastUpdateCheck = QDate::fromString(m_settings->autoUpdateLastCheck(), Qt::ISODate);
1524                 if((!firstRun) && ((!lastUpdateCheck.isValid()) || (MUtils::OS::current_date() >= lastUpdateCheck.addDays(14))))
1525                 {
1526                         SHOW_CORNER_WIDGET(true);
1527                         if(m_settings->autoUpdateEnabled())
1528                         {
1529                                 if(QMessageBox::information(this, tr("Update Reminder"), NOBR(lastUpdateCheck.isValid() ? tr("Your last update check was more than 14 days ago. Check for updates now?") : tr("Your did not check for LameXP updates yet. Check for updates now?")), tr("Check for Updates"), tr("Postpone")) == 0)
1530                                 {
1531                                         if(checkForUpdates())
1532                                         {
1533                                                 QApplication::quit();
1534                                                 return;
1535                                         }
1536                                 }
1537                         }
1538                 }
1539         }
1540
1541         //Check for AAC support
1542         const int aacEncoder = EncoderRegistry::getAacEncoder();
1543         if(aacEncoder == SettingsModel::AAC_ENCODER_NERO)
1544         {
1545                 if(m_settings->neroAacNotificationsEnabled())
1546                 {
1547                         if(lamexp_tools_version("neroAacEnc.exe") < lamexp_toolver_neroaac())
1548                         {
1549                                 QString messageText;
1550                                 messageText += NOBR(tr("LameXP detected that your version of the Nero AAC encoder is outdated!")).append("<br>");
1551                                 messageText += NOBR(tr("The current version available is %1 (or later), but you still have version %2 installed.").arg(lamexp_version2string("?.?.?.?", lamexp_toolver_neroaac(), tr("n/a")), lamexp_version2string("?.?.?.?", lamexp_tools_version("neroAacEnc.exe"), tr("n/a")))).append("<br><br>");
1552                                 messageText += NOBR(tr("You can download the latest version of the Nero AAC encoder from the Nero website at:")).append("<br>");
1553                                 messageText += "<nobr><tt>" + LINK(AboutDialog::neroAacUrl) + "</tt></nobr><br><br>";
1554                                 messageText += NOBR(tr("(Hint: Please ignore the name of the downloaded ZIP file and check the included 'changelog.txt' instead!)")).append("<br>");
1555                                 QMessageBox::information(this, tr("AAC Encoder Outdated"), messageText);
1556                         }
1557                 }
1558         }
1559         else
1560         {
1561                 if(m_settings->neroAacNotificationsEnabled() && (aacEncoder <= SettingsModel::AAC_ENCODER_NONE))
1562                 {
1563                         QString appPath = QDir(QCoreApplication::applicationDirPath()).canonicalPath();
1564                         if(appPath.isEmpty()) appPath = QCoreApplication::applicationDirPath();
1565                         QString messageText;
1566                         messageText += NOBR(tr("The Nero AAC encoder could not be found. AAC encoding support will be disabled.")).append("<br>");
1567                         messageText += NOBR(tr("Please put 'neroAacEnc.exe', 'neroAacDec.exe' and 'neroAacTag.exe' into the LameXP directory!")).append("<br><br>");
1568                         messageText += NOBR(tr("Your LameXP directory is located here:")).append("<br>");
1569                         messageText += QString("<nobr><tt>%1</tt></nobr><br><br>").arg(FSLINK(QDir::toNativeSeparators(appPath)));
1570                         messageText += NOBR(tr("You can download the Nero AAC encoder for free from the official Nero website at:")).append("<br>");
1571                         messageText += "<nobr><tt>" + LINK(AboutDialog::neroAacUrl) + "</tt></nobr><br>";
1572                         if(QMessageBox::information(this, tr("AAC Support Disabled"), messageText, tr("Discard"), tr("Don't Show Again")) == 1)
1573                         {
1574                                 m_settings->neroAacNotificationsEnabled(false);
1575                                 ui->actionDisableNeroAacNotifications->setChecked(!m_settings->neroAacNotificationsEnabled());
1576                         }
1577                 }
1578         }
1579
1580         //Add files from the command-line
1581         QStringList addedFiles;
1582         foreach(const QString &value, arguments.values("add"))
1583         {
1584                 if(!value.isEmpty())
1585                 {
1586                         QFileInfo currentFile(value);
1587                         qDebug("Adding file from CLI: %s", MUTILS_UTF8(currentFile.absoluteFilePath()));
1588                         addedFiles.append(currentFile.absoluteFilePath());
1589                 }
1590         }
1591         if(!addedFiles.isEmpty())
1592         {
1593                 addFilesDelayed(addedFiles);
1594         }
1595
1596         //Add folders from the command-line
1597         foreach(const QString &value, arguments.values("add-folder"))
1598         {
1599                 if(!value.isEmpty())
1600                 {
1601                         const QFileInfo currentFile(value);
1602                         qDebug("Adding folder from CLI: %s", MUTILS_UTF8(currentFile.absoluteFilePath()));
1603                         addFolder(currentFile.absoluteFilePath(), false, true);
1604                 }
1605         }
1606         foreach(const QString &value, arguments.values("add-recursive"))
1607         {
1608                 if(!value.isEmpty())
1609                 {
1610                         const QFileInfo currentFile(value);
1611                         qDebug("Adding folder recursively from CLI: %s", MUTILS_UTF8(currentFile.absoluteFilePath()));
1612                         addFolder(currentFile.absoluteFilePath(), true, true);
1613                 }
1614         }
1615
1616         //Enable shell integration
1617         if(m_settings->shellIntegrationEnabled())
1618         {
1619                 ShellIntegration::install();
1620         }
1621
1622         //Make DropBox visible
1623         if(m_settings->dropBoxWidgetEnabled())
1624         {
1625                 m_dropBox->setVisible(true);
1626         }
1627 }
1628
1629 /*
1630  * Show announce box
1631  */
1632 void MainWindow::showAnnounceBox(void)
1633 {
1634         const unsigned int timeout = 8U;
1635
1636         const QString announceText = QString("%1<br><br>%2<br><nobr><tt>%3</tt></nobr><br>").arg
1637         (
1638                 NOBR("We are still looking for LameXP translators!"),
1639                 NOBR("If you are willing to translate LameXP to your language or to complete an existing translation, please refer to:"),
1640                 LINK("http://lamexp.sourceforge.net/doc/Translate.html")
1641         );
1642
1643         QMessageBox *announceBox = new QMessageBox(QMessageBox::Warning, "We want you!", announceText, QMessageBox::NoButton, this);
1644         announceBox->setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
1645         announceBox->setIconPixmap(QIcon(":/images/Announcement.png").pixmap(64,79));
1646         
1647         QTimer *timers[timeout+1];
1648         QPushButton *buttons[timeout+1];
1649
1650         for(unsigned int i = 0; i <= timeout; i++)
1651         {
1652                 QString text = (i > 0) ? QString("%1 (%2)").arg(tr("Discard"), QString::number(i)) : tr("Discard");
1653                 buttons[i] = announceBox->addButton(text, (i > 0) ? QMessageBox::NoRole : QMessageBox::AcceptRole);
1654         }
1655
1656         for(unsigned int i = 0; i <= timeout; i++)
1657         {
1658                 buttons[i]->setEnabled(i == 0);
1659                 buttons[i]->setVisible(i == timeout);
1660         }
1661
1662         for(unsigned int i = 0; i < timeout; i++)
1663         {
1664                 timers[i] = new QTimer(this);
1665                 timers[i]->setSingleShot(true);
1666                 timers[i]->setInterval(1000);
1667                 connect(timers[i], SIGNAL(timeout()), buttons[i+1], SLOT(hide()));
1668                 connect(timers[i], SIGNAL(timeout()), buttons[i], SLOT(show()));
1669                 if(i > 0)
1670                 {
1671                         connect(timers[i], SIGNAL(timeout()), timers[i-1], SLOT(start()));
1672                 }
1673         }
1674
1675         timers[timeout-1]->start();
1676         announceBox->exec();
1677
1678         for(unsigned int i = 0; i < timeout; i++)
1679         {
1680                 timers[i]->stop();
1681                 MUTILS_DELETE(timers[i]);
1682         }
1683
1684         MUTILS_DELETE(announceBox);
1685 }
1686
1687 // =========================================================
1688 // Main button solots
1689 // =========================================================
1690
1691 /*
1692  * Encode button
1693  */
1694 void MainWindow::encodeButtonClicked(void)
1695 {
1696         static const unsigned __int64 oneGigabyte = 1073741824ui64; 
1697         static const unsigned __int64 minimumFreeDiskspaceMultiplier = 2ui64;
1698         static const char *writeTestBuffer = "LAMEXP_WRITE_TEST";
1699         
1700         ABORT_IF_BUSY;
1701
1702         if(m_fileListModel->rowCount() < 1)
1703         {
1704                 QMessageBox::warning(this, tr("LameXP"), NOBR(tr("You must add at least one file to the list before proceeding!")));
1705                 ui->tabWidget->setCurrentIndex(0);
1706                 return;
1707         }
1708         
1709         QString tempFolder = m_settings->customTempPathEnabled() ? m_settings->customTempPath() : MUtils::temp_folder();
1710         if(!QFileInfo(tempFolder).exists() || !QFileInfo(tempFolder).isDir())
1711         {
1712                 if(QMessageBox::warning(this, tr("Not Found"), QString("%1<br><tt>%2</tt>").arg(NOBR(tr("Your currently selected TEMP folder does not exist anymore:")), NOBR(QDir::toNativeSeparators(tempFolder))), tr("Restore Default"), tr("Cancel")) == 0)
1713                 {
1714                         SET_CHECKBOX_STATE(ui->checkBoxUseSystemTempFolder, (!m_settings->customTempPathEnabledDefault()));
1715                 }
1716                 return;
1717         }
1718
1719         quint64 currentFreeDiskspace = 0;
1720         if(MUtils::OS::free_diskspace(tempFolder, currentFreeDiskspace))
1721         {
1722                 if(currentFreeDiskspace < (oneGigabyte * minimumFreeDiskspaceMultiplier))
1723                 {
1724                         QStringList tempFolderParts = tempFolder.split("/", QString::SkipEmptyParts, Qt::CaseInsensitive);
1725                         tempFolderParts.takeLast();
1726                         PLAY_SOUND_OPTIONAL("whammy", false);
1727                         QString lowDiskspaceMsg = QString("%1<br>%2<br><br>%3<br>%4<br>").arg
1728                         (
1729                                 NOBR(tr("There are less than %1 GB of free diskspace available on your system's TEMP folder.").arg(QString::number(minimumFreeDiskspaceMultiplier))),
1730                                 NOBR(tr("It is highly recommend to free up more diskspace before proceeding with the encode!")),
1731                                 NOBR(tr("Your TEMP folder is located at:")),
1732                                 QString("<nobr><tt>%1</tt></nobr>").arg(FSLINK(tempFolderParts.join("\\")))
1733                         );
1734                         switch(QMessageBox::warning(this, tr("Low Diskspace Warning"), lowDiskspaceMsg, tr("Abort Encoding Process"), tr("Clean Disk Now"), tr("Ignore")))
1735                         {
1736                         case 1:
1737                                 QProcess::startDetached(QString("%1/cleanmgr.exe").arg(MUtils::OS::known_folder(MUtils::OS::FOLDER_SYSTEMFOLDER)), QStringList() << "/D" << tempFolderParts.first());
1738                         case 0:
1739                                 return;
1740                                 break;
1741                         default:
1742                                 QMessageBox::warning(this, tr("Low Diskspace"), NOBR(tr("You are proceeding with low diskspace. Problems might occur!")));
1743                                 break;
1744                         }
1745                 }
1746         }
1747
1748         switch(m_settings->compressionEncoder())
1749         {
1750         case SettingsModel::MP3Encoder:
1751         case SettingsModel::VorbisEncoder:
1752         case SettingsModel::AACEncoder:
1753         case SettingsModel::AC3Encoder:
1754         case SettingsModel::FLACEncoder:
1755         case SettingsModel::OpusEncoder:
1756         case SettingsModel::DCAEncoder:
1757         case SettingsModel::MACEncoder:
1758         case SettingsModel::PCMEncoder:
1759                 break;
1760         default:
1761                 QMessageBox::warning(this, tr("LameXP"), tr("Sorry, an unsupported encoder has been chosen!"));
1762                 ui->tabWidget->setCurrentIndex(3);
1763                 return;
1764         }
1765
1766         if(!m_settings->outputToSourceDir())
1767         {
1768                 QFile writeTest(QString("%1/~%2.txt").arg(m_settings->outputDir(), MUtils::next_rand_str()));
1769                 if(!(writeTest.open(QIODevice::ReadWrite) && (writeTest.write(writeTestBuffer) == strlen(writeTestBuffer))))
1770                 {
1771                         QMessageBox::warning(this, tr("LameXP"), QString("%1<br><nobr>%2</nobr><br><br>%3").arg(tr("Cannot write to the selected output directory."), m_settings->outputDir(), tr("Please choose a different directory!")));
1772                         ui->tabWidget->setCurrentIndex(1);
1773                         return;
1774                 }
1775                 else
1776                 {
1777                         writeTest.close();
1778                         writeTest.remove();
1779                 }
1780         }
1781
1782         m_accepted = true;
1783         close();
1784 }
1785
1786 /*
1787  * About button
1788  */
1789 void MainWindow::aboutButtonClicked(void)
1790 {
1791         ABORT_IF_BUSY;
1792         WidgetHideHelper hiderHelper(m_dropBox.data());
1793         QScopedPointer<AboutDialog> aboutBox(new AboutDialog(m_settings, this));
1794         aboutBox->exec();
1795 }
1796
1797 /*
1798  * Close button
1799  */
1800 void MainWindow::closeButtonClicked(void)
1801 {
1802         ABORT_IF_BUSY;
1803         close();
1804 }
1805
1806 // =========================================================
1807 // Tab widget slots
1808 // =========================================================
1809
1810 /*
1811  * Tab page changed
1812  */
1813 void MainWindow::tabPageChanged(int idx, const bool silent)
1814 {
1815         resizeEvent(NULL);
1816         
1817         //Update "view" menu
1818         QList<QAction*> actions = m_tabActionGroup->actions();
1819         for(int i = 0; i < actions.count(); i++)
1820         {
1821                 bool ok = false;
1822                 int actionIndex = actions.at(i)->data().toInt(&ok);
1823                 if(ok && actionIndex == idx)
1824                 {
1825                         actions.at(i)->setChecked(true);
1826                 }
1827         }
1828
1829         //Play tick sound
1830         if(!silent)
1831         {
1832                 PLAY_SOUND_OPTIONAL("tick", true);
1833         }
1834
1835         int initialWidth = this->width();
1836         int maximumWidth = QApplication::desktop()->availableGeometry().width();
1837
1838         //Make sure all tab headers are fully visible
1839         if(this->isVisible())
1840         {
1841                 int delta = ui->tabWidget->sizeHint().width() - ui->tabWidget->width();
1842                 if(delta > 0)
1843                 {
1844                         this->resize(qMin(this->width() + delta, maximumWidth), this->height());
1845                 }
1846         }
1847
1848         //Tab specific operations
1849         if(idx == ui->tabWidget->indexOf(ui->tabOptions) && ui->scrollArea->widget() && this->isVisible())
1850         {
1851                 ui->scrollArea->widget()->updateGeometry();
1852                 ui->scrollArea->viewport()->updateGeometry();
1853                 qApp->processEvents();
1854                 int delta = ui->scrollArea->widget()->width() - ui->scrollArea->viewport()->width();
1855                 if(delta > 0)
1856                 {
1857                         this->resize(qMin(this->width() + delta, maximumWidth), this->height());
1858                 }
1859         }
1860         else if(idx == ui->tabWidget->indexOf(ui->tabSourceFiles))
1861         {
1862                 m_dropNoteLabel->setGeometry(0, 0, ui->sourceFileView->width(), ui->sourceFileView->height());
1863         }
1864         else if(idx == ui->tabWidget->indexOf(ui->tabOutputDir))
1865         {
1866                 if(!m_fileSystemModel)
1867                 {
1868                         QTimer::singleShot(125, this, SLOT(initOutputFolderModel()));
1869                 }
1870                 else
1871                 {
1872                         CENTER_CURRENT_OUTPUT_FOLDER_DELAYED();
1873                 }
1874         }
1875
1876         //Center window around previous position
1877         if(initialWidth < this->width())
1878         {
1879                 QPoint prevPos = this->pos();
1880                 int delta = (this->width() - initialWidth) >> 2;
1881                 move(prevPos.x() - delta, prevPos.y());
1882         }
1883 }
1884
1885 /*
1886  * Tab action triggered
1887  */
1888 void MainWindow::tabActionActivated(QAction *action)
1889 {
1890         if(action && action->data().isValid())
1891         {
1892                 bool ok = false;
1893                 int index = action->data().toInt(&ok);
1894                 if(ok)
1895                 {
1896                         ui->tabWidget->setCurrentIndex(index);
1897                 }
1898         }
1899 }
1900
1901 // =========================================================
1902 // Menubar slots
1903 // =========================================================
1904
1905 /*
1906  * Handle corner widget Event
1907  */
1908 void MainWindow::cornerWidgetEventOccurred(QWidget *sender, QEvent *event)
1909 {
1910         if(event->type() == QEvent::MouseButtonPress)
1911         {
1912                 QTimer::singleShot(0, this, SLOT(checkUpdatesActionActivated()));
1913         }
1914 }
1915
1916 // =========================================================
1917 // View menu slots
1918 // =========================================================
1919
1920 /*
1921  * Style action triggered
1922  */
1923 void MainWindow::styleActionActivated(QAction *action)
1924 {
1925         //Change style setting
1926         if(action && action->data().isValid())
1927         {
1928                 bool ok = false;
1929                 int actionIndex = action->data().toInt(&ok);
1930                 if(ok)
1931                 {
1932                         m_settings->interfaceStyle(actionIndex);
1933                 }
1934         }
1935
1936         //Set up the new style
1937         switch(m_settings->interfaceStyle())
1938         {
1939         case 1:
1940                 if(ui->actionStyleCleanlooks->isEnabled())
1941                 {
1942                         ui->actionStyleCleanlooks->setChecked(true);
1943                         QApplication::setStyle(new QCleanlooksStyle());
1944                         break;
1945                 }
1946         case 2:
1947                 if(ui->actionStyleWindowsVista->isEnabled())
1948                 {
1949                         ui->actionStyleWindowsVista->setChecked(true);
1950                         QApplication::setStyle(new QWindowsVistaStyle());
1951                         break;
1952                 }
1953         case 3:
1954                 if(ui->actionStyleWindowsXP->isEnabled())
1955                 {
1956                         ui->actionStyleWindowsXP->setChecked(true);
1957                         QApplication::setStyle(new QWindowsXPStyle());
1958                         break;
1959                 }
1960         case 4:
1961                 if(ui->actionStyleWindowsClassic->isEnabled())
1962                 {
1963                         ui->actionStyleWindowsClassic->setChecked(true);
1964                         QApplication::setStyle(new QWindowsStyle());
1965                         break;
1966                 }
1967         default:
1968                 ui->actionStylePlastique->setChecked(true);
1969                 QApplication::setStyle(new QPlastiqueStyle());
1970                 break;
1971         }
1972
1973         //Force re-translate after style change
1974         if(QEvent *e = new QEvent(QEvent::LanguageChange))
1975         {
1976                 changeEvent(e);
1977                 MUTILS_DELETE(e);
1978         }
1979
1980         //Make transparent
1981         const type_info &styleType = typeid(*qApp->style());
1982         const bool bTransparent = ((typeid(QWindowsVistaStyle) == styleType) || (typeid(QWindowsXPStyle) == styleType));
1983         MAKE_TRANSPARENT(ui->scrollArea, bTransparent);
1984
1985         //Also force a re-size event
1986         QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1987         resizeEvent(NULL);
1988 }
1989
1990 /*
1991  * Language action triggered
1992  */
1993 void MainWindow::languageActionActivated(QAction *action)
1994 {
1995         if(action->data().type() == QVariant::String)
1996         {
1997                 QString langId = action->data().toString();
1998
1999                 if(MUtils::Translation::install_translator(langId))
2000                 {
2001                         action->setChecked(true);
2002                         ui->actionLoadTranslationFromFile->setChecked(false);
2003                         m_settings->currentLanguage(langId);
2004                         m_settings->currentLanguageFile(QString());
2005                 }
2006         }
2007 }
2008
2009 /*
2010  * Load language from file action triggered
2011  */
2012 void MainWindow::languageFromFileActionActivated(bool checked)
2013 {
2014         QFileDialog dialog(this, tr("Load Translation"));
2015         dialog.setFileMode(QFileDialog::ExistingFile);
2016         dialog.setNameFilter(QString("%1 (*.qm)").arg(tr("Translation Files")));
2017
2018         if(dialog.exec())
2019         {
2020                 QStringList selectedFiles = dialog.selectedFiles();
2021                 const QString qmFile = QFileInfo(selectedFiles.first()).canonicalFilePath();
2022                 if(MUtils::Translation::install_translator_from_file(qmFile))
2023                 {
2024                         QList<QAction*> actions = m_languageActionGroup->actions();
2025                         while(!actions.isEmpty())
2026                         {
2027                                 actions.takeFirst()->setChecked(false);
2028                         }
2029                         ui->actionLoadTranslationFromFile->setChecked(true);
2030                         m_settings->currentLanguageFile(qmFile);
2031                 }
2032                 else
2033                 {
2034                         languageActionActivated(m_languageActionGroup->actions().first());
2035                 }
2036         }
2037 }
2038
2039 // =========================================================
2040 // Tools menu slots
2041 // =========================================================
2042
2043 /*
2044  * Disable update reminder action
2045  */
2046 void MainWindow::disableUpdateReminderActionTriggered(bool checked)
2047 {
2048         if(checked)
2049         {
2050                 if(0 == QMessageBox::question(this, tr("Disable Update Reminder"), NOBR(tr("Do you really want to disable the update reminder?")), tr("Yes"), tr("No"), QString(), 1))
2051                 {
2052                         QMessageBox::information(this, tr("Update Reminder"), QString("%1<br>%2").arg(NOBR(tr("The update reminder has been disabled.")), NOBR(tr("Please remember to check for updates at regular intervals!"))));
2053                         m_settings->autoUpdateEnabled(false);
2054                 }
2055                 else
2056                 {
2057                         m_settings->autoUpdateEnabled(true);
2058                 }
2059         }
2060         else
2061         {
2062                         QMessageBox::information(this, tr("Update Reminder"), NOBR(tr("The update reminder has been re-enabled.")));
2063                         m_settings->autoUpdateEnabled(true);
2064         }
2065
2066         ui->actionDisableUpdateReminder->setChecked(!m_settings->autoUpdateEnabled());
2067 }
2068
2069 /*
2070  * Disable sound effects action
2071  */
2072 void MainWindow::disableSoundsActionTriggered(bool checked)
2073 {
2074         if(checked)
2075         {
2076                 if(0 == QMessageBox::question(this, tr("Disable Sound Effects"), NOBR(tr("Do you really want to disable all sound effects?")), tr("Yes"), tr("No"), QString(), 1))
2077                 {
2078                         QMessageBox::information(this, tr("Sound Effects"), NOBR(tr("All sound effects have been disabled.")));
2079                         m_settings->soundsEnabled(false);
2080                 }
2081                 else
2082                 {
2083                         m_settings->soundsEnabled(true);
2084                 }
2085         }
2086         else
2087         {
2088                         QMessageBox::information(this, tr("Sound Effects"), NOBR(tr("The sound effects have been re-enabled.")));
2089                         m_settings->soundsEnabled(true);
2090         }
2091
2092         ui->actionDisableSounds->setChecked(!m_settings->soundsEnabled());
2093 }
2094
2095 /*
2096  * Disable Nero AAC encoder action
2097  */
2098 void MainWindow::disableNeroAacNotificationsActionTriggered(bool checked)
2099 {
2100         if(checked)
2101         {
2102                 if(0 == QMessageBox::question(this, tr("Nero AAC Notifications"), NOBR(tr("Do you really want to disable all Nero AAC Encoder notifications?")), tr("Yes"), tr("No"), QString(), 1))
2103                 {
2104                         QMessageBox::information(this, tr("Nero AAC Notifications"), NOBR(tr("All Nero AAC Encoder notifications have been disabled.")));
2105                         m_settings->neroAacNotificationsEnabled(false);
2106                 }
2107                 else
2108                 {
2109                         m_settings->neroAacNotificationsEnabled(true);
2110                 }
2111         }
2112         else
2113         {
2114                         QMessageBox::information(this, tr("Nero AAC Notifications"), NOBR(tr("The Nero AAC Encoder notifications have been re-enabled.")));
2115                         m_settings->neroAacNotificationsEnabled(true);
2116         }
2117
2118         ui->actionDisableNeroAacNotifications->setChecked(!m_settings->neroAacNotificationsEnabled());
2119 }
2120
2121 /*
2122  * Disable slow startup action
2123  */
2124 void MainWindow::disableSlowStartupNotificationsActionTriggered(bool checked)
2125 {
2126         if(checked)
2127         {
2128                 if(0 == QMessageBox::question(this, tr("Slow Startup Notifications"), NOBR(tr("Do you really want to disable the slow startup notifications?")), tr("Yes"), tr("No"), QString(), 1))
2129                 {
2130                         QMessageBox::information(this, tr("Slow Startup Notifications"), NOBR(tr("The slow startup notifications have been disabled.")));
2131                         m_settings->antivirNotificationsEnabled(false);
2132                 }
2133                 else
2134                 {
2135                         m_settings->antivirNotificationsEnabled(true);
2136                 }
2137         }
2138         else
2139         {
2140                         QMessageBox::information(this, tr("Slow Startup Notifications"), NOBR(tr("The slow startup notifications have been re-enabled.")));
2141                         m_settings->antivirNotificationsEnabled(true);
2142         }
2143
2144         ui->actionDisableSlowStartupNotifications->setChecked(!m_settings->antivirNotificationsEnabled());
2145 }
2146
2147 /*
2148  * Import a Cue Sheet file
2149  */
2150 void MainWindow::importCueSheetActionTriggered(bool checked)
2151 {
2152         ABORT_IF_BUSY;
2153         WidgetHideHelper hiderHelper(m_dropBox.data());
2154
2155         while(true)
2156         {
2157                 int result = 0;
2158                 QString selectedCueFile;
2159
2160                 if(MUtils::GUI::themes_enabled())
2161                 {
2162                         selectedCueFile = QFileDialog::getOpenFileName(this, tr("Open Cue Sheet"), m_settings->mostRecentInputPath(), QString("%1 (*.cue)").arg(tr("Cue Sheet File")));
2163                 }
2164                 else
2165                 {
2166                         QFileDialog dialog(this, tr("Open Cue Sheet"));
2167                         dialog.setFileMode(QFileDialog::ExistingFile);
2168                         dialog.setNameFilter(QString("%1 (*.cue)").arg(tr("Cue Sheet File")));
2169                         dialog.setDirectory(m_settings->mostRecentInputPath());
2170                         if(dialog.exec())
2171                         {
2172                                 selectedCueFile = dialog.selectedFiles().first();
2173                         }
2174                 }
2175
2176                 if(!selectedCueFile.isEmpty())
2177                 {
2178                         m_settings->mostRecentInputPath(QFileInfo(selectedCueFile).canonicalPath());
2179                         FileListBlockHelper fileListBlocker(m_fileListModel);
2180                         QScopedPointer<CueImportDialog> cueImporter(new CueImportDialog(this, m_fileListModel, selectedCueFile, m_settings));
2181                         result = cueImporter->exec();
2182                 }
2183
2184                 if(result != (-1))
2185                 {
2186                         qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
2187                         ui->sourceFileView->update();
2188                         qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
2189                         ui->sourceFileView->scrollToBottom();
2190                         qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
2191                         break;
2192                 }
2193         }
2194 }
2195
2196 /*
2197  * Show the "drop box" widget
2198  */
2199 void MainWindow::showDropBoxWidgetActionTriggered(bool checked)
2200 {
2201         m_settings->dropBoxWidgetEnabled(true);
2202         
2203         if(!m_dropBox->isVisible())
2204         {
2205                 m_dropBox->show();
2206                 QTimer::singleShot(2500, m_dropBox.data(), SLOT(showToolTip()));
2207         }
2208         
2209         MUtils::GUI::blink_window(m_dropBox.data());
2210 }
2211
2212 /*
2213  * Check for beta (pre-release) updates
2214  */
2215 void MainWindow::checkForBetaUpdatesActionTriggered(bool checked)
2216 {       
2217         bool checkUpdatesNow = false;
2218         
2219         if(checked)
2220         {
2221                 if(0 == QMessageBox::question(this, tr("Beta Updates"), NOBR(tr("Do you really want LameXP to check for Beta (pre-release) updates?")), tr("Yes"), tr("No"), QString(), 1))
2222                 {
2223                         if(0 == QMessageBox::information(this, tr("Beta Updates"), NOBR(tr("LameXP will check for Beta (pre-release) updates from now on.")), tr("Check Now"), tr("Discard")))
2224                         {
2225                                 checkUpdatesNow = true;
2226                         }
2227                         m_settings->autoUpdateCheckBeta(true);
2228                 }
2229                 else
2230                 {
2231                         m_settings->autoUpdateCheckBeta(false);
2232                 }
2233         }
2234         else
2235         {
2236                         QMessageBox::information(this, tr("Beta Updates"), NOBR(tr("LameXP will <i>not</i> check for Beta (pre-release) updates from now on.")));
2237                         m_settings->autoUpdateCheckBeta(false);
2238         }
2239
2240         ui->actionCheckForBetaUpdates->setChecked(m_settings->autoUpdateCheckBeta());
2241
2242         if(checkUpdatesNow)
2243         {
2244                 if(checkForUpdates())
2245                 {
2246                         QApplication::quit();
2247                 }
2248         }
2249 }
2250
2251 /*
2252  * Hibernate computer action
2253  */
2254 void MainWindow::hibernateComputerActionTriggered(bool checked)
2255 {
2256         if(checked)
2257         {
2258                 if(0 == QMessageBox::question(this, tr("Hibernate Computer"), NOBR(tr("Do you really want the computer to be hibernated on shutdown?")), tr("Yes"), tr("No"), QString(), 1))
2259                 {
2260                         QMessageBox::information(this, tr("Hibernate Computer"), NOBR(tr("LameXP will hibernate the computer on shutdown from now on.")));
2261                         m_settings->hibernateComputer(true);
2262                 }
2263                 else
2264                 {
2265                         m_settings->hibernateComputer(false);
2266                 }
2267         }
2268         else
2269         {
2270                         QMessageBox::information(this, tr("Hibernate Computer"), NOBR(tr("LameXP will <i>not</i> hibernate the computer on shutdown from now on.")));
2271                         m_settings->hibernateComputer(false);
2272         }
2273
2274         ui->actionHibernateComputer->setChecked(m_settings->hibernateComputer());
2275 }
2276
2277 /*
2278  * Disable shell integration action
2279  */
2280 void MainWindow::disableShellIntegrationActionTriggered(bool checked)
2281 {
2282         if(checked)
2283         {
2284                 if(0 == QMessageBox::question(this, tr("Shell Integration"), NOBR(tr("Do you really want to disable the LameXP shell integration?")), tr("Yes"), tr("No"), QString(), 1))
2285                 {
2286                         ShellIntegration::remove();
2287                         QMessageBox::information(this, tr("Shell Integration"), NOBR(tr("The LameXP shell integration has been disabled.")));
2288                         m_settings->shellIntegrationEnabled(false);
2289                 }
2290                 else
2291                 {
2292                         m_settings->shellIntegrationEnabled(true);
2293                 }
2294         }
2295         else
2296         {
2297                         ShellIntegration::install();
2298                         QMessageBox::information(this, tr("Shell Integration"), NOBR(tr("The LameXP shell integration has been re-enabled.")));
2299                         m_settings->shellIntegrationEnabled(true);
2300         }
2301
2302         ui->actionDisableShellIntegration->setChecked(!m_settings->shellIntegrationEnabled());
2303         
2304         if(lamexp_version_portable() && ui->actionDisableShellIntegration->isChecked())
2305         {
2306                 ui->actionDisableShellIntegration->setEnabled(false);
2307         }
2308 }
2309
2310 // =========================================================
2311 // Help menu slots
2312 // =========================================================
2313
2314 /*
2315  * Visit homepage action
2316  */
2317 void MainWindow::visitHomepageActionActivated(void)
2318 {
2319         if(QAction *action = dynamic_cast<QAction*>(QObject::sender()))
2320         {
2321                 if(action->data().isValid() && (action->data().type() == QVariant::String))
2322                 {
2323                         QDesktopServices::openUrl(QUrl(action->data().toString()));
2324                 }
2325         }
2326 }
2327
2328 /*
2329  * Show document
2330  */
2331 void MainWindow::documentActionActivated(void)
2332 {
2333         if(QAction *action = dynamic_cast<QAction*>(QObject::sender()))
2334         {
2335                 openDocumentLink(action);
2336         }
2337 }
2338
2339 /*
2340  * Check for updates action
2341  */
2342 void MainWindow::checkUpdatesActionActivated(void)
2343 {
2344         ABORT_IF_BUSY;
2345         WidgetHideHelper hiderHelper(m_dropBox.data());
2346         
2347         if(checkForUpdates())
2348         {
2349                 QApplication::quit();
2350         }
2351 }
2352
2353 // =========================================================
2354 // Source file slots
2355 // =========================================================
2356
2357 /*
2358  * Add file(s) button
2359  */
2360 void MainWindow::addFilesButtonClicked(void)
2361 {
2362         ABORT_IF_BUSY;
2363         WidgetHideHelper hiderHelper(m_dropBox.data());
2364
2365         if(MUtils::GUI::themes_enabled() && (!MUTILS_DEBUG))
2366         {
2367                 QStringList fileTypeFilters = DecoderRegistry::getSupportedTypes();
2368                 QStringList selectedFiles = QFileDialog::getOpenFileNames(this, tr("Add file(s)"), m_settings->mostRecentInputPath(), fileTypeFilters.join(";;"));
2369                 if(!selectedFiles.isEmpty())
2370                 {
2371                         m_settings->mostRecentInputPath(QFileInfo(selectedFiles.first()).canonicalPath());
2372                         addFiles(selectedFiles);
2373                 }
2374         }
2375         else
2376         {
2377                 QFileDialog dialog(this, tr("Add file(s)"));
2378                 QStringList fileTypeFilters = DecoderRegistry::getSupportedTypes();
2379                 dialog.setFileMode(QFileDialog::ExistingFiles);
2380                 dialog.setNameFilter(fileTypeFilters.join(";;"));
2381                 dialog.setDirectory(m_settings->mostRecentInputPath());
2382                 if(dialog.exec())
2383                 {
2384                         QStringList selectedFiles = dialog.selectedFiles();
2385                         if(!selectedFiles.isEmpty())
2386                         {
2387                                 m_settings->mostRecentInputPath(QFileInfo(selectedFiles.first()).canonicalPath());
2388                                 addFiles(selectedFiles);
2389                         }
2390                 }
2391         }
2392 }
2393
2394 /*
2395  * Open folder action
2396  */
2397 void MainWindow::openFolderActionActivated(void)
2398 {
2399         ABORT_IF_BUSY;
2400         QString selectedFolder;
2401         
2402         if(QAction *action = dynamic_cast<QAction*>(QObject::sender()))
2403         {
2404                 WidgetHideHelper hiderHelper(m_dropBox.data());
2405                 if(MUtils::GUI::themes_enabled())
2406                 {
2407                         selectedFolder = QFileDialog::getExistingDirectory(this, tr("Add Folder"), m_settings->mostRecentInputPath());
2408                 }
2409                 else
2410                 {
2411                         QFileDialog dialog(this, tr("Add Folder"));
2412                         dialog.setFileMode(QFileDialog::DirectoryOnly);
2413                         dialog.setDirectory(m_settings->mostRecentInputPath());
2414                         if(dialog.exec())
2415                         {
2416                                 selectedFolder = dialog.selectedFiles().first();
2417                         }
2418                 }
2419
2420                 if(selectedFolder.isEmpty())
2421                 {
2422                         return;
2423                 }
2424
2425                 QStringList filterItems = DecoderRegistry::getSupportedExts();
2426                 filterItems.prepend("*.*");
2427
2428                 bool okay;
2429                 QString filterStr = QInputDialog::getItem(this, tr("Filter Files"), tr("Select filename filter:"), filterItems, 0, false, &okay).trimmed();
2430                 if(!okay)
2431                 {
2432                         return;
2433                 }
2434
2435                 QRegExp regExp("\\*\\.([A-Za-z0-9]+)", Qt::CaseInsensitive);
2436                 if(regExp.lastIndexIn(filterStr) >= 0)
2437                 {
2438                         filterStr = regExp.cap(1).trimmed();
2439                 }
2440                 else
2441                 {
2442                         filterStr.clear();
2443                 }
2444
2445                 m_settings->mostRecentInputPath(QDir(selectedFolder).canonicalPath());
2446                 addFolder(selectedFolder, action->data().toBool(), false, filterStr);
2447         }
2448 }
2449
2450 /*
2451  * Remove file button
2452  */
2453 void MainWindow::removeFileButtonClicked(void)
2454 {
2455         const QItemSelectionModel *const selection = ui->sourceFileView->selectionModel();
2456         if(selection && selection->hasSelection())
2457         {
2458                 int firstRow = -1;
2459                 const QModelIndexList selectedRows = INVERT_LIST(selection->selectedRows());
2460                 if(!selectedRows.isEmpty())
2461                 {
2462                         FileListBlockHelper fileListBlocker(m_fileListModel);
2463                         firstRow = selectedRows.last().row();
2464                         for(QModelIndexList::ConstIterator iter = selectedRows.constBegin(); iter != selectedRows.constEnd(); iter++)
2465                         {
2466                                 if(!m_fileListModel->removeFile(*iter))
2467                                 {
2468                                         break;
2469                                 }
2470                         }
2471                 }
2472                 if(m_fileListModel->rowCount() > 0)
2473                 {
2474                         const QModelIndex position = m_fileListModel->index(((firstRow >= 0) && (firstRow < m_fileListModel->rowCount())) ? firstRow : (m_fileListModel->rowCount() - 1), 0);
2475                         ui->sourceFileView->selectRow(position.row());
2476                         ui->sourceFileView->scrollTo(position, QAbstractItemView::PositionAtCenter);
2477                 }
2478         }
2479         else
2480         {
2481                 MUtils::Sound::beep(MUtils::Sound::BEEP_WRN);
2482         }
2483 }
2484
2485 /*
2486  * Clear files button
2487  */
2488 void MainWindow::clearFilesButtonClicked(void)
2489 {
2490         if(m_fileListModel->rowCount() > 0)
2491         {
2492                 m_fileListModel->clearFiles();
2493         }
2494         else
2495         {
2496                 MUtils::Sound::beep(MUtils::Sound::BEEP_WRN);
2497         }
2498 }
2499
2500 /*
2501  * Move file up button
2502  */
2503 void MainWindow::fileUpButtonClicked(void)
2504 {
2505         moveSelectedFiles(true);
2506 }
2507
2508 /*
2509  * Move file down button
2510  */
2511 void MainWindow::fileDownButtonClicked(void)
2512 {
2513         moveSelectedFiles(false);
2514 }
2515
2516 /*
2517  * Show details button
2518  */
2519 void MainWindow::showDetailsButtonClicked(void)
2520 {
2521         ABORT_IF_BUSY;
2522
2523         int iResult = 0;
2524         QModelIndex index = ui->sourceFileView->currentIndex();
2525         
2526         if(index.isValid())
2527         {
2528                 ui->sourceFileView->selectRow(index.row());
2529                 QScopedPointer<MetaInfoDialog> metaInfoDialog(new MetaInfoDialog(this));
2530                 forever
2531                 {
2532                         AudioFileModel &file = (*m_fileListModel)[index];
2533                         WidgetHideHelper hiderHelper(m_dropBox.data());
2534                         iResult = metaInfoDialog->exec(file, index.row() > 0, index.row() < m_fileListModel->rowCount() - 1);
2535                 
2536                         //Copy all info to Meta Info tab
2537                         if(iResult == INT_MAX)
2538                         {
2539                                 m_metaInfoModel->assignInfoFrom(file);
2540                                 ui->tabWidget->setCurrentIndex(ui->tabWidget->indexOf(ui->tabMetaData));
2541                                 break;
2542                         }
2543
2544                         if(iResult > 0)
2545                         {
2546                                 index = m_fileListModel->index(index.row() + 1, index.column()); 
2547                                 ui->sourceFileView->selectRow(index.row());
2548                                 continue;
2549                         }
2550                         else if(iResult < 0)
2551                         {
2552                                 index = m_fileListModel->index(index.row() - 1, index.column()); 
2553                                 ui->sourceFileView->selectRow(index.row());
2554                                 continue;
2555                         }               
2556
2557                         break; /*close dilalog now*/
2558                 }
2559         }
2560         else
2561         {
2562                 MUtils::Sound::beep(MUtils::Sound::BEEP_WRN);
2563         }
2564
2565         QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2566         sourceFilesScrollbarMoved(0);
2567 }
2568
2569 /*
2570  * Show context menu for source files
2571  */
2572 void MainWindow::sourceFilesContextMenu(const QPoint &pos)
2573 {
2574         QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(QObject::sender());
2575         QWidget *sender = scrollArea ? scrollArea->viewport() : dynamic_cast<QWidget*>(QObject::sender());
2576
2577         if(sender)
2578         {
2579                 if(pos.x() <= sender->width() && pos.y() <= sender->height() && pos.x() >= 0 && pos.y() >= 0)
2580                 {
2581                         m_sourceFilesContextMenu->popup(sender->mapToGlobal(pos));
2582                 }
2583         }
2584 }
2585
2586 /*
2587  * Scrollbar of source files moved
2588  */
2589 void MainWindow::sourceFilesScrollbarMoved(int)
2590 {
2591         ui->sourceFileView->resizeColumnToContents(0);
2592 }
2593
2594 /*
2595  * Open selected file in external player
2596  */
2597 void MainWindow::previewContextActionTriggered(void)
2598 {
2599         QModelIndex index = ui->sourceFileView->currentIndex();
2600         if(!index.isValid())
2601         {
2602                 return;
2603         }
2604
2605         if(!MUtils::OS::open_media_file(m_fileListModel->getFile(index).filePath()))
2606         {
2607                 qDebug("Player not found, falling back to default application...");
2608                 QDesktopServices::openUrl(QString("file:///").append(m_fileListModel->getFile(index).filePath()));
2609         }
2610 }
2611
2612 /*
2613  * Find selected file in explorer
2614  */
2615 void MainWindow::findFileContextActionTriggered(void)
2616 {
2617         QModelIndex index = ui->sourceFileView->currentIndex();
2618         if(index.isValid())
2619         {
2620                 QString systemRootPath;
2621
2622                 QDir systemRoot(MUtils::OS::known_folder(MUtils::OS::FOLDER_SYSTEMFOLDER));
2623                 if(systemRoot.exists() && systemRoot.cdUp())
2624                 {
2625                         systemRootPath = systemRoot.canonicalPath();
2626                 }
2627
2628                 if(!systemRootPath.isEmpty())
2629                 {
2630                         QFileInfo explorer(QString("%1/explorer.exe").arg(systemRootPath));
2631                         if(explorer.exists() && explorer.isFile())
2632                         {
2633                                 QProcess::execute(explorer.canonicalFilePath(), QStringList() << "/select," << QDir::toNativeSeparators(m_fileListModel->getFile(index).filePath()));
2634                                 return;
2635                         }
2636                 }
2637                 else
2638                 {
2639                         qWarning("SystemRoot directory could not be detected!");
2640                 }
2641         }
2642 }
2643
2644 /*
2645  * Add all dropped files
2646  */
2647 void MainWindow::handleDroppedFiles(void)
2648 {
2649         ABORT_IF_BUSY;
2650
2651         static const int MIN_COUNT = 16;
2652         const QString bannerText = tr("Loading dropped files or folders, please wait...");
2653         bool bUseBanner = false;
2654
2655         showBanner(bannerText, bUseBanner, (m_droppedFileList->count() >= MIN_COUNT));
2656
2657         QStringList droppedFiles;
2658         while(!m_droppedFileList->isEmpty())
2659         {
2660                 QFileInfo file(m_droppedFileList->takeFirst().toLocalFile());
2661                 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2662
2663                 if(!file.exists())
2664                 {
2665                         continue;
2666                 }
2667
2668                 if(file.isFile())
2669                 {
2670                         qDebug("Dropped File: %s", MUTILS_UTF8(file.canonicalFilePath()));
2671                         droppedFiles << file.canonicalFilePath();
2672                         continue;
2673                 }
2674
2675                 if(file.isDir())
2676                 {
2677                         qDebug("Dropped Folder: %s", MUTILS_UTF8(file.canonicalFilePath()));
2678                         QFileInfoList list = QDir(file.canonicalFilePath()).entryInfoList(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks);
2679                         if(list.count() > 0)
2680                         {
2681                                 showBanner(bannerText, bUseBanner, (list.count() >= MIN_COUNT));
2682                                 for(QFileInfoList::ConstIterator iter = list.constBegin(); iter != list.constEnd(); iter++)
2683                                 {
2684                                         droppedFiles << (*iter).canonicalFilePath();
2685                                 }
2686                         }
2687                         else
2688                         {
2689                                 list = QDir(file.canonicalFilePath()).entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
2690                                 showBanner(bannerText, bUseBanner, (list.count() >= MIN_COUNT));
2691                                 for(QFileInfoList::ConstIterator iter = list.constBegin(); iter != list.constEnd(); iter++)
2692                                 {
2693                                         qDebug("Descending to Folder: %s", MUTILS_UTF8((*iter).canonicalFilePath()));
2694                                         m_droppedFileList->prepend(QUrl::fromLocalFile((*iter).canonicalFilePath()));
2695                                 }
2696                         }
2697                 }
2698         }
2699         
2700         if(bUseBanner)
2701         {
2702                 m_banner->close();
2703         }
2704
2705         if(!droppedFiles.isEmpty())
2706         {
2707                 addFiles(droppedFiles);
2708         }
2709 }
2710
2711 /*
2712  * Add all pending files
2713  */
2714 void MainWindow::handleDelayedFiles(void)
2715 {
2716         m_delayedFileTimer->stop();
2717
2718         if(m_delayedFileList->isEmpty())
2719         {
2720                 return;
2721         }
2722
2723         if(BANNER_VISIBLE)
2724         {
2725                 m_delayedFileTimer->start(5000);
2726                 return;
2727         }
2728         
2729         if(ui->tabWidget->currentIndex() != 0)
2730         {
2731                 SignalBlockHelper signalBlockHelper(ui->tabWidget);
2732                 ui->tabWidget->setCurrentIndex(0);
2733                 tabPageChanged(ui->tabWidget->currentIndex(), true);
2734         }
2735         
2736         QStringList selectedFiles;
2737         while(!m_delayedFileList->isEmpty())
2738         {
2739                 QFileInfo currentFile = QFileInfo(m_delayedFileList->takeFirst());
2740                 if(!currentFile.exists() || !currentFile.isFile())
2741                 {
2742                         continue;
2743                 }
2744                 selectedFiles << currentFile.canonicalFilePath();
2745         }
2746         
2747         addFiles(selectedFiles);
2748 }
2749
2750 /*
2751  * Export Meta tags to CSV file
2752  */
2753 void MainWindow::exportCsvContextActionTriggered(void)
2754 {
2755         ABORT_IF_BUSY;
2756         WidgetHideHelper hiderHelper(m_dropBox.data());
2757
2758         QString selectedCsvFile;
2759         if(MUtils::GUI::themes_enabled())
2760         {
2761                 selectedCsvFile = QFileDialog::getSaveFileName(this, tr("Save CSV file"), m_settings->mostRecentInputPath(), QString("%1 (*.csv)").arg(tr("CSV File")));
2762         }
2763         else
2764         {
2765                 QFileDialog dialog(this, tr("Save CSV file"));
2766                 dialog.setFileMode(QFileDialog::AnyFile);
2767                 dialog.setAcceptMode(QFileDialog::AcceptSave);
2768                 dialog.setNameFilter(QString("%1 (*.csv)").arg(tr("CSV File")));
2769                 dialog.setDirectory(m_settings->mostRecentInputPath());
2770                 if(dialog.exec())
2771                 {
2772                         selectedCsvFile = dialog.selectedFiles().first();
2773                 }
2774         }
2775
2776         if(!selectedCsvFile.isEmpty())
2777         {
2778                 m_settings->mostRecentInputPath(QFileInfo(selectedCsvFile).canonicalPath());
2779                 switch(m_fileListModel->exportToCsv(selectedCsvFile))
2780                 {
2781                 case FileListModel::CsvError_NoTags:
2782                         QMessageBox::critical(this, tr("CSV Export"), NOBR(tr("Sorry, there are no meta tags that can be exported!")));
2783                         break;
2784                 case FileListModel::CsvError_FileOpen:
2785                         QMessageBox::critical(this, tr("CSV Export"), NOBR(tr("Sorry, failed to open CSV file for writing!")));
2786                         break;
2787                 case FileListModel::CsvError_FileWrite:
2788                         QMessageBox::critical(this, tr("CSV Export"), NOBR(tr("Sorry, failed to write to the CSV file!")));
2789                         break;
2790                 case FileListModel::CsvError_OK:
2791                         QMessageBox::information(this, tr("CSV Export"), NOBR(tr("The CSV files was created successfully!")));
2792                         break;
2793                 default:
2794                         qWarning("exportToCsv: Unknown return code!");
2795                 }
2796         }
2797 }
2798
2799
2800 /*
2801  * Import Meta tags from CSV file
2802  */
2803 void MainWindow::importCsvContextActionTriggered(void)
2804 {
2805         ABORT_IF_BUSY;
2806         WidgetHideHelper hiderHelper(m_dropBox.data());
2807
2808         QString selectedCsvFile;
2809         if(MUtils::GUI::themes_enabled())
2810         {
2811                 selectedCsvFile = QFileDialog::getOpenFileName(this, tr("Open CSV file"), m_settings->mostRecentInputPath(), QString("%1 (*.csv)").arg(tr("CSV File")));
2812         }
2813         else
2814         {
2815                 QFileDialog dialog(this, tr("Open CSV file"));
2816                 dialog.setFileMode(QFileDialog::ExistingFile);
2817                 dialog.setNameFilter(QString("%1 (*.csv)").arg(tr("CSV File")));
2818                 dialog.setDirectory(m_settings->mostRecentInputPath());
2819                 if(dialog.exec())
2820                 {
2821                         selectedCsvFile = dialog.selectedFiles().first();
2822                 }
2823         }
2824
2825         if(!selectedCsvFile.isEmpty())
2826         {
2827                 m_settings->mostRecentInputPath(QFileInfo(selectedCsvFile).canonicalPath());
2828                 switch(m_fileListModel->importFromCsv(this, selectedCsvFile))
2829                 {
2830                 case FileListModel::CsvError_FileOpen:
2831                         QMessageBox::critical(this, tr("CSV Import"), NOBR(tr("Sorry, failed to open CSV file for reading!")));
2832                         break;
2833                 case FileListModel::CsvError_FileRead:
2834                         QMessageBox::critical(this, tr("CSV Import"), NOBR(tr("Sorry, failed to read from the CSV file!")));
2835                         break;
2836                 case FileListModel::CsvError_NoTags:
2837                         QMessageBox::critical(this, tr("CSV Import"), NOBR(tr("Sorry, the CSV file does not contain any known fields!")));
2838                         break;
2839                 case FileListModel::CsvError_Incomplete:
2840                         QMessageBox::warning(this, tr("CSV Import"), NOBR(tr("CSV file is incomplete. Not all files were updated!")));
2841                         break;
2842                 case FileListModel::CsvError_OK:
2843                         QMessageBox::information(this, tr("CSV Import"), NOBR(tr("The CSV files was imported successfully!")));
2844                         break;
2845                 case FileListModel::CsvError_Aborted:
2846                         /* User aborted, ignore! */
2847                         break;
2848                 default:
2849                         qWarning("exportToCsv: Unknown return code!");
2850                 }
2851         }
2852 }
2853
2854 /*
2855  * Show or hide Drag'n'Drop notice after model reset
2856  */
2857 void MainWindow::sourceModelChanged(void)
2858 {
2859         m_dropNoteLabel->setVisible(m_fileListModel->rowCount() <= 0);
2860 }
2861
2862 // =========================================================
2863 // Output folder slots
2864 // =========================================================
2865
2866 /*
2867  * Output folder changed (mouse clicked)
2868  */
2869 void MainWindow::outputFolderViewClicked(const QModelIndex &index)
2870 {
2871         if(index.isValid() && (ui->outputFolderView->currentIndex() != index))
2872         {
2873                 ui->outputFolderView->setCurrentIndex(index);
2874         }
2875         
2876         if(m_fileSystemModel && index.isValid())
2877         {
2878                 QString selectedDir = m_fileSystemModel->filePath(index);
2879                 if(selectedDir.length() < 3) selectedDir.append(QDir::separator());
2880                 ui->outputFolderLabel->setText(QDir::toNativeSeparators(selectedDir));
2881                 ui->outputFolderLabel->setToolTip(ui->outputFolderLabel->text());
2882                 m_settings->outputDir(selectedDir);
2883         }
2884         else
2885         {
2886                 ui->outputFolderLabel->setText(QDir::toNativeSeparators(m_settings->outputDir()));
2887                 ui->outputFolderLabel->setToolTip(ui->outputFolderLabel->text());
2888         }
2889 }
2890
2891 /*
2892  * Output folder changed (mouse moved)
2893  */
2894 void MainWindow::outputFolderViewMoved(const QModelIndex &index)
2895 {
2896         if(QApplication::mouseButtons() & Qt::LeftButton)
2897         {
2898                 outputFolderViewClicked(index);
2899         }
2900 }
2901
2902 /*
2903  * Goto desktop button
2904  */
2905 void MainWindow::gotoDesktopButtonClicked(void)
2906 {
2907         if(!m_fileSystemModel)
2908         {
2909                 qWarning("File system model not initialized yet!");
2910                 return;
2911         }
2912         
2913         QString desktopPath = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
2914         
2915         if(!desktopPath.isEmpty() && QDir(desktopPath).exists())
2916         {
2917                 ui->outputFolderView->setCurrentIndex(m_fileSystemModel->index(desktopPath));
2918                 outputFolderViewClicked(ui->outputFolderView->currentIndex());
2919                 CENTER_CURRENT_OUTPUT_FOLDER_DELAYED();
2920         }
2921         else
2922         {
2923                 ui->buttonGotoDesktop->setEnabled(false);
2924         }
2925 }
2926
2927 /*
2928  * Goto home folder button
2929  */
2930 void MainWindow::gotoHomeFolderButtonClicked(void)
2931 {
2932         if(!m_fileSystemModel)
2933         {
2934                 qWarning("File system model not initialized yet!");
2935                 return;
2936         }
2937
2938         QString homePath = QDesktopServices::storageLocation(QDesktopServices::HomeLocation);
2939         
2940         if(!homePath.isEmpty() && QDir(homePath).exists())
2941         {
2942                 ui->outputFolderView->setCurrentIndex(m_fileSystemModel->index(homePath));
2943                 outputFolderViewClicked(ui->outputFolderView->currentIndex());
2944                 CENTER_CURRENT_OUTPUT_FOLDER_DELAYED();
2945         }
2946         else
2947         {
2948                 ui->buttonGotoHome->setEnabled(false);
2949         }
2950 }
2951
2952 /*
2953  * Goto music folder button
2954  */
2955 void MainWindow::gotoMusicFolderButtonClicked(void)
2956 {
2957         if(!m_fileSystemModel)
2958         {
2959                 qWarning("File system model not initialized yet!");
2960                 return;
2961         }
2962
2963         QString musicPath = QDesktopServices::storageLocation(QDesktopServices::MusicLocation);
2964         
2965         if(!musicPath.isEmpty() && QDir(musicPath).exists())
2966         {
2967                 ui->outputFolderView->setCurrentIndex(m_fileSystemModel->index(musicPath));
2968                 outputFolderViewClicked(ui->outputFolderView->currentIndex());
2969                 CENTER_CURRENT_OUTPUT_FOLDER_DELAYED();
2970         }
2971         else
2972         {
2973                 ui->buttonGotoMusic->setEnabled(false);
2974         }
2975 }
2976
2977 /*
2978  * Goto music favorite output folder
2979  */
2980 void MainWindow::gotoFavoriteFolder(void)
2981 {
2982         if(!m_fileSystemModel)
2983         {
2984                 qWarning("File system model not initialized yet!");
2985                 return;
2986         }
2987
2988         QAction *item = dynamic_cast<QAction*>(QObject::sender());
2989         
2990         if(item)
2991         {
2992                 QDir path(item->data().toString());
2993                 if(path.exists())
2994                 {
2995                         ui->outputFolderView->setCurrentIndex(m_fileSystemModel->index(path.canonicalPath()));
2996                         outputFolderViewClicked(ui->outputFolderView->currentIndex());
2997                         CENTER_CURRENT_OUTPUT_FOLDER_DELAYED();
2998                 }
2999                 else
3000                 {
3001                         MUtils::Sound::beep(MUtils::Sound::BEEP_ERR);
3002                         m_outputFolderFavoritesMenu->removeAction(item);
3003                         item->deleteLater();
3004                 }
3005         }
3006 }
3007
3008 /*
3009  * Make folder button
3010  */
3011 void MainWindow::makeFolderButtonClicked(void)
3012 {
3013         ABORT_IF_BUSY;
3014
3015         if(m_fileSystemModel.isNull())
3016         {
3017                 qWarning("File system model not initialized yet!");
3018                 return;
3019         }
3020
3021         QDir basePath(m_fileSystemModel->fileInfo(ui->outputFolderView->currentIndex()).absoluteFilePath());
3022         QString suggestedName = tr("New Folder");
3023
3024         if(!m_metaData->artist().isEmpty() && !m_metaData->album().isEmpty())
3025         {
3026                 suggestedName = QString("%1 - %2").arg(m_metaData->artist(),m_metaData->album());
3027         }
3028         else if(!m_metaData->artist().isEmpty())
3029         {
3030                 suggestedName = m_metaData->artist();
3031         }
3032         else if(!m_metaData->album().isEmpty())
3033         {
3034                 suggestedName = m_metaData->album();
3035         }
3036         else
3037         {
3038                 for(int i = 0; i < m_fileListModel->rowCount(); i++)
3039                 {
3040                         const AudioFileModel &audioFile = m_fileListModel->getFile(m_fileListModel->index(i, 0));
3041                         const AudioFileModel_MetaInfo &fileMetaInfo = audioFile.metaInfo();
3042
3043                         if(!fileMetaInfo.album().isEmpty() || !fileMetaInfo.artist().isEmpty())
3044                         {
3045                                 if(!fileMetaInfo.artist().isEmpty() && !fileMetaInfo.album().isEmpty())
3046                                 {
3047                                         suggestedName = QString("%1 - %2").arg(fileMetaInfo.artist(), fileMetaInfo.album());
3048                                 }
3049                                 else if(!fileMetaInfo.artist().isEmpty())
3050                                 {
3051                                         suggestedName = fileMetaInfo.artist();
3052                                 }
3053                                 else if(!fileMetaInfo.album().isEmpty())
3054                                 {
3055                                         suggestedName = fileMetaInfo.album();
3056                                 }
3057                                 break;
3058                         }
3059                 }
3060         }
3061         
3062         suggestedName = MUtils::clean_file_name(suggestedName, true);
3063
3064         while(true)
3065         {
3066                 bool bApplied = false;
3067                 QString folderName = QInputDialog::getText(this, tr("New Folder"), tr("Enter the name of the new folder:").leftJustified(96, ' '), QLineEdit::Normal, suggestedName, &bApplied, Qt::WindowStaysOnTopHint).simplified();
3068
3069                 if(bApplied)
3070                 {
3071                         folderName = MUtils::clean_file_path(folderName.simplified(), true);
3072
3073                         if(folderName.isEmpty())
3074                         {
3075                                 MUtils::Sound::beep(MUtils::Sound::BEEP_ERR);
3076                                 continue;
3077                         }
3078
3079                         int i = 1;
3080                         QString newFolder = folderName;
3081
3082                         while(basePath.exists(newFolder))
3083                         {
3084                                 newFolder = QString(folderName).append(QString().sprintf(" (%d)", ++i));
3085                         }
3086                         
3087                         if(basePath.mkpath(newFolder))
3088                         {
3089                                 QDir createdDir = basePath;
3090                                 if(createdDir.cd(newFolder))
3091                                 {
3092                                         QModelIndex newIndex = m_fileSystemModel->index(createdDir.canonicalPath());
3093                                         ui->outputFolderView->setCurrentIndex(newIndex);
3094                                         outputFolderViewClicked(newIndex);
3095                                         CENTER_CURRENT_OUTPUT_FOLDER_DELAYED();
3096                                 }
3097                         }
3098                         else
3099                         {
3100                                 QMessageBox::warning(this, tr("Failed to create folder"), QString("%1<br><nobr>%2</nobr><br><br>%3").arg(tr("The new folder could not be created:"), basePath.absoluteFilePath(newFolder), tr("Drive is read-only or insufficient access rights!")));
3101                         }
3102                 }
3103                 break;
3104         }
3105 }
3106
3107 /*
3108  * Output to source dir changed
3109  */
3110 void MainWindow::saveToSourceFolderChanged(void)
3111 {
3112         m_settings->outputToSourceDir(ui->saveToSourceFolderCheckBox->isChecked());
3113 }
3114
3115 /*
3116  * Prepend relative source file path to output file name changed
3117  */
3118 void MainWindow::prependRelativePathChanged(void)
3119 {
3120         m_settings->prependRelativeSourcePath(ui->prependRelativePathCheckBox->isChecked());
3121 }
3122
3123 /*
3124  * Show context menu for output folder
3125  */
3126 void MainWindow::outputFolderContextMenu(const QPoint &pos)
3127 {
3128         QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(QObject::sender());
3129         QWidget *sender = scrollArea ? scrollArea->viewport() : dynamic_cast<QWidget*>(QObject::sender());      
3130
3131         if(pos.x() <= sender->width() && pos.y() <= sender->height() && pos.x() >= 0 && pos.y() >= 0)
3132         {
3133                 m_outputFolderContextMenu->popup(sender->mapToGlobal(pos));
3134         }
3135 }
3136
3137 /*
3138  * Show selected folder in explorer
3139  */
3140 void MainWindow::showFolderContextActionTriggered(void)
3141 {
3142         if(!m_fileSystemModel)
3143         {
3144                 qWarning("File system model not initialized yet!");
3145                 return;
3146         }
3147
3148         QString path = QDir::toNativeSeparators(m_fileSystemModel->filePath(ui->outputFolderView->currentIndex()));
3149         if(!path.endsWith(QDir::separator())) path.append(QDir::separator());
3150         MUtils::OS::shell_open(this, path, true);
3151 }
3152
3153 /*
3154  * Refresh the directory outline
3155  */
3156 void MainWindow::refreshFolderContextActionTriggered(void)
3157 {
3158         //force re-initialization
3159         QTimer::singleShot(0, this, SLOT(initOutputFolderModel()));
3160 }
3161
3162 /*
3163  * Go one directory up
3164  */
3165 void MainWindow::goUpFolderContextActionTriggered(void)
3166 {
3167         QModelIndex current = ui->outputFolderView->currentIndex();
3168         if(current.isValid())
3169         {
3170                 QModelIndex parent = current.parent();
3171                 if(parent.isValid())
3172                 {
3173                         
3174                         ui->outputFolderView->setCurrentIndex(parent);
3175                         outputFolderViewClicked(parent);
3176                 }
3177                 else
3178                 {
3179                         MUtils::Sound::beep(MUtils::Sound::BEEP_WRN);
3180                 }
3181                 CENTER_CURRENT_OUTPUT_FOLDER_DELAYED();
3182         }
3183 }
3184
3185 /*
3186  * Add current folder to favorites
3187  */
3188 void MainWindow::addFavoriteFolderActionTriggered(void)
3189 {
3190         QString path = m_fileSystemModel->filePath(ui->outputFolderView->currentIndex());
3191         QStringList favorites = m_settings->favoriteOutputFolders().split("|", QString::SkipEmptyParts);
3192
3193         if(!favorites.contains(path, Qt::CaseInsensitive))
3194         {
3195                 favorites.append(path);
3196                 while(favorites.count() > 6) favorites.removeFirst();
3197         }
3198         else
3199         {
3200                 MUtils::Sound::beep(MUtils::Sound::BEEP_WRN);
3201         }
3202
3203         m_settings->favoriteOutputFolders(favorites.join("|"));
3204         refreshFavorites();
3205 }
3206
3207 /*
3208  * Output folder edit finished
3209  */
3210 void MainWindow::outputFolderEditFinished(void)
3211 {
3212         if(ui->outputFolderEdit->isHidden())
3213         {
3214                 return; //Not currently in edit mode!
3215         }
3216         
3217         bool ok = false;
3218         
3219         QString text = QDir::fromNativeSeparators(ui->outputFolderEdit->text().trimmed());
3220         while(text.startsWith('"') || text.startsWith('/')) text = text.right(text.length() - 1).trimmed();
3221         while(text.endsWith('"') || text.endsWith('/')) text = text.left(text.length() - 1).trimmed();
3222
3223         static const char *str = "?*<>|\"";
3224         for(size_t i = 0; str[i]; i++) text.replace(str[i], "_");
3225
3226         if(!((text.length() >= 2) && text.at(0).isLetter() && text.at(1) == QChar(':')))
3227         {
3228                 text = QString("%1/%2").arg(QDir::fromNativeSeparators(ui->outputFolderLabel->text()), text);
3229         }
3230
3231         if(text.length() == 2) text += "/"; /* "X:" => "X:/" */
3232
3233         while(text.length() > 2)
3234         {
3235                 QFileInfo info(text);
3236                 if(info.exists() && info.isDir())
3237                 {
3238                         QModelIndex index = m_fileSystemModel->index(QFileInfo(info.canonicalFilePath()).absoluteFilePath());
3239                         if(index.isValid())
3240                         {
3241                                 ok = true;
3242                                 ui->outputFolderView->setCurrentIndex(index);
3243                                 outputFolderViewClicked(index);
3244                                 break;
3245                         }
3246                 }
3247                 else if(info.exists() && info.isFile())
3248                 {
3249                         QModelIndex index = m_fileSystemModel->index(QFileInfo(info.canonicalPath()).absoluteFilePath());
3250                         if(index.isValid())
3251                         {
3252                                 ok = true;
3253                                 ui->outputFolderView->setCurrentIndex(index);
3254                                 outputFolderViewClicked(index);
3255                                 break;
3256                         }
3257                 }
3258
3259                 text = text.left(text.length() - 1).trimmed();
3260         }
3261
3262         ui->outputFolderEdit->setVisible(false);
3263         ui->outputFolderLabel->setVisible(true);
3264         ui->outputFolderView->setEnabled(true);
3265
3266         if(!ok) MUtils::Sound::beep(MUtils::Sound::BEEP_ERR);
3267         CENTER_CURRENT_OUTPUT_FOLDER_DELAYED();
3268 }
3269
3270 /*
3271  * Initialize file system model
3272  */
3273 void MainWindow::initOutputFolderModel(void)
3274 {
3275         if(m_outputFolderNoteBox->isHidden())
3276         {
3277                 m_outputFolderNoteBox->show();
3278                 m_outputFolderNoteBox->repaint();
3279                 m_outputFolderViewInitCounter = 4;
3280
3281                 if(m_fileSystemModel)
3282                 {
3283                         SET_MODEL(ui->outputFolderView, NULL);
3284                         ui->outputFolderView->repaint();
3285                 }
3286
3287                 m_fileSystemModel.reset(new QFileSystemModelEx());
3288                 if(!m_fileSystemModel.isNull())
3289                 {
3290                         m_fileSystemModel->installEventFilter(this);
3291                         connect(m_fileSystemModel.data(), SIGNAL(directoryLoaded(QString)),          this, SLOT(outputFolderDirectoryLoaded(QString)));
3292                         connect(m_fileSystemModel.data(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(outputFolderRowsInserted(QModelIndex,int,int)));
3293
3294                         SET_MODEL(ui->outputFolderView, m_fileSystemModel.data());
3295                         ui->outputFolderView->header()->setStretchLastSection(true);
3296                         ui->outputFolderView->header()->hideSection(1);
3297                         ui->outputFolderView->header()->hideSection(2);
3298                         ui->outputFolderView->header()->hideSection(3);
3299                 
3300                         m_fileSystemModel->setRootPath("");
3301                         QModelIndex index = m_fileSystemModel->index(m_settings->outputDir());
3302                         if(index.isValid()) ui->outputFolderView->setCurrentIndex(index);
3303                         outputFolderViewClicked(ui->outputFolderView->currentIndex());
3304                 }
3305
3306                 CENTER_CURRENT_OUTPUT_FOLDER_DELAYED();
3307                 QTimer::singleShot(125, this, SLOT(initOutputFolderModel_doAsync()));
3308         }
3309 }
3310
3311 /*
3312  * Initialize file system model (do NOT call this one directly!)
3313  */
3314 void MainWindow::initOutputFolderModel_doAsync(void)
3315 {
3316         if(m_outputFolderViewInitCounter > 0)
3317         {
3318                 m_outputFolderViewInitCounter--;
3319                 QTimer::singleShot(125, this, SLOT(initOutputFolderModel_doAsync()));
3320         }
3321         else
3322         {
3323                 QTimer::singleShot(125, m_outputFolderNoteBox.data(), SLOT(hide()));
3324                 ui->outputFolderView->setFocus();
3325         }
3326 }
3327
3328 /*
3329  * Center current folder in view
3330  */
3331 void MainWindow::centerOutputFolderModel(void)
3332 {
3333         if(ui->outputFolderView->isVisible())
3334         {
3335                 centerOutputFolderModel_doAsync();
3336                 QTimer::singleShot(125, this, SLOT(centerOutputFolderModel_doAsync()));
3337         }
3338 }
3339
3340 /*
3341  * Center current folder in view (do NOT call this one directly!)
3342  */
3343 void MainWindow::centerOutputFolderModel_doAsync(void)
3344 {
3345         if(ui->outputFolderView->isVisible())
3346         {
3347                 m_outputFolderViewCentering = true;
3348                 const QModelIndex index = ui->outputFolderView->currentIndex();
3349                 ui->outputFolderView->scrollTo(index, QAbstractItemView::PositionAtCenter);
3350                 ui->outputFolderView->setFocus();
3351         }
3352 }
3353
3354 /*
3355  * File system model asynchronously loaded a dir
3356  */
3357 void MainWindow::outputFolderDirectoryLoaded(const QString &path)
3358 {
3359         if(m_outputFolderViewCentering)
3360         {
3361                 CENTER_CURRENT_OUTPUT_FOLDER_DELAYED();
3362         }
3363 }
3364
3365 /*
3366  * File system model inserted new items
3367  */
3368 void MainWindow::outputFolderRowsInserted(const QModelIndex &parent, int start, int end)
3369 {
3370         if(m_outputFolderViewCentering)
3371         {
3372                 CENTER_CURRENT_OUTPUT_FOLDER_DELAYED();
3373         }
3374 }
3375
3376 /*
3377  * Directory view item was expanded by user
3378  */
3379 void MainWindow::outputFolderItemExpanded(const QModelIndex &item)
3380 {
3381         //We need to stop centering as soon as the user has expanded an item manually!
3382         m_outputFolderViewCentering = false;
3383 }
3384
3385 /*
3386  * View event for output folder control occurred
3387  */
3388 void MainWindow::outputFolderViewEventOccurred(QWidget *sender, QEvent *event)
3389 {
3390         switch(event->type())
3391         {
3392         case QEvent::Enter:
3393         case QEvent::Leave:
3394         case QEvent::KeyPress:
3395         case QEvent::KeyRelease:
3396         case QEvent::FocusIn:
3397         case QEvent::FocusOut:
3398         case QEvent::TouchEnd:
3399                 outputFolderViewClicked(ui->outputFolderView->currentIndex());
3400                 break;
3401         }
3402 }
3403
3404 /*
3405  * Mouse event for output folder control occurred
3406  */
3407 void MainWindow::outputFolderMouseEventOccurred(QWidget *sender, QEvent *event)
3408 {
3409         QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
3410         QPoint pos = (mouseEvent) ? mouseEvent->pos() : QPoint();
3411
3412         if(sender == ui->outputFolderLabel)
3413         {
3414                 switch(event->type())
3415                 {
3416                 case QEvent::MouseButtonPress:
3417                         if(mouseEvent && (mouseEvent->button() == Qt::LeftButton))
3418                         {
3419                                 QString path = ui->outputFolderLabel->text();
3420                                 if(!path.endsWith(QDir::separator())) path.append(QDir::separator());
3421                                 MUtils::OS::shell_open(this, path, true);
3422                         }
3423                         break;
3424                 case QEvent::Enter:
3425                         ui->outputFolderLabel->setForegroundRole(QPalette::Link);
3426                         break;
3427                 case QEvent::Leave:
3428                         ui->outputFolderLabel->setForegroundRole(QPalette::WindowText);
3429                         break;
3430                 }
3431         }
3432
3433         if((sender == ui->outputFoldersFovoritesLabel) || (sender == ui->outputFoldersEditorLabel) || (sender == ui->outputFoldersGoUpLabel))
3434         {
3435                 const type_info &styleType = typeid(*qApp->style());
3436                 if((typeid(QPlastiqueStyle) == styleType) || (typeid(QWindowsStyle) == styleType))
3437                 {
3438                         switch(event->type())
3439                         {
3440                         case QEvent::Enter:
3441                                 dynamic_cast<QLabel*>(sender)->setFrameShadow(ui->outputFolderView->isEnabled() ? QFrame::Raised : QFrame::Plain);
3442                                 break;
3443                         case QEvent::MouseButtonPress:
3444                                 dynamic_cast<QLabel*>(sender)->setFrameShadow(ui->outputFolderView->isEnabled() ? QFrame::Sunken : QFrame::Plain);
3445                                 break;
3446                         case QEvent::MouseButtonRelease:
3447                                 dynamic_cast<QLabel*>(sender)->setFrameShadow(ui->outputFolderView->isEnabled() ? QFrame::Raised : QFrame::Plain);
3448                                 break;
3449                         case QEvent::Leave:
3450                                 dynamic_cast<QLabel*>(sender)->setFrameShadow(ui->outputFolderView->isEnabled() ? QFrame::Plain : QFrame::Plain);
3451                                 break;
3452                         }
3453                 }
3454                 else
3455                 {
3456                         dynamic_cast<QLabel*>(sender)->setFrameShadow(QFrame::Plain);
3457                 }
3458
3459                 if((event->type() == QEvent::MouseButtonRelease) && ui->outputFolderView->isEnabled() && (mouseEvent))
3460                 {
3461                         if(pos.x() <= sender->width() && pos.y() <= sender->height() && pos.x() >= 0 && pos.y() >= 0 && mouseEvent->button() != Qt::MidButton)
3462                         {
3463                                 if(sender == ui->outputFoldersFovoritesLabel)
3464                                 {
3465                                         m_outputFolderFavoritesMenu->popup(sender->mapToGlobal(pos));
3466                                 }
3467                                 else if(sender == ui->outputFoldersEditorLabel)
3468                                 {
3469                                         ui->outputFolderView->setEnabled(false);
3470                                         ui->outputFolderLabel->setVisible(false);
3471                                         ui->outputFolderEdit->setVisible(true);
3472                                         ui->outputFolderEdit->setText(ui->outputFolderLabel->text());
3473                                         ui->outputFolderEdit->selectAll();
3474                                         ui->outputFolderEdit->setFocus();
3475                                 }
3476                                 else if(sender == ui->outputFoldersGoUpLabel)
3477                                 {
3478                                         QTimer::singleShot(0, this, SLOT(goUpFolderContextActionTriggered()));
3479                                 }
3480                                 else
3481                                 {
3482                                         MUTILS_THROW("Oups, this is not supposed to happen!");
3483                                 }
3484                         }
3485                 }
3486         }
3487 }
3488
3489 // =========================================================
3490 // Metadata tab slots
3491 // =========================================================
3492
3493 /*
3494  * Edit meta button clicked
3495  */
3496 void MainWindow::editMetaButtonClicked(void)
3497 {
3498         ABORT_IF_BUSY;
3499
3500         const QModelIndex index = ui->metaDataView->currentIndex();
3501
3502         if(index.isValid())
3503         {
3504                 m_metaInfoModel->editItem(index, this);
3505         
3506                 if(index.row() == 4)
3507                 {
3508                         m_settings->metaInfoPosition(m_metaData->position());
3509                 }
3510         }
3511 }
3512
3513 /*
3514  * Reset meta button clicked
3515  */
3516 void MainWindow::clearMetaButtonClicked(void)
3517 {
3518         ABORT_IF_BUSY;
3519         m_metaInfoModel->clearData();
3520 }
3521
3522 /*
3523  * Meta tags enabled changed
3524  */
3525 void MainWindow::metaTagsEnabledChanged(void)
3526 {
3527         m_settings->writeMetaTags(ui->writeMetaDataCheckBox->isChecked());
3528 }
3529
3530 /*
3531  * Playlist enabled changed
3532  */
3533 void MainWindow::playlistEnabledChanged(void)
3534 {
3535         m_settings->createPlaylist(ui->generatePlaylistCheckBox->isChecked());
3536 }
3537
3538 // =========================================================
3539 // Compression tab slots
3540 // =========================================================
3541
3542 /*
3543  * Update encoder
3544  */
3545 void MainWindow::updateEncoder(int id)
3546 {
3547         /*qWarning("\nupdateEncoder(%d)", id);*/
3548
3549         m_settings->compressionEncoder(id);
3550         const AbstractEncoderInfo *info = EncoderRegistry::getEncoderInfo(id);
3551
3552         //Update UI controls
3553         ui->radioButtonModeQuality       ->setEnabled(info->isModeSupported(SettingsModel::VBRMode));
3554         ui->radioButtonModeAverageBitrate->setEnabled(info->isModeSupported(SettingsModel::ABRMode));
3555         ui->radioButtonConstBitrate      ->setEnabled(info->isModeSupported(SettingsModel::CBRMode));
3556         
3557         //Initialize checkbox state
3558         if(ui->radioButtonModeQuality->isEnabled())             ui->radioButtonModeQuality->setChecked(true);
3559         else if(ui->radioButtonModeAverageBitrate->isEnabled()) ui->radioButtonModeAverageBitrate->setChecked(true);
3560         else if(ui->radioButtonConstBitrate->isEnabled())       ui->radioButtonConstBitrate->setChecked(true);
3561         else MUTILS_THROW("It appears that the encoder does not support *any* RC mode!");
3562
3563         //Apply current RC mode
3564         const int currentRCMode = EncoderRegistry::loadEncoderMode(m_settings, id);
3565         switch(currentRCMode)
3566         {
3567                 case SettingsModel::VBRMode: if(ui->radioButtonModeQuality->isEnabled())        ui->radioButtonModeQuality->setChecked(true);        break;
3568                 case SettingsModel::ABRMode: if(ui->radioButtonModeAverageBitrate->isEnabled()) ui->radioButtonModeAverageBitrate->setChecked(true); break;
3569                 case SettingsModel::CBRMode: if(ui->radioButtonConstBitrate->isEnabled())       ui->radioButtonConstBitrate->setChecked(true);       break;
3570                 default: MUTILS_THROW("updateEncoder(): Unknown rc-mode encountered!");
3571         }
3572
3573         //Display encoder description
3574         if(const char* description = info->description())
3575         {
3576                 ui->labelEncoderInfo->setVisible(true);
3577                 ui->labelEncoderInfo->setText(tr("Current Encoder: %1").arg(QString::fromUtf8(description)));
3578         }
3579         else
3580         {
3581                 ui->labelEncoderInfo->setVisible(false);
3582         }
3583
3584         //Update RC mode!
3585         updateRCMode(m_modeButtonGroup->checkedId());
3586 }
3587
3588 /*
3589  * Update rate-control mode
3590  */
3591 void MainWindow::updateRCMode(int id)
3592 {
3593         /*qWarning("updateRCMode(%d)", id);*/
3594
3595         //Store new RC mode
3596         const int currentEncoder = m_encoderButtonGroup->checkedId();
3597         EncoderRegistry::saveEncoderMode(m_settings, currentEncoder, id);
3598
3599         //Fetch encoder info
3600         const AbstractEncoderInfo *info = EncoderRegistry::getEncoderInfo(currentEncoder);
3601         const int valueCount = info->valueCount(id);
3602
3603         //Sanity check
3604         if(!info->isModeSupported(id))
3605         {
3606                 qWarning("Attempting to use an unsupported RC mode (%d) with current encoder (%d)!", id, currentEncoder);
3607                 ui->labelBitrate->setText("(ERROR)");
3608                 return;
3609         }
3610
3611         //Update slider min/max values
3612         if(valueCount > 0)
3613         {
3614                 SignalBlockHelper signalBlockHelper(ui->sliderBitrate);
3615                 ui->sliderBitrate->setEnabled(true);
3616                 ui->sliderBitrate->setMinimum(0);
3617                 ui->sliderBitrate->setMaximum(valueCount-1);
3618         }
3619         else
3620         {
3621                 SignalBlockHelper signalBlockHelper(ui->sliderBitrate);
3622                 ui->sliderBitrate->setEnabled(false);
3623                 ui->sliderBitrate->setMinimum(0);
3624                 ui->sliderBitrate->setMaximum(2);
3625         }
3626
3627         //Now update bitrate/quality value!
3628         if(valueCount > 0)
3629         {
3630                 const int currentValue = EncoderRegistry::loadEncoderValue(m_settings, currentEncoder, id);
3631                 ui->sliderBitrate->setValue(qBound(0, currentValue, valueCount-1));
3632                 updateBitrate(qBound(0, currentValue, valueCount-1));
3633         }
3634         else
3635         {
3636                 ui->sliderBitrate->setValue(1);
3637                 updateBitrate(0);
3638         }
3639 }
3640
3641 /*
3642  * Update bitrate
3643  */
3644 void MainWindow::updateBitrate(int value)
3645 {
3646         /*qWarning("updateBitrate(%d)", value);*/
3647
3648         //Load current encoder and RC mode
3649         const int currentEncoder = m_encoderButtonGroup->checkedId();
3650         const int currentRCMode = m_modeButtonGroup->checkedId();
3651
3652         //Fetch encoder info
3653         const AbstractEncoderInfo *info = EncoderRegistry::getEncoderInfo(currentEncoder);
3654         const int valueCount = info->valueCount(currentRCMode);
3655
3656         //Sanity check
3657         if(!info->isModeSupported(currentRCMode))
3658         {
3659                 qWarning("Attempting to use an unsupported RC mode (%d) with current encoder (%d)!", currentRCMode, currentEncoder);
3660                 ui->labelBitrate->setText("(ERROR)");
3661                 return;
3662         }
3663
3664         //Store new bitrate value
3665         if(valueCount > 0)
3666         {
3667                 EncoderRegistry::saveEncoderValue(m_settings, currentEncoder, currentRCMode, qBound(0, value, valueCount-1));
3668         }
3669
3670         //Update bitrate value
3671         const int displayValue = (valueCount > 0) ? info->valueAt(currentRCMode, qBound(0, value, valueCount-1)) : INT_MAX;
3672         switch(info->valueType(currentRCMode))
3673         {
3674         case AbstractEncoderInfo::TYPE_BITRATE:
3675                 ui->labelBitrate->setText(QString("%1 kbps").arg(QString::number(displayValue)));
3676                 break;
3677         case AbstractEncoderInfo::TYPE_APPROX_BITRATE:
3678                 ui->labelBitrate->setText(QString("&asymp; %1 kbps").arg(QString::number(displayValue)));
3679                 break;
3680         case AbstractEncoderInfo::TYPE_QUALITY_LEVEL_INT:
3681                 ui->labelBitrate->setText(tr("Quality Level %1").arg(QString::number(displayValue)));
3682                 break;
3683         case AbstractEncoderInfo::TYPE_QUALITY_LEVEL_FLT:
3684                 ui->labelBitrate->setText(tr("Quality Level %1").arg(QString().sprintf("%.2f", double(displayValue)/100.0)));
3685                 break;
3686         case AbstractEncoderInfo::TYPE_COMPRESSION_LEVEL:
3687                 ui->labelBitrate->setText(tr("Compression %1").arg(QString::number(displayValue)));
3688                 break;
3689         case AbstractEncoderInfo::TYPE_UNCOMPRESSED:
3690                 ui->labelBitrate->setText(tr("Uncompressed"));
3691                 break;
3692         default:
3693                 MUTILS_THROW("Unknown display value type encountered!");
3694                 break;
3695         }
3696 }
3697
3698 /*
3699  * Event for compression tab occurred
3700  */
3701 void MainWindow::compressionTabEventOccurred(QWidget *sender, QEvent *event)
3702 {
3703         static const QUrl helpUrl("http://lamexp.sourceforge.net/doc/FAQ.html#054010d9");
3704         
3705         if((sender == ui->labelCompressionHelp) && (event->type() == QEvent::MouseButtonPress))
3706         {
3707                 QDesktopServices::openUrl(helpUrl);
3708         }
3709         else if((sender == ui->labelResetEncoders) && (event->type() == QEvent::MouseButtonPress))
3710         {
3711                 PLAY_SOUND_OPTIONAL("blast", true);
3712                 EncoderRegistry::resetAllEncoders(m_settings);
3713                 m_settings->compressionEncoder(SettingsModel::MP3Encoder);
3714                 ui->radioButtonEncoderMP3->setChecked(true);
3715                 QTimer::singleShot(0, this, SLOT(updateEncoder()));
3716         }
3717 }
3718
3719 // =========================================================
3720 // Advanced option slots
3721 // =========================================================
3722
3723 /*
3724  * Lame algorithm quality changed
3725  */
3726 void MainWindow::updateLameAlgoQuality(int value)
3727 {
3728         QString text;
3729
3730         switch(value)
3731         {
3732         case 3:
3733                 text = tr("Best Quality (Slow)");
3734                 break;
3735         case 2:
3736                 text = tr("High Quality (Recommended)");
3737                 break;
3738         case 1:
3739                 text = tr("Acceptable Quality (Fast)");
3740                 break;
3741         case 0:
3742                 text = tr("Poor Quality (Very Fast)");
3743                 break;
3744         }
3745
3746         if(!text.isEmpty())
3747         {
3748                 m_settings->lameAlgoQuality(value);
3749                 ui->labelLameAlgoQuality->setText(text);
3750         }
3751
3752         bool warning = (value == 0), notice = (value == 3);
3753         ui->labelLameAlgoQualityWarning->setVisible(warning);
3754         ui->labelLameAlgoQualityWarningIcon->setVisible(warning);
3755         ui->labelLameAlgoQualityNotice->setVisible(notice);
3756         ui->labelLameAlgoQualityNoticeIcon->setVisible(notice);
3757         ui->labelLameAlgoQualitySpacer->setVisible(warning || notice);
3758 }
3759
3760 /*
3761  * Bitrate management endabled/disabled
3762  */
3763 void MainWindow::bitrateManagementEnabledChanged(bool checked)
3764 {
3765         m_settings->bitrateManagementEnabled(checked);
3766 }
3767
3768 /*
3769  * Minimum bitrate has changed
3770  */
3771 void MainWindow::bitrateManagementMinChanged(int value)
3772 {
3773         if(value > ui->spinBoxBitrateManagementMax->value())
3774         {
3775                 ui->spinBoxBitrateManagementMin->setValue(ui->spinBoxBitrateManagementMax->value());
3776                 m_settings->bitrateManagementMinRate(ui->spinBoxBitrateManagementMax->value());
3777         }
3778         else
3779         {
3780                 m_settings->bitrateManagementMinRate(value);
3781         }
3782 }
3783
3784 /*
3785  * Maximum bitrate has changed
3786  */
3787 void MainWindow::bitrateManagementMaxChanged(int value)
3788 {
3789         if(value < ui->spinBoxBitrateManagementMin->value())
3790         {
3791                 ui->spinBoxBitrateManagementMax->setValue(ui->spinBoxBitrateManagementMin->value());
3792                 m_settings->bitrateManagementMaxRate(ui->spinBoxBitrateManagementMin->value());
3793         }
3794         else
3795         {
3796                 m_settings->bitrateManagementMaxRate(value);
3797         }
3798 }
3799
3800 /*
3801  * Channel mode has changed
3802  */
3803 void MainWindow::channelModeChanged(int value)
3804 {
3805         if(value >= 0) m_settings->lameChannelMode(value);
3806 }
3807
3808 /*
3809  * Sampling rate has changed
3810  */
3811 void MainWindow::samplingRateChanged(int value)
3812 {
3813         if(value >= 0) m_settings->samplingRate(value);
3814 }
3815
3816 /*
3817  * Nero AAC 2-Pass mode changed
3818  */
3819 void MainWindow::neroAAC2PassChanged(bool checked)
3820 {
3821         m_settings->neroAACEnable2Pass(checked);
3822 }
3823
3824 /*
3825  * Nero AAC profile mode changed
3826  */
3827 void MainWindow::neroAACProfileChanged(int value)
3828 {
3829         if(value >= 0) m_settings->aacEncProfile(value);
3830 }
3831
3832 /*
3833  * Aften audio coding mode changed
3834  */
3835 void MainWindow::aftenCodingModeChanged(int value)
3836 {
3837         if(value >= 0) m_settings->aftenAudioCodingMode(value);
3838 }
3839
3840 /*
3841  * Aften DRC mode changed
3842  */
3843 void MainWindow::aftenDRCModeChanged(int value)
3844 {
3845         if(value >= 0) m_settings->aftenDynamicRangeCompression(value);
3846 }
3847
3848 /*
3849  * Aften exponent search size changed
3850  */
3851 void MainWindow::aftenSearchSizeChanged(int value)
3852 {
3853         if(value >= 0) m_settings->aftenExponentSearchSize(value);
3854 }
3855
3856 /*
3857  * Aften fast bit allocation changed
3858  */
3859 void MainWindow::aftenFastAllocationChanged(bool checked)
3860 {
3861         m_settings->aftenFastBitAllocation(checked);
3862 }
3863
3864
3865 /*
3866  * Opus encoder settings changed
3867  */
3868 void MainWindow::opusSettingsChanged(void)
3869 {
3870         m_settings->opusFramesize(ui->comboBoxOpusFramesize->currentIndex());
3871         m_settings->opusComplexity(ui->spinBoxOpusComplexity->value());
3872         m_settings->opusDisableResample(ui->checkBoxOpusDisableResample->isChecked());
3873 }
3874
3875 /*
3876  * Normalization filter enabled changed
3877  */
3878 void MainWindow::normalizationEnabledChanged(bool checked)
3879 {
3880         m_settings->normalizationFilterEnabled(checked);
3881         normalizationDynamicChanged(ui->checkBoxNormalizationFilterDynamic->isChecked());
3882 }
3883
3884 /*
3885  * Dynamic normalization enabled changed
3886  */
3887 void MainWindow::normalizationDynamicChanged(bool checked)
3888 {
3889         ui->spinBoxNormalizationFilterSize->setEnabled(ui->checkBoxNormalizationFilterEnabled->isChecked() && checked);
3890         m_settings->normalizationFilterDynamic(checked);
3891 }
3892
3893 /*
3894  * Normalization max. volume changed
3895  */
3896 void MainWindow::normalizationMaxVolumeChanged(double value)
3897 {
3898         m_settings->normalizationFilterMaxVolume(static_cast<int>(value * 100.0));
3899 }
3900
3901 /*
3902  * Normalization equalization mode changed
3903  */
3904 void MainWindow::normalizationCoupledChanged(bool checked)
3905 {
3906         m_settings->normalizationFilterCoupled(checked);
3907 }
3908
3909 /*
3910  * Normalization filter size changed
3911  */
3912 void MainWindow::normalizationFilterSizeChanged(int value)
3913 {
3914         m_settings->normalizationFilterSize(value);
3915 }
3916
3917 /*
3918  * Normalization filter size editing finished
3919  */
3920 void MainWindow::normalizationFilterSizeFinished(void)
3921 {
3922         const int value = ui->spinBoxNormalizationFilterSize->value();
3923         if((value % 2) != 1)
3924         {
3925                 bool rnd = MUtils::parity(MUtils::next_rand_u32());
3926                 ui->spinBoxNormalizationFilterSize->setValue(rnd ? value+1 : value-1);
3927         }
3928 }
3929
3930 /*
3931  * Tone adjustment has changed (Bass)
3932  */
3933 void MainWindow::toneAdjustBassChanged(double value)
3934 {
3935         m_settings->toneAdjustBass(static_cast<int>(value * 100.0));
3936         ui->spinBoxToneAdjustBass->setPrefix((value > 0) ? "+" : QString());
3937 }
3938
3939 /*
3940  * Tone adjustment has changed (Treble)
3941  */
3942 void MainWindow::toneAdjustTrebleChanged(double value)
3943 {
3944         m_settings->toneAdjustTreble(static_cast<int>(value * 100.0));
3945         ui->spinBoxToneAdjustTreble->setPrefix((value > 0) ? "+" : QString());
3946 }
3947
3948 /*
3949  * Tone adjustment has been reset
3950  */
3951 void MainWindow::toneAdjustTrebleReset(void)
3952 {
3953         ui->spinBoxToneAdjustBass->setValue(m_settings->toneAdjustBassDefault());
3954         ui->spinBoxToneAdjustTreble->setValue(m_settings->toneAdjustTrebleDefault());
3955         toneAdjustBassChanged(ui->spinBoxToneAdjustBass->value());
3956         toneAdjustTrebleChanged(ui->spinBoxToneAdjustTreble->value());
3957 }
3958
3959 /*
3960  * Custom encoder parameters changed
3961  */
3962 void MainWindow::customParamsChanged(void)
3963 {
3964         ui->lineEditCustomParamLAME->setText(ui->lineEditCustomParamLAME->text().simplified());
3965         ui->lineEditCustomParamOggEnc->setText(ui->lineEditCustomParamOggEnc->text().simplified());
3966         ui->lineEditCustomParamNeroAAC->setText(ui->lineEditCustomParamNeroAAC->text().simplified());
3967         ui->lineEditCustomParamFLAC->setText(ui->lineEditCustomParamFLAC->text().simplified());
3968         ui->lineEditCustomParamAften->setText(ui->lineEditCustomParamAften->text().simplified());
3969         ui->lineEditCustomParamOpus->setText(ui->lineEditCustomParamOpus->text().simplified());
3970
3971         bool customParamsUsed = false;
3972         if(!ui->lineEditCustomParamLAME->text().isEmpty()) customParamsUsed = true;
3973         if(!ui->lineEditCustomParamOggEnc->text().isEmpty()) customParamsUsed = true;
3974         if(!ui->lineEditCustomParamNeroAAC->text().isEmpty()) customParamsUsed = true;
3975         if(!ui->lineEditCustomParamFLAC->text().isEmpty()) customParamsUsed = true;
3976         if(!ui->lineEditCustomParamAften->text().isEmpty()) customParamsUsed = true;
3977         if(!ui->lineEditCustomParamOpus->text().isEmpty()) customParamsUsed = true;
3978
3979         ui->labelCustomParamsIcon->setVisible(customParamsUsed);
3980         ui->labelCustomParamsText->setVisible(customParamsUsed);
3981         ui->labelCustomParamsSpacer->setVisible(customParamsUsed);
3982
3983         EncoderRegistry::saveEncoderCustomParams(m_settings, SettingsModel::MP3Encoder,    ui->lineEditCustomParamLAME->text());
3984         EncoderRegistry::saveEncoderCustomParams(m_settings, SettingsModel::VorbisEncoder, ui->lineEditCustomParamOggEnc->text());
3985         EncoderRegistry::saveEncoderCustomParams(m_settings, SettingsModel::AACEncoder,    ui->lineEditCustomParamNeroAAC->text());
3986         EncoderRegistry::saveEncoderCustomParams(m_settings, SettingsModel::FLACEncoder,   ui->lineEditCustomParamFLAC->text());
3987         EncoderRegistry::saveEncoderCustomParams(m_settings, SettingsModel::AC3Encoder,    ui->lineEditCustomParamAften->text());
3988         EncoderRegistry::saveEncoderCustomParams(m_settings, SettingsModel::OpusEncoder,   ui->lineEditCustomParamOpus->text());
3989 }
3990
3991 /*
3992  * One of the rename buttons has been clicked
3993  */
3994 void MainWindow::renameButtonClicked(bool checked)
3995 {
3996         if(QPushButton *const button  = dynamic_cast<QPushButton*>(QObject::sender()))
3997         {
3998                 QWidget *pages[]       = { ui->pageRename_Rename,   ui->pageRename_RegExp,   ui->pageRename_FileEx   };
3999                 QPushButton *buttons[] = { ui->buttonRename_Rename, ui->buttonRename_RegExp, ui->buttonRename_FileEx };
4000                 for(int i = 0; i < 3; i++)
4001                 {
4002                         const bool match = (button == buttons[i]);
4003                         buttons[i]->setChecked(match);
4004                         if(match && checked) ui->stackedWidget->setCurrentWidget(pages[i]);
4005                 }
4006         }
4007 }
4008
4009 /*
4010  * Rename output files enabled changed
4011  */
4012 void MainWindow::renameOutputEnabledChanged(const bool &checked)
4013 {
4014         m_settings->renameFiles_renameEnabled(checked);
4015 }
4016
4017 /*
4018  * Rename output files patterm changed
4019  */
4020 void MainWindow::renameOutputPatternChanged(void)
4021 {
4022         QString temp = ui->lineEditRenamePattern->text().simplified();
4023         ui->lineEditRenamePattern->setText(temp.isEmpty() ? m_settings->renameFiles_renamePatternDefault() : temp);
4024         m_settings->renameFiles_renamePattern(ui->lineEditRenamePattern->text());
4025 }
4026
4027 /*
4028  * Rename output files patterm changed
4029  */
4030 void MainWindow::renameOutputPatternChanged(const QString &text, const bool &silent)
4031 {
4032         QString pattern(text.simplified());
4033         
4034         pattern.replace("<BaseName>", "The_White_Stripes_-_Fell_In_Love_With_A_Girl", Qt::CaseInsensitive);
4035         pattern.replace("<TrackNo>", "04", Qt::CaseInsensitive);
4036         pattern.replace("<Title>", "Fell In Love With A Girl", Qt::CaseInsensitive);
4037         pattern.replace("<Artist>", "The White Stripes", Qt::CaseInsensitive);
4038         pattern.replace("<Album>", "White Blood Cells", Qt::CaseInsensitive);
4039         pattern.replace("<Year>", "2001", Qt::CaseInsensitive);
4040         pattern.replace("<Comment>", "Encoded by LameXP", Qt::CaseInsensitive);
4041
4042         const QString patternClean = MUtils::clean_file_name(pattern, false);
4043
4044         if(pattern.compare(patternClean))
4045         {
4046                 if(ui->lineEditRenamePattern->palette().color(QPalette::Text) != Qt::red)
4047                 {
4048                         if(!silent) MUtils::Sound::beep(MUtils::Sound::BEEP_ERR);
4049                         SET_TEXT_COLOR(ui->lineEditRenamePattern, Qt::red);
4050                 }
4051         }
4052         else
4053         {
4054                 if(ui->lineEditRenamePattern->palette() != QPalette())
4055                 {
4056                         if(!silent) MUtils::Sound::beep(MUtils::Sound::BEEP_NFO);
4057                         ui->lineEditRenamePattern->setPalette(QPalette());
4058                 }
4059         }
4060
4061         ui->labelRanameExample->setText(patternClean);
4062 }
4063
4064 /*
4065  * Regular expression enabled changed
4066  */
4067 void MainWindow::renameRegExpEnabledChanged(const bool &checked)
4068 {
4069         m_settings->renameFiles_regExpEnabled(checked);
4070 }
4071
4072 /*
4073  * Regular expression value has changed
4074  */
4075 void  MainWindow::renameRegExpValueChanged(void)
4076 {
4077         const QString search  = ui->lineEditRenameRegExp_Search->text() .trimmed();
4078         const QString replace = ui->lineEditRenameRegExp_Replace->text().simplified();
4079         ui->lineEditRenameRegExp_Search ->setText(search.isEmpty()  ? m_settings->renameFiles_regExpSearchDefault()  : search);
4080         ui->lineEditRenameRegExp_Replace->setText(replace.isEmpty() ? m_settings->renameFiles_regExpReplaceDefault() : replace);
4081         m_settings->renameFiles_regExpSearch (ui->lineEditRenameRegExp_Search ->text());
4082         m_settings->renameFiles_regExpReplace(ui->lineEditRenameRegExp_Replace->text());
4083 }
4084
4085 /*
4086  * Regular expression search pattern has changed
4087  */
4088 void  MainWindow::renameRegExpSearchChanged(const QString &text, const bool &silent)
4089 {
4090         const QString pattern(text.trimmed());
4091
4092         if((!pattern.isEmpty()) && (!QRegExp(pattern.trimmed()).isValid()))
4093         {
4094                 if(ui->lineEditRenameRegExp_Search->palette().color(QPalette::Text) != Qt::red)
4095                 {
4096                         if(!silent) MUtils::Sound::beep(MUtils::Sound::BEEP_ERR);
4097                         SET_TEXT_COLOR(ui->lineEditRenameRegExp_Search, Qt::red);
4098                 }
4099         }
4100         else
4101         {
4102                 if(ui->lineEditRenameRegExp_Search->palette() != QPalette())
4103                 {
4104                         if(!silent) MUtils::Sound::beep(MUtils::Sound::BEEP_NFO);
4105                         ui->lineEditRenameRegExp_Search->setPalette(QPalette());
4106                 }
4107         }
4108
4109         renameRegExpReplaceChanged(ui->lineEditRenameRegExp_Replace->text(), silent);
4110 }
4111
4112 /*
4113  * Regular expression replacement string changed
4114  */
4115 void  MainWindow::renameRegExpReplaceChanged(const QString &text, const bool &silent)
4116 {
4117         QString replacement(text.simplified());
4118         const QString search(ui->lineEditRenameRegExp_Search->text().trimmed());
4119
4120         if(!search.isEmpty())
4121         {
4122                 const QRegExp regexp(search);
4123                 if(regexp.isValid())
4124                 {
4125                         const int count = regexp.captureCount();
4126                         const QString blank; 
4127                         for(int i = 0; i < count; i++)
4128                         {
4129                                 replacement.replace(QString("\\%0").arg(QString::number(i+1)), blank);
4130                         }
4131                 }
4132         }
4133
4134         if(replacement.compare(MUtils::clean_file_name(replacement, false)))
4135         {
4136                 if(ui->lineEditRenameRegExp_Replace->palette().color(QPalette::Text) != Qt::red)
4137                 {
4138                         if(!silent) MUtils::Sound::beep(MUtils::Sound::BEEP_ERR);
4139                         SET_TEXT_COLOR(ui->lineEditRenameRegExp_Replace, Qt::red);
4140                 }
4141         }
4142         else
4143         {
4144                 if(ui->lineEditRenameRegExp_Replace->palette() != QPalette())
4145                 {
4146                         if(!silent) MUtils::Sound::beep(MUtils::Sound::BEEP_NFO);
4147                         ui->lineEditRenameRegExp_Replace->setPalette(QPalette());
4148                 }
4149         }
4150 }
4151
4152 /*
4153  * Show list of rename macros
4154  */
4155 void MainWindow::showRenameMacros(const QString &text)
4156 {
4157         if(text.compare("reset", Qt::CaseInsensitive) == 0)
4158         {
4159                 ui->lineEditRenamePattern->setText(m_settings->renameFiles_renamePatternDefault());
4160                 return;
4161         }
4162
4163         if(text.compare("regexp", Qt::CaseInsensitive) == 0)
4164         {
4165                 MUtils::OS::shell_open(this, "http://www.regular-expressions.info/quickstart.html");
4166                 return;
4167         }
4168
4169         const QString format = QString("<tr><td><tt>&lt;%1&gt;</tt></td><td>&nbsp;&nbsp;</td><td>%2</td></tr>");
4170
4171         QString message = QString("<table>");
4172         message += QString(format).arg("BaseName", tr("File name without extension"));
4173         message += QString(format).arg("TrackNo", tr("Track number with leading zero"));
4174         message += QString(format).arg("Title", tr("Track title"));
4175         message += QString(format).arg("Artist", tr("Artist name"));
4176         message += QString(format).arg("Album", tr("Album name"));
4177         message += QString(format).arg("Year", tr("Year with (at least) four digits"));
4178         message += QString(format).arg("Comment", tr("Comment"));
4179         message += "</table><br><br>";
4180         message += QString("%1<br>").arg(tr("Characters forbidden in file names:"));
4181         message += "<b><tt>\\ / : * ? &lt; &gt; |<br>";
4182         
4183         QMessageBox::information(this, tr("Rename Macros"), message, tr("Discard"));
4184 }
4185
4186 void MainWindow::fileExtAddButtonClicked(void)
4187 {
4188         if(FileExtsModel *const model = dynamic_cast<FileExtsModel*>(ui->tableViewFileExts->model()))
4189         {
4190                 model->addOverwrite(this);
4191         }
4192 }
4193
4194 void MainWindow::fileExtRemoveButtonClicked(void)
4195 {
4196         if(FileExtsModel *const model = dynamic_cast<FileExtsModel*>(ui->tableViewFileExts->model()))
4197         {
4198                 const QModelIndex selected = ui->tableViewFileExts->currentIndex();
4199                 if(selected.isValid())
4200                 {
4201                         model->removeOverwrite(selected);
4202                 }
4203                 else
4204                 {
4205                         MUtils::Sound::beep(MUtils::Sound::BEEP_ERR);
4206                 }
4207         }
4208 }
4209
4210 void MainWindow::fileExtModelChanged(void)
4211 {
4212         if(FileExtsModel *const model = dynamic_cast<FileExtsModel*>(ui->tableViewFileExts->model()))
4213         {
4214                 m_settings->renameFiles_fileExtension(model->exportItems());
4215         }
4216 }
4217
4218 void MainWindow::forceStereoDownmixEnabledChanged(bool checked)
4219 {
4220         m_settings->forceStereoDownmix(checked);
4221 }
4222
4223 /*
4224  * Maximum number of instances changed
4225  */
4226 void MainWindow::updateMaximumInstances(const int value)
4227 {
4228         quint32 instances = qBound(1U, static_cast<quint32>(value), 32U);
4229         if (instances > 16U)
4230         {
4231                 instances += instances - 16U;
4232                 if (instances > 32U)
4233                 {
4234                         instances += instances - 32U;
4235                 }
4236         }
4237         m_settings->maximumInstances(ui->checkBoxAutoDetectInstances->isChecked() ? 0U : instances);
4238         ui->labelMaxInstances->setText(tr("%n Instance(s)", "", instances));
4239 }
4240
4241 /*
4242  * Auto-detect number of instances
4243  */
4244 void MainWindow::autoDetectInstancesChanged(const bool checked)
4245 {
4246         m_settings->maximumInstances(checked ? 0U : ui->sliderMaxInstances->value());
4247 }
4248
4249 /*
4250  * Browse for custom TEMP folder button clicked
4251  */
4252 void MainWindow::browseCustomTempFolderButtonClicked(void)
4253 {
4254         QString newTempFolder;
4255
4256         if(MUtils::GUI::themes_enabled())
4257         {
4258                 newTempFolder = QFileDialog::getExistingDirectory(this, QString(), m_settings->customTempPath());
4259         }
4260         else
4261         {
4262                 QFileDialog dialog(this);
4263                 dialog.setFileMode(QFileDialog::DirectoryOnly);
4264                 dialog.setDirectory(m_settings->customTempPath());
4265                 if(dialog.exec())
4266                 {
4267                         newTempFolder = dialog.selectedFiles().first();
4268                 }
4269         }
4270
4271         if(!newTempFolder.isEmpty())
4272         {
4273                 QFile writeTest(QString("%1/~%2.tmp").arg(newTempFolder, MUtils::next_rand_str()));
4274                 if(writeTest.open(QIODevice::ReadWrite))
4275                 {
4276                         writeTest.remove();
4277                         ui->lineEditCustomTempFolder->setText(QDir::toNativeSeparators(newTempFolder));
4278                 }
4279                 else
4280                 {
4281                         QMessageBox::warning(this, tr("Access Denied"), tr("Cannot write to the selected directory. Please choose another directory!"));
4282                 }
4283         }
4284 }
4285
4286 /*
4287  * Custom TEMP folder changed
4288  */
4289 void MainWindow::customTempFolderChanged(const QString &text)
4290 {
4291         m_settings->customTempPath(QDir::fromNativeSeparators(text));
4292 }
4293
4294 /*
4295  * Use custom TEMP folder option changed
4296  */
4297 void MainWindow::useCustomTempFolderChanged(bool checked)
4298 {
4299         m_settings->customTempPathEnabled(!checked);
4300 }
4301
4302 /*
4303  * Help for custom parameters was requested
4304  */
4305 void MainWindow::customParamsHelpRequested(QWidget *obj, QEvent *event)
4306 {
4307         if(event->type() != QEvent::MouseButtonRelease)
4308         {
4309                 return;
4310         }
4311
4312         if(QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event))
4313         {
4314                 QPoint pos = mouseEvent->pos();
4315                 if(!(pos.x() <= obj->width() && pos.y() <= obj->height() && pos.x() >= 0 && pos.y() >= 0 && mouseEvent->button() != Qt::MidButton))
4316                 {
4317                         return;
4318                 }
4319         }
4320
4321         if(obj == ui->helpCustomParamLAME)         showCustomParamsHelpScreen("lame.exe",    "--longhelp");
4322         else if(obj == ui->helpCustomParamOggEnc)  showCustomParamsHelpScreen("oggenc2.exe", "--help");
4323         else if(obj == ui->helpCustomParamNeroAAC)
4324         {
4325                 switch(EncoderRegistry::getAacEncoder())
4326                 {
4327                         case SettingsModel::AAC_ENCODER_QAAC: showCustomParamsHelpScreen("qaac64.exe|qaac.exe", "--help"); break;
4328                         case SettingsModel::AAC_ENCODER_FHG : showCustomParamsHelpScreen("fhgaacenc.exe",       ""      ); break;
4329                         case SettingsModel::AAC_ENCODER_FDK : showCustomParamsHelpScreen("fdkaac.exe",          "--help"); break;
4330                         case SettingsModel::AAC_ENCODER_NERO: showCustomParamsHelpScreen("neroAacEnc.exe",      "-help" ); break;
4331                         default: MUtils::Sound::beep(MUtils::Sound::BEEP_ERR); break;
4332                 }
4333         }
4334         else if(obj == ui->helpCustomParamFLAC)    showCustomParamsHelpScreen("flac.exe",    "--help");
4335         else if(obj == ui->helpCustomParamAften)   showCustomParamsHelpScreen("aften.exe",   "-h"    );
4336         else if(obj == ui->helpCustomParamOpus)    showCustomParamsHelpScreen("opusenc.exe", "--help");
4337         else MUtils::Sound::beep(MUtils::Sound::BEEP_ERR);
4338 }
4339
4340 /*
4341  * Show help for custom parameters
4342  */
4343 void MainWindow::showCustomParamsHelpScreen(const QString &toolName, const QString &command)
4344 {
4345         const QStringList toolNames = toolName.split('|', QString::SkipEmptyParts);
4346         QString binary;
4347         for(QStringList::ConstIterator iter = toolNames.constBegin(); iter != toolNames.constEnd(); iter++)
4348         {
4349                 if(lamexp_tools_check(*iter))
4350                 {
4351                         binary = lamexp_tools_lookup(*iter);
4352                         break;
4353                 }
4354         }
4355
4356         if(binary.isEmpty())
4357         {
4358                 MUtils::Sound::beep(MUtils::Sound::BEEP_ERR);
4359                 qWarning("customParamsHelpRequested: Binary could not be found!");
4360                 return;
4361         }
4362
4363         QProcess process;
4364         MUtils::init_process(process, QFileInfo(binary).absolutePath());
4365
4366         process.start(binary, command.isEmpty() ? QStringList() : QStringList() << command);
4367
4368         qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
4369
4370         if(process.waitForStarted(15000))
4371         {
4372                 qApp->processEvents();
4373                 process.waitForFinished(15000);
4374         }
4375         
4376         if(process.state() != QProcess::NotRunning)
4377         {
4378                 process.kill();
4379                 process.waitForFinished(-1);
4380         }
4381
4382         qApp->restoreOverrideCursor();
4383         QStringList output; bool spaceFlag = true;
4384
4385         while(process.canReadLine())
4386         {
4387                 QString temp = QString::fromUtf8(process.readLine());
4388                 TRIM_STRING_RIGHT(temp);
4389                 if(temp.isEmpty())
4390                 {
4391                         if(!spaceFlag) { output << temp; spaceFlag = true; }
4392                 }
4393                 else
4394                 {
4395                         output << temp; spaceFlag = false;
4396                 }
4397         }
4398
4399         if(output.count() < 1)
4400         {
4401                 qWarning("Empty output, cannot show help screen!");
4402                 MUtils::Sound::beep(MUtils::Sound::BEEP_ERR);
4403         }
4404
4405         WidgetHideHelper hiderHelper(m_dropBox.data());
4406         QScopedPointer<LogViewDialog> dialog(new LogViewDialog(this));
4407         dialog->exec(output);
4408 }
4409
4410 /*
4411 * File overwrite mode has changed
4412 */
4413
4414 void MainWindow::overwriteModeChanged(int id)
4415 {
4416         if((id == SettingsModel::Overwrite_Replaces) && (m_settings->overwriteMode() != SettingsModel::Overwrite_Replaces))
4417         {
4418                 if(QMessageBox::warning(this, tr("Overwrite Mode"), tr("Warning: This mode may overwrite existing files with no way to revert!"), tr("Continue"), tr("Revert"), QString(), 1) != 0)
4419                 {
4420                         ui->radioButtonOverwriteModeKeepBoth->setChecked(m_settings->overwriteMode() == SettingsModel::Overwrite_KeepBoth);
4421                         ui->radioButtonOverwriteModeSkipFile->setChecked(m_settings->overwriteMode() == SettingsModel::Overwrite_SkipFile);
4422                         return;
4423                 }
4424         }
4425
4426         m_settings->overwriteMode(id);
4427 }
4428
4429 /*
4430 * Keep original date/time opertion changed
4431 */
4432 void MainWindow::keepOriginalDateTimeChanged(bool checked)
4433 {
4434         m_settings->keepOriginalDataTime(checked);
4435 }
4436
4437 /*
4438  * Reset all advanced options to their defaults
4439  */
4440 void MainWindow::resetAdvancedOptionsButtonClicked(void)
4441 {
4442         PLAY_SOUND_OPTIONAL("blast", true);
4443
4444         ui->sliderLameAlgoQuality         ->setValue(m_settings->lameAlgoQualityDefault());
4445         ui->spinBoxBitrateManagementMin   ->setValue(m_settings->bitrateManagementMinRateDefault());
4446         ui->spinBoxBitrateManagementMax   ->setValue(m_settings->bitrateManagementMaxRateDefault());
4447         ui->spinBoxNormalizationFilterPeak->setValue(static_cast<double>(m_settings->normalizationFilterMaxVolumeDefault()) / 100.0);
4448         ui->spinBoxNormalizationFilterSize->setValue(m_settings->normalizationFilterSizeDefault());
4449         ui->spinBoxToneAdjustBass         ->setValue(static_cast<double>(m_settings->toneAdjustBassDefault()) / 100.0);
4450         ui->spinBoxToneAdjustTreble       ->setValue(static_cast<double>(m_settings->toneAdjustTrebleDefault()) / 100.0);
4451         ui->spinBoxAftenSearchSize        ->setValue(m_settings->aftenExponentSearchSizeDefault());
4452         ui->spinBoxOpusComplexity         ->setValue(m_settings->opusComplexityDefault());
4453         ui->comboBoxMP3ChannelMode        ->setCurrentIndex(m_settings->lameChannelModeDefault());
4454         ui->comboBoxSamplingRate          ->setCurrentIndex(m_settings->samplingRateDefault());
4455         ui->comboBoxAACProfile            ->setCurrentIndex(m_settings->aacEncProfileDefault());
4456         ui->comboBoxAftenCodingMode       ->setCurrentIndex(m_settings->aftenAudioCodingModeDefault());
4457         ui->comboBoxAftenDRCMode          ->setCurrentIndex(m_settings->aftenDynamicRangeCompressionDefault());
4458         ui->comboBoxOpusFramesize         ->setCurrentIndex(m_settings->opusFramesizeDefault());
4459
4460         SET_CHECKBOX_STATE(ui->checkBoxBitrateManagement,          m_settings->bitrateManagementEnabledDefault());
4461         SET_CHECKBOX_STATE(ui->checkBoxNeroAAC2PassMode,           m_settings->neroAACEnable2PassDefault());
4462         SET_CHECKBOX_STATE(ui->checkBoxNormalizationFilterEnabled, m_settings->normalizationFilterEnabledDefault());
4463         SET_CHECKBOX_STATE(ui->checkBoxNormalizationFilterDynamic, m_settings->normalizationFilterDynamicDefault());
4464         SET_CHECKBOX_STATE(ui->checkBoxNormalizationFilterCoupled, m_settings->normalizationFilterCoupledDefault());
4465         SET_CHECKBOX_STATE(ui->checkBoxAutoDetectInstances,        (m_settings->maximumInstancesDefault() < 1));
4466         SET_CHECKBOX_STATE(ui->checkBoxUseSystemTempFolder,        (!m_settings->customTempPathEnabledDefault()));
4467         SET_CHECKBOX_STATE(ui->checkBoxAftenFastAllocation,        m_settings->aftenFastBitAllocationDefault());
4468         SET_CHECKBOX_STATE(ui->checkBoxRename_Rename,              m_settings->renameFiles_renameEnabledDefault());
4469         SET_CHECKBOX_STATE(ui->checkBoxRename_RegExp,              m_settings->renameFiles_regExpEnabledDefault());
4470         SET_CHECKBOX_STATE(ui->checkBoxForceStereoDownmix,         m_settings->forceStereoDownmixDefault());
4471         SET_CHECKBOX_STATE(ui->checkBoxOpusDisableResample,        m_settings->opusDisableResampleDefault());
4472         SET_CHECKBOX_STATE(ui->checkBoxKeepOriginalDateTime,       m_settings->keepOriginalDataTimeDefault());
4473
4474         ui->lineEditCustomParamLAME     ->setText(m_settings->customParametersLAMEDefault());
4475         ui->lineEditCustomParamOggEnc   ->setText(m_settings->customParametersOggEncDefault());
4476         ui->lineEditCustomParamNeroAAC  ->setText(m_settings->customParametersAacEncDefault());
4477         ui->lineEditCustomParamFLAC     ->setText(m_settings->customParametersFLACDefault());
4478         ui->lineEditCustomParamOpus     ->setText(m_settings->customParametersOpusEncDefault());
4479         ui->lineEditCustomTempFolder    ->setText(QDir::toNativeSeparators(m_settings->customTempPathDefault()));
4480         ui->lineEditRenamePattern       ->setText(m_settings->renameFiles_renamePatternDefault());
4481         ui->lineEditRenameRegExp_Search ->setText(m_settings->renameFiles_regExpSearchDefault());
4482         ui->lineEditRenameRegExp_Replace->setText(m_settings->renameFiles_regExpReplaceDefault());
4483
4484         if(m_settings->overwriteModeDefault() == SettingsModel::Overwrite_KeepBoth) ui->radioButtonOverwriteModeKeepBoth->click();
4485         if(m_settings->overwriteModeDefault() == SettingsModel::Overwrite_SkipFile) ui->radioButtonOverwriteModeSkipFile->click();
4486         if(m_settings->overwriteModeDefault() == SettingsModel::Overwrite_Replaces) ui->radioButtonOverwriteModeReplaces->click();
4487
4488         if(FileExtsModel *const model = dynamic_cast<FileExtsModel*>(ui->tableViewFileExts->model()))
4489         {
4490                 model->importItems(m_settings->renameFiles_fileExtensionDefault());
4491         }
4492
4493         ui->scrollArea->verticalScrollBar()->setValue(0);
4494         ui->buttonRename_Rename->click();
4495         customParamsChanged();
4496         renameOutputPatternChanged();
4497         renameRegExpValueChanged();
4498 }
4499
4500 // =========================================================
4501 // Multi-instance handling slots
4502 // =========================================================
4503
4504 /*
4505  * Other instance detected
4506  */
4507 void MainWindow::notifyOtherInstance(void)
4508 {
4509         if(!(BANNER_VISIBLE))
4510         {
4511                 QMessageBox msgBox(QMessageBox::Warning, tr("Already Running"), tr("LameXP is already running, please use the running instance!"), QMessageBox::NoButton, this, Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowStaysOnTopHint);
4512                 msgBox.exec();
4513         }
4514 }
4515
4516 /*
4517  * Add file from another instance
4518  */
4519 void MainWindow::addFileDelayed(const QString &filePath, bool tryASAP)
4520 {
4521         if(tryASAP && !m_delayedFileTimer->isActive())
4522         {
4523                 qDebug("Received file: %s", MUTILS_UTF8(filePath));
4524                 m_delayedFileList->append(filePath);
4525                 QTimer::singleShot(0, this, SLOT(handleDelayedFiles()));
4526         }
4527         
4528         m_delayedFileTimer->stop();
4529         qDebug("Received file: %s", MUTILS_UTF8(filePath));
4530         m_delayedFileList->append(filePath);
4531         m_delayedFileTimer->start(5000);
4532 }
4533
4534 /*
4535  * Add files from another instance
4536  */
4537 void MainWindow::addFilesDelayed(const QStringList &filePaths, bool tryASAP)
4538 {
4539         if(tryASAP && (!m_delayedFileTimer->isActive()))
4540         {
4541                 qDebug("Received %d file(s).", filePaths.count());
4542                 m_delayedFileList->append(filePaths);
4543                 QTimer::singleShot(0, this, SLOT(handleDelayedFiles()));
4544         }
4545         else
4546         {
4547                 m_delayedFileTimer->stop();
4548                 qDebug("Received %d file(s).", filePaths.count());
4549                 m_delayedFileList->append(filePaths);
4550                 m_delayedFileTimer->start(5000);
4551         }
4552 }
4553
4554 /*
4555  * Add folder from another instance
4556  */
4557 void MainWindow::addFolderDelayed(const QString &folderPath, bool recursive)
4558 {
4559         if(!(BANNER_VISIBLE))
4560         {
4561                 addFolder(folderPath, recursive, true);
4562         }
4563 }
4564
4565 // =========================================================
4566 // Misc slots
4567 // =========================================================
4568
4569 /*
4570  * Restore the override cursor
4571  */
4572 void MainWindow::restoreCursor(void)
4573 {
4574         QApplication::restoreOverrideCursor();
4575 }