OSDN Git Service

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