File indexing completed on 2024-05-19 04:48:45

0001 /****************************************************************************************
0002  * Copyright (c) 2012 Matěj Laitl <matej@laitl.cz>                                      *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "MetadataConfig.h"
0018 
0019 #include "amarokconfig.h"
0020 #include "configdialog/ConfigDialog.h"
0021 #include "configdialog/dialogs/ExcludedLabelsDialog.h"
0022 #include "core/meta/support/MetaConstants.h"
0023 #include "core/support/Components.h"
0024 #include "core/support/Debug.h"
0025 #include "statsyncing/Config.h"
0026 #include "statsyncing/Controller.h"
0027 
0028 #include "MetaValues.h"
0029 
0030 MetadataConfig::MetadataConfig( Amarok2ConfigDialog *parent )
0031     : ConfigDialogBase( parent )
0032 {
0033     connect( this, &MetadataConfig::changed,
0034              parent, &Amarok2ConfigDialog::updateButtons );
0035 
0036     setupUi( this );
0037 
0038     m_writeBackCoverDimensions->addItem(
0039         i18nc("Maximum cover size option", "Small (200 px)" ), QVariant( 200 ) );
0040     m_writeBackCoverDimensions->addItem(
0041         i18nc("Maximum cover size option", "Medium (400 px)" ), QVariant( 400 ) );
0042     m_writeBackCoverDimensions->addItem(
0043         i18nc("Maximum cover size option", "Large (800 px)" ), QVariant( 800 ) );
0044     m_writeBackCoverDimensions->addItem(
0045         i18nc("Maximum cover size option", "Huge (1600 px)" ), QVariant( 1600 ) );
0046 
0047     m_writeBack->setChecked( AmarokConfig::writeBack() );
0048     m_writeBack->setVisible( false ); // probably not a usecase
0049     m_writeBackStatistics->setChecked( AmarokConfig::writeBackStatistics() );
0050     m_writeBackStatistics->setEnabled( m_writeBack->isChecked() );
0051     m_writeBackCover->setChecked( AmarokConfig::writeBackCover() );
0052     m_writeBackCover->setEnabled( m_writeBack->isChecked() );
0053     if( m_writeBackCoverDimensions->findData( AmarokConfig::writeBackCoverDimensions() ) != -1 )
0054         m_writeBackCoverDimensions->setCurrentIndex( m_writeBackCoverDimensions->findData( AmarokConfig::writeBackCoverDimensions() ) );
0055     else
0056         m_writeBackCoverDimensions->setCurrentIndex( 1 ); // medium
0057     m_writeBackCoverDimensions->setEnabled( m_writeBackCover->isEnabled() && m_writeBackCover->isChecked() );
0058     m_useCharsetDetector->setChecked( AmarokConfig::useCharsetDetector() );
0059     connect( m_writeBack, &QCheckBox::toggled, this, &MetadataConfig::changed );
0060     connect( m_writeBackStatistics, &QCheckBox::toggled, this, &MetadataConfig::changed );
0061     connect( m_writeBackCover, &QCheckBox::toggled, this, &MetadataConfig::changed );
0062     connect( m_writeBackCoverDimensions, QOverload<int>::of(&KComboBox::currentIndexChanged),
0063              this, &MetadataConfig::changed );
0064     connect( m_useCharsetDetector, &QCheckBox::toggled, this, &MetadataConfig::changed );
0065 
0066     StatSyncing::Controller *controller = Amarok::Components::statSyncingController();
0067     StatSyncing::Config *config = controller ? controller->config() : nullptr;
0068     m_statSyncingConfig = config;
0069     m_statSyncingProvidersView->setModel( config );
0070     m_synchronizeButton->setIcon( QIcon::fromTheme( QStringLiteral("amarok_playcount") ) );
0071     m_configureTargetButton->setIcon( QIcon::fromTheme( QStringLiteral("configure") ) );
0072     connect( config, &StatSyncing::Config::dataChanged, this, &MetadataConfig::changed );
0073     connect( config, &StatSyncing::Config::rowsInserted, this, &MetadataConfig::changed );
0074     connect( config, &StatSyncing::Config::rowsRemoved, this, &MetadataConfig::changed );
0075     connect( config, &StatSyncing::Config::modelReset, this, &MetadataConfig::changed );
0076 
0077     // Add target button
0078     m_addTargetButton->setEnabled( controller && controller->hasProviderFactories() );
0079     connect( m_addTargetButton, &QAbstractButton::clicked, this, &MetadataConfig::slotCreateProviderDialog );
0080 
0081     // Configure target button
0082     m_configureTargetButton->setEnabled( false );
0083     connect( m_statSyncingProvidersView->selectionModel(), &QItemSelectionModel::selectionChanged,
0084              this, &MetadataConfig::slotUpdateProviderConfigureButton );
0085     connect( m_configureTargetButton, &QAbstractButton::clicked, this, &MetadataConfig::slotConfigureProvider );
0086 
0087     // Forget target button
0088     connect( m_statSyncingProvidersView->selectionModel(), &QItemSelectionModel::selectionChanged,
0089              this, &MetadataConfig::slotUpdateForgetButton );
0090     connect( m_forgetTargetsButton, &QAbstractButton::clicked, this, &MetadataConfig::slotForgetCollections );
0091 
0092     // Synchronize button
0093     if( controller )
0094         connect( m_synchronizeButton, &QAbstractButton::clicked,
0095                  controller, &StatSyncing::Controller::synchronize );
0096     else
0097         m_synchronizeButton->setEnabled( false );
0098 
0099     slotUpdateForgetButton();
0100 
0101     const qint64 checkedFields = config ? config->checkedFields() : 0;
0102     QMap<qint64, QString> i18nSyncLabels; // to avoid word puzzles, bug 334561
0103     i18nSyncLabels.insert( Meta::valRating, i18nc( "Statistics sync checkbox label",
0104                                                    "Synchronize Ratings" ) );
0105     i18nSyncLabels.insert( Meta::valFirstPlayed, i18nc( "Statistics sync checkbox label",
0106                                                         "Synchronize First Played Times" ) );
0107     i18nSyncLabels.insert( Meta::valLastPlayed, i18nc( "Statistics sync checkbox label",
0108                                                        "Synchronize Last Played Times" ) );
0109     i18nSyncLabels.insert( Meta::valPlaycount, i18nc( "Statistics sync checkbox label",
0110                                                       "Synchronize Playcounts" ) );
0111     i18nSyncLabels.insert( Meta::valLabel, i18nc( "Statistics sync checkbox label",
0112                                                   "Synchronize Labels" ) );
0113 
0114     foreach( qint64 field, StatSyncing::Controller::availableFields() )
0115     {
0116         QString name = i18nSyncLabels.value( field );
0117         if( name.isEmpty() )
0118         {
0119             name = i18nc( "%1 is field name such as Play Count (this string is only used "
0120                           "as a fallback)", "Synchronize %1", Meta::i18nForField( field ) );
0121             warning() << Q_FUNC_INFO << "no explicit traslation for" << name << "using fallback";
0122         }
0123         QCheckBox *checkBox = new QCheckBox( name );
0124 
0125         if( field == Meta::valLabel ) // special case, we want plural:
0126         {
0127             QHBoxLayout *lineLayout = new QHBoxLayout();
0128             QLabel *button = new QLabel();
0129             button->setObjectName( QStringLiteral("configureLabelExceptions") );
0130             connect( button, &QLabel::linkActivated,
0131                      this, &MetadataConfig::slotConfigureExcludedLabels );
0132 
0133             lineLayout->addWidget( checkBox );
0134             lineLayout->addWidget( button );
0135             lineLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum ) );
0136             m_statSyncingFieldsLayout->addLayout( lineLayout );
0137 
0138             slotUpdateConfigureExcludedLabelsLabel();
0139         }
0140         else
0141         {
0142             m_statSyncingFieldsLayout->addWidget( checkBox );
0143         }
0144         checkBox->setCheckState( ( field & checkedFields ) ? Qt::Checked : Qt::Unchecked );
0145         checkBox->setProperty( "field", field );
0146         connect( checkBox, &QCheckBox::stateChanged, this, &MetadataConfig::changed );
0147     }
0148 }
0149 
0150 MetadataConfig::~MetadataConfig()
0151 {
0152     if( m_statSyncingConfig )
0153     {
0154         disconnect( this, &MetadataConfig::changed, nullptr, nullptr );
0155         m_statSyncingConfig.data()->read(); // reset unsaved changes
0156     }
0157 }
0158 
0159 bool
0160 MetadataConfig::isDefault()
0161 {
0162     return false;
0163 }
0164 
0165 bool
0166 MetadataConfig::hasChanged()
0167 {
0168     // a bit hacky, but updating enabled status here does the trick
0169     m_writeBackStatistics->setEnabled( m_writeBack->isChecked() );
0170     m_writeBackCover->setEnabled( m_writeBack->isChecked() );
0171     m_writeBackCoverDimensions->setEnabled( m_writeBackCover->isEnabled() && m_writeBackCover->isChecked() );
0172 
0173     return
0174         m_writeBack->isChecked() != AmarokConfig::writeBack() ||
0175         m_writeBackStatistics->isChecked() != AmarokConfig::writeBackStatistics() ||
0176         m_writeBackCover->isChecked() != AmarokConfig::writeBackCover() ||
0177         writeBackCoverDimensions() != AmarokConfig::writeBackCoverDimensions() ||
0178         m_useCharsetDetector->isChecked() != AmarokConfig::useCharsetDetector() ||
0179         ( m_statSyncingConfig.data() ? ( checkedFields() != m_statSyncingConfig->checkedFields() ) : false ) ||
0180         ( m_statSyncingConfig.data() ? m_statSyncingConfig->hasChanged() : false );
0181 }
0182 
0183 void
0184 MetadataConfig::updateSettings()
0185 {
0186     AmarokConfig::setWriteBack( m_writeBack->isChecked() );
0187     AmarokConfig::setWriteBackStatistics( m_writeBackStatistics->isChecked() );
0188     AmarokConfig::setWriteBackCover( m_writeBackCover->isChecked() );
0189     if( writeBackCoverDimensions() > 0 )
0190         AmarokConfig::setWriteBackCoverDimensions( writeBackCoverDimensions() );
0191     AmarokConfig::setUseCharsetDetector( m_useCharsetDetector->isChecked() );
0192     if( m_statSyncingConfig )
0193     {
0194         m_statSyncingConfig->setCheckedFields( checkedFields() );
0195         m_statSyncingConfig->save();
0196     }
0197 }
0198 
0199 void
0200 MetadataConfig::slotForgetCollections()
0201 {
0202     if( !m_statSyncingConfig )
0203         return;
0204     foreach( const QModelIndex &idx, m_statSyncingProvidersView->selectionModel()->selectedIndexes() )
0205     {
0206         QString id = idx.data( StatSyncing::Config::ProviderIdRole ).toString();
0207         m_statSyncingConfig->forgetProvider( id );
0208     }
0209 }
0210 
0211 void
0212 MetadataConfig::slotUpdateForgetButton()
0213 {
0214     QItemSelectionModel *selectionModel = m_statSyncingProvidersView->selectionModel();
0215     // note: hasSelection() and selection() gives false positives!
0216     m_forgetTargetsButton->setEnabled( !selectionModel->selectedIndexes().isEmpty() );
0217 }
0218 
0219 void
0220 MetadataConfig::slotUpdateConfigureExcludedLabelsLabel()
0221 {
0222     QLabel *label = findChild<QLabel *>( QStringLiteral("configureLabelExceptions") );
0223     if( !label || !m_statSyncingConfig )
0224     {
0225         warning() << __PRETTY_FUNCTION__ << "label or m_statSyncingConfig is null!";
0226         return;
0227     }
0228     int exceptions = m_statSyncingConfig->excludedLabels().count();
0229     QString begin = QStringLiteral("<a href='dummy'>");
0230     QString end = QStringLiteral("</a>");
0231     label->setText( i18np( "(%2one exception%3)", "(%2%1 exceptions%3)", exceptions,
0232                            begin, end ) );
0233 }
0234 
0235 void
0236 MetadataConfig::slotConfigureExcludedLabels()
0237 {
0238     ExcludedLabelsDialog dialog( m_statSyncingConfig.data(), this );
0239     if( dialog.exec() == QDialog::Accepted )
0240     {
0241         slotUpdateConfigureExcludedLabelsLabel();
0242         Q_EMIT changed();
0243     }
0244 }
0245 
0246 void
0247 MetadataConfig::slotConfigureProvider()
0248 {
0249     StatSyncing::Controller *controller = Amarok::Components::statSyncingController();
0250     if( controller )
0251     {
0252         QModelIndexList selected = m_statSyncingProvidersView->selectionModel()->selectedIndexes();
0253         if( selected.size() == 1 )
0254         {
0255             const QString id = selected.front().data( StatSyncing::Config::ProviderIdRole ).toString();
0256 
0257             QWidget *dialog = controller->providerConfigDialog( id );
0258             if( dialog )
0259             {
0260                 dialog->show();
0261                 dialog->activateWindow();
0262                 dialog->raise();
0263             }
0264         }
0265     }
0266 }
0267 
0268 void
0269 MetadataConfig::slotUpdateProviderConfigureButton()
0270 {
0271     QModelIndexList selected = m_statSyncingProvidersView->selectionModel()->selectedIndexes();
0272     StatSyncing::Controller *controller = Amarok::Components::statSyncingController();
0273 
0274     if( selected.size() != 1 || !controller )
0275     {
0276         m_configureTargetButton->setEnabled( false );
0277     }
0278     else
0279     {
0280         const QString id = selected.front().data( StatSyncing::Config::ProviderIdRole ).toString();
0281         m_configureTargetButton->setEnabled( controller->providerIsConfigurable( id ) );
0282     }
0283 }
0284 
0285 void
0286 MetadataConfig::slotCreateProviderDialog()
0287 {
0288     StatSyncing::Controller *controller = Amarok::Components::statSyncingController();
0289     if( controller )
0290     {
0291         QWidget *dialog = controller->providerCreationDialog();
0292         if( dialog )
0293         {
0294             dialog->show();
0295             dialog->activateWindow();
0296             dialog->raise();
0297         }
0298     }
0299 }
0300 
0301 int
0302 MetadataConfig::writeBackCoverDimensions() const
0303 {
0304     return m_writeBackCoverDimensions->itemData( m_writeBackCoverDimensions->currentIndex() ).toInt();
0305 }
0306 
0307 qint64
0308 MetadataConfig::checkedFields() const
0309 {
0310     qint64 ret = 0;
0311     foreach( QCheckBox *checkBox, m_statSyncingFieldsLayout->parentWidget()->findChildren<QCheckBox *>() )
0312     {
0313         if( checkBox->isChecked() && checkBox->property( "field" ).canConvert<qint64>() )
0314             ret |= checkBox->property( "field" ).value<qint64>();
0315     }
0316     return ret;
0317 }