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