OSDN Git Service

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