OSDN Git Service

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