OSDN Git Service

Force column resize, after an item in the source file list has been edited.
[lamexp/LameXP.git] / src / Dialog_MainWindow.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2011 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 "Thread_FileAnalyzer.h"
34 #include "Thread_MessageHandler.h"
35 #include "Model_MetaInfo.h"
36 #include "Model_Settings.h"
37 #include "Model_FileList.h"
38 #include "Model_FileSystem.h"
39 #include "WinSevenTaskbar.h"
40 #include "Registry_Decoder.h"
41 #include "ShellIntegration.h"
42
43 //Qt includes
44 #include <QMessageBox>
45 #include <QTimer>
46 #include <QDesktopWidget>
47 #include <QDate>
48 #include <QFileDialog>
49 #include <QInputDialog>
50 #include <QFileSystemModel>
51 #include <QDesktopServices>
52 #include <QUrl>
53 #include <QPlastiqueStyle>
54 #include <QCleanlooksStyle>
55 #include <QWindowsVistaStyle>
56 #include <QWindowsStyle>
57 #include <QSysInfo>
58 #include <QDragEnterEvent>
59 #include <QWindowsMime>
60 #include <QProcess>
61 #include <QUuid>
62 #include <QProcessEnvironment>
63 #include <QCryptographicHash>
64 #include <QTranslator>
65 #include <QResource>
66 #include <QScrollBar>
67
68 //System includes
69 #include <MMSystem.h>
70 #include <ShellAPI.h>
71
72 //Helper macros
73 #define ABORT_IF_BUSY if(m_banner->isVisible() || m_delayedFileTimer->isActive()) { MessageBeep(MB_ICONEXCLAMATION); return; }
74 #define SET_TEXT_COLOR(WIDGET,COLOR) { QPalette _palette = WIDGET->palette(); _palette.setColor(QPalette::WindowText, (COLOR)); _palette.setColor(QPalette::Text, (COLOR)); WIDGET->setPalette(_palette); }
75 #define SET_FONT_BOLD(WIDGET,BOLD) { QFont _font = WIDGET->font(); _font.setBold(BOLD); WIDGET->setFont(_font); }
76 #define LINK(URL) QString("<a href=\"%1\">%2</a>").arg(URL).arg(QString(URL).replace("-", "&minus;"))
77 #define FSLINK(PATH) QString("<a href=\"file:///%1\">%2</a>").arg(PATH).arg(QString(PATH).replace("-", "&minus;"))
78 #define TEMP_HIDE_DROPBOX(CMD) { bool __dropBoxVisible = m_dropBox->isVisible(); if(__dropBoxVisible) m_dropBox->hide(); {CMD}; if(__dropBoxVisible) m_dropBox->show(); }
79 #define USE_NATIVE_FILE_DIALOG (lamexp_themes_enabled() || ((QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) < QSysInfo::WV_XP))
80
81 ////////////////////////////////////////////////////////////
82 // Constructor
83 ////////////////////////////////////////////////////////////
84
85 MainWindow::MainWindow(FileListModel *fileListModel, AudioFileModel *metaInfo, SettingsModel *settingsModel, QWidget *parent)
86 :
87         QMainWindow(parent),
88         m_fileListModel(fileListModel),
89         m_metaData(metaInfo),
90         m_settings(settingsModel),
91         m_neroEncoderAvailable(lamexp_check_tool("neroAacEnc.exe") && lamexp_check_tool("neroAacDec.exe") && lamexp_check_tool("neroAacTag.exe")),
92         m_fhgEncoderAvailable(lamexp_check_tool("fhgaacenc.exe") && lamexp_check_tool("enc_fhgaac.dll") && lamexp_check_tool("nsutil.dll") && lamexp_check_tool("libmp4v2.dll")),
93         m_qaacEncoderAvailable(lamexp_check_tool("qaac.exe") && lamexp_check_tool("libsoxrate.dll")),
94         m_accepted(false),
95         m_firstTimeShown(true),
96         m_OutputFolderViewInitialized(false)
97 {
98         //Init the dialog, from the .ui file
99         setupUi(this);
100         setWindowFlags(windowFlags() ^ Qt::WindowMaximizeButtonHint);
101         
102         //Register meta types
103         qRegisterMetaType<AudioFileModel>("AudioFileModel");
104
105         //Enabled main buttons
106         connect(buttonAbout, SIGNAL(clicked()), this, SLOT(aboutButtonClicked()));
107         connect(buttonStart, SIGNAL(clicked()), this, SLOT(encodeButtonClicked()));
108         connect(buttonQuit, SIGNAL(clicked()), this, SLOT(closeButtonClicked()));
109
110         //Setup tab widget
111         tabWidget->setCurrentIndex(0);
112         connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabPageChanged(int)));
113
114         //Setup "Source" tab
115         sourceFileView->setModel(m_fileListModel);
116         sourceFileView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
117         sourceFileView->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
118         sourceFileView->setContextMenuPolicy(Qt::CustomContextMenu);
119         sourceFileView->viewport()->installEventFilter(this);
120         m_dropNoteLabel = new QLabel(sourceFileView);
121         m_dropNoteLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
122         SET_FONT_BOLD(m_dropNoteLabel, true);
123         SET_TEXT_COLOR(m_dropNoteLabel, Qt::darkGray);
124         m_sourceFilesContextMenu = new QMenu();
125         m_showDetailsContextAction = m_sourceFilesContextMenu->addAction(QIcon(":/icons/zoom.png"), "N/A");
126         m_previewContextAction = m_sourceFilesContextMenu->addAction(QIcon(":/icons/sound.png"), "N/A");
127         m_findFileContextAction = m_sourceFilesContextMenu->addAction(QIcon(":/icons/folder_go.png"), "N/A");
128         SET_FONT_BOLD(m_showDetailsContextAction, true);
129         connect(buttonAddFiles, SIGNAL(clicked()), this, SLOT(addFilesButtonClicked()));
130         connect(buttonRemoveFile, SIGNAL(clicked()), this, SLOT(removeFileButtonClicked()));
131         connect(buttonClearFiles, SIGNAL(clicked()), this, SLOT(clearFilesButtonClicked()));
132         connect(buttonFileUp, SIGNAL(clicked()), this, SLOT(fileUpButtonClicked()));
133         connect(buttonFileDown, SIGNAL(clicked()), this, SLOT(fileDownButtonClicked()));
134         connect(buttonShowDetails, SIGNAL(clicked()), this, SLOT(showDetailsButtonClicked()));
135         connect(m_fileListModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(sourceModelChanged()));
136         connect(m_fileListModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(sourceModelChanged()));
137         connect(m_fileListModel, SIGNAL(modelReset()), this, SLOT(sourceModelChanged()));
138         connect(sourceFileView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(sourceFilesContextMenu(QPoint)));
139         connect(sourceFileView->verticalScrollBar(), SIGNAL(sliderMoved(int)), this, SLOT(sourceFilesScrollbarMoved(int)));
140         connect(sourceFileView->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(sourceFilesScrollbarMoved(int)));
141         connect(m_showDetailsContextAction, SIGNAL(triggered(bool)), this, SLOT(showDetailsButtonClicked()));
142         connect(m_previewContextAction, SIGNAL(triggered(bool)), this, SLOT(previewContextActionTriggered()));
143         connect(m_findFileContextAction, SIGNAL(triggered(bool)), this, SLOT(findFileContextActionTriggered()));
144
145         //Setup "Output" tab
146         m_fileSystemModel = new QFileSystemModelEx();
147         m_fileSystemModel->installEventFilter(this);
148         outputFolderView->setModel(m_fileSystemModel);
149         outputFolderView->header()->setStretchLastSection(true);
150         outputFolderView->header()->hideSection(1);
151         outputFolderView->header()->hideSection(2);
152         outputFolderView->header()->hideSection(3);
153         outputFolderView->setHeaderHidden(true);
154         outputFolderView->setAnimated(false);
155         outputFolderView->setMouseTracking(false);
156         outputFolderView->setContextMenuPolicy(Qt::CustomContextMenu);
157         outputFolderView->installEventFilter(this);
158         outputFoldersFovoritesLabel->installEventFilter(this);
159         while(saveToSourceFolderCheckBox->isChecked() != m_settings->outputToSourceDir()) saveToSourceFolderCheckBox->click();
160         prependRelativePathCheckBox->setChecked(m_settings->prependRelativeSourcePath());
161         connect(outputFolderView, SIGNAL(clicked(QModelIndex)), this, SLOT(outputFolderViewClicked(QModelIndex)));
162         connect(outputFolderView, SIGNAL(activated(QModelIndex)), this, SLOT(outputFolderViewClicked(QModelIndex)));
163         connect(outputFolderView, SIGNAL(pressed(QModelIndex)), this, SLOT(outputFolderViewClicked(QModelIndex)));
164         connect(outputFolderView, SIGNAL(entered(QModelIndex)), this, SLOT(outputFolderViewMoved(QModelIndex)));
165         connect(buttonMakeFolder, SIGNAL(clicked()), this, SLOT(makeFolderButtonClicked()));
166         connect(buttonGotoHome, SIGNAL(clicked()), SLOT(gotoHomeFolderButtonClicked()));
167         connect(buttonGotoDesktop, SIGNAL(clicked()), this, SLOT(gotoDesktopButtonClicked()));
168         connect(buttonGotoMusic, SIGNAL(clicked()), this, SLOT(gotoMusicFolderButtonClicked()));
169         connect(saveToSourceFolderCheckBox, SIGNAL(clicked()), this, SLOT(saveToSourceFolderChanged()));
170         connect(prependRelativePathCheckBox, SIGNAL(clicked()), this, SLOT(prependRelativePathChanged()));
171         m_outputFolderContextMenu = new QMenu();
172         m_showFolderContextAction = m_outputFolderContextMenu->addAction(QIcon(":/icons/zoom.png"), "N/A");
173         m_outputFolderFavoritesMenu = new QMenu();
174         m_addFavoriteFolderAction = m_outputFolderFavoritesMenu->addAction(QIcon(":/icons/add.png"), "N/A");
175         m_outputFolderFavoritesMenu->insertSeparator(m_addFavoriteFolderAction);
176         connect(outputFolderView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(outputFolderContextMenu(QPoint)));
177         connect(m_showFolderContextAction, SIGNAL(triggered(bool)), this, SLOT(showFolderContextActionTriggered()));
178         connect(m_addFavoriteFolderAction, SIGNAL(triggered(bool)), this, SLOT(addFavoriteFolderActionTriggered()));
179         outputFolderLabel->installEventFilter(this);
180         outputFolderView->setCurrentIndex(m_fileSystemModel->index(m_settings->outputDir()));
181         outputFolderViewClicked(outputFolderView->currentIndex());
182         refreshFavorites();
183         
184         //Setup "Meta Data" tab
185         m_metaInfoModel = new MetaInfoModel(m_metaData, 6);
186         m_metaInfoModel->clearData();
187         m_metaInfoModel->setData(m_metaInfoModel->index(4, 1), m_settings->metaInfoPosition());
188         metaDataView->setModel(m_metaInfoModel);
189         metaDataView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
190         metaDataView->verticalHeader()->hide();
191         metaDataView->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
192         while(writeMetaDataCheckBox->isChecked() != m_settings->writeMetaTags()) writeMetaDataCheckBox->click();
193         generatePlaylistCheckBox->setChecked(m_settings->createPlaylist());
194         connect(buttonEditMeta, SIGNAL(clicked()), this, SLOT(editMetaButtonClicked()));
195         connect(buttonClearMeta, SIGNAL(clicked()), this, SLOT(clearMetaButtonClicked()));
196         connect(writeMetaDataCheckBox, SIGNAL(clicked()), this, SLOT(metaTagsEnabledChanged()));
197         connect(generatePlaylistCheckBox, SIGNAL(clicked()), this, SLOT(playlistEnabledChanged()));
198
199         //Setup "Compression" tab
200         m_encoderButtonGroup = new QButtonGroup(this);
201         m_encoderButtonGroup->addButton(radioButtonEncoderMP3, SettingsModel::MP3Encoder);
202         m_encoderButtonGroup->addButton(radioButtonEncoderVorbis, SettingsModel::VorbisEncoder);
203         m_encoderButtonGroup->addButton(radioButtonEncoderAAC, SettingsModel::AACEncoder);
204         m_encoderButtonGroup->addButton(radioButtonEncoderAC3, SettingsModel::AC3Encoder);
205         m_encoderButtonGroup->addButton(radioButtonEncoderFLAC, SettingsModel::FLACEncoder);
206         m_encoderButtonGroup->addButton(radioButtonEncoderPCM, SettingsModel::PCMEncoder);
207         m_modeButtonGroup = new QButtonGroup(this);
208         m_modeButtonGroup->addButton(radioButtonModeQuality, SettingsModel::VBRMode);
209         m_modeButtonGroup->addButton(radioButtonModeAverageBitrate, SettingsModel::ABRMode);
210         m_modeButtonGroup->addButton(radioButtonConstBitrate, SettingsModel::CBRMode);
211         radioButtonEncoderAAC->setEnabled(m_neroEncoderAvailable || m_fhgEncoderAvailable || m_qaacEncoderAvailable);
212         radioButtonEncoderMP3->setChecked(m_settings->compressionEncoder() == SettingsModel::MP3Encoder);
213         radioButtonEncoderVorbis->setChecked(m_settings->compressionEncoder() == SettingsModel::VorbisEncoder);
214         radioButtonEncoderAAC->setChecked((m_settings->compressionEncoder() == SettingsModel::AACEncoder) && (m_neroEncoderAvailable || m_fhgEncoderAvailable || m_qaacEncoderAvailable));
215         radioButtonEncoderAC3->setChecked(m_settings->compressionEncoder() == SettingsModel::AC3Encoder);
216         radioButtonEncoderFLAC->setChecked(m_settings->compressionEncoder() == SettingsModel::FLACEncoder);
217         radioButtonEncoderPCM->setChecked(m_settings->compressionEncoder() == SettingsModel::PCMEncoder);
218         radioButtonModeQuality->setChecked(m_settings->compressionRCMode() == SettingsModel::VBRMode);
219         radioButtonModeAverageBitrate->setChecked(m_settings->compressionRCMode() == SettingsModel::ABRMode);
220         radioButtonConstBitrate->setChecked(m_settings->compressionRCMode() == SettingsModel::CBRMode);
221         sliderBitrate->setValue(m_settings->compressionBitrate());
222         connect(m_encoderButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(updateEncoder(int)));
223         connect(m_modeButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(updateRCMode(int)));
224         connect(sliderBitrate, SIGNAL(valueChanged(int)), this, SLOT(updateBitrate(int)));
225         updateEncoder(m_encoderButtonGroup->checkedId());
226
227         //Setup "Advanced Options" tab
228         sliderLameAlgoQuality->setValue(m_settings->lameAlgoQuality());
229         if(m_settings->maximumInstances() > 0) sliderMaxInstances->setValue(m_settings->maximumInstances());
230         spinBoxBitrateManagementMin->setValue(m_settings->bitrateManagementMinRate());
231         spinBoxBitrateManagementMax->setValue(m_settings->bitrateManagementMaxRate());
232         spinBoxNormalizationFilter->setValue(static_cast<double>(m_settings->normalizationFilterMaxVolume()) / 100.0);
233         spinBoxToneAdjustBass->setValue(static_cast<double>(m_settings->toneAdjustBass()) / 100.0);
234         spinBoxToneAdjustTreble->setValue(static_cast<double>(m_settings->toneAdjustTreble()) / 100.0);
235         spinBoxAftenSearchSize->setValue(m_settings->aftenExponentSearchSize());
236         comboBoxMP3ChannelMode->setCurrentIndex(m_settings->lameChannelMode());
237         comboBoxSamplingRate->setCurrentIndex(m_settings->samplingRate());
238         comboBoxAACProfile->setCurrentIndex(m_settings->aacEncProfile());
239         comboBoxAftenCodingMode->setCurrentIndex(m_settings->aftenAudioCodingMode());
240         comboBoxAftenDRCMode->setCurrentIndex(m_settings->aftenDynamicRangeCompression());
241         comboBoxNormalizationMode->setCurrentIndex(m_settings->normalizationFilterEqualizationMode());
242         while(checkBoxBitrateManagement->isChecked() != m_settings->bitrateManagementEnabled()) checkBoxBitrateManagement->click();
243         while(checkBoxNeroAAC2PassMode->isChecked() != m_settings->neroAACEnable2Pass()) checkBoxNeroAAC2PassMode->click();
244         while(checkBoxAftenFastAllocation->isChecked() != m_settings->aftenFastBitAllocation()) checkBoxAftenFastAllocation->click();
245         while(checkBoxNormalizationFilter->isChecked() != m_settings->normalizationFilterEnabled()) checkBoxNormalizationFilter->click();
246         while(checkBoxAutoDetectInstances->isChecked() != (m_settings->maximumInstances() < 1)) checkBoxAutoDetectInstances->click();
247         while(checkBoxUseSystemTempFolder->isChecked() == m_settings->customTempPathEnabled()) checkBoxUseSystemTempFolder->click();
248         while(checkBoxRenameOutput->isChecked() != m_settings->renameOutputFilesEnabled()) checkBoxRenameOutput->click();
249         while(checkBoxForceStereoDownmix->isChecked() != m_settings->forceStereoDownmix()) checkBoxForceStereoDownmix->click();
250         checkBoxNeroAAC2PassMode->setEnabled(!(m_fhgEncoderAvailable || m_qaacEncoderAvailable));
251         lineEditCustomParamLAME->setText(m_settings->customParametersLAME());
252         lineEditCustomParamOggEnc->setText(m_settings->customParametersOggEnc());
253         lineEditCustomParamNeroAAC->setText(m_settings->customParametersAacEnc());
254         lineEditCustomParamFLAC->setText(m_settings->customParametersFLAC());
255         lineEditCustomParamAften->setText(m_settings->customParametersAften());
256         lineEditCustomTempFolder->setText(QDir::toNativeSeparators(m_settings->customTempPath()));
257         lineEditRenamePattern->setText(m_settings->renameOutputFilesPattern());
258         connect(sliderLameAlgoQuality, SIGNAL(valueChanged(int)), this, SLOT(updateLameAlgoQuality(int)));
259         connect(checkBoxBitrateManagement, SIGNAL(clicked(bool)), this, SLOT(bitrateManagementEnabledChanged(bool)));
260         connect(spinBoxBitrateManagementMin, SIGNAL(valueChanged(int)), this, SLOT(bitrateManagementMinChanged(int)));
261         connect(spinBoxBitrateManagementMax, SIGNAL(valueChanged(int)), this, SLOT(bitrateManagementMaxChanged(int)));
262         connect(comboBoxMP3ChannelMode, SIGNAL(currentIndexChanged(int)), this, SLOT(channelModeChanged(int)));
263         connect(comboBoxSamplingRate, SIGNAL(currentIndexChanged(int)), this, SLOT(samplingRateChanged(int)));
264         connect(checkBoxNeroAAC2PassMode, SIGNAL(clicked(bool)), this, SLOT(neroAAC2PassChanged(bool)));
265         connect(comboBoxAACProfile, SIGNAL(currentIndexChanged(int)), this, SLOT(neroAACProfileChanged(int)));
266         connect(checkBoxNormalizationFilter, SIGNAL(clicked(bool)), this, SLOT(normalizationEnabledChanged(bool)));
267         connect(comboBoxAftenCodingMode, SIGNAL(currentIndexChanged(int)), this, SLOT(aftenCodingModeChanged(int)));
268         connect(comboBoxAftenDRCMode, SIGNAL(currentIndexChanged(int)), this, SLOT(aftenDRCModeChanged(int)));
269         connect(spinBoxAftenSearchSize, SIGNAL(valueChanged(int)), this, SLOT(aftenSearchSizeChanged(int)));
270         connect(checkBoxAftenFastAllocation, SIGNAL(clicked(bool)), this, SLOT(aftenFastAllocationChanged(bool)));
271         connect(spinBoxNormalizationFilter, SIGNAL(valueChanged(double)), this, SLOT(normalizationMaxVolumeChanged(double)));
272         connect(comboBoxNormalizationMode, SIGNAL(currentIndexChanged(int)), this, SLOT(normalizationModeChanged(int)));
273         connect(spinBoxToneAdjustBass, SIGNAL(valueChanged(double)), this, SLOT(toneAdjustBassChanged(double)));
274         connect(spinBoxToneAdjustTreble, SIGNAL(valueChanged(double)), this, SLOT(toneAdjustTrebleChanged(double)));
275         connect(buttonToneAdjustReset, SIGNAL(clicked()), this, SLOT(toneAdjustTrebleReset()));
276         connect(lineEditCustomParamLAME, SIGNAL(editingFinished()), this, SLOT(customParamsChanged()));
277         connect(lineEditCustomParamOggEnc, SIGNAL(editingFinished()), this, SLOT(customParamsChanged()));
278         connect(lineEditCustomParamNeroAAC, SIGNAL(editingFinished()), this, SLOT(customParamsChanged()));
279         connect(lineEditCustomParamFLAC, SIGNAL(editingFinished()), this, SLOT(customParamsChanged()));
280         connect(lineEditCustomParamAften, SIGNAL(editingFinished()), this, SLOT(customParamsChanged()));
281         connect(sliderMaxInstances, SIGNAL(valueChanged(int)), this, SLOT(updateMaximumInstances(int)));
282         connect(checkBoxAutoDetectInstances, SIGNAL(clicked(bool)), this, SLOT(autoDetectInstancesChanged(bool)));
283         connect(buttonBrowseCustomTempFolder, SIGNAL(clicked()), this, SLOT(browseCustomTempFolderButtonClicked()));
284         connect(lineEditCustomTempFolder, SIGNAL(textChanged(QString)), this, SLOT(customTempFolderChanged(QString)));
285         connect(checkBoxUseSystemTempFolder, SIGNAL(clicked(bool)), this, SLOT(useCustomTempFolderChanged(bool)));
286         connect(buttonResetAdvancedOptions, SIGNAL(clicked()), this, SLOT(resetAdvancedOptionsButtonClicked()));
287         connect(checkBoxRenameOutput, SIGNAL(clicked(bool)), this, SLOT(renameOutputEnabledChanged(bool)));
288         connect(lineEditRenamePattern, SIGNAL(editingFinished()), this, SLOT(renameOutputPatternChanged()));
289         connect(lineEditRenamePattern, SIGNAL(textChanged(QString)), this, SLOT(renameOutputPatternChanged(QString)));
290         connect(labelShowRenameMacros, SIGNAL(linkActivated(QString)), this, SLOT(showRenameMacros(QString)));
291         connect(checkBoxForceStereoDownmix, SIGNAL(clicked(bool)), this, SLOT(forceStereoDownmixEnabledChanged(bool)));
292         updateLameAlgoQuality(sliderLameAlgoQuality->value());
293         updateMaximumInstances(sliderMaxInstances->value());
294         toneAdjustTrebleChanged(spinBoxToneAdjustTreble->value());
295         toneAdjustBassChanged(spinBoxToneAdjustBass->value());
296         customParamsChanged();
297         
298         //Activate file menu actions
299         actionOpenFolder->setData(QVariant::fromValue<bool>(false));
300         actionOpenFolderRecursively->setData(QVariant::fromValue<bool>(true));
301         connect(actionOpenFolder, SIGNAL(triggered()), this, SLOT(openFolderActionActivated()));
302         connect(actionOpenFolderRecursively, SIGNAL(triggered()), this, SLOT(openFolderActionActivated()));
303
304         //Activate view menu actions
305         m_tabActionGroup = new QActionGroup(this);
306         m_tabActionGroup->addAction(actionSourceFiles);
307         m_tabActionGroup->addAction(actionOutputDirectory);
308         m_tabActionGroup->addAction(actionCompression);
309         m_tabActionGroup->addAction(actionMetaData);
310         m_tabActionGroup->addAction(actionAdvancedOptions);
311         actionSourceFiles->setData(0);
312         actionOutputDirectory->setData(1);
313         actionMetaData->setData(2);
314         actionCompression->setData(3);
315         actionAdvancedOptions->setData(4);
316         actionSourceFiles->setChecked(true);
317         connect(m_tabActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(tabActionActivated(QAction*)));
318
319         //Activate style menu actions
320         m_styleActionGroup = new QActionGroup(this);
321         m_styleActionGroup->addAction(actionStylePlastique);
322         m_styleActionGroup->addAction(actionStyleCleanlooks);
323         m_styleActionGroup->addAction(actionStyleWindowsVista);
324         m_styleActionGroup->addAction(actionStyleWindowsXP);
325         m_styleActionGroup->addAction(actionStyleWindowsClassic);
326         actionStylePlastique->setData(0);
327         actionStyleCleanlooks->setData(1);
328         actionStyleWindowsVista->setData(2);
329         actionStyleWindowsXP->setData(3);
330         actionStyleWindowsClassic->setData(4);
331         actionStylePlastique->setChecked(true);
332         actionStyleWindowsXP->setEnabled((QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) >= QSysInfo::WV_XP && lamexp_themes_enabled());
333         actionStyleWindowsVista->setEnabled((QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) >= QSysInfo::WV_VISTA && lamexp_themes_enabled());
334         connect(m_styleActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(styleActionActivated(QAction*)));
335         styleActionActivated(NULL);
336
337         //Populate the language menu
338         m_languageActionGroup = new QActionGroup(this);
339         QStringList translations = lamexp_query_translations();
340         while(!translations.isEmpty())
341         {
342                 QString langId = translations.takeFirst();
343                 QAction *currentLanguage = new QAction(this);
344                 currentLanguage->setData(langId);
345                 currentLanguage->setText(lamexp_translation_name(langId));
346                 currentLanguage->setIcon(QIcon(QString(":/flags/%1.png").arg(langId)));
347                 currentLanguage->setCheckable(true);
348                 m_languageActionGroup->addAction(currentLanguage);
349                 menuLanguage->insertAction(actionLoadTranslationFromFile, currentLanguage);
350         }
351         menuLanguage->insertSeparator(actionLoadTranslationFromFile);
352         connect(actionLoadTranslationFromFile, SIGNAL(triggered(bool)), this, SLOT(languageFromFileActionActivated(bool)));
353         connect(m_languageActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(languageActionActivated(QAction*)));
354
355         //Activate tools menu actions
356         actionDisableUpdateReminder->setChecked(!m_settings->autoUpdateEnabled());
357         actionDisableSounds->setChecked(!m_settings->soundsEnabled());
358         actionDisableNeroAacNotifications->setChecked(!m_settings->neroAacNotificationsEnabled());
359         actionDisableSlowStartupNotifications->setChecked(!m_settings->antivirNotificationsEnabled());
360         actionDisableShellIntegration->setChecked(!m_settings->shellIntegrationEnabled());
361         actionDisableShellIntegration->setDisabled(lamexp_portable_mode() && actionDisableShellIntegration->isChecked());
362         actionCheckForBetaUpdates->setChecked(m_settings->autoUpdateCheckBeta() || lamexp_version_demo());
363         actionCheckForBetaUpdates->setEnabled(!lamexp_version_demo());
364         actionHibernateComputer->setChecked(m_settings->hibernateComputer());
365         actionHibernateComputer->setEnabled(lamexp_is_hibernation_supported());
366         connect(actionDisableUpdateReminder, SIGNAL(triggered(bool)), this, SLOT(disableUpdateReminderActionTriggered(bool)));
367         connect(actionDisableSounds, SIGNAL(triggered(bool)), this, SLOT(disableSoundsActionTriggered(bool)));
368         connect(actionDisableNeroAacNotifications, SIGNAL(triggered(bool)), this, SLOT(disableNeroAacNotificationsActionTriggered(bool)));
369         connect(actionDisableSlowStartupNotifications, SIGNAL(triggered(bool)), this, SLOT(disableSlowStartupNotificationsActionTriggered(bool)));
370         connect(actionDisableShellIntegration, SIGNAL(triggered(bool)), this, SLOT(disableShellIntegrationActionTriggered(bool)));
371         connect(actionShowDropBoxWidget, SIGNAL(triggered(bool)), this, SLOT(showDropBoxWidgetActionTriggered(bool)));
372         connect(actionHibernateComputer, SIGNAL(triggered(bool)), this, SLOT(hibernateComputerActionTriggered(bool)));
373         connect(actionCheckForBetaUpdates, SIGNAL(triggered(bool)), this, SLOT(checkForBetaUpdatesActionTriggered(bool)));
374         connect(actionImportCueSheet, SIGNAL(triggered(bool)), this, SLOT(importCueSheetActionTriggered(bool)));
375                 
376         //Activate help menu actions
377         actionVisitHomepage->setData(QString::fromLatin1(lamexp_website_url()));
378         actionVisitSupport->setData(QString::fromLatin1(lamexp_support_url()));
379         actionDocumentFAQ->setData(QString("%1/FAQ.html").arg(QApplication::applicationDirPath()));
380         actionDocumentChangelog->setData(QString("%1/Changelog.html").arg(QApplication::applicationDirPath()));
381         actionDocumentTranslate->setData(QString("%1/Translate.html").arg(QApplication::applicationDirPath()));
382         connect(actionCheckUpdates, SIGNAL(triggered()), this, SLOT(checkUpdatesActionActivated()));
383         connect(actionVisitHomepage, SIGNAL(triggered()), this, SLOT(visitHomepageActionActivated()));
384         connect(actionVisitSupport, SIGNAL(triggered()), this, SLOT(visitHomepageActionActivated()));
385         connect(actionDocumentFAQ, SIGNAL(triggered()), this, SLOT(documentActionActivated()));
386         connect(actionDocumentChangelog, SIGNAL(triggered()), this, SLOT(documentActionActivated()));
387         connect(actionDocumentTranslate, SIGNAL(triggered()), this, SLOT(documentActionActivated()));
388         
389         //Center window in screen
390         QRect desktopRect = QApplication::desktop()->screenGeometry();
391         QRect thisRect = this->geometry();
392         move((desktopRect.width() - thisRect.width()) / 2, (desktopRect.height() - thisRect.height()) / 2);
393         setMinimumSize(thisRect.width(), thisRect.height());
394
395         //Create banner
396         m_banner = new WorkingBanner(this);
397
398         //Create DropBox widget
399         m_dropBox = new DropBox(this, m_fileListModel, m_settings);
400         connect(m_fileListModel, SIGNAL(modelReset()), m_dropBox, SLOT(modelChanged()));
401         connect(m_fileListModel, SIGNAL(rowsInserted(QModelIndex,int,int)), m_dropBox, SLOT(modelChanged()));
402         connect(m_fileListModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), m_dropBox, SLOT(modelChanged()));
403
404         //Create message handler thread
405         m_messageHandler = new MessageHandlerThread();
406         m_delayedFileList = new QStringList();
407         m_delayedFileTimer = new QTimer();
408         m_delayedFileTimer->setSingleShot(true);
409         m_delayedFileTimer->setInterval(5000);
410         connect(m_messageHandler, SIGNAL(otherInstanceDetected()), this, SLOT(notifyOtherInstance()), Qt::QueuedConnection);
411         connect(m_messageHandler, SIGNAL(fileReceived(QString)), this, SLOT(addFileDelayed(QString)), Qt::QueuedConnection);
412         connect(m_messageHandler, SIGNAL(folderReceived(QString, bool)), this, SLOT(addFolderDelayed(QString, bool)), Qt::QueuedConnection);
413         connect(m_messageHandler, SIGNAL(killSignalReceived()), this, SLOT(close()), Qt::QueuedConnection);
414         connect(m_delayedFileTimer, SIGNAL(timeout()), this, SLOT(handleDelayedFiles()));
415         m_messageHandler->start();
416
417         //Load translation file
418         QList<QAction*> languageActions = m_languageActionGroup->actions();
419         while(!languageActions.isEmpty())
420         {
421                 QAction *currentLanguage = languageActions.takeFirst();
422                 if(currentLanguage->data().toString().compare(m_settings->currentLanguage(), Qt::CaseInsensitive) == 0)
423                 {
424                         currentLanguage->setChecked(true);
425                         languageActionActivated(currentLanguage);
426                 }
427         }
428
429         //Re-translate (make sure we translate once)
430         QEvent languageChangeEvent(QEvent::LanguageChange);
431         changeEvent(&languageChangeEvent);
432
433         //Enable Drag & Drop
434         this->setAcceptDrops(true);
435 }
436
437 ////////////////////////////////////////////////////////////
438 // Destructor
439 ////////////////////////////////////////////////////////////
440
441 MainWindow::~MainWindow(void)
442 {
443         //Stop message handler thread
444         if(m_messageHandler && m_messageHandler->isRunning())
445         {
446                 m_messageHandler->stop();
447                 if(!m_messageHandler->wait(10000))
448                 {
449                         m_messageHandler->terminate();
450                         m_messageHandler->wait();
451                 }
452         }
453
454         //Unset models
455         sourceFileView->setModel(NULL);
456         metaDataView->setModel(NULL);
457
458         //Free memory
459         LAMEXP_DELETE(m_tabActionGroup);
460         LAMEXP_DELETE(m_styleActionGroup);
461         LAMEXP_DELETE(m_languageActionGroup);
462         LAMEXP_DELETE(m_banner);
463         LAMEXP_DELETE(m_fileSystemModel);
464         LAMEXP_DELETE(m_messageHandler);
465         LAMEXP_DELETE(m_delayedFileList);
466         LAMEXP_DELETE(m_delayedFileTimer);
467         LAMEXP_DELETE(m_metaInfoModel);
468         LAMEXP_DELETE(m_encoderButtonGroup);
469         LAMEXP_DELETE(m_encoderButtonGroup);
470         LAMEXP_DELETE(m_sourceFilesContextMenu);
471         LAMEXP_DELETE(m_outputFolderFavoritesMenu);
472         LAMEXP_DELETE(m_dropBox);
473 }
474
475 ////////////////////////////////////////////////////////////
476 // PRIVATE FUNCTIONS
477 ////////////////////////////////////////////////////////////
478
479 /*
480  * Add file to source list
481  */
482 void MainWindow::addFiles(const QStringList &files)
483 {
484         if(files.isEmpty())
485         {
486                 return;
487         }
488
489         tabWidget->setCurrentIndex(0);
490
491         FileAnalyzer *analyzer = new FileAnalyzer(files);
492         connect(analyzer, SIGNAL(fileSelected(QString)), m_banner, SLOT(setText(QString)), Qt::QueuedConnection);
493         connect(analyzer, SIGNAL(fileAnalyzed(AudioFileModel)), m_fileListModel, SLOT(addFile(AudioFileModel)), Qt::QueuedConnection);
494         connect(m_banner, SIGNAL(userAbort()), analyzer, SLOT(abortProcess()), Qt::DirectConnection);
495
496         m_banner->show(tr("Adding file(s), please wait..."), analyzer);
497
498         if(analyzer->filesDenied())
499         {
500                 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."))));
501         }
502         if(analyzer->filesDummyCDDA())
503         {
504                 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>"))));
505         }
506         if(analyzer->filesCueSheet())
507         {
508                 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."))));
509         }
510         if(analyzer->filesRejected())
511         {
512                 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."))));
513         }
514
515         LAMEXP_DELETE(analyzer);
516         sourceFileView->scrollToBottom();
517         m_banner->close();
518 }
519
520 /*
521  * Add folder to source list
522  */
523 void MainWindow::addFolder(const QString &path, bool recursive, bool delayed)
524 {
525         QFileInfoList folderInfoList;
526         folderInfoList << QFileInfo(path);
527         QStringList fileList;
528         
529         m_banner->show(tr("Scanning folder(s) for files, please wait..."));
530         
531         QApplication::processEvents();
532         GetAsyncKeyState(VK_ESCAPE);
533
534         while(!folderInfoList.isEmpty())
535         {
536                 if(GetAsyncKeyState(VK_ESCAPE) & 0x0001)
537                 {
538                         MessageBeep(MB_ICONERROR);
539                         qWarning("Operation cancelled by user!");
540                         fileList.clear();
541                         break;
542                 }
543                 
544                 QDir currentDir(folderInfoList.takeFirst().canonicalFilePath());
545                 QFileInfoList fileInfoList = currentDir.entryInfoList(QDir::Files | QDir::NoSymLinks);
546
547                 while(!fileInfoList.isEmpty())
548                 {
549                         fileList << fileInfoList.takeFirst().canonicalFilePath();
550                 }
551
552                 QApplication::processEvents();
553
554                 if(recursive)
555                 {
556                         folderInfoList.append(currentDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks));
557                         QApplication::processEvents();
558                 }
559         }
560         
561         m_banner->close();
562         QApplication::processEvents();
563
564         if(!fileList.isEmpty())
565         {
566                 if(delayed)
567                 {
568                         addFilesDelayed(fileList);
569                 }
570                 else
571                 {
572                         addFiles(fileList);
573                 }
574         }
575 }
576
577 /*
578  * Check for updates
579  */
580 bool MainWindow::checkForUpdates(void)
581 {
582         bool bReadyToInstall = false;
583         
584         UpdateDialog *updateDialog = new UpdateDialog(m_settings, this);
585         updateDialog->exec();
586
587         if(updateDialog->getSuccess())
588         {
589                 m_settings->autoUpdateLastCheck(QDate::currentDate().toString(Qt::ISODate));
590                 bReadyToInstall = updateDialog->updateReadyToInstall();
591         }
592
593         LAMEXP_DELETE(updateDialog);
594         return bReadyToInstall;
595 }
596
597 void MainWindow::refreshFavorites(void)
598 {
599         QList<QAction*> folderList = m_outputFolderFavoritesMenu->actions();
600         QStringList favorites = m_settings->favoriteOutputFolders().split("|", QString::SkipEmptyParts);
601         while(favorites.count() > 6) favorites.removeFirst();
602
603         while(!folderList.isEmpty())
604         {
605                 QAction *currentItem = folderList.takeFirst();
606                 if(currentItem->isSeparator()) break;
607                 m_outputFolderFavoritesMenu->removeAction(currentItem);
608                 LAMEXP_DELETE(currentItem);
609         }
610
611         QAction *lastItem = m_outputFolderFavoritesMenu->actions().first();
612
613         while(!favorites.isEmpty())
614         {
615                 QString path = favorites.takeLast();
616                 if(QDir(path).exists())
617                 {
618                         QAction *action = new QAction(QIcon(":/icons/folder_go.png"), QDir::toNativeSeparators(path), this);
619                         action->setData(path);
620                         m_outputFolderFavoritesMenu->insertAction(lastItem, action);
621                         connect(action, SIGNAL(triggered(bool)), this, SLOT(gotoFavoriteFolder()));
622                         lastItem = action;
623                 }
624         }
625 }
626
627 ////////////////////////////////////////////////////////////
628 // EVENTS
629 ////////////////////////////////////////////////////////////
630
631 /*
632  * Window is about to be shown
633  */
634 void MainWindow::showEvent(QShowEvent *event)
635 {
636         m_accepted = false;
637         m_dropNoteLabel->setGeometry(0, 0, sourceFileView->width(), sourceFileView->height());
638         sourceModelChanged();
639         
640         if(!event->spontaneous())
641         {
642                 tabWidget->setCurrentIndex(0);
643         }
644
645         if(m_firstTimeShown)
646         {
647                 m_firstTimeShown = false;
648                 QTimer::singleShot(0, this, SLOT(windowShown()));
649         }
650         else
651         {
652                 if(m_settings->dropBoxWidgetEnabled())
653                 {
654                         m_dropBox->setVisible(true);
655                 }
656         }
657 }
658
659 /*
660  * Re-translate the UI
661  */
662 void MainWindow::changeEvent(QEvent *e)
663 {
664         if(e->type() == QEvent::LanguageChange)
665         {
666                 int comboBoxIndex[6];
667                 
668                 //Backup combobox indices, as retranslateUi() resets
669                 comboBoxIndex[0] = comboBoxMP3ChannelMode->currentIndex();
670                 comboBoxIndex[1] = comboBoxSamplingRate->currentIndex();
671                 comboBoxIndex[2] = comboBoxAACProfile->currentIndex();
672                 comboBoxIndex[3] = comboBoxAftenCodingMode->currentIndex();
673                 comboBoxIndex[4] = comboBoxAftenDRCMode->currentIndex();
674                 comboBoxIndex[5] = comboBoxNormalizationMode->currentIndex();
675                 
676                 //Re-translate from UIC
677                 Ui::MainWindow::retranslateUi(this);
678
679                 //Restore combobox indices
680                 comboBoxMP3ChannelMode->setCurrentIndex(comboBoxIndex[0]);
681                 comboBoxSamplingRate->setCurrentIndex(comboBoxIndex[1]);
682                 comboBoxAACProfile->setCurrentIndex(comboBoxIndex[2]);
683                 comboBoxAftenCodingMode->setCurrentIndex(comboBoxIndex[3]);
684                 comboBoxAftenDRCMode->setCurrentIndex(comboBoxIndex[4]);
685                 comboBoxNormalizationMode->setCurrentIndex(comboBoxIndex[5]);
686
687                 //Update the window title
688                 if(LAMEXP_DEBUG)
689                 {
690                         setWindowTitle(QString("%1 [!!! DEBUG BUILD !!!]").arg(windowTitle()));
691                 }
692                 else if(lamexp_version_demo())
693                 {
694                         setWindowTitle(QString("%1 [%2]").arg(windowTitle(), tr("DEMO VERSION")));
695                 }
696
697                 //Manually re-translate widgets that UIC doesn't handle
698                 m_dropNoteLabel->setText(QString("» %1 Â«").arg(tr("You can drop in audio files here!")));
699                 m_showDetailsContextAction->setText(tr("Show Details"));
700                 m_previewContextAction->setText(tr("Open File in External Application"));
701                 m_findFileContextAction->setText(tr("Browse File Location"));
702                 m_showFolderContextAction->setText(tr("Browse Selected Folder"));
703                 m_addFavoriteFolderAction->setText(tr("Bookmark Current Output Folder"));
704
705                 //Force GUI update
706                 m_metaInfoModel->clearData();
707                 m_metaInfoModel->setData(m_metaInfoModel->index(4, 1), m_settings->metaInfoPosition());
708                 updateEncoder(m_settings->compressionEncoder());
709                 updateLameAlgoQuality(sliderLameAlgoQuality->value());
710                 updateMaximumInstances(sliderMaxInstances->value());
711                 renameOutputPatternChanged(lineEditRenamePattern->text());
712
713                 //Re-install shell integration
714                 if(m_settings->shellIntegrationEnabled())
715                 {
716                         ShellIntegration::install();
717                 }
718
719                 //Force resize, if needed
720                 tabPageChanged(tabWidget->currentIndex());
721         }
722 }
723
724 /*
725  * File dragged over window
726  */
727 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
728 {
729         QStringList formats = event->mimeData()->formats();
730         
731         if(formats.contains("application/x-qt-windows-mime;value=\"FileNameW\"", Qt::CaseInsensitive) && formats.contains("text/uri-list", Qt::CaseInsensitive))
732         {
733                 event->acceptProposedAction();
734         }
735 }
736
737 /*
738  * File dropped onto window
739  */
740 void MainWindow::dropEvent(QDropEvent *event)
741 {
742         ABORT_IF_BUSY;
743
744         QStringList droppedFiles;
745         QList<QUrl> urls = event->mimeData()->urls();
746
747         while(!urls.isEmpty())
748         {
749                 QUrl currentUrl = urls.takeFirst();
750                 QFileInfo file(currentUrl.toLocalFile());
751                 if(!file.exists())
752                 {
753                         continue;
754                 }
755                 if(file.isFile())
756                 {
757                         qDebug("Dropped File: %s", file.canonicalFilePath().toUtf8().constData());
758                         droppedFiles << file.canonicalFilePath();
759                         continue;
760                 }
761                 if(file.isDir())
762                 {
763                         qDebug("Dropped Folder: %s", file.canonicalFilePath().toUtf8().constData());
764                         QList<QFileInfo> list = QDir(file.canonicalFilePath()).entryInfoList(QDir::Files | QDir::NoSymLinks);
765                         if(list.count() > 0)
766                         {
767                                 for(int j = 0; j < list.count(); j++)
768                                 {
769                                         droppedFiles << list.at(j).canonicalFilePath();
770                                 }
771                         }
772                         else
773                         {
774                                 list = QDir(file.canonicalFilePath()).entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
775                                 for(int j = 0; j < list.count(); j++)
776                                 {
777                                         qDebug("Descending to Folder: %s", list.at(j).canonicalFilePath().toUtf8().constData());
778                                         urls.prepend(QUrl::fromLocalFile(list.at(j).canonicalFilePath()));
779                                 }
780                         }
781                 }
782         }
783         
784         if(!droppedFiles.isEmpty())
785         {
786                 addFilesDelayed(droppedFiles, true);
787         }
788 }
789
790 /*
791  * Window tries to close
792  */
793 void MainWindow::closeEvent(QCloseEvent *event)
794 {
795         if(m_banner->isVisible() || m_delayedFileTimer->isActive())
796         {
797                 MessageBeep(MB_ICONEXCLAMATION);
798                 event->ignore();
799         }
800         
801         if(m_dropBox)
802         {
803                 m_dropBox->hide();
804         }
805 }
806
807 /*
808  * Window was resized
809  */
810 void MainWindow::resizeEvent(QResizeEvent *event)
811 {
812         QMainWindow::resizeEvent(event);
813         m_dropNoteLabel->setGeometry(0, 0, sourceFileView->width(), sourceFileView->height());
814 }
815
816 /*
817  * Event filter
818  */
819 bool MainWindow::eventFilter(QObject *obj, QEvent *event)
820 {
821         if(obj == m_fileSystemModel)
822         {
823                 if(QApplication::overrideCursor() == NULL)
824                 {
825                         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
826                         QTimer::singleShot(250, this, SLOT(restoreCursor()));
827                 }
828         }
829         else if(obj == outputFolderView)
830         {
831                 switch(event->type())
832                 {
833                 case QEvent::Enter:
834                 case QEvent::Leave:
835                 case QEvent::KeyPress:
836                 case QEvent::KeyRelease:
837                 case QEvent::FocusIn:
838                 case QEvent::FocusOut:
839                 case QEvent::TouchEnd:
840                         outputFolderViewClicked(outputFolderView->currentIndex());
841                         break;
842                 }
843         }
844         else if(obj == outputFolderLabel)
845         {
846                 switch(event->type())
847                 {
848                 case QEvent::MouseButtonPress:
849                         if(dynamic_cast<QMouseEvent*>(event)->button() == Qt::LeftButton)
850                         {
851                                 QDesktopServices::openUrl(QString("file:///%1").arg(outputFolderLabel->text()));
852                         }
853                         break;
854                 case QEvent::Enter:
855                         outputFolderLabel->setForegroundRole(QPalette::Link);
856                         break;
857                 case QEvent::Leave:
858                         outputFolderLabel->setForegroundRole(QPalette::WindowText);
859                         break;
860                 }
861         }
862         else if(obj == outputFoldersFovoritesLabel)
863         {
864                 QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
865                 QPoint pos = (mouseEvent != NULL) ? mouseEvent->pos() : QPoint();
866                 QWidget *sender = dynamic_cast<QLabel*>(obj);
867
868                 switch(event->type())
869                 {
870                 case QEvent::Enter:
871                         outputFoldersFovoritesLabel->setFrameShadow(QFrame::Raised);
872                         break;
873                 case QEvent::MouseButtonPress:
874                         outputFoldersFovoritesLabel->setFrameShadow(QFrame::Sunken);
875                         break;
876                 case QEvent::MouseButtonRelease:
877                         outputFoldersFovoritesLabel->setFrameShadow(QFrame::Raised);
878                         if(sender && mouseEvent)
879                         {
880                                 if(pos.x() <= sender->width() && pos.y() <= sender->height() && pos.x() >= 0 && pos.y() >= 0 && mouseEvent->button() != Qt::MidButton)
881                                 {
882                                         m_outputFolderFavoritesMenu->popup(sender->mapToGlobal(pos));
883                                 }
884                         }
885                         break;
886                 case QEvent::Leave:
887                         outputFoldersFovoritesLabel->setFrameShadow(QFrame::Plain);
888                         break;
889                 }
890         }
891
892         return false;
893 }
894
895 bool MainWindow::winEvent(MSG *message, long *result)
896 {
897         return WinSevenTaskbar::handleWinEvent(message, result);
898 }
899
900 ////////////////////////////////////////////////////////////
901 // Slots
902 ////////////////////////////////////////////////////////////
903
904 // =========================================================
905 // Show window slots
906 // =========================================================
907
908 /*
909  * Window shown
910  */
911 void MainWindow::windowShown(void)
912 {
913         QStringList arguments = QApplication::arguments();
914
915         //First run?
916         bool firstRun = false;
917         for(int i = 0; i < arguments.count(); i++)
918         {
919                 if(!arguments[i].compare("--first-run", Qt::CaseInsensitive)) firstRun = true;
920         }
921
922         //Check license
923         if((m_settings->licenseAccepted() <= 0) || firstRun)
924         {
925                 int iAccepted = -1;
926
927                 if((m_settings->licenseAccepted() == 0) || firstRun)
928                 {
929                         AboutDialog *about = new AboutDialog(m_settings, this, true);
930                         iAccepted = about->exec();
931                         LAMEXP_DELETE(about);
932                 }
933
934                 if(iAccepted <= 0)
935                 {
936                         m_settings->licenseAccepted(-1);
937                         QApplication::processEvents();
938                         PlaySound(MAKEINTRESOURCE(IDR_WAVE_WHAMMY), GetModuleHandle(NULL), SND_RESOURCE | SND_SYNC);
939                         QMessageBox::critical(this, tr("License Declined"), tr("You have declined the license. Consequently the application will exit now!"), tr("Goodbye!"));
940                         QFileInfo uninstallerInfo = QFileInfo(QString("%1/Uninstall.exe").arg(QApplication::applicationDirPath()));
941                         if(uninstallerInfo.exists())
942                         {
943                                 QString uninstallerDir = uninstallerInfo.canonicalPath();
944                                 QString uninstallerPath = uninstallerInfo.canonicalFilePath();
945                                 for(int i = 0; i < 3; i++)
946                                 {
947                                         HINSTANCE res = ShellExecuteW(this->winId(), L"open", QWCHAR(QDir::toNativeSeparators(uninstallerPath)), L"/Force", QWCHAR(QDir::toNativeSeparators(uninstallerDir)), SW_SHOWNORMAL);
948                                         if(reinterpret_cast<int>(res) > 32) break;
949                                 }
950                         }
951                         else
952                         {
953                                 MoveFileEx(QWCHAR(QDir::toNativeSeparators(QFileInfo(QApplication::applicationFilePath()).canonicalFilePath())), NULL, MOVEFILE_DELAY_UNTIL_REBOOT | MOVEFILE_REPLACE_EXISTING);
954                         }
955                         QApplication::quit();
956                         return;
957                 }
958                 
959                 PlaySound(MAKEINTRESOURCE(IDR_WAVE_WOOHOO), GetModuleHandle(NULL), SND_RESOURCE | SND_SYNC);
960                 m_settings->licenseAccepted(1);
961                 if(lamexp_version_demo()) showAnnounceBox();
962         }
963         
964         //Check for expiration
965         if(lamexp_version_demo())
966         {
967                 if(QDate::currentDate() >= lamexp_version_expires())
968                 {
969                         qWarning("Binary has expired !!!");
970                         PlaySound(MAKEINTRESOURCE(IDR_WAVE_WHAMMY), GetModuleHandle(NULL), SND_RESOURCE | SND_SYNC);
971                         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)
972                         {
973                                 checkForUpdates();
974                         }
975                         QApplication::quit();
976                         return;
977                 }
978         }
979
980         //Slow startup indicator
981         if(m_settings->slowStartup() && m_settings->antivirNotificationsEnabled())
982         {
983                 QString message;
984                 message += NOBR(tr("It seems that a bogus anti-virus software is slowing down the startup of LameXP.")).append("<br>");
985                 message += NOBR(tr("Please refer to the %1 document for details and solutions!")).arg("<a href=\"http://lamexp.git.sourceforge.net/git/gitweb.cgi?p=lamexp/lamexp;a=blob_plain;f=doc/FAQ.html;hb=HEAD#df406578\">F.A.Q.</a>").append("<br>");
986                 if(QMessageBox::warning(this, tr("Slow Startup"), message, tr("Discard"), tr("Don't Show Again")) == 1)
987                 {
988                         m_settings->antivirNotificationsEnabled(false);
989                         actionDisableSlowStartupNotifications->setChecked(!m_settings->antivirNotificationsEnabled());
990                 }
991         }
992
993         //Update reminder
994         if(QDate::currentDate() >= lamexp_version_date().addYears(1))
995         {
996                 qWarning("Binary is more than a year old, time to update!");
997                 if(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")) == 0)
998                 {
999                         if(checkForUpdates())
1000                         {
1001                                 QApplication::quit();
1002                                 return;
1003                         }
1004                 }
1005                 else
1006                 {
1007                         QApplication::quit();
1008                         return;
1009                 }
1010         }
1011         else if(m_settings->autoUpdateEnabled())
1012         {
1013                 QDate lastUpdateCheck = QDate::fromString(m_settings->autoUpdateLastCheck(), Qt::ISODate);
1014                 if(!firstRun && (!lastUpdateCheck.isValid() || QDate::currentDate() >= lastUpdateCheck.addDays(14)))
1015                 {
1016                         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)
1017                         {
1018                                 if(checkForUpdates())
1019                                 {
1020                                         QApplication::quit();
1021                                         return;
1022                                 }
1023                         }
1024                 }
1025         }
1026
1027         //Check for AAC support
1028         if(m_neroEncoderAvailable)
1029         {
1030                 if(m_settings->neroAacNotificationsEnabled())
1031                 {
1032                         if(lamexp_tool_version("neroAacEnc.exe") < lamexp_toolver_neroaac())
1033                         {
1034                                 QString messageText;
1035                                 messageText += NOBR(tr("LameXP detected that your version of the Nero AAC encoder is outdated!")).append("<br>");
1036                                 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>");
1037                                 messageText += NOBR(tr("You can download the latest version of the Nero AAC encoder from the Nero website at:")).append("<br>");
1038                                 messageText += "<nobr><tt>" + LINK(AboutDialog::neroAacUrl) + "</tt></nobr><br><br>";
1039                                 messageText += NOBR(tr("(Hint: Please ignore the name of the downloaded ZIP file and check the included 'changelog.txt' instead!)")).append("<br>");
1040                                 QMessageBox::information(this, tr("AAC Encoder Outdated"), messageText);
1041                         }
1042                 }
1043         }
1044         else
1045         {
1046                 if(m_settings->neroAacNotificationsEnabled() && (!(m_fhgEncoderAvailable || m_qaacEncoderAvailable)))
1047                 {
1048                         QString appPath = QDir(QCoreApplication::applicationDirPath()).canonicalPath();
1049                         if(appPath.isEmpty()) appPath = QCoreApplication::applicationDirPath();
1050                         QString messageText;
1051                         messageText += NOBR(tr("The Nero AAC encoder could not be found. AAC encoding support will be disabled.")).append("<br>");
1052                         messageText += NOBR(tr("Please put 'neroAacEnc.exe', 'neroAacDec.exe' and 'neroAacTag.exe' into the LameXP directory!")).append("<br><br>");
1053                         messageText += NOBR(tr("Your LameXP directory is located here:")).append("<br>");
1054                         messageText += QString("<nobr><tt>%1</tt></nobr><br><br>").arg(FSLINK(QDir::toNativeSeparators(appPath)));
1055                         messageText += NOBR(tr("You can download the Nero AAC encoder for free from the official Nero website at:")).append("<br>");
1056                         messageText += "<nobr><tt>" + LINK(AboutDialog::neroAacUrl) + "</tt></nobr><br>";
1057                         if(QMessageBox::information(this, tr("AAC Support Disabled"), messageText, tr("Discard"), tr("Don't Show Again")) == 1)
1058                         {
1059                                 m_settings->neroAacNotificationsEnabled(false);
1060                                 actionDisableNeroAacNotifications->setChecked(!m_settings->neroAacNotificationsEnabled());
1061                         }
1062                 }
1063         }
1064
1065         //Add files from the command-line
1066         for(int i = 0; i < arguments.count() - 1; i++)
1067         {
1068                 QStringList addedFiles;
1069                 if(!arguments[i].compare("--add", Qt::CaseInsensitive))
1070                 {
1071                         QFileInfo currentFile(arguments[++i].trimmed());
1072                         qDebug("Adding file from CLI: %s", currentFile.absoluteFilePath().toUtf8().constData());
1073                         addedFiles.append(currentFile.absoluteFilePath());
1074                 }
1075                 if(!addedFiles.isEmpty())
1076                 {
1077                         addFilesDelayed(addedFiles);
1078                 }
1079         }
1080
1081         //Add folders from the command-line
1082         for(int i = 0; i < arguments.count() - 1; i++)
1083         {
1084                 if(!arguments[i].compare("--add-folder", Qt::CaseInsensitive))
1085                 {
1086                         QFileInfo currentFile(arguments[++i].trimmed());
1087                         qDebug("Adding folder from CLI: %s", currentFile.absoluteFilePath().toUtf8().constData());
1088                         addFolder(currentFile.absoluteFilePath(), false, true);
1089                 }
1090                 if(!arguments[i].compare("--add-recursive", Qt::CaseInsensitive))
1091                 {
1092                         QFileInfo currentFile(arguments[++i].trimmed());
1093                         qDebug("Adding folder recursively from CLI: %s", currentFile.absoluteFilePath().toUtf8().constData());
1094                         addFolder(currentFile.absoluteFilePath(), true, true);
1095                 }
1096         }
1097
1098         //Enable shell integration
1099         if(m_settings->shellIntegrationEnabled())
1100         {
1101                 ShellIntegration::install();
1102         }
1103
1104         //Make DropBox visible
1105         if(m_settings->dropBoxWidgetEnabled())
1106         {
1107                 m_dropBox->setVisible(true);
1108         }
1109 }
1110
1111 /*
1112  * Show announce box
1113  */
1114 void MainWindow::showAnnounceBox(void)
1115 {
1116         const QString announceText = QString("%1<br><br>%2<br><nobr><tt>%3</tt></nobr><br>").arg
1117         (
1118                 NOBR("We are still looking for LameXP translators!"),
1119                 NOBR("If you are willing to translate LameXP to your language or to complete an existing translation, please refer to:"),
1120                 LINK("http://mulder.brhack.net/public/doc/lamexp_translate.html")
1121         );
1122         
1123         QMessageBox *announceBox = new QMessageBox(QMessageBox::Warning, "We want you!", announceText, QMessageBox::NoButton, this);
1124         announceBox->setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
1125         announceBox->setIconPixmap(QIcon(":/images/Announcement.png").pixmap(64,79));
1126         QPushButton *button1 = announceBox->addButton(tr("Discard"), QMessageBox::AcceptRole);
1127         QPushButton *button2 = announceBox->addButton(tr("Discard"), QMessageBox::NoRole);
1128         button1->setVisible(false);
1129         button2->setEnabled(false);
1130
1131         QTimer *announceTimer = new QTimer(this);
1132         announceTimer->setSingleShot(true);
1133         announceTimer->setInterval(8000);
1134         connect(announceTimer, SIGNAL(timeout()), button1, SLOT(show()));
1135         connect(announceTimer, SIGNAL(timeout()), button2, SLOT(hide()));
1136         
1137         announceTimer->start();
1138         while(announceTimer->isActive()) announceBox->exec();
1139         announceTimer->stop();
1140
1141         LAMEXP_DELETE(announceTimer);
1142         LAMEXP_DELETE(announceBox);
1143 }
1144
1145 // =========================================================
1146 // Main button solots
1147 // =========================================================
1148
1149 /*
1150  * Encode button
1151  */
1152 void MainWindow::encodeButtonClicked(void)
1153 {
1154         static const unsigned __int64 oneGigabyte = 1073741824ui64; 
1155         static const unsigned __int64 minimumFreeDiskspaceMultiplier = 2ui64;
1156         static const char *writeTestBuffer = "LAMEXP_WRITE_TEST";
1157         
1158         ABORT_IF_BUSY;
1159
1160         if(m_fileListModel->rowCount() < 1)
1161         {
1162                 QMessageBox::warning(this, tr("LameXP"), NOBR(tr("You must add at least one file to the list before proceeding!")));
1163                 tabWidget->setCurrentIndex(0);
1164                 return;
1165         }
1166         
1167         QString tempFolder = m_settings->customTempPathEnabled() ? m_settings->customTempPath() : lamexp_temp_folder2();
1168         if(!QFileInfo(tempFolder).exists() || !QFileInfo(tempFolder).isDir())
1169         {
1170                 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)
1171                 {
1172                         while(checkBoxUseSystemTempFolder->isChecked() == m_settings->customTempPathEnabledDefault()) checkBoxUseSystemTempFolder->click();
1173                 }
1174                 return;
1175         }
1176
1177         bool ok = false;
1178         unsigned __int64 currentFreeDiskspace = lamexp_free_diskspace(tempFolder, &ok);
1179
1180         if(ok && (currentFreeDiskspace < (oneGigabyte * minimumFreeDiskspaceMultiplier)))
1181         {
1182                 QStringList tempFolderParts = tempFolder.split("/", QString::SkipEmptyParts, Qt::CaseInsensitive);
1183                 tempFolderParts.takeLast();
1184                 if(m_settings->soundsEnabled()) PlaySound(MAKEINTRESOURCE(IDR_WAVE_WHAMMY), GetModuleHandle(NULL), SND_RESOURCE | SND_SYNC);
1185                 QString lowDiskspaceMsg = QString("%1<br>%2<br><br>%3<br>%4<br>").arg
1186                 (
1187                         NOBR(tr("There are less than %1 GB of free diskspace available on your system's TEMP folder.").arg(QString::number(minimumFreeDiskspaceMultiplier))),
1188                         NOBR(tr("It is highly recommend to free up more diskspace before proceeding with the encode!")),
1189                         NOBR(tr("Your TEMP folder is located at:")),
1190                         QString("<nobr><tt>%1</tt></nobr>").arg(FSLINK(tempFolderParts.join("\\")))
1191                 );
1192                 switch(QMessageBox::warning(this, tr("Low Diskspace Warning"), lowDiskspaceMsg, tr("Abort Encoding Process"), tr("Clean Disk Now"), tr("Ignore")))
1193                 {
1194                 case 1:
1195                         QProcess::startDetached(QString("%1/cleanmgr.exe").arg(lamexp_known_folder(lamexp_folder_systemfolder)), QStringList() << "/D" << tempFolderParts.first());
1196                 case 0:
1197                         return;
1198                         break;
1199                 default:
1200                         QMessageBox::warning(this, tr("Low Diskspace"), tr("You are proceeding with low diskspace. Problems might occur!"));
1201                         break;
1202                 }
1203         }
1204
1205         switch(m_settings->compressionEncoder())
1206         {
1207         case SettingsModel::MP3Encoder:
1208         case SettingsModel::VorbisEncoder:
1209         case SettingsModel::AACEncoder:
1210         case SettingsModel::AC3Encoder:
1211         case SettingsModel::FLACEncoder:
1212         case SettingsModel::PCMEncoder:
1213                 break;
1214         default:
1215                 QMessageBox::warning(this, tr("LameXP"), tr("Sorry, an unsupported encoder has been chosen!"));
1216                 tabWidget->setCurrentIndex(3);
1217                 return;
1218         }
1219
1220         if(!m_settings->outputToSourceDir())
1221         {
1222                 QFile writeTest(QString("%1/~%2.txt").arg(m_settings->outputDir(), lamexp_rand_str()));
1223                 if(!(writeTest.open(QIODevice::ReadWrite) && (writeTest.write(writeTestBuffer) == strlen(writeTestBuffer))))
1224                 {
1225                         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!")));
1226                         tabWidget->setCurrentIndex(1);
1227                         return;
1228                 }
1229                 else
1230                 {
1231                         writeTest.close();
1232                         writeTest.remove();
1233                 }
1234         }
1235                 
1236         m_accepted = true;
1237         close();
1238 }
1239
1240 /*
1241  * About button
1242  */
1243 void MainWindow::aboutButtonClicked(void)
1244 {
1245         ABORT_IF_BUSY;
1246
1247         TEMP_HIDE_DROPBOX
1248         (
1249                 AboutDialog *aboutBox = new AboutDialog(m_settings, this);
1250                 aboutBox->exec();
1251                 LAMEXP_DELETE(aboutBox);
1252         )
1253 }
1254
1255 /*
1256  * Close button
1257  */
1258 void MainWindow::closeButtonClicked(void)
1259 {
1260         ABORT_IF_BUSY;
1261         close();
1262 }
1263
1264 // =========================================================
1265 // Tab widget slots
1266 // =========================================================
1267
1268 /*
1269  * Tab page changed
1270  */
1271 void MainWindow::tabPageChanged(int idx)
1272 {
1273         QList<QAction*> actions = m_tabActionGroup->actions();
1274         for(int i = 0; i < actions.count(); i++)
1275         {
1276                 bool ok = false;
1277                 int actionIndex = actions.at(i)->data().toInt(&ok);
1278                 if(ok && actionIndex == idx)
1279                 {
1280                         actions.at(i)->setChecked(true);
1281                 }
1282         }
1283
1284         int initialWidth = this->width();
1285         int maximumWidth = QApplication::desktop()->width();
1286
1287         if(this->isVisible())
1288         {
1289                 while(tabWidget->width() < tabWidget->sizeHint().width())
1290                 {
1291                         int previousWidth = this->width();
1292                         this->resize(this->width() + 1, this->height());
1293                         if(this->frameGeometry().width() >= maximumWidth) break;
1294                         if(this->width() <= previousWidth) break;
1295                 }
1296         }
1297
1298         if(idx == tabWidget->indexOf(tabOptions) && scrollArea->widget() && this->isVisible())
1299         {
1300                 for(int i = 0; i < 2; i++)
1301                 {
1302                         QApplication::processEvents();
1303                         while(scrollArea->viewport()->width() < scrollArea->widget()->width())
1304                         {
1305                                 int previousWidth = this->width();
1306                                 this->resize(this->width() + 1, this->height());
1307                                 if(this->frameGeometry().width() >= maximumWidth) break;
1308                                 if(this->width() <= previousWidth) break;
1309                         }
1310                 }
1311         }
1312         else if(idx == tabWidget->indexOf(tabSourceFiles))
1313         {
1314                 m_dropNoteLabel->setGeometry(0, 0, sourceFileView->width(), sourceFileView->height());
1315         }
1316         else if(idx == tabWidget->indexOf(tabOutputDir))
1317         {
1318                 if(!m_OutputFolderViewInitialized)
1319                 {
1320                         QTimer::singleShot(0, this, SLOT(initOutputFolderModel()));
1321                 }
1322         }
1323
1324         if(initialWidth < this->width())
1325         {
1326                 QPoint prevPos = this->pos();
1327                 int delta = (this->width() - initialWidth) >> 2;
1328                 move(prevPos.x() - delta, prevPos.y());
1329         }
1330 }
1331
1332 /*
1333  * Tab action triggered
1334  */
1335 void MainWindow::tabActionActivated(QAction *action)
1336 {
1337         if(action && action->data().isValid())
1338         {
1339                 bool ok = false;
1340                 int index = action->data().toInt(&ok);
1341                 if(ok)
1342                 {
1343                         tabWidget->setCurrentIndex(index);
1344                 }
1345         }
1346 }
1347
1348 // =========================================================
1349 // View menu slots
1350 // =========================================================
1351
1352 /*
1353  * Style action triggered
1354  */
1355 void MainWindow::styleActionActivated(QAction *action)
1356 {
1357         //Change style setting
1358         if(action && action->data().isValid())
1359         {
1360                 bool ok = false;
1361                 int actionIndex = action->data().toInt(&ok);
1362                 if(ok)
1363                 {
1364                         m_settings->interfaceStyle(actionIndex);
1365                 }
1366         }
1367
1368         //Set up the new style
1369         switch(m_settings->interfaceStyle())
1370         {
1371         case 1:
1372                 if(actionStyleCleanlooks->isEnabled())
1373                 {
1374                         actionStyleCleanlooks->setChecked(true);
1375                         QApplication::setStyle(new QCleanlooksStyle());
1376                         break;
1377                 }
1378         case 2:
1379                 if(actionStyleWindowsVista->isEnabled())
1380                 {
1381                         actionStyleWindowsVista->setChecked(true);
1382                         QApplication::setStyle(new QWindowsVistaStyle());
1383                         break;
1384                 }
1385         case 3:
1386                 if(actionStyleWindowsXP->isEnabled())
1387                 {
1388                         actionStyleWindowsXP->setChecked(true);
1389                         QApplication::setStyle(new QWindowsXPStyle());
1390                         break;
1391                 }
1392         case 4:
1393                 if(actionStyleWindowsClassic->isEnabled())
1394                 {
1395                         actionStyleWindowsClassic->setChecked(true);
1396                         QApplication::setStyle(new QWindowsStyle());
1397                         break;
1398                 }
1399         default:
1400                 actionStylePlastique->setChecked(true);
1401                 QApplication::setStyle(new QPlastiqueStyle());
1402                 break;
1403         }
1404
1405         //Force re-translate after style change
1406         changeEvent(new QEvent(QEvent::LanguageChange));
1407 }
1408
1409 /*
1410  * Language action triggered
1411  */
1412 void MainWindow::languageActionActivated(QAction *action)
1413 {
1414         if(action->data().type() == QVariant::String)
1415         {
1416                 QString langId = action->data().toString();
1417
1418                 if(lamexp_install_translator(langId))
1419                 {
1420                         action->setChecked(true);
1421                         m_settings->currentLanguage(langId);
1422                 }
1423         }
1424 }
1425
1426 /*
1427  * Load language from file action triggered
1428  */
1429 void MainWindow::languageFromFileActionActivated(bool checked)
1430 {
1431         QFileDialog dialog(this, tr("Load Translation"));
1432         dialog.setFileMode(QFileDialog::ExistingFile);
1433         dialog.setNameFilter(QString("%1 (*.qm)").arg(tr("Translation Files")));
1434
1435         if(dialog.exec())
1436         {
1437                 QStringList selectedFiles = dialog.selectedFiles();
1438                 if(lamexp_install_translator_from_file(selectedFiles.first()))
1439                 {
1440                         QList<QAction*> actions = m_languageActionGroup->actions();
1441                         while(!actions.isEmpty())
1442                         {
1443                                 actions.takeFirst()->setChecked(false);
1444                         }
1445                 }
1446                 else
1447                 {
1448                         languageActionActivated(m_languageActionGroup->actions().first());
1449                 }
1450         }
1451 }
1452
1453 // =========================================================
1454 // Tools menu slots
1455 // =========================================================
1456
1457 /*
1458  * Disable update reminder action
1459  */
1460 void MainWindow::disableUpdateReminderActionTriggered(bool checked)
1461 {
1462         if(checked)
1463         {
1464                 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))
1465                 {
1466                         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!"))));
1467                         m_settings->autoUpdateEnabled(false);
1468                 }
1469                 else
1470                 {
1471                         m_settings->autoUpdateEnabled(true);
1472                 }
1473         }
1474         else
1475         {
1476                         QMessageBox::information(this, tr("Update Reminder"), NOBR(tr("The update reminder has been re-enabled.")));
1477                         m_settings->autoUpdateEnabled(true);
1478         }
1479
1480         actionDisableUpdateReminder->setChecked(!m_settings->autoUpdateEnabled());
1481 }
1482
1483 /*
1484  * Disable sound effects action
1485  */
1486 void MainWindow::disableSoundsActionTriggered(bool checked)
1487 {
1488         if(checked)
1489         {
1490                 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))
1491                 {
1492                         QMessageBox::information(this, tr("Sound Effects"), NOBR(tr("All sound effects have been disabled.")));
1493                         m_settings->soundsEnabled(false);
1494                 }
1495                 else
1496                 {
1497                         m_settings->soundsEnabled(true);
1498                 }
1499         }
1500         else
1501         {
1502                         QMessageBox::information(this, tr("Sound Effects"), NOBR(tr("The sound effects have been re-enabled.")));
1503                         m_settings->soundsEnabled(true);
1504         }
1505
1506         actionDisableSounds->setChecked(!m_settings->soundsEnabled());
1507 }
1508
1509 /*
1510  * Disable Nero AAC encoder action
1511  */
1512 void MainWindow::disableNeroAacNotificationsActionTriggered(bool checked)
1513 {
1514         if(checked)
1515         {
1516                 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))
1517                 {
1518                         QMessageBox::information(this, tr("Nero AAC Notifications"), NOBR(tr("All Nero AAC Encoder notifications have been disabled.")));
1519                         m_settings->neroAacNotificationsEnabled(false);
1520                 }
1521                 else
1522                 {
1523                         m_settings->neroAacNotificationsEnabled(true);
1524                 }
1525         }
1526         else
1527         {
1528                         QMessageBox::information(this, tr("Nero AAC Notifications"), NOBR(tr("The Nero AAC Encoder notifications have been re-enabled.")));
1529                         m_settings->neroAacNotificationsEnabled(true);
1530         }
1531
1532         actionDisableNeroAacNotifications->setChecked(!m_settings->neroAacNotificationsEnabled());
1533 }
1534
1535 /*
1536  * Disable slow startup action
1537  */
1538 void MainWindow::disableSlowStartupNotificationsActionTriggered(bool checked)
1539 {
1540         if(checked)
1541         {
1542                 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))
1543                 {
1544                         QMessageBox::information(this, tr("Slow Startup Notifications"), NOBR(tr("The slow startup notifications have been disabled.")));
1545                         m_settings->antivirNotificationsEnabled(false);
1546                 }
1547                 else
1548                 {
1549                         m_settings->antivirNotificationsEnabled(true);
1550                 }
1551         }
1552         else
1553         {
1554                         QMessageBox::information(this, tr("Slow Startup Notifications"), NOBR(tr("The slow startup notifications have been re-enabled.")));
1555                         m_settings->antivirNotificationsEnabled(true);
1556         }
1557
1558         actionDisableSlowStartupNotifications->setChecked(!m_settings->antivirNotificationsEnabled());
1559 }
1560
1561 /*
1562  * Import a Cue Sheet file
1563  */
1564 void MainWindow::importCueSheetActionTriggered(bool checked)
1565 {
1566         ABORT_IF_BUSY;
1567         
1568         TEMP_HIDE_DROPBOX
1569         (
1570                 while(true)
1571                 {
1572                         int result = 0;
1573                         QString selectedCueFile;
1574
1575                         if(USE_NATIVE_FILE_DIALOG)
1576                         {
1577                                 selectedCueFile = QFileDialog::getOpenFileName(this, tr("Open Cue Sheet"), m_settings->mostRecentInputPath(), QString("%1 (*.cue)").arg(tr("Cue Sheet File")));
1578                         }
1579                         else
1580                         {
1581                                 QFileDialog dialog(this, tr("Open Cue Sheet"));
1582                                 dialog.setFileMode(QFileDialog::ExistingFile);
1583                                 dialog.setNameFilter(QString("%1 (*.cue)").arg(tr("Cue Sheet File")));
1584                                 dialog.setDirectory(m_settings->mostRecentInputPath());
1585                                 if(dialog.exec())
1586                                 {
1587                                         selectedCueFile = dialog.selectedFiles().first();
1588                                 }
1589                         }
1590
1591                         if(!selectedCueFile.isEmpty())
1592                         {
1593                                 m_settings->mostRecentInputPath(QFileInfo(selectedCueFile).canonicalPath());
1594                                 CueImportDialog *cueImporter  = new CueImportDialog(this, m_fileListModel, selectedCueFile);
1595                                 result = cueImporter->exec();
1596                                 LAMEXP_DELETE(cueImporter);
1597                         }
1598
1599                         if(result != (-1)) break;
1600                 }
1601         )
1602 }
1603
1604 /*
1605  * Show the "drop box" widget
1606  */
1607 void MainWindow::showDropBoxWidgetActionTriggered(bool checked)
1608 {
1609         m_settings->dropBoxWidgetEnabled(true);
1610         
1611         if(!m_dropBox->isVisible())
1612         {
1613                 m_dropBox->show();
1614         }
1615         
1616         lamexp_blink_window(m_dropBox);
1617 }
1618
1619 /*
1620  * Check for beta (pre-release) updates
1621  */
1622 void MainWindow::checkForBetaUpdatesActionTriggered(bool checked)
1623 {       
1624         bool checkUpdatesNow = false;
1625         
1626         if(checked)
1627         {
1628                 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))
1629                 {
1630                         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")))
1631                         {
1632                                 checkUpdatesNow = true;
1633                         }
1634                         m_settings->autoUpdateCheckBeta(true);
1635                 }
1636                 else
1637                 {
1638                         m_settings->autoUpdateCheckBeta(false);
1639                 }
1640         }
1641         else
1642         {
1643                         QMessageBox::information(this, tr("Beta Updates"), NOBR(tr("LameXP will <i>not</i> check for Beta (pre-release) updates from now on.")));
1644                         m_settings->autoUpdateCheckBeta(false);
1645         }
1646
1647         actionCheckForBetaUpdates->setChecked(m_settings->autoUpdateCheckBeta());
1648
1649         if(checkUpdatesNow)
1650         {
1651                 if(checkForUpdates())
1652                 {
1653                         QApplication::quit();
1654                 }
1655         }
1656 }
1657
1658 /*
1659  * Hibernate computer action
1660  */
1661 void MainWindow::hibernateComputerActionTriggered(bool checked)
1662 {
1663         if(checked)
1664         {
1665                 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))
1666                 {
1667                         QMessageBox::information(this, tr("Hibernate Computer"), NOBR(tr("LameXP will hibernate the computer on shutdown from now on.")));
1668                         m_settings->hibernateComputer(true);
1669                 }
1670                 else
1671                 {
1672                         m_settings->hibernateComputer(false);
1673                 }
1674         }
1675         else
1676         {
1677                         QMessageBox::information(this, tr("Hibernate Computer"), NOBR(tr("LameXP will <i>not</i> hibernate the computer on shutdown from now on.")));
1678                         m_settings->hibernateComputer(false);
1679         }
1680
1681         actionHibernateComputer->setChecked(m_settings->hibernateComputer());
1682 }
1683
1684 /*
1685  * Disable shell integration action
1686  */
1687 void MainWindow::disableShellIntegrationActionTriggered(bool checked)
1688 {
1689         if(checked)
1690         {
1691                 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))
1692                 {
1693                         ShellIntegration::remove();
1694                         QMessageBox::information(this, tr("Shell Integration"), NOBR(tr("The LameXP shell integration has been disabled.")));
1695                         m_settings->shellIntegrationEnabled(false);
1696                 }
1697                 else
1698                 {
1699                         m_settings->shellIntegrationEnabled(true);
1700                 }
1701         }
1702         else
1703         {
1704                         ShellIntegration::install();
1705                         QMessageBox::information(this, tr("Shell Integration"), NOBR(tr("The LameXP shell integration has been re-enabled.")));
1706                         m_settings->shellIntegrationEnabled(true);
1707         }
1708
1709         actionDisableShellIntegration->setChecked(!m_settings->shellIntegrationEnabled());
1710         
1711         if(lamexp_portable_mode() && actionDisableShellIntegration->isChecked())
1712         {
1713                 actionDisableShellIntegration->setEnabled(false);
1714         }
1715 }
1716
1717 // =========================================================
1718 // Help menu slots
1719 // =========================================================
1720
1721 /*
1722  * Visit homepage action
1723  */
1724 void MainWindow::visitHomepageActionActivated(void)
1725 {
1726         if(QAction *action = dynamic_cast<QAction*>(QObject::sender()))
1727         {
1728                 if(action->data().isValid() && (action->data().type() == QVariant::String))
1729                 {
1730                         QDesktopServices::openUrl(QUrl(action->data().toString()));
1731                 }
1732         }
1733 }
1734
1735 /*
1736  * Show document
1737  */
1738 void MainWindow::documentActionActivated(void)
1739 {
1740         if(QAction *action = dynamic_cast<QAction*>(QObject::sender()))
1741         {
1742                 if(action->data().isValid() && (action->data().type() == QVariant::String))
1743                 {
1744                         QFileInfo document(action->data().toString());
1745                         QFileInfo resource(QString(":/doc/%1.html").arg(document.baseName()));
1746                         if(document.exists() && document.isFile() && (document.size() == resource.size()))
1747                         {
1748                                 QDesktopServices::openUrl(QUrl::fromLocalFile(document.canonicalFilePath()));
1749                         }
1750                         else
1751                         {
1752                                 QFile source(resource.filePath());
1753                                 QFile output(QString("%1/%2.%3.html").arg(lamexp_temp_folder2(), document.baseName(), lamexp_rand_str().left(8)));
1754                                 if(source.open(QIODevice::ReadOnly) && output.open(QIODevice::ReadWrite))
1755                                 {
1756                                         output.write(source.readAll());
1757                                         action->setData(output.fileName());
1758                                         source.close();
1759                                         output.close();
1760                                         QDesktopServices::openUrl(QUrl::fromLocalFile(output.fileName()));
1761                                 }
1762                         }
1763                 }
1764         }
1765 }
1766
1767 /*
1768  * Check for updates action
1769  */
1770 void MainWindow::checkUpdatesActionActivated(void)
1771 {
1772         ABORT_IF_BUSY;
1773         bool bFlag = false;
1774
1775         TEMP_HIDE_DROPBOX
1776         (
1777                 bFlag = checkForUpdates();
1778         )
1779         
1780         if(bFlag)
1781         {
1782                 QApplication::quit();
1783         }
1784 }
1785
1786 // =========================================================
1787 // Source file slots
1788 // =========================================================
1789
1790 /*
1791  * Add file(s) button
1792  */
1793 void MainWindow::addFilesButtonClicked(void)
1794 {
1795         ABORT_IF_BUSY;
1796
1797         TEMP_HIDE_DROPBOX
1798         (
1799                 if(USE_NATIVE_FILE_DIALOG)
1800                 {
1801                         QStringList fileTypeFilters = DecoderRegistry::getSupportedTypes();
1802                         QStringList selectedFiles = QFileDialog::getOpenFileNames(this, tr("Add file(s)"), m_settings->mostRecentInputPath(), fileTypeFilters.join(";;"));
1803                         if(!selectedFiles.isEmpty())
1804                         {
1805                                 m_settings->mostRecentInputPath(QFileInfo(selectedFiles.first()).canonicalPath());
1806                                 addFiles(selectedFiles);
1807                         }
1808                 }
1809                 else
1810                 {
1811                         QFileDialog dialog(this, tr("Add file(s)"));
1812                         QStringList fileTypeFilters = DecoderRegistry::getSupportedTypes();
1813                         dialog.setFileMode(QFileDialog::ExistingFiles);
1814                         dialog.setNameFilter(fileTypeFilters.join(";;"));
1815                         dialog.setDirectory(m_settings->mostRecentInputPath());
1816                         if(dialog.exec())
1817                         {
1818                                 QStringList selectedFiles = dialog.selectedFiles();
1819                                 if(!selectedFiles.isEmpty())
1820                                 {
1821                                         m_settings->mostRecentInputPath(QFileInfo(selectedFiles.first()).canonicalPath());
1822                                         addFiles(selectedFiles);
1823                                 }
1824                         }
1825                 }
1826         )
1827 }
1828
1829 /*
1830  * Open folder action
1831  */
1832 void MainWindow::openFolderActionActivated(void)
1833 {
1834         ABORT_IF_BUSY;
1835         QString selectedFolder;
1836         
1837         if(QAction *action = dynamic_cast<QAction*>(QObject::sender()))
1838         {
1839                 TEMP_HIDE_DROPBOX
1840                 (
1841                         if(USE_NATIVE_FILE_DIALOG)
1842                         {
1843                                 selectedFolder = QFileDialog::getExistingDirectory(this, tr("Add Folder"), m_settings->mostRecentInputPath());
1844                         }
1845                         else
1846                         {
1847                                 QFileDialog dialog(this, tr("Add Folder"));
1848                                 dialog.setFileMode(QFileDialog::DirectoryOnly);
1849                                 dialog.setDirectory(m_settings->mostRecentInputPath());
1850                                 if(dialog.exec())
1851                                 {
1852                                         selectedFolder = dialog.selectedFiles().first();
1853                                 }
1854                         }
1855                         
1856                         if(!selectedFolder.isEmpty())
1857                         {
1858                                 m_settings->mostRecentInputPath(QDir(selectedFolder).canonicalPath());
1859                                 addFolder(selectedFolder, action->data().toBool());
1860                         }
1861                 )
1862         }
1863 }
1864
1865 /*
1866  * Remove file button
1867  */
1868 void MainWindow::removeFileButtonClicked(void)
1869 {
1870         if(sourceFileView->currentIndex().isValid())
1871         {
1872                 int iRow = sourceFileView->currentIndex().row();
1873                 m_fileListModel->removeFile(sourceFileView->currentIndex());
1874                 sourceFileView->selectRow(iRow < m_fileListModel->rowCount() ? iRow : m_fileListModel->rowCount()-1);
1875         }
1876 }
1877
1878 /*
1879  * Clear files button
1880  */
1881 void MainWindow::clearFilesButtonClicked(void)
1882 {
1883         m_fileListModel->clearFiles();
1884 }
1885
1886 /*
1887  * Move file up button
1888  */
1889 void MainWindow::fileUpButtonClicked(void)
1890 {
1891         if(sourceFileView->currentIndex().isValid())
1892         {
1893                 int iRow = sourceFileView->currentIndex().row() - 1;
1894                 m_fileListModel->moveFile(sourceFileView->currentIndex(), -1);
1895                 sourceFileView->selectRow(iRow >= 0 ? iRow : 0);
1896         }
1897 }
1898
1899 /*
1900  * Move file down button
1901  */
1902 void MainWindow::fileDownButtonClicked(void)
1903 {
1904         if(sourceFileView->currentIndex().isValid())
1905         {
1906                 int iRow = sourceFileView->currentIndex().row() + 1;
1907                 m_fileListModel->moveFile(sourceFileView->currentIndex(), 1);
1908                 sourceFileView->selectRow(iRow < m_fileListModel->rowCount() ? iRow : m_fileListModel->rowCount()-1);
1909         }
1910 }
1911
1912 /*
1913  * Show details button
1914  */
1915 void MainWindow::showDetailsButtonClicked(void)
1916 {
1917         ABORT_IF_BUSY;
1918
1919         int iResult = 0;
1920         MetaInfoDialog *metaInfoDialog = new MetaInfoDialog(this);
1921         QModelIndex index = sourceFileView->currentIndex();
1922
1923         while(index.isValid())
1924         {
1925                 if(iResult > 0)
1926                 {
1927                         index = m_fileListModel->index(index.row() + 1, index.column()); 
1928                         sourceFileView->selectRow(index.row());
1929                 }
1930                 if(iResult < 0)
1931                 {
1932                         index = m_fileListModel->index(index.row() - 1, index.column()); 
1933                         sourceFileView->selectRow(index.row());
1934                 }
1935
1936                 AudioFileModel &file = (*m_fileListModel)[index];
1937                 TEMP_HIDE_DROPBOX
1938                 (
1939                         iResult = metaInfoDialog->exec(file, index.row() > 0, index.row() < m_fileListModel->rowCount() - 1);
1940                 )
1941                 
1942                 if(iResult == INT_MAX)
1943                 {
1944                         m_metaInfoModel->assignInfoFrom(file);
1945                         tabWidget->setCurrentIndex(tabWidget->indexOf(tabMetaData));
1946                         break;
1947                 }
1948
1949                 if(!iResult) break;
1950         }
1951
1952         LAMEXP_DELETE(metaInfoDialog);
1953         QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1954         sourceFilesScrollbarMoved(0);
1955 }
1956
1957 /*
1958  * Show context menu for source files
1959  */
1960 void MainWindow::sourceFilesContextMenu(const QPoint &pos)
1961 {
1962         QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(QObject::sender());
1963         QWidget *sender = scrollArea ? scrollArea->viewport() : dynamic_cast<QWidget*>(QObject::sender());
1964
1965         if(sender)
1966         {
1967                 if(pos.x() <= sender->width() && pos.y() <= sender->height() && pos.x() >= 0 && pos.y() >= 0)
1968                 {
1969                         m_sourceFilesContextMenu->popup(sender->mapToGlobal(pos));
1970                 }
1971         }
1972 }
1973
1974 /*
1975  * Scrollbar of source files moved
1976  */
1977 void MainWindow::sourceFilesScrollbarMoved(int)
1978 {
1979         sourceFileView->resizeColumnToContents(0);
1980 }
1981
1982 /*
1983  * Open selected file in external player
1984  */
1985 void MainWindow::previewContextActionTriggered(void)
1986 {
1987         const static char *appNames[3] = {"smplayer_portable.exe", "smplayer.exe", "mplayer.exe"};
1988         const static wchar_t *registryKey = L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{DB9E4EAB-2717-499F-8D56-4CC8A644AB60}";
1989         
1990         QModelIndex index = sourceFileView->currentIndex();
1991         if(!index.isValid())
1992         {
1993                 return;
1994         }
1995
1996         QString mplayerPath;
1997         HKEY registryKeyHandle;
1998
1999         if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, registryKey, 0, KEY_READ, &registryKeyHandle) == ERROR_SUCCESS)
2000         {
2001                 wchar_t Buffer[4096];
2002                 DWORD BuffSize = sizeof(wchar_t*) * 4096;
2003                 if(RegQueryValueExW(registryKeyHandle, L"InstallLocation", 0, 0, reinterpret_cast<BYTE*>(Buffer), &BuffSize) == ERROR_SUCCESS)
2004                 {
2005                         mplayerPath = QString::fromUtf16(reinterpret_cast<const unsigned short*>(Buffer));
2006                 }
2007         }
2008
2009         if(!mplayerPath.isEmpty())
2010         {
2011                 QDir mplayerDir(mplayerPath);
2012                 if(mplayerDir.exists())
2013                 {
2014                         for(int i = 0; i < 3; i++)
2015                         {
2016                                 if(mplayerDir.exists(appNames[i]))
2017                                 {
2018                                         QProcess::startDetached(mplayerDir.absoluteFilePath(appNames[i]), QStringList() << QDir::toNativeSeparators(m_fileListModel->getFile(index).filePath()));
2019                                         return;
2020                                 }
2021                         }
2022                 }
2023         }
2024         
2025         QDesktopServices::openUrl(QString("file:///").append(m_fileListModel->getFile(index).filePath()));
2026 }
2027
2028 /*
2029  * Find selected file in explorer
2030  */
2031 void MainWindow::findFileContextActionTriggered(void)
2032 {
2033         QModelIndex index = sourceFileView->currentIndex();
2034         if(index.isValid())
2035         {
2036                 QString systemRootPath;
2037
2038                 QDir systemRoot(lamexp_known_folder(lamexp_folder_systemfolder));
2039                 if(systemRoot.exists() && systemRoot.cdUp())
2040                 {
2041                         systemRootPath = systemRoot.canonicalPath();
2042                 }
2043
2044                 if(!systemRootPath.isEmpty())
2045                 {
2046                         QFileInfo explorer(QString("%1/explorer.exe").arg(systemRootPath));
2047                         if(explorer.exists() && explorer.isFile())
2048                         {
2049                                 QProcess::execute(explorer.canonicalFilePath(), QStringList() << "/select," << QDir::toNativeSeparators(m_fileListModel->getFile(index).filePath()));
2050                                 return;
2051                         }
2052                 }
2053                 else
2054                 {
2055                         qWarning("SystemRoot directory could not be detected!");
2056                 }
2057         }
2058 }
2059
2060 /*
2061  * Add all pending files
2062  */
2063 void MainWindow::handleDelayedFiles(void)
2064 {
2065         m_delayedFileTimer->stop();
2066
2067         if(m_delayedFileList->isEmpty())
2068         {
2069                 return;
2070         }
2071
2072         if(m_banner->isVisible())
2073         {
2074                 m_delayedFileTimer->start(5000);
2075                 return;
2076         }
2077
2078         QStringList selectedFiles;
2079         tabWidget->setCurrentIndex(0);
2080
2081         while(!m_delayedFileList->isEmpty())
2082         {
2083                 QFileInfo currentFile = QFileInfo(m_delayedFileList->takeFirst());
2084                 if(!currentFile.exists() || !currentFile.isFile())
2085                 {
2086                         continue;
2087                 }
2088                 selectedFiles << currentFile.canonicalFilePath();
2089         }
2090         
2091         addFiles(selectedFiles);
2092 }
2093
2094 /*
2095  * Show or hide Drag'n'Drop notice after model reset
2096  */
2097 void MainWindow::sourceModelChanged(void)
2098 {
2099         m_dropNoteLabel->setVisible(m_fileListModel->rowCount() <= 0);
2100 }
2101
2102 // =========================================================
2103 // Output folder slots
2104 // =========================================================
2105
2106 /*
2107  * Output folder changed (mouse clicked)
2108  */
2109 void MainWindow::outputFolderViewClicked(const QModelIndex &index)
2110 {
2111         if(outputFolderView->currentIndex() != index)
2112         {
2113                 outputFolderView->setCurrentIndex(index);
2114         }
2115         QString selectedDir = m_fileSystemModel->filePath(index);
2116         if(selectedDir.length() < 3) selectedDir.append(QDir::separator());
2117         outputFolderLabel->setText(QDir::toNativeSeparators(selectedDir));
2118         m_settings->outputDir(selectedDir);
2119 }
2120
2121 /*
2122  * Output folder changed (mouse moved)
2123  */
2124 void MainWindow::outputFolderViewMoved(const QModelIndex &index)
2125 {
2126         if(QApplication::mouseButtons() & Qt::LeftButton)
2127         {
2128                 outputFolderViewClicked(index);
2129         }
2130 }
2131
2132 /*
2133  * Goto desktop button
2134  */
2135 void MainWindow::gotoDesktopButtonClicked(void)
2136 {
2137         QString desktopPath = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
2138         
2139         if(!desktopPath.isEmpty() && QDir(desktopPath).exists())
2140         {
2141                 outputFolderView->setCurrentIndex(m_fileSystemModel->index(desktopPath));
2142                 outputFolderViewClicked(outputFolderView->currentIndex());
2143                 outputFolderView->setFocus();
2144         }
2145         else
2146         {
2147                 buttonGotoDesktop->setEnabled(false);
2148         }
2149 }
2150
2151 /*
2152  * Goto home folder button
2153  */
2154 void MainWindow::gotoHomeFolderButtonClicked(void)
2155 {
2156         QString homePath = QDesktopServices::storageLocation(QDesktopServices::HomeLocation);
2157         
2158         if(!homePath.isEmpty() && QDir(homePath).exists())
2159         {
2160                 outputFolderView->setCurrentIndex(m_fileSystemModel->index(homePath));
2161                 outputFolderViewClicked(outputFolderView->currentIndex());
2162                 outputFolderView->setFocus();
2163         }
2164         else
2165         {
2166                 buttonGotoHome->setEnabled(false);
2167         }
2168 }
2169
2170 /*
2171  * Goto music folder button
2172  */
2173 void MainWindow::gotoMusicFolderButtonClicked(void)
2174 {
2175         QString musicPath = QDesktopServices::storageLocation(QDesktopServices::MusicLocation);
2176         
2177         if(!musicPath.isEmpty() && QDir(musicPath).exists())
2178         {
2179                 outputFolderView->setCurrentIndex(m_fileSystemModel->index(musicPath));
2180                 outputFolderViewClicked(outputFolderView->currentIndex());
2181                 outputFolderView->setFocus();
2182         }
2183         else
2184         {
2185                 buttonGotoMusic->setEnabled(false);
2186         }
2187 }
2188
2189 /*
2190  * Goto music favorite output folder
2191  */
2192 void MainWindow::gotoFavoriteFolder(void)
2193 {
2194         QAction *item = dynamic_cast<QAction*>(QObject::sender());
2195         
2196         if(item)
2197         {
2198                 QDir path(item->data().toString());
2199                 if(path.exists())
2200                 {
2201                         outputFolderView->setCurrentIndex(m_fileSystemModel->index(path.canonicalPath()));
2202                         outputFolderViewClicked(outputFolderView->currentIndex());
2203                         outputFolderView->setFocus();
2204                 }
2205                 else
2206                 {
2207                         MessageBeep(MB_ICONERROR);
2208                         m_outputFolderFavoritesMenu->removeAction(item);
2209                         item->deleteLater();
2210                 }
2211         }
2212 }
2213
2214 /*
2215  * Make folder button
2216  */
2217 void MainWindow::makeFolderButtonClicked(void)
2218 {
2219         ABORT_IF_BUSY;
2220
2221         QDir basePath(m_fileSystemModel->fileInfo(outputFolderView->currentIndex()).absoluteFilePath());
2222         QString suggestedName = tr("New Folder");
2223
2224         if(!m_metaData->fileArtist().isEmpty() && !m_metaData->fileAlbum().isEmpty())
2225         {
2226                 suggestedName = QString("%1 - %2").arg(m_metaData->fileArtist(), m_metaData->fileAlbum());
2227         }
2228         else if(!m_metaData->fileArtist().isEmpty())
2229         {
2230                 suggestedName = m_metaData->fileArtist();
2231         }
2232         else if(!m_metaData->fileAlbum().isEmpty())
2233         {
2234                 suggestedName = m_metaData->fileAlbum();
2235         }
2236         else
2237         {
2238                 for(int i = 0; i < m_fileListModel->rowCount(); i++)
2239                 {
2240                         AudioFileModel audioFile = m_fileListModel->getFile(m_fileListModel->index(i, 0));
2241                         if(!audioFile.fileAlbum().isEmpty() || !audioFile.fileArtist().isEmpty())
2242                         {
2243                                 if(!audioFile.fileArtist().isEmpty() && !audioFile.fileAlbum().isEmpty())
2244                                 {
2245                                         suggestedName = QString("%1 - %2").arg(audioFile.fileArtist(), audioFile.fileAlbum());
2246                                 }
2247                                 else if(!audioFile.fileArtist().isEmpty())
2248                                 {
2249                                         suggestedName = audioFile.fileArtist();
2250                                 }
2251                                 else if(!audioFile.fileAlbum().isEmpty())
2252                                 {
2253                                         suggestedName = audioFile.fileAlbum();
2254                                 }
2255                                 break;
2256                         }
2257                 }
2258         }
2259         
2260         suggestedName = lamexp_clean_filename(suggestedName);
2261
2262         while(true)
2263         {
2264                 bool bApplied = false;
2265                 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();
2266
2267                 if(bApplied)
2268                 {
2269                         folderName = lamexp_clean_filepath(folderName.simplified());
2270
2271                         if(folderName.isEmpty())
2272                         {
2273                                 MessageBeep(MB_ICONERROR);
2274                                 continue;
2275                         }
2276
2277                         int i = 1;
2278                         QString newFolder = folderName;
2279
2280                         while(basePath.exists(newFolder))
2281                         {
2282                                 newFolder = QString(folderName).append(QString().sprintf(" (%d)", ++i));
2283                         }
2284                         
2285                         if(basePath.mkpath(newFolder))
2286                         {
2287                                 QDir createdDir = basePath;
2288                                 if(createdDir.cd(newFolder))
2289                                 {
2290                                         outputFolderView->setCurrentIndex(m_fileSystemModel->index(createdDir.canonicalPath()));
2291                                         outputFolderViewClicked(outputFolderView->currentIndex());
2292                                         outputFolderView->setFocus();
2293                                 }
2294                         }
2295                         else
2296                         {
2297                                 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!")));
2298                         }
2299                 }
2300                 break;
2301         }
2302 }
2303
2304 /*
2305  * Output to source dir changed
2306  */
2307 void MainWindow::saveToSourceFolderChanged(void)
2308 {
2309         m_settings->outputToSourceDir(saveToSourceFolderCheckBox->isChecked());
2310 }
2311
2312 /*
2313  * Prepend relative source file path to output file name changed
2314  */
2315 void MainWindow::prependRelativePathChanged(void)
2316 {
2317         m_settings->prependRelativeSourcePath(prependRelativePathCheckBox->isChecked());
2318 }
2319
2320 /*
2321  * Show context menu for output folder
2322  */
2323 void MainWindow::outputFolderContextMenu(const QPoint &pos)
2324 {
2325         QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(QObject::sender());
2326         QWidget *sender = scrollArea ? scrollArea->viewport() : dynamic_cast<QWidget*>(QObject::sender());      
2327
2328         if(pos.x() <= sender->width() && pos.y() <= sender->height() && pos.x() >= 0 && pos.y() >= 0)
2329         {
2330                 m_outputFolderContextMenu->popup(sender->mapToGlobal(pos));
2331         }
2332 }
2333
2334 /*
2335  * Show selected folder in explorer
2336  */
2337 void MainWindow::showFolderContextActionTriggered(void)
2338 {
2339         QDesktopServices::openUrl(QUrl::fromLocalFile(m_fileSystemModel->filePath(outputFolderView->currentIndex())));
2340 }
2341
2342 /*
2343  * Add current folder to favorites
2344  */
2345 void MainWindow::addFavoriteFolderActionTriggered(void)
2346 {
2347         QString path = m_fileSystemModel->filePath(outputFolderView->currentIndex());
2348         QStringList favorites = m_settings->favoriteOutputFolders().split("|", QString::SkipEmptyParts);
2349
2350         if(!favorites.contains(path, Qt::CaseInsensitive))
2351         {
2352                 favorites.append(path);
2353                 while(favorites.count() > 6) favorites.removeFirst();
2354         }
2355         else
2356         {
2357                 MessageBeep(MB_ICONWARNING);
2358         }
2359
2360         m_settings->favoriteOutputFolders(favorites.join("|"));
2361         refreshFavorites();
2362 }
2363
2364 /*
2365  * Initialize file system model
2366  */
2367 void MainWindow::initOutputFolderModel(void)
2368 {
2369         QModelIndex previousIndex = outputFolderView->currentIndex();
2370         m_fileSystemModel->setRootPath(m_fileSystemModel->rootPath());
2371         QApplication::processEvents();
2372         outputFolderView->reset();
2373         outputFolderView->setCurrentIndex(previousIndex);
2374         m_OutputFolderViewInitialized = true;
2375 }
2376
2377 // =========================================================
2378 // Metadata tab slots
2379 // =========================================================
2380
2381 /*
2382  * Edit meta button clicked
2383  */
2384 void MainWindow::editMetaButtonClicked(void)
2385 {
2386         ABORT_IF_BUSY;
2387
2388         const QModelIndex index = metaDataView->currentIndex();
2389
2390         if(index.isValid())
2391         {
2392                 m_metaInfoModel->editItem(index, this);
2393         
2394                 if(index.row() == 4)
2395                 {
2396                         m_settings->metaInfoPosition(m_metaData->filePosition());
2397                 }
2398         }
2399 }
2400
2401 /*
2402  * Reset meta button clicked
2403  */
2404 void MainWindow::clearMetaButtonClicked(void)
2405 {
2406         ABORT_IF_BUSY;
2407         m_metaInfoModel->clearData();
2408 }
2409
2410 /*
2411  * Meta tags enabled changed
2412  */
2413 void MainWindow::metaTagsEnabledChanged(void)
2414 {
2415         m_settings->writeMetaTags(writeMetaDataCheckBox->isChecked());
2416 }
2417
2418 /*
2419  * Playlist enabled changed
2420  */
2421 void MainWindow::playlistEnabledChanged(void)
2422 {
2423         m_settings->createPlaylist(generatePlaylistCheckBox->isChecked());
2424 }
2425
2426 // =========================================================
2427 // Compression tab slots
2428 // =========================================================
2429
2430 /*
2431  * Update encoder
2432  */
2433 void MainWindow::updateEncoder(int id)
2434 {
2435         m_settings->compressionEncoder(id);
2436
2437         switch(m_settings->compressionEncoder())
2438         {
2439         case SettingsModel::VorbisEncoder:
2440                 radioButtonModeQuality->setEnabled(true);
2441                 radioButtonModeAverageBitrate->setEnabled(true);
2442                 radioButtonConstBitrate->setEnabled(false);
2443                 if(radioButtonConstBitrate->isChecked()) radioButtonModeQuality->setChecked(true);
2444                 sliderBitrate->setEnabled(true);
2445                 break;
2446         case SettingsModel::AC3Encoder:
2447                 radioButtonModeQuality->setEnabled(true);
2448                 radioButtonModeQuality->setChecked(true);
2449                 radioButtonModeAverageBitrate->setEnabled(false);
2450                 radioButtonConstBitrate->setEnabled(true);
2451                 sliderBitrate->setEnabled(true);
2452                 break;
2453         case SettingsModel::FLACEncoder:
2454                 radioButtonModeQuality->setEnabled(false);
2455                 radioButtonModeQuality->setChecked(true);
2456                 radioButtonModeAverageBitrate->setEnabled(false);
2457                 radioButtonConstBitrate->setEnabled(false);
2458                 sliderBitrate->setEnabled(true);
2459                 break;
2460         case SettingsModel::PCMEncoder:
2461                 radioButtonModeQuality->setEnabled(false);
2462                 radioButtonModeQuality->setChecked(true);
2463                 radioButtonModeAverageBitrate->setEnabled(false);
2464                 radioButtonConstBitrate->setEnabled(false);
2465                 sliderBitrate->setEnabled(false);
2466                 break;
2467         case SettingsModel::AACEncoder:
2468                 radioButtonModeQuality->setEnabled(true);
2469                 radioButtonModeAverageBitrate->setEnabled(!m_fhgEncoderAvailable);
2470                 if(m_fhgEncoderAvailable && radioButtonModeAverageBitrate->isChecked()) radioButtonConstBitrate->setChecked(true);
2471                 radioButtonConstBitrate->setEnabled(true);
2472                 sliderBitrate->setEnabled(true);
2473                 break;
2474         default:
2475                 radioButtonModeQuality->setEnabled(true);
2476                 radioButtonModeAverageBitrate->setEnabled(true);
2477                 radioButtonConstBitrate->setEnabled(true);
2478                 sliderBitrate->setEnabled(true);
2479                 break;
2480         }
2481
2482         updateRCMode(m_modeButtonGroup->checkedId());
2483 }
2484
2485 /*
2486  * Update rate-control mode
2487  */
2488 void MainWindow::updateRCMode(int id)
2489 {
2490         m_settings->compressionRCMode(id);
2491
2492         switch(m_settings->compressionEncoder())
2493         {
2494         case SettingsModel::MP3Encoder:
2495                 switch(m_settings->compressionRCMode())
2496                 {
2497                 case SettingsModel::VBRMode:
2498                         sliderBitrate->setMinimum(0);
2499                         sliderBitrate->setMaximum(9);
2500                         break;
2501                 default:
2502                         sliderBitrate->setMinimum(0);
2503                         sliderBitrate->setMaximum(13);
2504                         break;
2505                 }
2506                 break;
2507         case SettingsModel::VorbisEncoder:
2508                 switch(m_settings->compressionRCMode())
2509                 {
2510                 case SettingsModel::VBRMode:
2511                         sliderBitrate->setMinimum(-2);
2512                         sliderBitrate->setMaximum(10);
2513                         break;
2514                 default:
2515                         sliderBitrate->setMinimum(4);
2516                         sliderBitrate->setMaximum(63);
2517                         break;
2518                 }
2519                 break;
2520         case SettingsModel::AC3Encoder:
2521                 switch(m_settings->compressionRCMode())
2522                 {
2523                 case SettingsModel::VBRMode:
2524                         sliderBitrate->setMinimum(0);
2525                         sliderBitrate->setMaximum(16);
2526                         break;
2527                 default:
2528                         sliderBitrate->setMinimum(0);
2529                         sliderBitrate->setMaximum(18);
2530                         break;
2531                 }
2532                 break;
2533         case SettingsModel::AACEncoder:
2534                 switch(m_settings->compressionRCMode())
2535                 {
2536                 case SettingsModel::VBRMode:
2537                         sliderBitrate->setMinimum(0);
2538                         sliderBitrate->setMaximum(20);
2539                         break;
2540                 default:
2541                         sliderBitrate->setMinimum(4);
2542                         sliderBitrate->setMaximum(63);
2543                         break;
2544                 }
2545                 break;
2546         case SettingsModel::FLACEncoder:
2547                 sliderBitrate->setMinimum(0);
2548                 sliderBitrate->setMaximum(8);
2549                 break;
2550         case SettingsModel::PCMEncoder:
2551                 sliderBitrate->setMinimum(0);
2552                 sliderBitrate->setMaximum(2);
2553                 sliderBitrate->setValue(1);
2554                 break;
2555         default:
2556                 sliderBitrate->setMinimum(0);
2557                 sliderBitrate->setMaximum(0);
2558                 break;
2559         }
2560
2561         updateBitrate(sliderBitrate->value());
2562 }
2563
2564 /*
2565  * Update bitrate
2566  */
2567 void MainWindow::updateBitrate(int value)
2568 {
2569         m_settings->compressionBitrate(value);
2570         
2571         switch(m_settings->compressionRCMode())
2572         {
2573         case SettingsModel::VBRMode:
2574                 switch(m_settings->compressionEncoder())
2575                 {
2576                 case SettingsModel::MP3Encoder:
2577                         labelBitrate->setText(tr("Quality Level %1").arg(9 - value));
2578                         break;
2579                 case SettingsModel::VorbisEncoder:
2580                         labelBitrate->setText(tr("Quality Level %1").arg(value));
2581                         break;
2582                 case SettingsModel::AACEncoder:
2583                         labelBitrate->setText(tr("Quality Level %1").arg(QString().sprintf("%.2f", static_cast<double>(value * 5) / 100.0)));
2584                         break;
2585                 case SettingsModel::FLACEncoder:
2586                         labelBitrate->setText(tr("Compression %1").arg(value));
2587                         break;
2588                 case SettingsModel::AC3Encoder:
2589                         labelBitrate->setText(tr("Quality Level %1").arg(qMin(1024, qMax(0, value * 64))));
2590                         break;
2591                 case SettingsModel::PCMEncoder:
2592                         labelBitrate->setText(tr("Uncompressed"));
2593                         break;
2594                 default:
2595                         labelBitrate->setText(QString::number(value));
2596                         break;
2597                 }
2598                 break;
2599         case SettingsModel::ABRMode:
2600                 switch(m_settings->compressionEncoder())
2601                 {
2602                 case SettingsModel::MP3Encoder:
2603                         labelBitrate->setText(QString("&asymp; %1 kbps").arg(SettingsModel::mp3Bitrates[value]));
2604                         break;
2605                 case SettingsModel::FLACEncoder:
2606                         labelBitrate->setText(tr("Compression %1").arg(value));
2607                         break;
2608                 case SettingsModel::AC3Encoder:
2609                         labelBitrate->setText(QString("&asymp; %1 kbps").arg(SettingsModel::ac3Bitrates[value]));
2610                         break;
2611                 case SettingsModel::PCMEncoder:
2612                         labelBitrate->setText(tr("Uncompressed"));
2613                         break;
2614                 default:
2615                         labelBitrate->setText(QString("&asymp; %1 kbps").arg(qMin(500, value * 8)));
2616                         break;
2617                 }
2618                 break;
2619         default:
2620                 switch(m_settings->compressionEncoder())
2621                 {
2622                 case SettingsModel::MP3Encoder:
2623                         labelBitrate->setText(QString("%1 kbps").arg(SettingsModel::mp3Bitrates[value]));
2624                         break;
2625                 case SettingsModel::FLACEncoder:
2626                         labelBitrate->setText(tr("Compression %1").arg(value));
2627                         break;
2628                 case SettingsModel::AC3Encoder:
2629                         labelBitrate->setText(QString("%1 kbps").arg(SettingsModel::ac3Bitrates[value]));
2630                         break;
2631                 case SettingsModel::PCMEncoder:
2632                         labelBitrate->setText(tr("Uncompressed"));
2633                         break;
2634                 default:
2635                         labelBitrate->setText(QString("%1 kbps").arg(qMin(500, value * 8)));
2636                         break;
2637                 }
2638                 break;
2639         }
2640 }
2641
2642 // =========================================================
2643 // Advanced option slots
2644 // =========================================================
2645
2646 /*
2647  * Lame algorithm quality changed
2648  */
2649 void MainWindow::updateLameAlgoQuality(int value)
2650 {
2651         QString text;
2652
2653         switch(value)
2654         {
2655         case 4:
2656                 text = tr("Best Quality (Very Slow)");
2657                 break;
2658         case 3:
2659                 text = tr("High Quality (Recommended)");
2660                 break;
2661         case 2:
2662                 text = tr("Average Quality (Default)");
2663                 break;
2664         case 1:
2665                 text = tr("Low Quality (Fast)");
2666                 break;
2667         case 0:
2668                 text = tr("Poor Quality (Very Fast)");
2669                 break;
2670         }
2671
2672         if(!text.isEmpty())
2673         {
2674                 m_settings->lameAlgoQuality(value);
2675                 labelLameAlgoQuality->setText(text);
2676         }
2677
2678         bool warning = (value == 0), notice = (value == 4);
2679         labelLameAlgoQualityWarning->setVisible(warning);
2680         labelLameAlgoQualityWarningIcon->setVisible(warning);
2681         labelLameAlgoQualityNotice->setVisible(notice);
2682         labelLameAlgoQualityNoticeIcon->setVisible(notice);
2683         labelLameAlgoQualitySpacer->setVisible(warning || notice);
2684 }
2685
2686 /*
2687  * Bitrate management endabled/disabled
2688  */
2689 void MainWindow::bitrateManagementEnabledChanged(bool checked)
2690 {
2691         m_settings->bitrateManagementEnabled(checked);
2692 }
2693
2694 /*
2695  * Minimum bitrate has changed
2696  */
2697 void MainWindow::bitrateManagementMinChanged(int value)
2698 {
2699         if(value > spinBoxBitrateManagementMax->value())
2700         {
2701                 spinBoxBitrateManagementMin->setValue(spinBoxBitrateManagementMax->value());
2702                 m_settings->bitrateManagementMinRate(spinBoxBitrateManagementMax->value());
2703         }
2704         else
2705         {
2706                 m_settings->bitrateManagementMinRate(value);
2707         }
2708 }
2709
2710 /*
2711  * Maximum bitrate has changed
2712  */
2713 void MainWindow::bitrateManagementMaxChanged(int value)
2714 {
2715         if(value < spinBoxBitrateManagementMin->value())
2716         {
2717                 spinBoxBitrateManagementMax->setValue(spinBoxBitrateManagementMin->value());
2718                 m_settings->bitrateManagementMaxRate(spinBoxBitrateManagementMin->value());
2719         }
2720         else
2721         {
2722                 m_settings->bitrateManagementMaxRate(value);
2723         }
2724 }
2725
2726 /*
2727  * Channel mode has changed
2728  */
2729 void MainWindow::channelModeChanged(int value)
2730 {
2731         if(value >= 0) m_settings->lameChannelMode(value);
2732 }
2733
2734 /*
2735  * Sampling rate has changed
2736  */
2737 void MainWindow::samplingRateChanged(int value)
2738 {
2739         if(value >= 0) m_settings->samplingRate(value);
2740 }
2741
2742 /*
2743  * Nero AAC 2-Pass mode changed
2744  */
2745 void MainWindow::neroAAC2PassChanged(bool checked)
2746 {
2747         m_settings->neroAACEnable2Pass(checked);
2748 }
2749
2750 /*
2751  * Nero AAC profile mode changed
2752  */
2753 void MainWindow::neroAACProfileChanged(int value)
2754 {
2755         if(value >= 0) m_settings->aacEncProfile(value);
2756 }
2757
2758 /*
2759  * Aften audio coding mode changed
2760  */
2761 void MainWindow::aftenCodingModeChanged(int value)
2762 {
2763         if(value >= 0) m_settings->aftenAudioCodingMode(value);
2764 }
2765
2766 /*
2767  * Aften DRC mode changed
2768  */
2769 void MainWindow::aftenDRCModeChanged(int value)
2770 {
2771         if(value >= 0) m_settings->aftenDynamicRangeCompression(value);
2772 }
2773
2774 /*
2775  * Aften exponent search size changed
2776  */
2777 void MainWindow::aftenSearchSizeChanged(int value)
2778 {
2779         if(value >= 0) m_settings->aftenExponentSearchSize(value);
2780 }
2781
2782 /*
2783  * Aften fast bit allocation changed
2784  */
2785 void MainWindow::aftenFastAllocationChanged(bool checked)
2786 {
2787         m_settings->aftenFastBitAllocation(checked);
2788 }
2789
2790 /*
2791  * Normalization filter enabled changed
2792  */
2793 void MainWindow::normalizationEnabledChanged(bool checked)
2794 {
2795         m_settings->normalizationFilterEnabled(checked);
2796 }
2797
2798 /*
2799  * Normalization max. volume changed
2800  */
2801 void MainWindow::normalizationMaxVolumeChanged(double value)
2802 {
2803         m_settings->normalizationFilterMaxVolume(static_cast<int>(value * 100.0));
2804 }
2805
2806 /*
2807  * Normalization equalization mode changed
2808  */
2809 void MainWindow::normalizationModeChanged(int mode)
2810 {
2811         m_settings->normalizationFilterEqualizationMode(mode);
2812 }
2813
2814 /*
2815  * Tone adjustment has changed (Bass)
2816  */
2817 void MainWindow::toneAdjustBassChanged(double value)
2818 {
2819         m_settings->toneAdjustBass(static_cast<int>(value * 100.0));
2820         spinBoxToneAdjustBass->setPrefix((value > 0) ? "+" : QString());
2821 }
2822
2823 /*
2824  * Tone adjustment has changed (Treble)
2825  */
2826 void MainWindow::toneAdjustTrebleChanged(double value)
2827 {
2828         m_settings->toneAdjustTreble(static_cast<int>(value * 100.0));
2829         spinBoxToneAdjustTreble->setPrefix((value > 0) ? "+" : QString());
2830 }
2831
2832 /*
2833  * Tone adjustment has been reset
2834  */
2835 void MainWindow::toneAdjustTrebleReset(void)
2836 {
2837         spinBoxToneAdjustBass->setValue(m_settings->toneAdjustBassDefault());
2838         spinBoxToneAdjustTreble->setValue(m_settings->toneAdjustTrebleDefault());
2839         toneAdjustBassChanged(spinBoxToneAdjustBass->value());
2840         toneAdjustTrebleChanged(spinBoxToneAdjustTreble->value());
2841 }
2842
2843 /*
2844  * Custom encoder parameters changed
2845  */
2846 void MainWindow::customParamsChanged(void)
2847 {
2848         lineEditCustomParamLAME->setText(lineEditCustomParamLAME->text().simplified());
2849         lineEditCustomParamOggEnc->setText(lineEditCustomParamOggEnc->text().simplified());
2850         lineEditCustomParamNeroAAC->setText(lineEditCustomParamNeroAAC->text().simplified());
2851         lineEditCustomParamFLAC->setText(lineEditCustomParamFLAC->text().simplified());
2852         lineEditCustomParamAften->setText(lineEditCustomParamAften->text().simplified());
2853
2854         bool customParamsUsed = false;
2855         if(!lineEditCustomParamLAME->text().isEmpty()) customParamsUsed = true;
2856         if(!lineEditCustomParamOggEnc->text().isEmpty()) customParamsUsed = true;
2857         if(!lineEditCustomParamNeroAAC->text().isEmpty()) customParamsUsed = true;
2858         if(!lineEditCustomParamFLAC->text().isEmpty()) customParamsUsed = true;
2859         if(!lineEditCustomParamAften->text().isEmpty()) customParamsUsed = true;
2860
2861         labelCustomParamsIcon->setVisible(customParamsUsed);
2862         labelCustomParamsText->setVisible(customParamsUsed);
2863         labelCustomParamsSpacer->setVisible(customParamsUsed);
2864
2865         m_settings->customParametersLAME(lineEditCustomParamLAME->text());
2866         m_settings->customParametersOggEnc(lineEditCustomParamOggEnc->text());
2867         m_settings->customParametersAacEnc(lineEditCustomParamNeroAAC->text());
2868         m_settings->customParametersFLAC(lineEditCustomParamFLAC->text());
2869         m_settings->customParametersAften(lineEditCustomParamAften->text());
2870 }
2871
2872
2873 /*
2874  * Rename output files enabled changed
2875  */
2876 void MainWindow::renameOutputEnabledChanged(bool checked)
2877 {
2878         m_settings->renameOutputFilesEnabled(checked);
2879 }
2880
2881 /*
2882  * Rename output files patterm changed
2883  */
2884 void MainWindow::renameOutputPatternChanged(void)
2885 {
2886         QString temp = lineEditRenamePattern->text().simplified();
2887         lineEditRenamePattern->setText(temp.isEmpty() ? m_settings->renameOutputFilesPatternDefault() : temp);
2888         m_settings->renameOutputFilesPattern(lineEditRenamePattern->text());
2889 }
2890
2891 /*
2892  * Rename output files patterm changed
2893  */
2894 void MainWindow::renameOutputPatternChanged(const QString &text)
2895 {
2896         QString pattern(text.simplified());
2897         
2898         pattern.replace("<BaseName>", "The_White_Stripes_-_Fell_In_Love_With_A_Girl", Qt::CaseInsensitive);
2899         pattern.replace("<TrackNo>", "04", Qt::CaseInsensitive);
2900         pattern.replace("<Title>", "Fell In Love With A Girl", Qt::CaseInsensitive);
2901         pattern.replace("<Artist>", "The White Stripes", Qt::CaseInsensitive);
2902         pattern.replace("<Album>", "White Blood Cells", Qt::CaseInsensitive);
2903         pattern.replace("<Year>", "2001", Qt::CaseInsensitive);
2904         pattern.replace("<Comment>", "Encoded by LameXP", Qt::CaseInsensitive);
2905
2906         if(pattern.compare(lamexp_clean_filename(pattern)))
2907         {
2908                 if(lineEditRenamePattern->palette().color(QPalette::Text) != Qt::red)
2909                 {
2910                         MessageBeep(MB_ICONERROR);
2911                         SET_TEXT_COLOR(lineEditRenamePattern, Qt::red);
2912                 }
2913         }
2914         else
2915         {
2916                 if(lineEditRenamePattern->palette().color(QPalette::Text) != Qt::black)
2917                 {
2918                         MessageBeep(MB_ICONINFORMATION);
2919                         SET_TEXT_COLOR(lineEditRenamePattern, Qt::black);
2920                 }
2921         }
2922
2923         labelRanameExample->setText(lamexp_clean_filename(pattern));
2924 }
2925
2926 /*
2927  * Show list of rename macros
2928  */
2929 void MainWindow::showRenameMacros(const QString &text)
2930 {
2931         if(text.compare("reset", Qt::CaseInsensitive) == 0)
2932         {
2933                 lineEditRenamePattern->setText(m_settings->renameOutputFilesPatternDefault());
2934                 return;
2935         }
2936
2937         const QString format = QString("<tr><td><tt>&lt;%1&gt;</tt></td><td>&nbsp;&nbsp;</td><td>%2</td></tr>");
2938
2939         QString message = QString("<table>");
2940         message += QString(format).arg("BaseName", tr("File name without extension"));
2941         message += QString(format).arg("TrackNo", tr("Track number with leading zero"));
2942         message += QString(format).arg("Title", tr("Track title"));
2943         message += QString(format).arg("Artist", tr("Artist name"));
2944         message += QString(format).arg("Album", tr("Album name"));
2945         message += QString(format).arg("Year", tr("Year with (at least) four digits"));
2946         message += QString(format).arg("Comment", tr("Comment"));
2947         message += "</table><br><br>";
2948         message += QString("%1<br>").arg(tr("Characters forbidden in file names:"));
2949         message += "<b><tt>\\ / : * ? &lt; &gt; |<br>";
2950         
2951         QMessageBox::information(this, tr("Rename Macros"), message, tr("Discard"));
2952 }
2953
2954 void MainWindow::forceStereoDownmixEnabledChanged(bool checked)
2955 {
2956         m_settings->forceStereoDownmix(checked);
2957 }
2958
2959 /*
2960  * Maximum number of instances changed
2961  */
2962 void MainWindow::updateMaximumInstances(int value)
2963 {
2964         labelMaxInstances->setText(tr("%1 Instance(s)").arg(QString::number(value)));
2965         m_settings->maximumInstances(checkBoxAutoDetectInstances->isChecked() ? NULL : value);
2966 }
2967
2968 /*
2969  * Auto-detect number of instances
2970  */
2971 void MainWindow::autoDetectInstancesChanged(bool checked)
2972 {
2973         m_settings->maximumInstances(checked ? NULL : sliderMaxInstances->value());
2974 }
2975
2976 /*
2977  * Browse for custom TEMP folder button clicked
2978  */
2979 void MainWindow::browseCustomTempFolderButtonClicked(void)
2980 {
2981         QString newTempFolder;
2982
2983         if(USE_NATIVE_FILE_DIALOG)
2984         {
2985                 newTempFolder = QFileDialog::getExistingDirectory(this, QString(), m_settings->customTempPath());
2986         }
2987         else
2988         {
2989                 QFileDialog dialog(this);
2990                 dialog.setFileMode(QFileDialog::DirectoryOnly);
2991                 dialog.setDirectory(m_settings->customTempPath());
2992                 if(dialog.exec())
2993                 {
2994                         newTempFolder = dialog.selectedFiles().first();
2995                 }
2996         }
2997
2998         if(!newTempFolder.isEmpty())
2999         {
3000                 QFile writeTest(QString("%1/~%2.tmp").arg(newTempFolder, lamexp_rand_str()));
3001                 if(writeTest.open(QIODevice::ReadWrite))
3002                 {
3003                         writeTest.remove();
3004                         lineEditCustomTempFolder->setText(QDir::toNativeSeparators(newTempFolder));
3005                 }
3006                 else
3007                 {
3008                         QMessageBox::warning(this, tr("Access Denied"), tr("Cannot write to the selected directory. Please choose another directory!"));
3009                 }
3010         }
3011 }
3012
3013 /*
3014  * Custom TEMP folder changed
3015  */
3016 void MainWindow::customTempFolderChanged(const QString &text)
3017 {
3018         m_settings->customTempPath(QDir::fromNativeSeparators(text));
3019 }
3020
3021 /*
3022  * Use custom TEMP folder option changed
3023  */
3024 void MainWindow::useCustomTempFolderChanged(bool checked)
3025 {
3026         m_settings->customTempPathEnabled(!checked);
3027 }
3028
3029 /*
3030  * Reset all advanced options to their defaults
3031  */
3032 void MainWindow::resetAdvancedOptionsButtonClicked(void)
3033 {
3034         sliderLameAlgoQuality->setValue(m_settings->lameAlgoQualityDefault());
3035         spinBoxBitrateManagementMin->setValue(m_settings->bitrateManagementMinRateDefault());
3036         spinBoxBitrateManagementMax->setValue(m_settings->bitrateManagementMaxRateDefault());
3037         spinBoxNormalizationFilter->setValue(static_cast<double>(m_settings->normalizationFilterMaxVolumeDefault()) / 100.0);
3038         spinBoxToneAdjustBass->setValue(static_cast<double>(m_settings->toneAdjustBassDefault()) / 100.0);
3039         spinBoxToneAdjustTreble->setValue(static_cast<double>(m_settings->toneAdjustTrebleDefault()) / 100.0);
3040         spinBoxAftenSearchSize->setValue(m_settings->aftenExponentSearchSizeDefault());
3041         comboBoxMP3ChannelMode->setCurrentIndex(m_settings->lameChannelModeDefault());
3042         comboBoxSamplingRate->setCurrentIndex(m_settings->samplingRateDefault());
3043         comboBoxAACProfile->setCurrentIndex(m_settings->aacEncProfileDefault());
3044         comboBoxAftenCodingMode->setCurrentIndex(m_settings->aftenAudioCodingModeDefault());
3045         comboBoxAftenDRCMode->setCurrentIndex(m_settings->aftenDynamicRangeCompressionDefault());
3046         comboBoxNormalizationMode->setCurrentIndex(m_settings->normalizationFilterEqualizationModeDefault());
3047         while(checkBoxBitrateManagement->isChecked() != m_settings->bitrateManagementEnabledDefault()) checkBoxBitrateManagement->click();
3048         while(checkBoxNeroAAC2PassMode->isChecked() != m_settings->neroAACEnable2PassDefault()) checkBoxNeroAAC2PassMode->click();
3049         while(checkBoxNormalizationFilter->isChecked() != m_settings->normalizationFilterEnabledDefault()) checkBoxNormalizationFilter->click();
3050         while(checkBoxAutoDetectInstances->isChecked() != (m_settings->maximumInstancesDefault() < 1)) checkBoxAutoDetectInstances->click();
3051         while(checkBoxUseSystemTempFolder->isChecked() == m_settings->customTempPathEnabledDefault()) checkBoxUseSystemTempFolder->click();
3052         while(checkBoxAftenFastAllocation->isChecked() != m_settings->aftenFastBitAllocationDefault()) checkBoxAftenFastAllocation->click();
3053         while(checkBoxRenameOutput->isChecked() != m_settings->renameOutputFilesEnabledDefault()) checkBoxRenameOutput->click();
3054         while(checkBoxForceStereoDownmix->isChecked() != m_settings->forceStereoDownmixDefault()) checkBoxForceStereoDownmix->click();
3055         lineEditCustomParamLAME->setText(m_settings->customParametersLAMEDefault());
3056         lineEditCustomParamOggEnc->setText(m_settings->customParametersOggEncDefault());
3057         lineEditCustomParamNeroAAC->setText(m_settings->customParametersAacEncDefault());
3058         lineEditCustomParamFLAC->setText(m_settings->customParametersFLACDefault());
3059         lineEditCustomTempFolder->setText(QDir::toNativeSeparators(m_settings->customTempPathDefault()));
3060         lineEditRenamePattern->setText(m_settings->renameOutputFilesPatternDefault());
3061         customParamsChanged();
3062         scrollArea->verticalScrollBar()->setValue(0);
3063 }
3064
3065 // =========================================================
3066 // Multi-instance handling slots
3067 // =========================================================
3068
3069 /*
3070  * Other instance detected
3071  */
3072 void MainWindow::notifyOtherInstance(void)
3073 {
3074         if(!m_banner->isVisible())
3075         {
3076                 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);
3077                 msgBox.exec();
3078         }
3079 }
3080
3081 /*
3082  * Add file from another instance
3083  */
3084 void MainWindow::addFileDelayed(const QString &filePath, bool tryASAP)
3085 {
3086         if(tryASAP && !m_delayedFileTimer->isActive())
3087         {
3088                 qDebug("Received file: %s", filePath.toUtf8().constData());
3089                 m_delayedFileList->append(filePath);
3090                 QTimer::singleShot(0, this, SLOT(handleDelayedFiles()));
3091         }
3092         
3093         m_delayedFileTimer->stop();
3094         qDebug("Received file: %s", filePath.toUtf8().constData());
3095         m_delayedFileList->append(filePath);
3096         m_delayedFileTimer->start(5000);
3097 }
3098
3099 /*
3100  * Add files from another instance
3101  */
3102 void MainWindow::addFilesDelayed(const QStringList &filePaths, bool tryASAP)
3103 {
3104         if(tryASAP && !m_delayedFileTimer->isActive())
3105         {
3106                 qDebug("Received %d file(s).", filePaths.count());
3107                 m_delayedFileList->append(filePaths);
3108                 QTimer::singleShot(0, this, SLOT(handleDelayedFiles()));
3109         }
3110         else
3111         {
3112                 m_delayedFileTimer->stop();
3113                 qDebug("Received %d file(s).", filePaths.count());
3114                 m_delayedFileList->append(filePaths);
3115                 m_delayedFileTimer->start(5000);
3116         }
3117 }
3118
3119 /*
3120  * Add folder from another instance
3121  */
3122 void MainWindow::addFolderDelayed(const QString &folderPath, bool recursive)
3123 {
3124         if(!m_banner->isVisible())
3125         {
3126                 addFolder(folderPath, recursive, true);
3127         }
3128 }
3129
3130 // =========================================================
3131 // Misc slots
3132 // =========================================================
3133
3134 /*
3135  * Restore the override cursor
3136  */
3137 void MainWindow::restoreCursor(void)
3138 {
3139         QApplication::restoreOverrideCursor();
3140 }