1 /****************************************************************************************
2  * Copyright (c) 2008 Daniel Jones <danielcjones@gmail.com>                             *
3  * Copyright (c) 2009-2010 Leo Franchi <lfranchi@kde.org>                               *
4  * Copyright (c) 2009 Mark Kretschmann <kretschmann@kde.org>                            *
5  * Copyright (c) 2010-2011 Ralf Engels <ralf-engels@gmx.de>                             *
6  *                                                                                      *
7  * This program is free software; you can redistribute it and/or modify it under        *
8  * the terms of the GNU General Public License as published by the Free Software        *
9  * Foundation; either version 2 of the License, or (at your option) any later           *
10  * version.                                                                             *
11  *                                                                                      *
12  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
13  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
14  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
15  *                                                                                      *
16  * You should have received a copy of the GNU General Public License along with         *
17  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
18  ****************************************************************************************/
19 
20 #include "DynamicCategory.h"
21 #include "DynamicView.h"
22 
23 #include "amarokconfig.h"
24 #include "core/support/Amarok.h"
25 #include "core/support/Debug.h"
26 #include "playlist/PlaylistActions.h"
27 #include "playlist/PlaylistModelStack.h"
28 #include "dynamic/DynamicModel.h"
29 #include "dynamic/BiasedPlaylist.h"
30 
31 #include <QCheckBox>
32 #include <QIcon>
33 #include <QInputDialog>
34 #include <QLabel>
35 #include <QPushButton>
36 #include <QSpinBox>
37 #include <QStandardPaths>
38 #include <QToolButton>
39 
40 #include <KToolBar>
41 
42 
DynamicCategory(QWidget * parent)43 PlaylistBrowserNS::DynamicCategory::DynamicCategory( QWidget* parent )
44     : BrowserCategory( QStringLiteral("dynamic category"), parent )
45 {
46     setPrettyName( i18n( "Dynamic Playlists" ) );
47     setShortDescription( i18n( "Dynamically updating parameter based playlists" ) );
48     setIcon( QIcon::fromTheme( QStringLiteral("dynamic-amarok") ) );
49 
50     setLongDescription( i18n( "With a dynamic playlist, Amarok becomes your own personal dj, automatically selecting tracks for you, based on a number of parameters that you select." ) );
51 
52     setImagePath( QStandardPaths::locate( QStandardPaths::GenericDataLocation, QStringLiteral("amarok/images/hover_info_dynamic_playlists.png") ) );
53 
54     // set background
55     if( AmarokConfig::showBrowserBackgroundImage() )
56         setBackgroundImage( imagePath() );
57 
58     bool enabled = AmarokConfig::dynamicMode();
59 
60     setContentsMargins( 0, 0, 0, 0 );
61 
62     BoxWidget* controls2Layout = new BoxWidget( false, this );
63 
64     QLabel *label;
65     label = new QLabel( i18n( "Previous:" ), controls2Layout );
66     label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
67 
68     m_previous = new QSpinBox( controls2Layout );
69     m_previous->setMinimum( 0 );
70     m_previous->setToolTip( i18n( "Number of previous tracks to remain in the playlist." ) );
71     m_previous->setValue( AmarokConfig::previousTracks() );
72     connect( m_previous, QOverload<int>::of(&QSpinBox::valueChanged),
73              this, &PlaylistBrowserNS::DynamicCategory::setPreviousTracks );
74 
75     label = new QLabel( i18n( "Upcoming:" ), controls2Layout );
76     // label->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding );
77     label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
78 
79     m_upcoming = new QSpinBox( controls2Layout );
80     m_upcoming->setMinimum( 1 );
81     m_upcoming->setToolTip( i18n( "Number of upcoming tracks to add to the playlist." ) );
82     m_upcoming->setValue( AmarokConfig::upcomingTracks() );
83     connect( m_upcoming, QOverload<int>::of(&QSpinBox::valueChanged),
84              this, &PlaylistBrowserNS::DynamicCategory::setUpcomingTracks );
85 
86 
87     connect( Amarok::actionCollection()->action( QStringLiteral("playlist_clear") ),  &QAction::triggered,  this, &DynamicCategory::playlistCleared );
88     connect( Amarok::actionCollection()->action( QStringLiteral("disable_dynamic") ),  &QAction::triggered,  this, &DynamicCategory::playlistCleared, Qt::DirectConnection );
89 
90 
91     // -- the tool bar
92 
93     BoxWidget* presetLayout = new BoxWidget( false, this );
94     KToolBar* presetToolbar = new KToolBar( presetLayout );
95     presetToolbar->setIconSize( QSize( 22, 22 ) );
96 
97     presetToolbar->setToolButtonStyle( Qt::ToolButtonIconOnly );
98     presetToolbar->setMovable( false );
99     presetToolbar->setFloatable( false );
100     presetToolbar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred );
101 
102     m_onOffButton = new QToolButton( presetToolbar );
103     m_onOffButton->setText( i18nc( "Turn dynamic mode on", "On") );
104     m_onOffButton->setCheckable( true );
105     m_onOffButton->setIcon( QIcon::fromTheme( QStringLiteral("dynamic-amarok") ) );
106     m_onOffButton->setToolTip( i18n( "Turn dynamic mode on." ) );
107     presetToolbar->addWidget( m_onOffButton );
108 
109     m_duplicateButton = new QToolButton( presetToolbar );
110     m_duplicateButton->setText( i18n("Duplicates") );
111     m_duplicateButton->setCheckable( true );
112     m_duplicateButton->setChecked( allowDuplicates() );
113     m_duplicateButton->setIcon( QIcon::fromTheme( QStringLiteral("edit-copy") ) );
114     m_duplicateButton->setToolTip( i18n( "Allow duplicate songs in result" ) );
115     presetToolbar->addWidget( m_duplicateButton );
116 
117     m_addButton = new QToolButton( presetToolbar );
118     m_addButton->setText( i18n("New") );
119     m_addButton->setIcon( QIcon::fromTheme( QStringLiteral("document-new") ) );
120     m_addButton->setToolTip( i18n( "New playlist" ) );
121     presetToolbar->addWidget( m_addButton );
122 
123     m_editButton = new QToolButton( presetToolbar );
124     m_editButton->setText( i18n("Edit") );
125     m_editButton->setIcon( QIcon::fromTheme( QStringLiteral("document-properties-amarok") ) );
126     m_editButton->setToolTip( i18n( "Edit the selected playlist or bias" ) );
127     presetToolbar->addWidget( m_editButton );
128 
129     m_deleteButton = new QToolButton( presetToolbar );
130     m_deleteButton->setText( i18n("Delete") );
131     m_deleteButton->setEnabled( false );
132     m_deleteButton->setIcon( QIcon::fromTheme( QStringLiteral("edit-delete") ) );
133     m_deleteButton->setToolTip( i18n( "Delete the selected playlist or bias") );
134     presetToolbar->addWidget( m_deleteButton );
135 
136     m_repopulateButton = new QPushButton( presetLayout );
137     m_repopulateButton->setText( i18n("Repopulate") );
138     m_repopulateButton->setToolTip( i18n("Replace the upcoming tracks with fresh ones.") );
139     m_repopulateButton->setIcon( QIcon::fromTheme( QStringLiteral("view-refresh-amarok") ) );
140     m_repopulateButton->setEnabled( enabled );
141     // m_repopulateButton->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) );
142     QObject::connect( m_repopulateButton, &QAbstractButton::clicked, The::playlistActions(), &Playlist::Actions::repopulateDynamicPlaylist );
143 
144 
145     // -- the tree view
146 
147     m_tree = new DynamicView( this );
148     connect( m_tree->selectionModel(), &QItemSelectionModel::selectionChanged,
149              this, &DynamicCategory::selectionChanged );
150 
151     connect( m_onOffButton, &QAbstractButton::toggled, The::playlistActions(), &Playlist::Actions::enableDynamicMode );
152     connect( m_duplicateButton, &QAbstractButton::toggled, this, &DynamicCategory::setAllowDuplicates );
153 
154     connect( m_addButton, &QAbstractButton::clicked, m_tree, &DynamicView::addPlaylist );
155     connect( m_editButton, &QAbstractButton::clicked, m_tree, &DynamicView::editSelected );
156     connect( m_deleteButton, &QAbstractButton::clicked, m_tree, &DynamicView::removeSelected );
157 
158     navigatorChanged();
159     selectionChanged();
160 
161     connect( The::playlistActions(), &Playlist::Actions::navigatorChanged,
162              this, &DynamicCategory::navigatorChanged );
163 }
164 
165 
~DynamicCategory()166 PlaylistBrowserNS::DynamicCategory::~DynamicCategory()
167 { }
168 
169 void
navigatorChanged()170 PlaylistBrowserNS::DynamicCategory::navigatorChanged()
171 {
172     m_onOffButton->setChecked( AmarokConfig::dynamicMode() );
173     m_repopulateButton->setEnabled( AmarokConfig::dynamicMode() );
174 }
175 
176 void
selectionChanged()177 PlaylistBrowserNS::DynamicCategory::selectionChanged()
178 {
179     DEBUG_BLOCK;
180 
181     QModelIndexList indexes = m_tree->selectionModel()->selectedIndexes();
182 
183     if( indexes.isEmpty() )
184     {
185         m_addButton->setEnabled( true );
186         m_editButton->setEnabled( false );
187         m_deleteButton->setEnabled( false );
188         return;
189     }
190 
191     QVariant v = m_tree->model()->data( indexes.first(), Dynamic::DynamicModel::PlaylistRole );
192     if( v.isValid() )
193     {
194         m_addButton->setEnabled( true );
195         m_editButton->setEnabled( true );
196         m_deleteButton->setEnabled( true );
197         return;
198     }
199 
200     v = m_tree->model()->data( indexes.first(), Dynamic::DynamicModel::BiasRole );
201     if( v.isValid() )
202     {
203         m_addButton->setEnabled( true );
204         m_editButton->setEnabled( true );
205         m_deleteButton->setEnabled( false ); // TODO
206         return;
207     }
208 }
209 
210 bool
allowDuplicates() const211 PlaylistBrowserNS::DynamicCategory::allowDuplicates() const
212 {
213     return AmarokConfig::dynamicDuplicates();
214 }
215 
216 
217 void
playlistCleared()218 PlaylistBrowserNS::DynamicCategory::playlistCleared() // SLOT
219 {
220     The::playlistActions()->enableDynamicMode( false );
221 }
222 
223 void
setUpcomingTracks(int n)224 PlaylistBrowserNS::DynamicCategory::setUpcomingTracks( int n ) // SLOT
225 {
226     if( n >= 1 )
227         AmarokConfig::setUpcomingTracks( n );
228 }
229 
230 void
setPreviousTracks(int n)231 PlaylistBrowserNS::DynamicCategory::setPreviousTracks( int n ) // SLOT
232 {
233     if( n >= 0 )
234         AmarokConfig::setPreviousTracks( n );
235 }
236 
237 void
setAllowDuplicates(bool value)238 PlaylistBrowserNS::DynamicCategory::setAllowDuplicates( bool value ) // SLOT
239 {
240     if( AmarokConfig::dynamicDuplicates() == value )
241         return;
242 
243     AmarokConfig::setDynamicDuplicates( value );
244     AmarokConfig::self()->save();
245 
246     m_duplicateButton->setChecked( value );
247 }
248 
249 
250 
251