File indexing completed on 2024-05-05 04:48:44

0001 /****************************************************************************************
0002  * Copyright (c) 2009 Téo Mrnjavac <teo@kde.org>                                        *
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 "PlaylistSortWidget.h"
0018 
0019 #include "core/support/Debug.h"
0020 #include "PlaylistActions.h"
0021 #include "PlaylistModelStack.h"
0022 #include "proxymodels/SortScheme.h"
0023 
0024 #include <KConfigGroup>
0025 #include <QStandardPaths>
0026 
0027 namespace Playlist
0028 {
0029 
0030 SortWidget::SortWidget( QWidget *parent )
0031     : QWidget( parent )
0032 {
0033     setFixedHeight( 28 );
0034     setContentsMargins( 3, 0, 3, 0 );
0035 
0036     m_layout = new QHBoxLayout( this );
0037     setLayout( m_layout );
0038     m_layout->setSpacing( 0 );
0039     m_layout->setContentsMargins( 0, 0, 0, 0 );
0040 
0041     BreadcrumbItemButton *rootItem = new BreadcrumbItemButton(
0042             QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/playlist-sorting-16.png" ) ) ),
0043             QString(), this );
0044     rootItem->setToolTip( i18n( "Clear the playlist sorting configuration." ) );
0045     m_layout->addWidget( rootItem );
0046     connect( rootItem, &BreadcrumbItemButton::clicked, this, &SortWidget::trimToLevel );
0047 
0048     m_ribbon = new QHBoxLayout();
0049     m_layout->addLayout( m_ribbon );
0050     m_ribbon->setContentsMargins( 0, 0, 0, 0 );
0051     m_ribbon->setSpacing( 0 );
0052 
0053     m_addButton = new BreadcrumbAddMenuButton( this );
0054     m_addButton->setToolTip( i18n( "Add a playlist sorting level." ) );
0055     m_layout->addWidget( m_addButton );
0056     m_layout->addStretch( 10 );
0057 
0058     m_urlButton = new BreadcrumbUrlMenuButton( QStringLiteral("playlist"), this );
0059     m_layout->addWidget( m_urlButton );
0060 
0061     connect( m_addButton->menu(), &BreadcrumbItemMenu::actionClicked,
0062              this, &SortWidget::addLevelAscending );
0063     connect( m_addButton->menu(), &BreadcrumbItemMenu::shuffleActionClicked,
0064              The::playlistActions(), &Actions::shuffle );
0065 
0066     QString sortPath = Amarok::config( QStringLiteral("Playlist Sorting") ).readEntry( "SortPath", QString() );
0067     readSortPath( sortPath );
0068 }
0069 
0070 SortWidget::~SortWidget()
0071 {}
0072 
0073 void
0074 SortWidget::addLevel( const QString &internalColumnName, Qt::SortOrder sortOrder )  //private slot
0075 {
0076     BreadcrumbLevel *bLevel = new BreadcrumbLevel( internalColumnName );
0077     BreadcrumbItem *item = new BreadcrumbItem( bLevel, this );
0078     m_ribbon->addWidget( item );
0079     connect( item, &BreadcrumbItem::clicked, this, &SortWidget::onItemClicked );
0080     connect( item->menu(), &BreadcrumbItemMenu::actionClicked, this, &SortWidget::onItemSiblingClicked );
0081     connect( item->menu(), &BreadcrumbItemMenu::shuffleActionClicked, this, &SortWidget::onShuffleSiblingClicked );
0082     connect( item, &BreadcrumbItem::orderInverted, this, &SortWidget::updateSortScheme );
0083     if( sortOrder != item->sortOrder() )
0084         item->invertOrder();
0085     m_addButton->updateMenu( levels() );
0086     updateSortScheme();
0087 }
0088 
0089 void
0090 SortWidget::addLevelAscending ( const QString &internalColumnName )
0091 {
0092     addLevel(internalColumnName, Qt::AscendingOrder);
0093 }
0094 
0095 void
0096 SortWidget::trimToLevel( const int level )
0097 {
0098     for( int i = m_ribbon->count() - 1 ; i > level; i-- )
0099     {
0100         BreadcrumbItem *item = qobject_cast< BreadcrumbItem * >( m_ribbon->itemAt( i )->widget() );
0101         m_ribbon->removeWidget( item );
0102         item->deleteLater();
0103     }
0104     updateSortScheme();
0105     m_addButton->updateMenu( levels() );
0106 }
0107 
0108 QStringList
0109 SortWidget::levels() const
0110 {
0111     QStringList levels = QStringList();
0112     for( int i = 0; i < m_ribbon->count(); ++i )
0113         levels << qobject_cast< BreadcrumbItem * >( m_ribbon->itemAt( i )->widget() )->name();
0114     return levels;
0115 }
0116 
0117 void
0118 SortWidget::onItemClicked()
0119 {
0120     const int level = m_ribbon->indexOf( qobject_cast< QWidget * >( sender()->parent() ) );
0121     trimToLevel( level );
0122 }
0123 
0124 void
0125 SortWidget::onItemSiblingClicked( const QString &internalColumnName )
0126 {
0127     const int level = m_ribbon->indexOf( qobject_cast< QWidget * >( sender()->parent() ) );
0128     trimToLevel( level - 1 );
0129     addLevel( internalColumnName );
0130 }
0131 
0132 void
0133 SortWidget::onShuffleSiblingClicked()
0134 {
0135     const int level = m_ribbon->indexOf( qobject_cast< QWidget * >( sender()->parent() ) );
0136     trimToLevel( level - 1 );
0137     The::playlistActions()->shuffle();
0138 }
0139 
0140 void
0141 SortWidget::updateSortScheme()
0142 {
0143     SortScheme scheme = SortScheme();
0144     for( int i = 0; i < m_ribbon->count(); ++i )    //could be faster if done with iterator
0145     {
0146         QString name( qobject_cast< BreadcrumbItem * >( m_ribbon->itemAt( i )->widget() )->name() );
0147         Column category = columnForName( name );
0148         Qt::SortOrder sortOrder = qobject_cast< BreadcrumbItem * >( m_ribbon->itemAt( i )->widget() )->sortOrder();
0149         scheme.addLevel( SortLevel( category, sortOrder ) );
0150     }
0151     ModelStack::instance()->sortProxy()->updateSortMap( scheme );
0152 
0153     KConfigGroup config = Amarok::config( QStringLiteral("Playlist Sorting") );
0154     config.writeEntry( "SortPath", sortPath() );
0155 }
0156 
0157 QString
0158 SortWidget::sortPath() const
0159 {
0160     QString path;
0161     for( int i = 0; i < m_ribbon->count(); ++i )    //could be faster if done with iterator
0162     {
0163         QString name( qobject_cast< BreadcrumbItem * >( m_ribbon->itemAt( i )->widget() )->name() );
0164         Qt::SortOrder sortOrder = qobject_cast< BreadcrumbItem * >( m_ribbon->itemAt( i )->widget() )->sortOrder();
0165         QString level = name + '_' + ( sortOrder ? "des" : "asc" );
0166         path.append( ( i == m_ribbon->count() - 1 ) ? level : ( level + '-' ) );
0167     }
0168     return path;
0169 }
0170 
0171 void
0172 SortWidget::readSortPath( const QString &sortPath )
0173 {
0174     trimToLevel();
0175 
0176     QStringList levels = sortPath.split( QLatin1Char('-') );
0177     foreach( const QString &level, levels )
0178     {
0179         QStringList levelParts = level.split( '_' );
0180     /*
0181      * Check whether the configuration is valid. If indexOf
0182      * returns -1, the entry is corrupted. We can't use columnForName
0183      * here, as it will do a static_cast, which is UB when indexOf is -1
0184      * as there's no corresponding enum value
0185      * (C++ standard 5.2.9 Static cast [expr.static.cast] paragraph 7)
0186      */
0187         if( levelParts.count() > 2
0188         || ( Playlist::PlaylistColumnInfos::internalNames().
0189                            indexOf( levelParts.value(0) ) == -1) )
0190             warning() << "Playlist sorting load error: Invalid sort level " << level;
0191         else if( levelParts.value( 1 ) == QStringLiteral( "asc" ) )
0192             addLevel( levelParts.value( 0 ), Qt::AscendingOrder );
0193         else if( levelParts.value( 1 ) == QStringLiteral( "des" ) )
0194             addLevel( levelParts.value( 0 ), Qt::DescendingOrder );
0195         else
0196             warning() << "Playlist sorting load error: Invalid sort order for level " << level;
0197     }
0198 }
0199 
0200 QString
0201 SortWidget::prettySortPath() const
0202 {
0203     QString prettyPath;
0204     for( int i = 0; i < m_ribbon->count(); ++i )    //could be faster if done with iterator
0205     {
0206         QString prettyName( qobject_cast< BreadcrumbItem * >( m_ribbon->itemAt( i )->widget() )->prettyName() );
0207         Qt::SortOrder sortOrder = qobject_cast< BreadcrumbItem * >( m_ribbon->itemAt( i )->widget() )->sortOrder();
0208         QString prettyLevel = prettyName + ( sortOrder ? "↓" : "↑" );
0209         prettyPath.append( ( i == m_ribbon->count() - 1 ) ? prettyLevel : ( prettyLevel + " > " ) );
0210         //TODO: see how this behaves on RTL systems
0211     }
0212     return prettyPath;
0213 }
0214 
0215 }