OSDN Git Service

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