OSDN Git Service

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