OSDN Git Service

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