OSDN Git Service

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