Not crap after all...
[amarok.git] / src / queuemanager.cpp
blobb20da862019f65464d23d995c8c81e4a1462c606
1 /***************************************************************************
2 * copyright : (C) 2005 Seb Ruiz <me@sebruiz.net> *
3 **************************************************************************/
5 /***************************************************************************
6 * *
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. *
11 * *
12 ***************************************************************************/
14 #define DEBUG_PREFIX "QueueManager"
15 #include "debug.h"
17 #include "amarok.h"
18 #include "amarokconfig.h" //check if dynamic mode
19 #include "playlist.h"
20 #include "queuemanager.h"
22 #include <QPainter>
23 #include <QTextDocument>
24 #include <QToolTip>
26 #include <k3urldrag.h>
27 #include <kapplication.h>
28 #include <kguiitem.h>
29 #include <klocale.h>
30 #include <kpushbutton.h>
31 #include <kvbox.h>
32 #include <kwindowsystem.h>
34 #if 0
35 //////////////////////////////////////////////////////////////////////////////////////////
36 /// CLASS QueueItem
37 //////////////////////////////////////////////////////////////////////////////////////////
38 void
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 );
61 #endif
63 //////////////////////////////////////////////////////////////////////////////////////////
64 /// CLASS QueueList
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 );
80 void
81 QueueList::paintEvent( QPaintEvent *e )
83 if( e ) QListWidget::paintEvent( e );
85 if( !count() && e )
87 QPainter p( viewport() );
88 QString minimumText(i18n(
89 "<div align=center>"
90 "<h3>The Queue Manager</h3>"
91 "To create a queue, "
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."
95 "</div>" ) );
96 QTextDocument t;
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() )
101 //too big, giving up
102 return;
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() );
114 void
115 QueueList::keyPressEvent( QKeyEvent *e )
117 switch( e->key() ) {
119 case Qt::Key_Delete: //remove
120 removeSelected();
121 break;
123 case Qt::ControlModifier+Qt::Key_Up:
124 moveSelectedUp();
125 break;
127 case Qt::ControlModifier+Qt::Key_Down:
128 moveSelectedDown();
129 break;
133 bool
134 QueueList::hasSelection()
136 return selectedItems().size() == 0;
139 void
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 ) )
151 continue;
152 insertItem( position + direction, takeItem( position ) );
153 item_moved = true;
156 scrollToItem( selected.first() ); //apparently this deselects?
157 foreach( QListWidgetItem* it, selected )
158 it->setSelected( true );
160 if( item_moved )
161 emit changed();
164 void
165 QueueList::removeSelected() //SLOT
167 currentItem()->setSelected( true );
169 bool item_removed = false;
170 QList<QListWidgetItem* > selected = selectedItems();
172 foreach( QListWidgetItem* item, selected )
174 delete item;
175 item_removed = true;
178 if( count() == 0 )
179 QueueManager::instance()->updateButtons();
181 if( item_removed )
182 emit changed();
185 void
186 QueueList::clear() // SLOT
188 QListWidget::clear();
189 emit changed();
192 void
193 QueueList::dragEnterEvent( QDragEnterEvent *e )
195 debug() << "dragEnterEvent()" << endl;
196 if( e->source() == reinterpret_cast<K3ListView*>( Playlist::instance() )->viewport() )
197 e->acceptProposedAction();
200 void
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();
212 void
213 QueueList::dropEvent( QDropEvent *e )
215 debug() << "dragDropEvent()" << endl;
216 if( e->source() == viewport() )
218 QListWidget::dropEvent( e );
219 emit changed();
221 else
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 )
237 : KDialog( parent )
239 setObjectName( name );
240 setModal( false );
241 setButtons( Ok|Apply|Cancel );
242 setDefaultButton( Ok );
243 showButtonSeparator( true );
245 s_instance = this;
247 // Gives the window a small title bar, and skips a taskbar entry
248 #ifdef Q_WS_X11
249 KWindowSystem::setType( winId(), NET::Utility );
250 KWindowSystem::setState( winId(), NET::SkipTaskbar );
251 #endif
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);
298 insertItems();
301 QueueManager::~QueueManager()
303 s_instance = 0;
306 void
307 QueueManager::applyNow()
309 Playlist *pl = Playlist::instance();
310 pl->changeFromQueueManager( newQueue() );
311 s_instance->enableButtonApply(false);
314 void
315 QueueManager::addItems( QListWidgetItem *after )
318 HACK!!!!! We can know which items where dragged since they should still be selected
319 I do this, because:
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();
326 if( !after )
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;
343 item_added = true;
345 #undef item
348 if( item_added )
349 emit m_listview->changed();
352 void
353 QueueManager::changeQueuedItems( const QList<PlaylistItem*> &in, const QList<PlaylistItem*> &out ) //SLOT
355 foreach( PlaylistItem* it, in )
356 addQueuedItem( it );
357 foreach( PlaylistItem* it, out )
358 removeQueuedItem( it );
361 void
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;
371 else
373 int find = m_listview->count();
374 if( index - 1 <= find )
375 find = index - 1;
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;
390 void
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;
399 else
401 int find = m_listview->count();
402 if( index - 1 <= find )
403 find = index - 1;
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() )
417 it.next();
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;
425 return;
431 /// Playlist uses this to determine the altered queue and reflect the changes.
433 QList<PlaylistItem*>
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 ) ] );
441 return queue;
444 void
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;
460 updateButtons();
463 void
464 QueueManager::changed() // SLOT
466 s_instance->enableButtonApply(true);
470 void
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 );
482 m_map.erase( it );
484 //Remove the item from the queuelist
485 m_listview->takeItem( m_listview->row( item ) );
486 delete item;
487 item_removed = true;
490 if( item_removed )
491 emit m_listview->changed();
494 void
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"