OSDN Git Service

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