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

0001 /****************************************************************************************
0002  * Copyright (c) 2008 Nikolaj Hald Nielsen <nhn@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 "ProgressiveSearchWidget.h"
0018 
0019 #include "core/support/Debug.h"
0020 #include "playlist/PlaylistModel.h"
0021 
0022 #include <KColorScheme>
0023 #include <KConfigGroup>
0024 #include <KLocalizedString>
0025 
0026 #include <QAction>
0027 #include <QHBoxLayout>
0028 #include <QVBoxLayout>
0029 #include <QKeyEvent>
0030 #include <QLabel>
0031 #include <QMenu>
0032 #include <QToolBar>
0033 #include <QToolButton>
0034 
0035 namespace Playlist
0036 {
0037 
0038 ProgressiveSearchWidget::ProgressiveSearchWidget( QWidget * parent )
0039 : BoxWidget( true, parent )
0040 {
0041     DEBUG_BLOCK
0042 
0043     readConfig();
0044 
0045     BoxWidget *searchBox = new BoxWidget( false, this );
0046 
0047     m_searchEdit = new Amarok::LineEdit( searchBox );
0048     m_searchEdit->setPlaceholderText( i18n( "Search playlist" ) );
0049     m_searchEdit->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
0050     m_searchEdit->setClearButtonEnabled( true );
0051     m_searchEdit->setFrame( true );
0052     m_searchEdit->setToolTip( i18n( "Start typing to progressively search through the playlist" ) );
0053     m_searchEdit->setFocusPolicy( Qt::ClickFocus ); // Without this, the widget goes into text input mode directly on startup
0054 
0055     connect( m_searchEdit, &Amarok::LineEdit::textChanged, this, &ProgressiveSearchWidget::slotFilterChanged );
0056     connect( m_searchEdit, &Amarok::LineEdit::returnPressed, this, &ProgressiveSearchWidget::activateFilterResult );
0057     connect( m_searchEdit, &Amarok::LineEdit::returnPressed, this, &ProgressiveSearchWidget::slotFilterClear );
0058     connect( m_searchEdit, &Amarok::LineEdit::returnPressed, this, &ProgressiveSearchWidget::defocus );
0059     connect( m_searchEdit, &Amarok::LineEdit::downPressed, this, &ProgressiveSearchWidget::downPressed );
0060     connect( m_searchEdit, &Amarok::LineEdit::upPressed, this, &ProgressiveSearchWidget::upPressed );
0061 
0062     m_nextAction = new QAction( QIcon::fromTheme( QStringLiteral("go-down") ), i18n( "&Next" ), this );
0063     connect( m_nextAction, &QAction::triggered, this, &ProgressiveSearchWidget::slotNext );
0064 
0065     m_previousAction = new QAction( QIcon::fromTheme( QStringLiteral("go-up") ), i18n( "&Previous" ), this );
0066     connect( m_previousAction, &QAction::triggered, this, &ProgressiveSearchWidget::slotPrevious );
0067 
0068     m_nextAction->setEnabled( false );
0069     m_previousAction->setEnabled( false );
0070 
0071     m_menu = new QMenu( this );
0072 
0073     QAction * searchTracksAction = new QAction( i18n( "Tracks" ), this );
0074     searchTracksAction->setCheckable( true );
0075     connect( searchTracksAction, &QAction::toggled, this, &ProgressiveSearchWidget::slotSearchTracks );
0076     if( m_searchFieldsMask & Playlist::MatchTrack )
0077         searchTracksAction->setChecked( true );
0078     m_menu->addAction( searchTracksAction );
0079 
0080     QAction * searchAlbumsAction = new QAction( i18n( "Albums" ), this );
0081     searchAlbumsAction->setCheckable( true );
0082     connect( searchAlbumsAction, &QAction::toggled, this, &ProgressiveSearchWidget::slotSearchAlbums );
0083     if( m_searchFieldsMask & Playlist::MatchAlbum )
0084         searchAlbumsAction->setChecked( true );
0085     m_menu->addAction( searchAlbumsAction );
0086 
0087     QAction * searchArtistsAction = new QAction( i18n( "Artists" ), this );
0088     searchArtistsAction->setCheckable( true );
0089     connect( searchArtistsAction, &QAction::toggled, this, &ProgressiveSearchWidget::slotSearchArtists );
0090     if( m_searchFieldsMask & Playlist::MatchArtist )
0091         searchArtistsAction->setChecked( true );
0092     m_menu->addAction( searchArtistsAction );
0093 
0094     QAction * searchGenreAction = new QAction( i18n( "Genre" ), this );
0095     searchGenreAction->setCheckable( true );
0096     connect( searchGenreAction, &QAction::toggled, this, &ProgressiveSearchWidget::slotSearchGenre );
0097     if( m_searchFieldsMask & Playlist::MatchGenre )
0098         searchGenreAction->setChecked( true );
0099     m_menu->addAction( searchGenreAction );
0100 
0101     QAction * searchComposersAction = new QAction( i18n( "Composers" ), this );
0102     searchComposersAction->setCheckable( true );
0103     connect( searchComposersAction, &QAction::toggled, this, &ProgressiveSearchWidget::slotSearchComposers );
0104     if( m_searchFieldsMask & Playlist::MatchComposer )
0105         searchComposersAction->setChecked( true );
0106     m_menu->addAction( searchComposersAction );
0107 
0108     QAction * searchRatingAction = new QAction( i18n( "Rating" ), this );
0109     searchRatingAction->setCheckable( true );
0110     connect( searchRatingAction, &QAction::toggled, this, &ProgressiveSearchWidget::slotSearchRating );
0111     if( m_searchFieldsMask & Playlist::MatchRating )
0112         searchRatingAction->setChecked( true );
0113     m_menu->addAction( searchRatingAction );
0114 
0115     QAction * searchYearsAction = new QAction( i18n( "Years" ), this );
0116     searchYearsAction->setCheckable( true );
0117     connect( searchYearsAction, &QAction::toggled, this, &ProgressiveSearchWidget::slotSearchYears );
0118     if( m_searchFieldsMask & Playlist::MatchYear)
0119         searchYearsAction->setChecked( true );
0120     m_menu->addAction( searchYearsAction );
0121 
0122     m_menu->addSeparator();
0123 
0124     QAction * showOnlyMatchesAction = new QAction( i18n( "Show only matches" ), this );
0125     showOnlyMatchesAction->setCheckable( true );
0126     connect( showOnlyMatchesAction, &QAction::toggled, this, &ProgressiveSearchWidget::slotShowOnlyMatches );
0127 
0128     m_toolBar = new QToolBar( searchBox );
0129     showOnlyMatchesAction->setChecked( m_showOnlyMatches );
0130     m_menu->addAction( showOnlyMatchesAction );
0131     slotShowOnlyMatches( m_showOnlyMatches );
0132 
0133     m_nextAction->setVisible( !m_showOnlyMatches );
0134     m_previousAction->setVisible( !m_showOnlyMatches );
0135 
0136     QAction *searchMenuAction = new QAction( QIcon::fromTheme( QStringLiteral("preferences-other") ), i18n( "Search Preferences" ), this );
0137     searchMenuAction->setMenu( m_menu );
0138 
0139     m_toolBar->addAction( searchMenuAction );
0140 
0141     QToolButton *tbutton = qobject_cast<QToolButton*>( m_toolBar->widgetForAction( searchMenuAction ) );
0142     if( tbutton )
0143         tbutton->setPopupMode( QToolButton::InstantPopup );
0144 
0145     m_toolBar->setFixedHeight( m_searchEdit->sizeHint().height() );
0146 
0147     //make sure that this edit is cleared when the playlist is cleared:
0148     connect( Amarok::actionCollection()->action( QStringLiteral("playlist_clear") ), &QAction::triggered, this, &ProgressiveSearchWidget::slotFilterClear );
0149 }
0150 
0151 void ProgressiveSearchWidget::slotFilterChanged( const QString & filter )
0152 {
0153     DEBUG_BLOCK
0154 
0155     //when the clear button is pressed, we get 2 calls to this slot... filter this out  as it messes with
0156     //resetting the view:
0157     if ( filter == m_lastFilter )
0158         return;
0159 
0160     debug() << "New filter: " << filter;
0161 
0162     m_lastFilter = filter;
0163 
0164     if( filter.isEmpty() )
0165     {
0166         m_nextAction->setEnabled( false );
0167         m_previousAction->setEnabled( false );
0168 
0169         QPalette p = m_searchEdit->palette();
0170         p.setColor( QPalette::Base, palette().color( QPalette::Base ) );
0171         m_searchEdit->setPalette( p );
0172 
0173         Q_EMIT( filterCleared() );
0174 
0175         return;
0176     }
0177 
0178     Q_EMIT( filterChanged( filter, m_searchFieldsMask, m_showOnlyMatches ) );
0179 }
0180 
0181 void ProgressiveSearchWidget::slotNext()
0182 {
0183     DEBUG_BLOCK
0184     Q_EMIT( next( m_searchEdit->text(), m_searchFieldsMask ) );
0185 }
0186 
0187 void ProgressiveSearchWidget::slotPrevious()
0188 {
0189     DEBUG_BLOCK
0190     Q_EMIT( previous( m_searchEdit->text(), m_searchFieldsMask ) );
0191 }
0192 
0193 void ProgressiveSearchWidget::match()
0194 {
0195     m_nextAction->setEnabled( true );
0196     m_previousAction->setEnabled( true );
0197 
0198     QPalette p = m_searchEdit->palette();
0199     p.setColor( QPalette::Base, palette().color( QPalette::Base ) );
0200     m_searchEdit->setPalette( p );
0201 }
0202 
0203 void ProgressiveSearchWidget::noMatch()
0204 {
0205     m_nextAction->setEnabled( false );
0206     m_previousAction->setEnabled( false );
0207 
0208     const KStatefulBrush backgroundBrush( KColorScheme::View, KColorScheme::NegativeBackground );
0209 
0210     QPalette p = m_searchEdit->palette();
0211     p.setColor( QPalette::Base, backgroundBrush.brush( p ).color() );
0212     m_searchEdit->setPalette( p );
0213 }
0214 
0215 void ProgressiveSearchWidget::slotSearchTracks( bool search )
0216 {
0217     if( search )
0218         m_searchFieldsMask |= Playlist::MatchTrack;
0219     else
0220         m_searchFieldsMask ^= Playlist::MatchTrack;
0221 
0222     Amarok::config( QStringLiteral("Playlist Search") ).writeEntry( "MatchTrack", search );
0223 
0224     if( !m_searchEdit->text().isEmpty() )
0225         Q_EMIT( filterChanged( m_searchEdit->text(), m_searchFieldsMask, m_showOnlyMatches ) );
0226 }
0227 
0228 void ProgressiveSearchWidget::slotSearchArtists( bool search )
0229 {
0230     if( search )
0231         m_searchFieldsMask |= Playlist::MatchArtist;
0232     else
0233         m_searchFieldsMask ^= Playlist::MatchArtist;
0234 
0235     Amarok::config( QStringLiteral("Playlist Search") ).writeEntry( "MatchArtist", search );
0236 
0237     if( !m_searchEdit->text().isEmpty() )
0238         Q_EMIT( filterChanged( m_searchEdit->text(), m_searchFieldsMask, m_showOnlyMatches ) );
0239 }
0240 
0241 void ProgressiveSearchWidget::slotSearchAlbums( bool search )
0242 {
0243     if( search )
0244         m_searchFieldsMask |= Playlist::MatchAlbum;
0245     else
0246         m_searchFieldsMask ^= Playlist::MatchAlbum;
0247 
0248     Amarok::config( QStringLiteral("Playlist Search") ).writeEntry( "MatchAlbum", search );
0249 
0250     if( !m_searchEdit->text().isEmpty() )
0251         Q_EMIT( filterChanged( m_searchEdit->text(), m_searchFieldsMask, m_showOnlyMatches ) );
0252 }
0253 
0254 void ProgressiveSearchWidget::slotSearchGenre( bool search )
0255 {
0256     if( search )
0257         m_searchFieldsMask |= Playlist::MatchGenre;
0258     else
0259         m_searchFieldsMask ^= Playlist::MatchGenre;
0260 
0261     Amarok::config( QStringLiteral("Playlist Search") ).writeEntry( "MatchGenre", search );
0262 
0263     if( !m_searchEdit->text().isEmpty() )
0264         Q_EMIT( filterChanged( m_searchEdit->text(), m_searchFieldsMask, m_showOnlyMatches ) );
0265 }
0266 
0267 void ProgressiveSearchWidget::slotSearchComposers( bool search )
0268 {
0269     if( search )
0270         m_searchFieldsMask |= Playlist::MatchComposer;
0271     else
0272         m_searchFieldsMask ^= Playlist::MatchComposer;
0273 
0274     Amarok::config( QStringLiteral("Playlist Search") ).writeEntry( "MatchComposer", search );
0275 
0276     if( !m_searchEdit->text().isEmpty() )
0277         Q_EMIT( filterChanged( m_searchEdit->text(), m_searchFieldsMask, m_showOnlyMatches ) );
0278 }
0279 
0280 void ProgressiveSearchWidget::slotSearchRating( bool search )
0281 {
0282     if( search )
0283         m_searchFieldsMask |= Playlist::MatchRating;
0284     else
0285         m_searchFieldsMask ^= Playlist::MatchRating;
0286 
0287     Amarok::config( QStringLiteral("Playlist Search") ).writeEntry( "MatchRating", search );
0288 
0289     if( !m_searchEdit->text().isEmpty() )
0290         Q_EMIT( filterChanged( m_searchEdit->text(), m_searchFieldsMask, m_showOnlyMatches ) );
0291 }
0292 
0293 void ProgressiveSearchWidget::slotSearchYears( bool search )
0294 {
0295     if( search )
0296         m_searchFieldsMask |= Playlist::MatchYear;
0297     else
0298         m_searchFieldsMask ^= Playlist::MatchYear;
0299 
0300     Amarok::config( QStringLiteral("Playlist Search") ).writeEntry( "MatchYear", search );
0301 
0302     if( !m_searchEdit->text().isEmpty() )
0303         Q_EMIT( filterChanged( m_searchEdit->text(), m_searchFieldsMask, m_showOnlyMatches ) );
0304 }
0305 
0306 void ProgressiveSearchWidget::readConfig()
0307 {
0308     m_searchFieldsMask = 0;
0309 
0310     KConfigGroup config = Amarok::config(QStringLiteral("Playlist Search"));
0311 
0312     if( config.readEntry( "MatchTrack", true ) )
0313         m_searchFieldsMask |= Playlist::MatchTrack;
0314     if( config.readEntry( "MatchArtist", true ) )
0315         m_searchFieldsMask |= Playlist::MatchArtist;
0316     if( config.readEntry( "MatchAlbum", true ) )
0317         m_searchFieldsMask |= Playlist::MatchAlbum;
0318     if( config.readEntry( "MatchGenre", false ) )
0319         m_searchFieldsMask |= Playlist::MatchGenre;
0320     if( config.readEntry( "MatchComposer", false ) )
0321         m_searchFieldsMask |= Playlist::MatchComposer;
0322     if( config.readEntry( "MatchRating", false ) )
0323         m_searchFieldsMask |= Playlist::MatchRating;
0324     if( config.readEntry( "MatchYear", false ) )
0325         m_searchFieldsMask |= Playlist::MatchYear;
0326 
0327     m_showOnlyMatches = config.readEntry( "ShowOnlyMatches", false );
0328 }
0329 
0330 void ProgressiveSearchWidget::slotShowOnlyMatches( bool onlyMatches )
0331 {
0332     DEBUG_BLOCK
0333 
0334     if( onlyMatches )
0335     {
0336         m_toolBar->removeAction( m_previousAction );
0337         m_toolBar->removeAction( m_nextAction );
0338     }
0339     else
0340     {
0341         m_toolBar->insertAction( m_menu->menuAction(), m_nextAction );
0342         m_toolBar->insertAction( m_nextAction, m_previousAction );
0343     }
0344 
0345     m_showOnlyMatches = onlyMatches;
0346     m_nextAction->setVisible( !onlyMatches );
0347     m_previousAction->setVisible( !onlyMatches );
0348 
0349     KConfigGroup cg = Amarok::config( QStringLiteral("Playlist Search") );
0350     cg.writeEntry( "ShowOnlyMatches", m_showOnlyMatches );
0351     cg.sync();
0352 
0353     Q_EMIT( showOnlyMatches( onlyMatches ) );
0354 }
0355 
0356 void
0357 ProgressiveSearchWidget::keyPressEvent( QKeyEvent *event )
0358 {
0359     if( event->matches( QKeySequence::FindNext ) )
0360     {
0361         event->accept();
0362         slotNext();
0363     }
0364     else if( event->matches( QKeySequence::FindPrevious ) )
0365     {
0366         event->accept();
0367         slotPrevious();
0368     }
0369     else
0370     {
0371         event->ignore();
0372         BoxWidget::keyPressEvent( event );
0373     }
0374 }
0375 
0376 void
0377 ProgressiveSearchWidget::focusInputLine()
0378 {
0379     m_searchEdit->setFocus();
0380 }
0381 
0382 void ProgressiveSearchWidget::slotFilterClear()
0383 {
0384     DEBUG_BLOCK
0385     m_searchEdit->setText( QString() );
0386 }
0387 
0388 }   //namespace Playlist
0389