Warning, file /multimedia/amarok/src/widgets/FilenameLayoutWidget.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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 "FilenameLayoutWidget.h" 0021 #include "TokenDropTarget.h" 0022 #include "TokenPool.h" 0023 0024 #include "amarokconfig.h" 0025 0026 #include "MetaValues.h" 0027 #include "core/support/Amarok.h" 0028 #include "core/support/Debug.h" 0029 #include "core/meta/support/MetaConstants.h" 0030 0031 #include <QInputDialog> 0032 #include <QLineEdit> 0033 #include <KLocalizedString> 0034 0035 #include <QComboBox> 0036 #include <QGroupBox> 0037 #include <QLabel> 0038 #include <QPushButton> 0039 #include <QBoxLayout> 0040 #include <QStackedWidget> 0041 0042 // the order of these strings depends on the order of the 0043 // Type enum. 0044 static const QStringList typeElements = ( QStringList() 0045 << QString() 0046 << QLatin1String("%ignore%") 0047 << QLatin1String("%track%") 0048 << QLatin1String("%title%") 0049 << QLatin1String("%artist%") 0050 << QLatin1String("%composer%") 0051 << QLatin1String("%year%") 0052 << QLatin1String("%album%") 0053 << QLatin1String("%albumartist%") 0054 << QLatin1String("%comment%") 0055 << QLatin1String("%genre%") 0056 << QLatin1String("%filetype%") 0057 << QLatin1String("%folder%") 0058 << QLatin1String("%initial%") 0059 << QLatin1String("%discnumber%") 0060 << QLatin1String(" ") 0061 << QLatin1String("/") 0062 << QLatin1String(".") 0063 << QLatin1String("-") 0064 << QLatin1String("_") 0065 << QLatin1String("%collectionroot%") ); 0066 0067 using namespace Meta; 0068 0069 // ------------------------- FilenameLayoutWidget ------------------- 0070 0071 FilenameLayoutWidget::FilenameLayoutWidget( QWidget *parent ) 0072 : QWidget( parent ) 0073 , m_advancedMode( false ) 0074 { 0075 m_mainLayout = new QVBoxLayout( this ); 0076 m_mainLayout->setContentsMargins( 0, 0, 0, 0 ); 0077 0078 QGroupBox* schemeGroup = new QGroupBox( i18n("Scheme"), this ); 0079 QVBoxLayout* schemeGroupLayout = new QVBoxLayout( schemeGroup ); 0080 0081 // --- presets 0082 QHBoxLayout* presetLayout1 = new QHBoxLayout(); 0083 0084 QLabel* presetLabel = new QLabel( i18n("Preset:"), this ); 0085 presetLayout1->addWidget( presetLabel, 0 ); 0086 0087 m_presetCombo = new QComboBox( this ); 0088 m_presetCombo->setWhatsThis( i18n("A list of selectable filename scheme/format presets." ) ); 0089 presetLayout1->addWidget( m_presetCombo, 1 ); 0090 0091 // - the preset buttons 0092 m_addPresetButton = new QPushButton( i18n("Add preset"), this ); 0093 m_addPresetButton->setToolTip( i18n("Saves the current scheme/format as new preset.") ); 0094 presetLayout1->addWidget( m_addPresetButton, 0 ); 0095 0096 m_updatePresetButton = new QPushButton( i18n("Update preset"), this ); 0097 m_updatePresetButton->setToolTip( i18n("Updates the preset with the current scheme/format.") ); 0098 presetLayout1->addWidget( m_updatePresetButton, 0 ); 0099 0100 m_removePresetButton = new QPushButton( i18n("Remove preset"), this ); 0101 m_removePresetButton->setToolTip( i18n("Removes the currently selected preset.") ); 0102 presetLayout1->addWidget( m_removePresetButton, 0 ); 0103 0104 schemeGroupLayout->addLayout( presetLayout1 ); 0105 0106 // --- stacked widget 0107 m_schemeStack = new QStackedWidget( this ); 0108 0109 // -- simple schema 0110 QWidget* simpleLayoutWidget = new QWidget( this ); 0111 QVBoxLayout *simpleLayout = new QVBoxLayout( simpleLayoutWidget ); 0112 0113 // a token pool 0114 m_tokenPool = new TokenPool( this ); 0115 simpleLayout->addWidget( m_tokenPool, 1 ); 0116 0117 // token drop target inside a frame 0118 QFrame* dropTargetFrame = new QFrame( this ); 0119 dropTargetFrame->setFrameShape(QFrame::StyledPanel); 0120 dropTargetFrame->setFrameShadow(QFrame::Sunken); 0121 m_dropTarget = new TokenDropTarget( this ); 0122 m_dropTarget->setRowLimit( 1 ); 0123 0124 m_schemaLineLayout = new QHBoxLayout(); 0125 m_schemaLineLayout->setSpacing( 0 ); 0126 m_schemaLineLayout->setContentsMargins( 0, 0, 0, 0 ); 0127 m_schemaLineLayout->addWidget( m_dropTarget ); 0128 dropTargetFrame->setLayout( m_schemaLineLayout ); 0129 simpleLayout->addWidget( dropTargetFrame, 0 ); 0130 0131 m_schemeStack->addWidget( simpleLayoutWidget ); 0132 0133 // -- advanced schema 0134 QWidget* advancedLayoutWidget = new QWidget( this ); 0135 QVBoxLayout *advancedLayout = new QVBoxLayout( advancedLayoutWidget ); 0136 0137 m_syntaxLabel = new QLabel( this ); // placeholder for format description 0138 advancedLayout->addWidget( m_syntaxLabel ); 0139 0140 m_filenameLayoutEdit = new QLineEdit( this ); 0141 advancedLayout->addWidget( m_filenameLayoutEdit ); 0142 0143 m_schemeStack->addWidget( advancedLayoutWidget ); 0144 0145 schemeGroupLayout->addWidget( m_schemeStack ); 0146 0147 m_advancedButton = new QPushButton( i18n("Advanced"), this ); 0148 schemeGroupLayout->addWidget( m_advancedButton ); 0149 0150 // -- 0151 0152 m_mainLayout->addWidget( schemeGroup ); 0153 0154 connect( m_tokenPool, &TokenPool::onDoubleClick, 0155 m_dropTarget, &TokenDropTarget::appendToken ); 0156 connect( m_advancedButton, &QAbstractButton::clicked, 0157 this, &FilenameLayoutWidget::toggleAdvancedMode ); 0158 connect( m_dropTarget, &TokenDropTarget::changed, 0159 this, &FilenameLayoutWidget::schemeChanged ); 0160 connect( m_dropTarget, &TokenDropTarget::changed, 0161 this, &FilenameLayoutWidget::slotUpdatePresetButton ); 0162 connect( m_addPresetButton, &QPushButton::clicked, 0163 this, &FilenameLayoutWidget::slotAddFormat ); 0164 connect( m_removePresetButton, &QPushButton::clicked, 0165 this, &FilenameLayoutWidget::slotRemoveFormat ); 0166 connect( m_updatePresetButton, &QPushButton::clicked, 0167 this, &FilenameLayoutWidget::slotUpdateFormat ); 0168 0169 connect( m_filenameLayoutEdit, &QLineEdit::textChanged, 0170 this, &FilenameLayoutWidget::schemeChanged ); 0171 connect( m_filenameLayoutEdit, &QLineEdit::textChanged, 0172 this, &FilenameLayoutWidget::slotUpdatePresetButton ); 0173 } 0174 0175 Token* 0176 FilenameLayoutWidget::createToken(qint64 value) const 0177 { 0178 struct TokenDefinition 0179 { 0180 QString name; 0181 QString iconName; 0182 Type value; 0183 }; 0184 0185 static const TokenDefinition tokenDefinitions[] = { 0186 { i18nForField( valTrackNr ), iconForField( valTrackNr ), TrackNumber }, 0187 { i18nForField( valDiscNr ), iconForField( valDiscNr ), DiscNumber }, 0188 { i18nForField( valTitle ), iconForField( valTitle ), Title }, 0189 { i18nForField( valArtist ), iconForField( valArtist ), Artist }, 0190 { i18nForField( valComposer ), iconForField( valComposer ), Composer }, 0191 { i18nForField( valYear ), iconForField( valYear ), Year }, 0192 { i18nForField( valAlbum ), iconForField( valAlbum ), Album }, 0193 { i18nForField( valAlbumArtist ), iconForField( valAlbumArtist ), AlbumArtist }, 0194 { i18nForField( valComment ), iconForField( valComment ), Comment }, 0195 { i18nForField( valGenre ), iconForField( valGenre ), Genre }, 0196 { i18nForField( valFormat ), iconForField( valFormat ), FileType }, 0197 0198 { i18n( "Ignore" ), "filename-ignore-amarok", Ignore }, 0199 { i18n( "Folder" ), "filename-folder-amarok", Folder }, 0200 { i18nc( "Artist's Initial", "Initial" ), "filename-initial-amarok", Initial }, 0201 0202 { "/", "filename-slash-amarok", Slash }, 0203 { "_", "filename-underscore-amarok", Underscore }, 0204 { "-", "filename-dash-amarok", Dash }, 0205 { ".", "filename-dot-amarok", Dot }, 0206 { " ", "filename-space-amarok", Space }, 0207 { i18n( "Collection root" ), "drive-harddisk", CollectionRoot }, 0208 { QString(), nullptr, Space } 0209 }; 0210 0211 for( int i = 0; !tokenDefinitions[i].name.isNull(); ++i ) 0212 { 0213 if( value == tokenDefinitions[i].value ) 0214 { 0215 return new Token( tokenDefinitions[i].name, 0216 tokenDefinitions[i].iconName, 0217 static_cast<qint64>(tokenDefinitions[i].value) ); 0218 } 0219 } 0220 0221 return nullptr; 0222 } 0223 0224 Token* 0225 FilenameLayoutWidget::createStaticToken(qint64 value) const 0226 { 0227 Token* token = createToken( value ); 0228 token->setEnabled( false ); 0229 token->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred ); 0230 0231 return token; 0232 } 0233 0234 //Stores the configuration when the dialog is accepted. 0235 void 0236 FilenameLayoutWidget::onAccept() //SLOT 0237 { 0238 QString custom = getParsableScheme(); 0239 0240 // Custom scheme is stored per dialog 0241 debug() << "--- saving custom scheme for" << m_configCategory << custom; 0242 Amarok::config( m_configCategory ).writeEntry( "Custom Scheme", custom ); 0243 } 0244 0245 QString 0246 FilenameLayoutWidget::getParsableScheme() const 0247 { 0248 return m_advancedMode ? m_filenameLayoutEdit->text() : dropTargetScheme(); 0249 } 0250 0251 // attempts to set the scheme 0252 void FilenameLayoutWidget::setScheme(const QString& scheme) 0253 { 0254 m_filenameLayoutEdit->setText( scheme ); 0255 inferScheme( scheme ); 0256 0257 slotUpdatePresetButton(); 0258 Q_EMIT schemeChanged(); 0259 } 0260 0261 //Handles the modifications to the dialog to toggle between advanced and basic editing mode. 0262 void 0263 FilenameLayoutWidget::toggleAdvancedMode() 0264 { 0265 setAdvancedMode( !m_advancedMode ); 0266 } 0267 0268 //handles switching between basic and advanced mode 0269 void 0270 FilenameLayoutWidget::setAdvancedMode( bool isAdvanced ) 0271 { 0272 setScheme( getParsableScheme() ); // setScheme set's both the edit and the drop target 0273 m_advancedMode = isAdvanced; 0274 0275 if( isAdvanced ) 0276 { 0277 m_advancedButton->setText( i18n( "&Basic..." ) ); 0278 m_schemeStack->setCurrentIndex( 1 ); 0279 } 0280 else // set Basic mode 0281 { 0282 m_advancedButton->setText( i18n( "&Advanced..." ) ); 0283 m_schemeStack->setCurrentIndex( 0 ); 0284 } 0285 0286 QString entryValue = m_advancedMode ? "Advanced" : "Basic"; 0287 0288 Amarok::config( m_configCategory ).writeEntry( "Mode", entryValue ); 0289 } 0290 0291 QString 0292 FilenameLayoutWidget::dropTargetScheme() const 0293 { 0294 QString parsableScheme = ""; 0295 0296 QList< Token *> list = m_dropTarget->tokensAtRow(); 0297 0298 foreach( Token *token, list ) 0299 { 0300 parsableScheme += typeElements[token->value()]; 0301 } 0302 0303 return parsableScheme; 0304 } 0305 0306 void 0307 FilenameLayoutWidget::inferScheme( const QString &s ) //SLOT 0308 { 0309 DEBUG_BLOCK 0310 0311 debug() << "inferring scheme: " << s; 0312 0313 m_dropTarget->clear(); 0314 for( int i = 0; i < s.size(); ) 0315 { 0316 // - search if there is a type with the matching string 0317 // representation. 0318 bool found = false; 0319 for( int j = 1; j < typeElements.size() && !found; j++ ) 0320 { 0321 int typeNameLength = typeElements[j].length(); 0322 Type type = static_cast<Type>(j); 0323 if( s.midRef( i, typeNameLength ) == typeElements[j] ) 0324 { 0325 m_dropTarget->appendToken( createToken( type ) ); 0326 i += typeNameLength; 0327 found = true; 0328 } 0329 } 0330 0331 if( !found ) 0332 { 0333 debug() << "'" << s.at(i) << "' can't be represented as TokenLayoutWidget Token"; 0334 ++i; // skip junk 0335 } 0336 } 0337 } 0338 0339 void 0340 FilenameLayoutWidget::populateConfiguration() 0341 { 0342 QString mode = Amarok::config( m_configCategory ).readEntry( "Mode" ); 0343 setAdvancedMode( mode == QLatin1String( "Advanced" ) ); 0344 0345 // Custom scheme is stored per dialog 0346 QString custom = Amarok::config( m_configCategory ).readEntryUntranslated( "Custom Scheme" ); 0347 debug() << "--- got custom scheme for" << m_configCategory << custom; 0348 0349 populateFormatList( custom ); 0350 0351 setScheme( custom ); 0352 } 0353 0354 void 0355 FilenameLayoutWidget::populateFormatList( const QString& custom ) 0356 { 0357 DEBUG_BLOCK 0358 0359 // Configuration is not symmetric: dialog-specific settings are saved 0360 // using m_configCategory, that is different per dialog. The presets are saved 0361 // only in one single place, so these can be shared. This place is the "default" one, 0362 // that is the configuration for OrganizeCollectionDialog. 0363 0364 // items are stored in the config list in the following format: 0365 // Label#DELIM#format string 0366 QStringList presets_raw; 0367 int selected_index = -1; 0368 m_presetCombo->clear(); 0369 presets_raw = AmarokConfig::formatPresets(); // Always use the one in OrganizeCollectionDialog 0370 // presets_raw = Amarok::config( m_configCategory ).readEntry( QString::fromLatin1( "Format Presets" ), QStringList() ); 0371 0372 debug() << "--- got presets" << presets_raw; 0373 0374 foreach( const QString &str, presets_raw ) 0375 { 0376 QStringList items; 0377 items = str.split( "#DELIM#", Qt::SkipEmptyParts ); 0378 if( items.size() < 2 ) 0379 continue; 0380 m_presetCombo->addItem( items.at( 0 ), items.at( 1 ) ); // Label, format string 0381 if( items.at( 1 ) == custom ) 0382 selected_index = m_presetCombo->findData( items.at( 1 ) ); 0383 } 0384 0385 if( selected_index >= 0 ) 0386 m_presetCombo->setCurrentIndex( selected_index ); 0387 0388 connect( m_presetCombo, QOverload<int>::of(&QComboBox::activated), 0389 this, &FilenameLayoutWidget::slotFormatPresetSelected ); 0390 connect( m_presetCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), 0391 this, &FilenameLayoutWidget::slotFormatPresetSelected ); 0392 } 0393 0394 void 0395 FilenameLayoutWidget::saveFormatList() const 0396 { 0397 DEBUG_BLOCK 0398 0399 QStringList presets_raw; 0400 int n = m_presetCombo->count(); 0401 0402 for( int i = 0; i < n; ++i ) 0403 { 0404 QString item = "%1#DELIM#%2"; 0405 QString scheme = m_presetCombo->itemData( i ).toString(); 0406 QString label = m_presetCombo->itemText( i ); 0407 item = item.arg( label, scheme ); 0408 presets_raw.append( item ); 0409 } 0410 0411 debug() << "--- saving presets" << presets_raw; 0412 AmarokConfig::setFormatPresets( presets_raw ); // Always use the one in OrganizeCollectionDialog 0413 // Amarok::config( m_configCategory ).writeEntry( QString::fromLatin1( "Format Presets" ), presets_raw ); 0414 } 0415 0416 void 0417 FilenameLayoutWidget::slotUpdatePresetButton() 0418 { 0419 QString comboScheme = m_presetCombo->itemData( m_presetCombo->currentIndex() ).toString(); 0420 m_updatePresetButton->setEnabled( comboScheme != getParsableScheme() ); 0421 } 0422 0423 void 0424 FilenameLayoutWidget::slotFormatPresetSelected( int index ) 0425 { 0426 QString scheme = m_presetCombo->itemData( index ).toString(); 0427 setScheme( scheme ); 0428 } 0429 0430 void 0431 FilenameLayoutWidget::slotAddFormat() 0432 { 0433 bool ok = false; 0434 QString name = QInputDialog::getText( this, i18n( "New Preset" ), i18n( "Preset Name" ), QLineEdit::Normal, i18n( "New Preset" ), &ok ); 0435 if( !ok ) 0436 return; // user canceled. 0437 0438 QString format = getParsableScheme(); 0439 m_presetCombo->addItem( name, format ); 0440 m_presetCombo->setCurrentIndex( m_presetCombo->count() - 1 ); 0441 0442 saveFormatList(); 0443 } 0444 0445 void 0446 FilenameLayoutWidget::slotRemoveFormat() 0447 { 0448 int idx = m_presetCombo->currentIndex(); 0449 m_presetCombo->removeItem( idx ); 0450 0451 saveFormatList(); 0452 } 0453 0454 void 0455 FilenameLayoutWidget::slotUpdateFormat() 0456 { 0457 int idx = m_presetCombo->currentIndex(); 0458 QString formatString = getParsableScheme(); 0459 m_presetCombo->setItemData( idx, formatString ); 0460 m_updatePresetButton->setEnabled( false ); 0461 0462 saveFormatList(); 0463 }