1 /***************************************************************************
2 * copyright : (C) 2005 Seb Ruiz <me@sebruiz.net> *
3 **************************************************************************/
5 /***************************************************************************
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
12 ***************************************************************************/
14 #define DEBUG_PREFIX "QueueManager"
18 #include "amarokconfig.h" //check if dynamic mode
20 #include "queuemanager.h"
23 #include <QTextDocument>
26 #include <k3urldrag.h>
27 #include <kapplication.h>
30 #include <kpushbutton.h>
32 #include <kwindowsystem.h>
35 //////////////////////////////////////////////////////////////////////////////////////////
37 //////////////////////////////////////////////////////////////////////////////////////////
39 QueueItem::paintCell( QPainter
*p
, const QColorGroup
&cg
, int column
, int width
, int align
)
41 QListWidgetItem::paintCell( p
, cg
, column
, width
, align
);
43 QString str
= QString::number( ( static_cast<K3ListView
*>( listView() ) )->itemIndex( this ) + 1 );
45 //draw the symbol's outline
46 uint fw
= p
->fontMetrics().width( str
) + 2;
47 const uint w
= 16; //keep this even
48 const uint h
= height() - 2;
50 p
->setBrush( cg
.highlight() );
51 p
->setPen( cg
.highlight().dark() ); //TODO blend with background color
52 p
->drawEllipse( width
- fw
- w
/2, 1, w
, h
);
53 p
->drawRect( width
- fw
, 1, fw
, h
);
54 p
->setPen( cg
.highlight() );
55 p
->drawLine( width
- fw
, 2, width
- fw
, h
- 1 );
57 fw
+= 2; //add some more padding
58 p
->setPen( cg
.highlightedText() );
59 p
->drawText( width
- fw
, 2, fw
, h
-1, Qt::AlignCenter
, str
);
63 //////////////////////////////////////////////////////////////////////////////////////////
65 //////////////////////////////////////////////////////////////////////////////////////////
67 QueueList::QueueList( QWidget
*parent
, const char *name
)
68 : QListWidget( parent
)
70 setObjectName( name
);
71 setResizeMode( QListWidget::Adjust
);
72 setSelectionMode( QAbstractItemView::ExtendedSelection
);
74 setAcceptDrops( true );
75 setDragEnabled( true );
76 //setDropVisualizer( true ); //the visualizer (a line marker) is drawn when dragging over tracks
77 //setDropVisualizerWidth( 3 );
81 QueueList::paintEvent( QPaintEvent
*e
)
83 if( e
) QListWidget::paintEvent( e
);
87 QPainter
p( viewport() );
88 QString
minimumText(i18n(
90 "<h3>The Queue Manager</h3>"
92 "<b>drag</b> tracks from the playlist, and "
93 "<b>drop</b> them here.<br><br>"
94 "Drag and drop tracks within the manager to resort queue orders."
97 t
.setHtml( minimumText
);
98 const int w
= static_cast<int>( t
.size().width() );
99 const int h
= static_cast<int>( t
.size().height() );
100 if ( w
+30 >= viewport()->width() || h
+30 >= viewport()->height() )
104 const uint x
= (viewport()->width() - w
- 30) / 2 ;
105 const uint y
= (viewport()->height() - h
- 30) / 2 ;
107 p
.setBrush( palette().window().color() );
108 p
.drawRoundRect( x
, y
, w
+30, h
+30, (8*200)/w
, (8*200)/h
);
109 // t.draw( &p, x+15, y+15, QRect(), colorGroup() );
110 t
.drawContents( &p
, QRect() );
115 QueueList::keyPressEvent( QKeyEvent
*e
)
119 case Qt::Key_Delete
: //remove
123 case Qt::ControlModifier
+Qt::Key_Up
:
127 case Qt::ControlModifier
+Qt::Key_Down
:
134 QueueList::hasSelection()
136 return selectedItems().size() == 0;
140 QueueList::moveSelected( int direction
)
142 QList
<QListWidgetItem
*> selected
= selectedItems();
143 bool item_moved
= false;
145 // Whilst it would be substantially faster to do this: ((*it)->itemAbove())->move( *it ),
146 // this would only work for sequentially ordered items
147 foreach( QListWidgetItem
* it
, selected
)
149 int position
= row( it
);
150 if( (direction
< 0 && position
== 0 ) || ( direction
> 0 && position
== count() - 1 ) )
152 insertItem( position
+ direction
, takeItem( position
) );
156 scrollToItem( selected
.first() ); //apparently this deselects?
157 foreach( QListWidgetItem
* it
, selected
)
158 it
->setSelected( true );
165 QueueList::removeSelected() //SLOT
167 currentItem()->setSelected( true );
169 bool item_removed
= false;
170 QList
<QListWidgetItem
* > selected
= selectedItems();
172 foreach( QListWidgetItem
* item
, selected
)
179 QueueManager::instance()->updateButtons();
186 QueueList::clear() // SLOT
188 QListWidget::clear();
193 QueueList::dragEnterEvent( QDragEnterEvent
*e
)
195 debug() << "dragEnterEvent()" << endl
;
196 if( e
->source() == reinterpret_cast<K3ListView
*>( Playlist::instance() )->viewport() )
197 e
->acceptProposedAction();
201 QueueList::dragMoveEvent( QDragMoveEvent
*e
)
203 debug() << "dragMoveEvent()" << endl
;
204 QListWidget::dragMoveEvent( e
);
206 //Comment From 1.4: Must be overloaded for dnd to work
207 if (( e
->source() == reinterpret_cast<K3ListView
*>( Playlist::instance() )->viewport() ) ||
208 e
->source() == viewport() )
209 e
->acceptProposedAction();
213 QueueList::dropEvent( QDropEvent
*e
)
215 debug() << "dragDropEvent()" << endl
;
216 if( e
->source() == viewport() )
218 QListWidget::dropEvent( e
);
223 QListWidgetItem
*after
= itemAt( e
->pos() );
225 QueueManager::instance()->addItems( after
);
230 //////////////////////////////////////////////////////////////////////////////////////////
231 /// CLASS QueueManager
232 //////////////////////////////////////////////////////////////////////////////////////////
234 QueueManager
*QueueManager::s_instance
= 0;
236 QueueManager::QueueManager( QWidget
*parent
, const char *name
)
239 setObjectName( name
);
241 setButtons( Ok
|Apply
|Cancel
);
242 setDefaultButton( Ok
);
243 showButtonSeparator( true );
247 // Gives the window a small title bar, and skips a taskbar entry
249 KWindowSystem::setType( winId(), NET::Utility
);
250 KWindowSystem::setState( winId(), NET::SkipTaskbar
);
253 kapp
->setTopWidget( this );
254 setCaption( KDialog::makeStandardCaption( i18n("Queue Manager") ) );
255 setInitialSize( QSize( 400, 260 ) );
257 KVBox
*mainBox
= new KVBox( this );
258 setMainWidget( mainBox
);
260 KHBox
*box
= new KHBox( mainWidget() );
261 box
->setSpacing( 5 );
262 m_listview
= new QueueList( box
);
264 KVBox
*buttonBox
= new KVBox( box
);
265 m_up
= new KPushButton( KGuiItem( QString(), "up" ), buttonBox
);
266 m_down
= new KPushButton( KGuiItem( QString(), "down" ), buttonBox
);
267 m_remove
= new KPushButton( KGuiItem( QString(), Amarok::icon( "dequeue_track" ) ), buttonBox
);
268 m_add
= new KPushButton( KGuiItem( QString(), Amarok::icon( "queue_track" ) ), buttonBox
);
269 m_clear
= new KPushButton( KGuiItem( QString(), Amarok::icon( "playlist_clear" ) ), buttonBox
);
271 m_up
->setToolTip( i18n( "Move up" ) );
272 m_down
->setToolTip( i18n( "Move down" ) );
273 m_remove
->setToolTip( i18n( "Remove" ) );
274 m_add
->setToolTip( i18n( "Enqueue track" ) );
275 m_clear
->setToolTip( i18n( "Clear queue" ) );
277 m_up
->setEnabled( false );
278 m_down
->setEnabled( false );
279 m_remove
->setEnabled( false );
280 m_add
->setEnabled( false );
281 m_clear
->setEnabled( false );
283 connect( m_up
, SIGNAL( clicked() ), m_listview
, SLOT( moveSelectedUp() ) );
284 connect( m_down
, SIGNAL( clicked() ), m_listview
, SLOT( moveSelectedDown() ) );
285 connect( m_remove
, SIGNAL( clicked() ), this, SLOT( removeSelected() ) );
286 connect( m_add
, SIGNAL( clicked() ), this, SLOT( addItems() ) );
287 connect( m_clear
, SIGNAL( clicked() ), m_listview
, SLOT( clear() ) );
289 Playlist
*pl
= Playlist::instance();
290 connect( pl
, SIGNAL( selectionChanged() ), SLOT( updateButtons() ) );
291 connect( m_listview
, SIGNAL( selectionChanged() ), SLOT( updateButtons() ) );
292 connect( pl
, SIGNAL( queueChanged(const QList
<PlaylistItem
*> &, const QList
<PlaylistItem
*> &) ),
293 SLOT( changeQueuedItems(const QList
<PlaylistItem
*> &, const QList
<PlaylistItem
*> &) ) );
294 connect( this, SIGNAL( applyClicked()), SLOT( applyNow() ) );
295 connect( m_listview
, SIGNAL( changed() ), this, SLOT ( changed() ) );
296 s_instance
->enableButtonApply(false);
301 QueueManager::~QueueManager()
307 QueueManager::applyNow()
309 Playlist
*pl
= Playlist::instance();
310 pl
->changeFromQueueManager( newQueue() );
311 s_instance
->enableButtonApply(false);
315 QueueManager::addItems( QListWidgetItem
*after
)
318 HACK!!!!! We can know which items where dragged since they should still be selected
320 - Dragging items from the playlist provides urls
321 - Providing urls, requires iterating through the entire list in order to find which
322 item was selected. Possibly a very expensive task - worst case: O(n)
323 - After a drag, those items are still selected in the playlist, so we can find out
324 which PlaylistItems were dragged by selectedItems();
327 after
= m_listview
->item( m_listview
->count() - 1 );
329 QList
<Q3ListViewItem
*> list
= Playlist::instance()->selectedItems();
331 bool item_added
= false;
332 for( QList
<Q3ListViewItem
*>::const_iterator it
= list
.begin(); it
!= list
.end(); ++it
) {
333 #define item static_cast<PlaylistItem*>(*it)
334 const QList
<PlaylistItem
*> current
= m_map
.values();
336 if( !current
.contains( item
) ) //avoid duplication
338 QString title
= i18n("%1 - %2", item
->artist(), item
->title() );
340 QListWidgetItem
* createdItem
= new QueueItem( title
);
341 m_listview
->insertItem( m_listview
->row( after
+1 ), createdItem
);
342 m_map
[ createdItem
] = item
;
349 emit m_listview
->changed();
353 QueueManager::changeQueuedItems( const QList
<PlaylistItem
*> &in
, const QList
<PlaylistItem
*> &out
) //SLOT
355 foreach( PlaylistItem
* it
, in
)
357 foreach( PlaylistItem
* it
, out
)
358 removeQueuedItem( it
);
362 QueueManager::addQueuedItem( PlaylistItem
*item
)
364 Playlist
*pl
= Playlist::instance();
365 if( !pl
) return; //should never happen
367 const int index
= pl
->m_nextTracks
.indexOf( item
);
369 QListWidgetItem
*after
;
370 if( !index
) after
= 0;
373 int find
= m_listview
->count();
374 if( index
- 1 <= find
)
376 after
= m_listview
->item( find
);
379 const QString title
= i18n("%1 - %2", item
->artist(), item
->title() );
380 const QList
<PlaylistItem
*> current
= m_map
.values();
382 if( !current
.contains( item
) ) //avoid duplication
384 QueueItem
* createdItem
= new QueueItem( title
);
385 m_listview
->insertItem( m_listview
->row( after
+1 ), createdItem
);
386 m_map
[ createdItem
] = item
;
391 QueueManager::removeQueuedItem( PlaylistItem
*item
)
393 Playlist
*pl
= Playlist::instance();
394 if( !pl
) return; //should never happen
396 const int index
= pl
->m_nextTracks
.indexOf( item
);
397 QListWidgetItem
*after
;
398 if( !index
) after
= 0;
401 int find
= m_listview
->count();
402 if( index
- 1 <= find
)
404 after
= m_listview
->item( find
);
407 const QString title
= i18n("%1 - %2", item
->artist(), item
->title() );
408 const QList
<QListWidgetItem
*> items
= m_listview
->findItems( title
, 0 );
410 if( items
.count() > 0 )
412 QListWidgetItem
*removableItem
= items
.first();
413 //Remove the key from the map, so we can re-queue the item
414 QMapIterator
<QListWidgetItem
*, PlaylistItem
*> it(m_map
);
415 while( it
.hasNext() )
418 if( it
.value() == item
)
420 m_map
.remove( it
.key() );
422 //Remove the item from the queuelist
423 m_listview
->takeItem( m_listview
->row( removableItem
) );
424 delete removableItem
;
431 /// Playlist uses this to determine the altered queue and reflect the changes.
434 QueueManager::newQueue()
436 QList
<PlaylistItem
*> queue
;
437 for( int i
= 0; i
< m_listview
->count(); ++i
)
439 queue
.append( m_map
[ m_listview
->item( i
) ] );
445 QueueManager::insertItems()
447 QList
<PlaylistItem
*> list
= Playlist::instance()->m_nextTracks
;
448 //QListWidgetItem *last = 0;
450 foreach( PlaylistItem
*item
, list
)
452 QString title
= i18n("%1 - %2", item
->artist(), item
->title() );
454 QueueItem
* createdItem
= new QueueItem( title
);
455 m_listview
->addItem( createdItem
);
456 m_map
[ createdItem
] = item
;
457 // last = createdItem;
464 QueueManager::changed() // SLOT
466 s_instance
->enableButtonApply(true);
471 QueueManager::removeSelected() //SLOT
473 QList
<QListWidgetItem
*> selected
= m_listview
->selectedItems();
475 bool item_removed
= false;
477 foreach( QListWidgetItem
*item
, selected
)
479 //Remove the key from the map, so we can re-queue the item
480 QMap
<QListWidgetItem
*, PlaylistItem
*>::iterator it
= m_map
.find( item
);
484 //Remove the item from the queuelist
485 m_listview
->takeItem( m_listview
->row( item
) );
491 emit m_listview
->changed();
495 QueueManager::updateButtons() //SLOT
497 const bool enablePL
= !Playlist::instance()->selectedItems().isEmpty();
498 const bool emptyLV
= m_listview
->isEmpty();
499 const bool enableQL
= m_listview
->hasSelection() && !emptyLV
;
501 m_up
->setEnabled( enableQL
);
502 m_down
->setEnabled( enableQL
);
503 m_remove
->setEnabled( enableQL
);
504 m_add
->setEnabled( enablePL
);
505 m_clear
->setEnabled( !emptyLV
);
508 #include "queuemanager.moc"