1 
2 #include "optionssimple.h"
3 #include "config.h"
4 #include "outputdirectory.h"
5 #include "global.h"
6 #include "codecproblems.h"
7 
8 #include <QApplication>
9 #include <QLayout>
10 #include <QBoxLayout>
11 #include <QLabel>
12 #include <QString>
13 #include <QToolTip>
14 
15 #include <KLocale>
16 #include <KIcon>
17 #include <KMessageBox>
18 #include <KComboBox>
19 #include <QCheckBox>
20 #include <KPushButton>
21 #include <QFile>
22 #include <KStandardDirs>
23 
24 
25 // FIXME when changing the output directory, check if the profile is a user defined and set it to 'User defined', if it is
26 
27 // TODO hide lossless/hybrid/etc. when not available
OptionsSimple(Config * _config,const QString & text,QWidget * parent)28 OptionsSimple::OptionsSimple( Config *_config, /*OptionsDetailed* _optionsDetailed,*/ const QString &text, QWidget *parent )
29     : QWidget( parent ),
30     config( _config )
31 {
32     const int fontHeight = QFontMetrics(QApplication::font()).boundingRect("M").size().height();
33 
34     QGridLayout *grid = new QGridLayout( this );
35     grid->setMargin( fontHeight );
36     grid->setSpacing( fontHeight );
37 
38     QLabel *lQuality = new QLabel( i18n("Quality:"), this );
39     grid->addWidget( lQuality, 0, 0 );
40 
41     QHBoxLayout *topBoxQuality = new QHBoxLayout();
42     grid->addLayout( topBoxQuality, 0, 1 );
43     cProfile = new KComboBox( this );
44     topBoxQuality->addWidget( cProfile );
45     connect( cProfile, SIGNAL(activated(int)), this, SLOT(profileChanged()) );
46     topBoxQuality->addSpacing( 0.25*fontHeight );
47     pProfileRemove = new KPushButton( KIcon("edit-delete"), i18n("Remove"), this );
48     topBoxQuality->addWidget( pProfileRemove );
49     pProfileRemove->setToolTip( i18n("Remove the selected profile") );
50     pProfileRemove->hide();
51     connect( pProfileRemove, SIGNAL(clicked()), this, SLOT(profileRemove()) );
52     pProfileInfo = new KPushButton( KIcon("dialog-information"), i18n("Info"), this );
53     topBoxQuality->addWidget( pProfileInfo );
54     pProfileInfo->setToolTip( i18n("Information about the selected profile") );
55 //     cProfile->setFixedHeight( pProfileInfo->minimumSizeHint().height() );
56     connect( pProfileInfo, SIGNAL(clicked()), this, SLOT(profileInfo()) );
57     topBoxQuality->addStretch( );
58 
59     topBoxQuality->addSpacing( fontHeight );
60 
61     QLabel *lFormat = new QLabel( i18n("Format:"), this );
62     grid->addWidget( lFormat, 0, 2 );
63 
64     QHBoxLayout *topBoxFormat = new QHBoxLayout();
65     grid->addLayout( topBoxFormat, 0, 3 );
66     cFormat = new KComboBox( this );
67     topBoxFormat->addWidget( cFormat );
68 //     connect( cFormat, SIGNAL(activated(int)), this, SLOT(formatChanged()) );
69     connect( cFormat, SIGNAL(activated(int)), this, SLOT(somethingChanged()) );
70     topBoxFormat->addSpacing( 0.25*fontHeight );
71     pFormatInfo = new KPushButton( KIcon("dialog-information"), i18n("Info"), this );
72     topBoxFormat->addWidget( pFormatInfo );
73     pFormatInfo->setToolTip( i18n("Information about the selected file format") );
74 //     cFormat->setFixedHeight( pFormatInfo->minimumSizeHint().height() );
75     connect( pFormatInfo, SIGNAL(clicked()), this, SLOT(formatInfo()) );
76     topBoxFormat->addSpacing( 0.25*fontHeight );
77     QLabel *formatHelp = new QLabel( "<a href=\"format-help\">" + i18n("More formats...") + "</a>", this );
78     topBoxFormat->addWidget( formatHelp );
79     connect( formatHelp, SIGNAL(linkActivated(const QString&)), this, SLOT(showHelp()) );
80     topBoxFormat->addStretch( );
81 
82     QLabel *lOutput = new QLabel( i18n("Destination:"), this );
83     grid->addWidget( lOutput, 1, 0 );
84 
85     QHBoxLayout *middleBox = new QHBoxLayout();
86     grid->addLayout( middleBox, 1, 1, 1, 3 );
87     outputDirectory = new OutputDirectory( config, this );
88     middleBox->addWidget( outputDirectory );
89     connect( outputDirectory, SIGNAL(modeChanged(int)), this, SLOT(outputDirectoryChanged()) );
90     connect( outputDirectory, SIGNAL(directoryChanged(const QString&)), this, SLOT(outputDirectoryChanged()) );
91 
92     QHBoxLayout *estimSizeBox = new QHBoxLayout();
93     grid->addLayout( estimSizeBox, 2, 0 );
94     estimSizeBox->addStretch();
95     lEstimSize = new QLabel( QString(QChar(8776))+"? B / min." );
96     lEstimSize->hide(); // hide for now because most plugins report inaccurate data
97     estimSizeBox->addWidget( lEstimSize );
98 
99     QLabel *lOptional = new QLabel( i18n("Optional:") );
100     grid->addWidget( lOptional, 3, 0 );
101 
102     QHBoxLayout *optionalBox = new QHBoxLayout();
103     grid->addLayout( optionalBox, 3, 1 );
104     cReplayGain = new QCheckBox( i18n("Calculate Replay Gain tags"), this );
105     optionalBox->addWidget( cReplayGain );
106     connect( cReplayGain, SIGNAL(toggled(bool)), this, SLOT(somethingChanged()) );
107     optionalBox->addStretch();
108 
109     QLabel *lInfo = new QLabel( text, this );
110     grid->addWidget( lInfo, 4, 0, 1, 4, Qt::AlignVCenter | Qt::AlignCenter );
111     grid->setRowStretch( 4, 1 );
112 
113     grid->setColumnStretch( 1, 1 );
114     grid->setColumnStretch( 3, 1 );
115 }
116 
~OptionsSimple()117 OptionsSimple::~OptionsSimple()
118 {}
119 
init()120 void OptionsSimple::init()
121 {
122     updateProfiles();
123 }
124 
setReplayGainEnabled(bool enabled,const QString & toolTip)125 void OptionsSimple::setReplayGainEnabled( bool enabled, const QString& toolTip )
126 {
127     cReplayGain->setEnabled(enabled);
128     cReplayGain->setToolTip(toolTip);
129     if( !enabled )
130     {
131         QPalette notificationPalette = cReplayGain->palette();
132         notificationPalette.setColor( QPalette::Disabled, QPalette::WindowText, QColor(174,127,130) );
133         cReplayGain->setPalette( notificationPalette );
134     }
135 }
136 
setReplayGainChecked(bool enabled)137 void OptionsSimple::setReplayGainChecked( bool enabled )
138 {
139     cReplayGain->setChecked(enabled);
140 }
141 
currentProfile()142 QString OptionsSimple::currentProfile()
143 {
144     return cProfile->currentText();
145 }
146 
currentFormat()147 QString OptionsSimple::currentFormat()
148 {
149     return cFormat->currentText();
150 }
151 
isReplayGainChecked()152 bool OptionsSimple::isReplayGainChecked()
153 {
154     return cReplayGain->isChecked();
155 }
156 
updateProfiles()157 void OptionsSimple::updateProfiles()
158 {
159     const QString lastProfile = cProfile->currentText();
160     QStringList sProfile;
161     cProfile->clear();
162 
163     sProfile += i18n("Very low");
164     sProfile += i18n("Low");
165     sProfile += i18n("Medium");
166     sProfile += i18n("High");
167     sProfile += i18n("Very high");
168     sProfile += i18n("Lossless");
169 //     sProfile += i18n("Hybrid"); // currently unused
170     sProfile += config->customProfiles();
171     sProfile += i18n("User defined");
172     cProfile->addItems( sProfile );
173 
174     if( cProfile->findText(lastProfile) != -1 )
175     {
176         cProfile->setCurrentIndex( cProfile->findText(lastProfile) );
177     }
178     else
179     {
180         profileChanged();
181     }
182 }
183 
profileInfo()184 void OptionsSimple::profileInfo()
185 {
186     const QString sProfileString = cProfile->currentText();
187 
188     QString info;
189 
190     if( sProfileString == i18n("Very low") )
191     {
192         info = i18n("Encoding files with a very low quality can be useful if you have a mobile device where your storage space is limited.\nIt is not recommended to save your music in this quality without a copy of higher quality.");
193     }
194     else if( sProfileString == i18n("Low") )
195     {
196         info = i18n("Encoding files with a low quality can be useful if you have a mobile device where your storage space is limited.\nIt is not recommended to save your music in this quality without a copy of higher quality.");
197     }
198     else if( sProfileString == i18n("Medium") )
199     {
200         info = i18n("Encoding files with a medium quality is a compromise between quality and file size.\nMost people might not hear much of a difference to the original files but you should only use it to save music if your storage space is limited.");
201     }
202     else if( sProfileString == i18n("High") )
203     {
204         info = i18n("Encoding files with a high quality is a good choice if you have enough storage space available. You can use this to save your music.");
205     }
206     else if( sProfileString == i18n("Very high") )
207     {
208         info = i18n("Encoding files with a very high quality is fine if you have enough storage space.");
209     }
210     else if( sProfileString == i18n("Lossless") )
211     {
212         info = i18n("Encoding files lossless will preserve the quality of the original file.\nIt is only then useful if the original file has a good quality (e.g. audio CD).\nThe file size will be big, so you should only use it if you have enough storage space.");
213     }
214     else if( sProfileString == i18n("Hybrid") )
215     {
216         // info = i18n("This produces two files. One lossy compressed playable file and one correction file.\nBoth files together result in a file that is equivalent to the input file.");
217     }
218     else if( sProfileString == i18n("User defined") )
219     {
220         info = i18n("You can define your own profile in the \"Detailed\" tab.");
221     }
222     else // the info button is hidden when showing user defined profiles
223     {
224         info = i18n("This is a user defined profile.");
225     }
226 
227     KMessageBox::information( this, info, i18n("Profile info for %1",sProfileString) );
228 }
229 
profileRemove()230 void OptionsSimple::profileRemove()
231 {
232     const QString profileName = cProfile->currentText();
233 
234     const int ret = KMessageBox::questionYesNo( this, i18n("Do you really want to remove the profile: %1").arg(profileName), i18n("Remove profile?") );
235     if( ret == KMessageBox::Yes )
236     {
237         QDomDocument list("soundkonverter_profilelist");
238 
239         QFile listFile( KStandardDirs::locateLocal("data","soundkonverter/profiles.xml") );
240         if( listFile.open( QIODevice::ReadOnly ) )
241         {
242             if( list.setContent( &listFile ) )
243             {
244                 QDomElement root = list.documentElement();
245                 if( root.nodeName() == "soundkonverter" && root.attribute("type") == "profilelist" )
246                 {
247                     QDomElement profileElement;
248                     QDomNodeList conversionOptionsElements = root.elementsByTagName("conversionOptions");
249                     for( int i=0; i<conversionOptionsElements.count(); i++ )
250                     {
251                         if( conversionOptionsElements.at(i).toElement().attribute("profileName") == profileName )
252                         {
253                             delete config->data.profiles[profileName];
254                             config->data.profiles.remove(profileName);
255                             root.removeChild(conversionOptionsElements.at(i));
256                             break;
257                         }
258                     }
259                 }
260             }
261             listFile.close();
262         }
263 
264         if( listFile.open( QIODevice::WriteOnly ) )
265         {
266             updateProfiles();
267             emit customProfilesEdited();
268 
269             QTextStream stream(&listFile);
270             stream << list.toString();
271             listFile.close();
272         }
273     }
274 }
275 
formatInfo()276 void OptionsSimple::formatInfo()
277 {
278     const QString format = cFormat->currentText();
279     const QString info = config->pluginLoader()->codecDescription(format);
280 
281     if( !info.isEmpty() )
282     {
283         KMessageBox::information( this, info, i18n("Format info for %1",format), QString(), KMessageBox::Notify | KMessageBox::AllowLink );
284     }
285     else
286     {
287         KMessageBox::information( this, i18n("Sorry, no format information available.") );
288     }
289 }
290 
profileChanged()291 void OptionsSimple::profileChanged()
292 {
293     const QString profile = cProfile->currentText();
294     const QString lastFormat = cFormat->currentText();
295     cFormat->clear();
296 
297     pProfileRemove->hide();
298     pProfileInfo->show();
299 
300     if( profile == i18n("Very low") || profile == i18n("Low") || profile == i18n("Medium") || profile == i18n("High") || profile == i18n("Very high") )
301     {
302         cFormat->addItems( config->pluginLoader()->formatList(PluginLoader::Encode,PluginLoader::Lossy) );
303     }
304     else if( profile == i18n("Lossless") )
305     {
306         cFormat->addItems( config->pluginLoader()->formatList(PluginLoader::Encode,PluginLoader::Lossless) );
307     }
308     else if( profile == i18n("Hybrid") )
309     {
310         cFormat->addItems( config->pluginLoader()->formatList(PluginLoader::Encode,PluginLoader::Hybrid) );
311     }
312     else if( profile == i18n("User defined") )
313     {
314         cFormat->addItems( config->pluginLoader()->formatList(PluginLoader::Encode,PluginLoader::CompressionType(PluginLoader::InferiorQuality|PluginLoader::Lossy|PluginLoader::Lossless|PluginLoader::Hybrid)) );
315     }
316     else
317     {
318         foreach( const QString& profileName, config->data.profiles.keys() )
319         {
320             if( profileName == profile )
321             {
322                 const ConversionOptions *conversionOptions = config->data.profiles.value( profileName );
323                 if( conversionOptions )
324                 {
325                     cFormat->addItem( conversionOptions->codecName );
326                     outputDirectory->setMode( (OutputDirectory::Mode)conversionOptions->outputDirectoryMode );
327                     outputDirectory->setDirectory( conversionOptions->outputDirectory );
328                     cReplayGain->setChecked( conversionOptions->replaygain );
329                     pProfileRemove->show();
330                     pProfileInfo->hide();
331                 }
332                 break;
333             }
334         }
335     }
336 
337     if( cFormat->findText(lastFormat) != -1 )
338     {
339         cFormat->setCurrentIndex( cFormat->findText(lastFormat) );
340     }
341 
342     somethingChanged();
343 }
344 
345 // void OptionsSimple::formatChanged()
346 // {
347 //     QStringList errorList;
348 //     cReplayGain->setEnabled( config->pluginLoader()->canReplayGain(cFormat->currentText(),currentPlugin,&errorList) );
349 //     if( !cReplayGain->isEnabled() )
350 //     {
351 //         if( !errorList.isEmpty() )
352 //         {
353 //             errorList.prepend( i18n("Replay Gain is not supported for the %1 file format.\nPossible solutions are listed below.",cFormat->currentText()) );
354 //         }
355 //         else
356 //         {
357 //             errorList += i18n("Replay Gain is not supported for the %1 file format.\nPlease check your distribution's package manager in order to install an additional Replay Gain plugin.",cFormat->currentText());
358 //         }
359 //         cReplayGain->setToolTip( errorList.join("\n\n") );
360 //     }
361 //     else
362 //     {
363 //         cReplayGain->setToolTip( "" );
364 //     }
365 // }
366 
outputDirectoryChanged()367 void OptionsSimple::outputDirectoryChanged()
368 {
369     const QString profileName = cProfile->currentText();
370     const ConversionOptions *conversionOptions = config->data.profiles.value( profileName );
371     if( conversionOptions )
372     {
373         if( conversionOptions->outputDirectoryMode != outputDirectory->mode() || conversionOptions->outputDirectory != outputDirectory->directory() )
374         {
375             cProfile->setCurrentIndex( cProfile->findText(i18n("User defined")) );
376             profileChanged();
377         }
378     }
379 }
380 
somethingChanged()381 void OptionsSimple::somethingChanged()
382 {
383     emit optionsChanged();
384 }
385 
currentDataRateChanged(int dataRate)386 void OptionsSimple::currentDataRateChanged( int dataRate )
387 {
388     if( dataRate > 0 )
389     {
390         const QString dataRateString = Global::prettyNumber(dataRate,"B");
391         lEstimSize->setText( QString(QChar(8776))+" "+dataRateString+" / min." );
392         lEstimSize->setToolTip( i18n("Using the current conversion options will create files with approximately %1 per minute.").arg(dataRateString) );
393     }
394     else
395     {
396         lEstimSize->setText( QString(QChar(8776))+" ? B / min." );
397         lEstimSize->setToolTip( "" );
398     }
399 }
400 
setCurrentProfile(const QString & profile)401 void OptionsSimple::setCurrentProfile( const QString& profile )
402 {
403     // TODO check profile (and don't change, if not available)
404     cProfile->setCurrentIndex( cProfile->findText(profile) );
405     profileChanged();
406 }
407 
setCurrentFormat(const QString & format)408 void OptionsSimple::setCurrentFormat( const QString& format )
409 {
410     cFormat->setCurrentIndex( cFormat->findText(format) );
411 //     formatChanged();
412 }
413 
setCurrentOutputDirectory(const QString & directory)414 void OptionsSimple::setCurrentOutputDirectory( const QString& directory )
415 {
416     outputDirectory->setDirectory( directory );
417     outputDirectoryChanged();
418 }
419 
setCurrentOutputDirectoryMode(int mode)420 void OptionsSimple::setCurrentOutputDirectoryMode( int mode )
421 {
422     outputDirectory->setMode( (OutputDirectory::Mode)mode );
423     outputDirectoryChanged();
424 }
425 
showHelp()426 void OptionsSimple::showHelp()
427 {
428     QList<CodecProblems::Problem> problemList;
429     QMap<QString,QStringList> problems = config->pluginLoader()->encodeProblems();
430     for( int i=0; i<problems.count(); i++ )
431     {
432         CodecProblems::Problem problem;
433         problem.codecName = problems.keys().at(i);
434         if( problem.codecName != "wav" )
435         {
436             problem.solutions = problems.value(problem.codecName);
437             problemList += problem;
438         }
439     }
440     CodecProblems *problemsDialog = new CodecProblems( CodecProblems::Debug, problemList, this );
441     problemsDialog->exec();
442 }
443