OSDN Git Service

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