File indexing completed on 2024-05-05 04:48:27
0001 /**************************************************************************************** 0002 * Copyright (c) 2008 Téo Mrnjavac <teo@kde.org> * 0003 * Copyright (c) 2009 Nikolaj Hald Nielsen <nhn@kde.org> * 0004 * Copyright (c) 2009 Daniel Dewald <Daniel.Dewald@time.shift.de> * 0005 * Copyright (c) 2012 Ralf Engels <ralf-engels@gmx.de> * 0006 * * 0007 * This program is free software; you can redistribute it and/or modify it under * 0008 * the terms of the GNU General Public License as published by the Free Software * 0009 * Foundation; either version 2 of the License, or (at your option) any later * 0010 * version. * 0011 * * 0012 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0013 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0014 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0015 * * 0016 * You should have received a copy of the GNU General Public License along with * 0017 * this program. If not, see <http://www.gnu.org/licenses/>. * 0018 ****************************************************************************************/ 0019 0020 #include "TagGuesserDialog.h" 0021 #include "TagGuesser.h" 0022 0023 #include "../widgets/TokenPool.h" 0024 0025 #include "core/support/Amarok.h" 0026 #include "core/support/Debug.h" 0027 0028 #include "MetaValues.h" 0029 #include "TagsFromFileNameGuesser.h" 0030 0031 #include <QBoxLayout> 0032 #include <KConfigGroup> 0033 #include <QDialogButtonBox> 0034 #include <QPushButton> 0035 #include <QVBoxLayout> 0036 0037 #define album_color Qt::red 0038 #define albumartist_color Qt::blue 0039 #define artist_color Qt::blue 0040 #define comment_color Qt::gray 0041 #define composer_color Qt::magenta 0042 #define genre_color Qt::cyan 0043 #define title_color Qt::green 0044 #define track_color Qt::yellow 0045 #define discnr_color Qt::yellow 0046 #define year_color Qt::darkRed 0047 0048 0049 // -------------- TagGuessOptionWidget ------------ 0050 TagGuessOptionWidget::TagGuessOptionWidget( QWidget *parent ) 0051 : QWidget( parent ) 0052 { 0053 setupUi( this ); 0054 0055 m_caseEditRadioButtons << rbAllUpper 0056 << rbAllLower 0057 << rbFirstLetter 0058 << rbTitleCase; 0059 0060 int caseOptions = Amarok::config( "TagGuesser" ).readEntry( "Case options", 4 ); 0061 if( !caseOptions ) 0062 cbCase->setChecked( false ); 0063 else 0064 { 0065 cbCase->setChecked( true ); 0066 switch( caseOptions ) 0067 { 0068 case 4: 0069 rbAllLower->setChecked( true ); 0070 break; 0071 case 3: 0072 rbAllUpper->setChecked( true ); 0073 break; 0074 case 2: 0075 rbFirstLetter->setChecked( true ); 0076 break; 0077 case 1: 0078 rbTitleCase->setChecked( true ); 0079 break; 0080 default: 0081 debug() << "OUCH"; 0082 } 0083 } 0084 0085 cbEliminateSpaces->setChecked( Amarok::config( "TagGuesser" ).readEntry( "Eliminate trailing spaces", false ) ); 0086 cbReplaceUnderscores->setChecked( Amarok::config( "TagGuesser" ).readEntry( "Replace underscores", false ) ); 0087 0088 connect( cbCase, &QCheckBox::toggled, 0089 this, &TagGuessOptionWidget::editStateEnable ); 0090 connect( cbCase, &QCheckBox::toggled, 0091 this, &TagGuessOptionWidget::changed ); 0092 connect( rbTitleCase, &QCheckBox::toggled, 0093 this, &TagGuessOptionWidget::changed ); 0094 connect( rbFirstLetter, &QCheckBox::toggled, 0095 this, &TagGuessOptionWidget::changed ); 0096 connect( rbAllLower, &QCheckBox::toggled, 0097 this, &TagGuessOptionWidget::changed ); 0098 connect( rbAllUpper, &QCheckBox::toggled, 0099 this, &TagGuessOptionWidget::changed ); 0100 connect( cbEliminateSpaces, &QCheckBox::toggled, 0101 this, &TagGuessOptionWidget::changed ); 0102 connect( cbReplaceUnderscores, &QCheckBox::toggled, 0103 this, &TagGuessOptionWidget::changed ); 0104 } 0105 0106 void 0107 TagGuessOptionWidget::editStateEnable( bool checked ) //SLOT 0108 { 0109 foreach( QRadioButton *rb, m_caseEditRadioButtons ) 0110 rb->setEnabled( checked ); 0111 } 0112 0113 //Returns a code for the configuration. 0114 int 0115 TagGuessOptionWidget::getCaseOptions() 0116 { 0117 //Amarok::config( "TagGuesser" ).readEntry( "Filename schemes", QStringList() ); 0118 if( !cbCase->isChecked() ) 0119 return 0; 0120 else 0121 { 0122 if( rbAllLower->isChecked() ) 0123 return 4; 0124 else if( rbAllUpper->isChecked() ) 0125 return 3; 0126 else if( rbFirstLetter->isChecked() ) 0127 return 2; 0128 else if( rbTitleCase->isChecked() ) 0129 return 1; 0130 else 0131 { 0132 debug() << "OUCH!"; 0133 return 0; 0134 } 0135 } 0136 } 0137 0138 //As above 0139 bool 0140 TagGuessOptionWidget::getWhitespaceOptions() 0141 { 0142 return cbEliminateSpaces->isChecked(); 0143 } 0144 0145 //As above 0146 bool 0147 TagGuessOptionWidget::getUnderscoreOptions() 0148 { 0149 return cbReplaceUnderscores->isChecked(); 0150 } 0151 0152 0153 // ------------------------- TagGuesserWidget ------------------- 0154 0155 TagGuesserWidget::TagGuesserWidget( QWidget *parent ) 0156 : FilenameLayoutWidget( parent ) 0157 { 0158 m_configCategory = "FilenameLayoutWidget"; 0159 0160 m_tokenPool->addToken( createToken( Title ) ); 0161 m_tokenPool->addToken( createToken( Artist ) ); 0162 m_tokenPool->addToken( createToken( AlbumArtist ) ); 0163 m_tokenPool->addToken( createToken( Album ) ); 0164 m_tokenPool->addToken( createToken( Genre ) ); 0165 m_tokenPool->addToken( createToken( Composer ) ); 0166 m_tokenPool->addToken( createToken( Comment ) ); 0167 m_tokenPool->addToken( createToken( Year ) ); 0168 m_tokenPool->addToken( createToken( TrackNumber ) ); 0169 m_tokenPool->addToken( createToken( DiscNumber ) ); 0170 m_tokenPool->addToken( createToken( Ignore ) ); 0171 0172 m_tokenPool->addToken( createToken( Slash ) ); 0173 m_tokenPool->addToken( createToken( Underscore ) ); 0174 m_tokenPool->addToken( createToken( Dash ) ); 0175 m_tokenPool->addToken( createToken( Dot ) ); 0176 m_tokenPool->addToken( createToken( Space ) ); 0177 0178 m_syntaxLabel->setText( i18nc("Please do not translate the %foo% words as they define a syntax used internally by a parser to describe a filename.", 0179 // xgettext: no-c-format 0180 "The following tokens can be used to define a filename scheme:<br> \ 0181 <font color=\"%1\">%track%</font>, <font color=\"%2\">%title%</font>, \ 0182 <font color=\"%3\">%artist%</font>, <font color=\"%4\">%composer%</font>, \ 0183 <font color=\"%5\">%year%</font>, <font color=\"%6\">%album%</font>, \ 0184 <font color=\"%7\">%albumartist%</font>, <font color=\"%8\">%comment%</font>, \ 0185 <font color=\"%9\">%genre%</font>, %ignore%." 0186 , QColor( track_color ).name(), QColor( title_color ).name(), QColor( artist_color ).name(), \ 0187 QColor( composer_color ).name(), QColor( year_color ).name(), QColor( album_color ).name(), QColor( albumartist_color ).name(), \ 0188 QColor( comment_color ).name(), QColor( genre_color ).name() ) ); 0189 0190 populateConfiguration(); 0191 } 0192 0193 Token* 0194 TagGuesserWidget::createToken(qint64 value) const 0195 { 0196 Token* token = FilenameLayoutWidget::createToken( value ); 0197 0198 // return colored tokens. 0199 QColor color = Qt::transparent; 0200 switch( value ) 0201 { 0202 case TrackNumber: color = QColor( track_color ); break; 0203 case Title: color = QColor( title_color ); break; 0204 case Artist: color = QColor( artist_color ); break; 0205 case Composer: color = QColor( composer_color ); break; 0206 case Year: color = QColor( year_color ); break; 0207 case Album: color = QColor( album_color ); break; 0208 case AlbumArtist: color = QColor( albumartist_color ); break; 0209 case Comment: color = QColor( comment_color ); break; 0210 case Genre: color = QColor( genre_color ); 0211 } 0212 if (color != Qt::transparent) 0213 token->setTextColor( color ); 0214 0215 return token; 0216 } 0217 0218 // -------------- TagGuesserDialog ------------ 0219 TagGuesserDialog::TagGuesserDialog( const QString &fileName, QWidget *parent ) 0220 : QDialog( parent ) 0221 , m_fileName( fileName ) 0222 { 0223 setWindowTitle( i18n( "Guess Tags from Filename" ) ); 0224 0225 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, this); 0226 QWidget* mainWidget = new QWidget( this ); 0227 QBoxLayout* mainLayout = new QVBoxLayout(this); 0228 QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); 0229 okButton->setDefault(true); 0230 okButton->setShortcut(Qt::CTRL | Qt::Key_Return); 0231 connect(buttonBox, &QDialogButtonBox::accepted, this, &TagGuesserDialog::accept); 0232 connect(buttonBox, &QDialogButtonBox::rejected, this, &TagGuesserDialog::reject); 0233 0234 m_layoutWidget = new TagGuesserWidget( this ); 0235 mainLayout->addWidget( m_layoutWidget ); 0236 0237 m_filenamePreview = new QLabel(); 0238 m_filenamePreview->setAlignment( Qt::AlignHCenter ); 0239 mainLayout->addWidget( m_filenamePreview ); 0240 0241 m_optionsWidget = new TagGuessOptionWidget(); 0242 mainLayout->addWidget( m_optionsWidget ); 0243 0244 connect( m_layoutWidget, &TagGuesserWidget::schemeChanged, 0245 this, &TagGuesserDialog::updatePreview ); 0246 connect( m_optionsWidget, &TagGuessOptionWidget::changed, 0247 this, &TagGuesserDialog::updatePreview ); 0248 0249 updatePreview(); 0250 0251 mainLayout->addWidget(mainWidget); 0252 mainLayout->addWidget(buttonBox); 0253 } 0254 0255 //Sets Filename for Preview 0256 void 0257 TagGuesserDialog::setFileName( const QString& fileName ) 0258 { 0259 m_fileName = fileName; 0260 updatePreview(); 0261 } 0262 0263 //Stores the configuration when the dialog is accepted. 0264 void 0265 TagGuesserDialog::onAccept() //SLOT 0266 { 0267 m_layoutWidget->onAccept(); 0268 0269 Amarok::config( "TagGuesser" ).writeEntry( "Case options", m_optionsWidget->getCaseOptions() ); 0270 Amarok::config( "TagGuesser" ).writeEntry( "Eliminate trailing spaces", m_optionsWidget->getWhitespaceOptions() ); 0271 Amarok::config( "TagGuesser" ).writeEntry( "Replace underscores", m_optionsWidget->getUnderscoreOptions() ); 0272 } 0273 0274 QMap<qint64,QString> 0275 TagGuesserDialog::guessedTags() 0276 { 0277 DEBUG_BLOCK; 0278 0279 QString scheme = m_layoutWidget->getParsableScheme(); 0280 QString fileName = getParsableFileName(); 0281 0282 if( scheme.isEmpty() ) 0283 return QMap<qint64,QString>(); 0284 0285 TagGuesser guesser; 0286 guesser.setFilename( fileName ); 0287 guesser.setCaseType( m_optionsWidget->getCaseOptions() ); 0288 guesser.setConvertUnderscores( m_optionsWidget->getUnderscoreOptions() ); 0289 guesser.setCutTrailingSpaces( m_optionsWidget->getWhitespaceOptions() ); 0290 guesser.setSchema( scheme ); 0291 0292 if( !guesser.guess() ) 0293 { 0294 m_filenamePreview->setText( getParsableFileName() ); 0295 return QMap<qint64,QString>(); 0296 } 0297 0298 return guesser.tags(); 0299 } 0300 0301 0302 //Updates the Filename Preview 0303 void 0304 TagGuesserDialog::updatePreview() //SLOT 0305 { 0306 DEBUG_BLOCK; 0307 0308 QMap<qint64,QString> tags = guessedTags(); 0309 0310 m_filenamePreview->setText( coloredFileName( tags ) ); 0311 0312 QString emptyTagText = i18nc( "Text to represent an empty tag. Braces (<>) are only to clarify emptiness.", "<empty>" ); 0313 0314 quint64 fields[] = { 0315 Meta::valAlbum, 0316 Meta::valAlbumArtist, 0317 Meta::valTitle, 0318 Meta::valAlbum, 0319 Meta::valArtist, 0320 Meta::valComposer, 0321 Meta::valGenre, 0322 Meta::valComment, 0323 Meta::valTrackNr, 0324 Meta::valYear, 0325 0}; 0326 0327 QLabel *labels[] = { 0328 m_optionsWidget->Album_result, 0329 m_optionsWidget->AlbumArtist_result, 0330 m_optionsWidget->Title_result, 0331 m_optionsWidget->Album_result, 0332 m_optionsWidget->Artist_result, 0333 m_optionsWidget->Composer_result, 0334 m_optionsWidget->Genre_result, 0335 m_optionsWidget->Comment_result, 0336 m_optionsWidget->Track_result, 0337 m_optionsWidget->Year_result, 0338 nullptr}; 0339 0340 for( int i = 0; fields[i]; i++ ) 0341 { 0342 if( tags.contains( fields[i] ) ) 0343 labels[i]->setText( "<font color='" + TagGuesserDialog::fieldColor( fields[i] ) + "'>" + tags[ fields[i] ] + "</font>" ); 0344 else 0345 labels[i]->setText( emptyTagText ); 0346 } 0347 } 0348 0349 QString 0350 TagGuesserDialog::parsableFileName( const QFileInfo &fileInfo ) const 0351 { 0352 DEBUG_BLOCK; 0353 QString path = fileInfo.absoluteFilePath(); 0354 0355 debug() << m_layoutWidget->getParsableScheme() << "; " << path; 0356 0357 int schemaLevels = m_layoutWidget->getParsableScheme().count( QLatin1Char('/') ); 0358 int pathLevels = path.count( QLatin1Char('/') ); 0359 0360 // -- cut paths 0361 int pos; 0362 for( pos = 0; pathLevels > schemaLevels && pos < path.length(); pos++ ) 0363 if( path[pos] == '/' ) 0364 pathLevels--; 0365 0366 // -- cut extension 0367 int dotPos = path.lastIndexOf( QLatin1Char('.') ); 0368 if( dotPos >= 0 ) 0369 dotPos -= pos; 0370 0371 debug() << "parsableFileName schemaLevels:" << schemaLevels << "pathLevels:" << pathLevels << "path:" << path << "pos:" << pos << dotPos << path.mid( pos, dotPos ); 0372 return path.mid( pos, dotPos ); 0373 } 0374 0375 QString 0376 TagGuesserDialog::getParsableFileName() 0377 { 0378 return parsableFileName( QFileInfo( m_fileName ) ); 0379 } 0380 0381 // creates a colored version of the filename 0382 QString 0383 TagGuesserDialog::coloredFileName( const QMap<qint64,QString> &tags ) 0384 { 0385 QString coloredFileName = m_fileName; 0386 0387 foreach( qint64 key, tags.keys() ) 0388 { 0389 QString value = tags[key]; 0390 // TODO: replace is not the right way to do this. 0391 coloredFileName.replace( value, "<font color=\"" + fieldColor( key ) + 0392 "\">" + value + "</font>", Qt::CaseInsensitive ); 0393 } 0394 return coloredFileName; 0395 } 0396 0397 QString 0398 TagGuesserDialog::fieldColor( qint64 field ) 0399 { 0400 Qt::GlobalColor color; 0401 switch ( field ) 0402 { 0403 case Meta::valAlbum: 0404 color = album_color; 0405 break; 0406 0407 case Meta::valAlbumArtist: 0408 color = albumartist_color; 0409 break; 0410 0411 case Meta::valArtist: 0412 color = artist_color; 0413 break; 0414 0415 case Meta::valComment: 0416 color = comment_color; 0417 break; 0418 0419 case Meta::valComposer: 0420 color = composer_color; 0421 break; 0422 0423 case Meta::valDiscNr: 0424 color = discnr_color; 0425 break; 0426 0427 case Meta::valGenre: 0428 color = genre_color; 0429 break; 0430 0431 case Meta::valTitle: 0432 color = title_color; 0433 break; 0434 0435 case Meta::valTrackNr: 0436 color = track_color; 0437 break; 0438 0439 case Meta::valYear: 0440 color = year_color; 0441 break; 0442 0443 default: 0444 color = Qt::black; 0445 } 0446 0447 return QColor( color ).name(); 0448 }