/*************************************************************************** * copyright : (C) 2005 Seb Ruiz * **************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #define DEBUG_PREFIX "QueueManager" #include "debug.h" #include "amarok.h" #include "amarokconfig.h" //check if dynamic mode #include "playlist.h" #include "queuemanager.h" #include #include #include #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////////////////// /// CLASS QueueItem ////////////////////////////////////////////////////////////////////////////////////////// void QueueItem::paintCell( TQPainter *p, const TQColorGroup &cg, int column, int width, int align ) { TDEListViewItem::paintCell( p, cg, column, width, align ); TQString str = TQString::number( ( static_cast( listView() ) )->itemIndex( this ) + 1 ); //draw the symbol's outline uint fw = p->fontMetrics().width( str ) + 2; const uint w = 16; //keep this even const uint h = height() - 2; p->setBrush( cg.highlight() ); p->setPen( cg.highlight().dark() ); //TODO blend with background color p->drawEllipse( width - fw - w/2, 1, w, h ); p->drawRect( width - fw, 1, fw, h ); p->setPen( cg.highlight() ); p->drawLine( width - fw, 2, width - fw, h - 1 ); fw += 2; //add some more padding p->setPen( cg.highlightedText() ); p->drawText( width - fw, 2, fw, h-1, TQt::AlignCenter, str ); } ////////////////////////////////////////////////////////////////////////////////////////// /// CLASS QueueList ////////////////////////////////////////////////////////////////////////////////////////// QueueList::QueueList( TQWidget *parent, const char *name ) : TDEListView( parent, name ) { addColumn( i18n("Name") ); setResizeMode( TQListView::LastColumn ); setSelectionMode( TQListView::Extended ); setSorting( -1 ); setAcceptDrops( true ); setDragEnabled( true ); setDropVisualizer( true ); //the visualizer (a line marker) is drawn when dragging over tracks setDropVisualizerWidth( 3 ); } void QueueList::viewportPaintEvent( TQPaintEvent *e ) { if( e ) TDEListView::viewportPaintEvent( e ); if( !childCount() && e ) { TQPainter p( viewport() ); TQString minimumText(i18n( "
" "

The Queue Manager

" "To create a queue, " "drag tracks from the playlist, and " "drop them here.

" "Drag and drop tracks within the manager to resort queue orders." "
" ) ); TQSimpleRichText t( minimumText, TQApplication::font() ); if ( t.width()+30 >= viewport()->width() || t.height()+30 >= viewport()->height() ) //too big, giving up return; const uint w = t.width(); const uint h = t.height(); const uint x = (viewport()->width() - w - 30) / 2 ; const uint y = (viewport()->height() - h - 30) / 2 ; p.setBrush( colorGroup().background() ); p.drawRoundRect( x, y, w+30, h+30, (8*200)/w, (8*200)/h ); t.draw( &p, x+15, y+15, TQRect(), colorGroup() ); } } void QueueList::keyPressEvent( TQKeyEvent *e ) { switch( e->key() ) { case Key_Delete: //remove removeSelected(); break; case CTRL+Key_Up: moveSelectedUp(); break; case CTRL+Key_Down: moveSelectedDown(); break; } } bool QueueList::hasSelection() { TQListViewItemIterator it( this, TQListViewItemIterator::Selected ); if( !it.current() ) return false; return true; } TQPtrList QueueList::selectedItems() { TQPtrList selected; TQListViewItemIterator it( this, TQListViewItemIterator::Selected ); for( ; it.current(); ++it ) selected.append( it.current() ); return selected; } void QueueList::moveSelectedUp() // SLOT { TQPtrList selected = selectedItems(); bool item_moved = false; // Whilst it would be substantially faster to do this: ((*it)->itemAbove())->move( *it ), // this would only work for sequentially ordered items for( TQListViewItem *item = selected.first(); item; item = selected.next() ) { if( item == itemAtIndex(0) ) continue; TQListViewItem *after; item == itemAtIndex(1) ? after = 0: after = ( item->itemAbove() )->itemAbove(); moveItem( item, 0, after ); item_moved = true; } ensureItemVisible( selected.first() ); if( item_moved ) emit changed(); } void QueueList::moveSelectedDown() // SLOT { TQPtrList list = selectedItems(); bool item_moved = false; for( TQListViewItem *item = list.last(); item; item = list.prev() ) { TQListViewItem *after = item->nextSibling(); if( !after ) continue; moveItem( item, 0, after ); item_moved = true; } ensureItemVisible( list.last() ); if( item_moved ) emit changed(); } void QueueList::removeSelected() //SLOT { setSelected( currentItem(), true ); bool item_removed = false; TQPtrList selected = selectedItems(); for( TQListViewItem *item = selected.first(); item; item = selected.next() ) { delete item; item_removed = true; } if( isEmpty() ) QueueManager::instance()->updateButtons(); if( item_removed ) emit changed(); } void QueueList::clear() // SLOT { TDEListView::clear(); emit changed(); } void QueueList::contentsDragEnterEvent( TQDragEnterEvent *e ) { debug() << "contentsDrageEnterEvent()" << endl; e->accept( e->source() == reinterpret_cast( Playlist::instance() )->viewport() ); } void QueueList::contentsDragMoveEvent( TQDragMoveEvent *e ) { debug() << "contentsDrageMoveEvent()" << endl; TDEListView::contentsDragMoveEvent( e ); // Must be overloaded for dnd to work e->accept( ( e->source() == reinterpret_cast( Playlist::instance() )->viewport() ) || e->source() == viewport() ); } void QueueList::contentsDropEvent( TQDropEvent *e ) { debug() << "contentsDragDropEvent()" << endl; if( e->source() == viewport() ) { TDEListView::contentsDropEvent( e ); emit changed(); } else { TQListViewItem *parent = 0; TQListViewItem *after; findDrop( e->pos(), parent, after ); QueueManager::instance()->addItems( after ); } } ////////////////////////////////////////////////////////////////////////////////////////// /// CLASS QueueManager ////////////////////////////////////////////////////////////////////////////////////////// QueueManager *QueueManager::s_instance = 0; QueueManager::QueueManager( TQWidget *parent, const char *name ) : KDialogBase( KDialogBase::Swallow, 0, parent, name, false, 0, Ok|Apply|Cancel ) { s_instance = this; // Gives the window a small title bar, and skips a taskbar entry KWin::setType( winId(), NET::Utility ); KWin::setState( winId(), NET::SkipTaskbar ); kapp->setTopWidget( this ); setCaption( kapp->makeStdCaption( i18n("Queue Manager") ) ); setInitialSize( TQSize( 400, 260 ) ); TQVBox *mainBox = new TQVBox( this ); setMainWidget( mainBox ); TQHBox *box = new TQHBox( mainWidget() ); box->setSpacing( 5 ); m_listview = new QueueList( box ); TQVBox *buttonBox = new TQVBox( box ); m_up = new KPushButton( KGuiItem( TQString(), "go-up" ), buttonBox ); m_down = new KPushButton( KGuiItem( TQString(), "go-down" ), buttonBox ); m_remove = new KPushButton( KGuiItem( TQString(), Amarok::icon( "dequeue_track" ) ), buttonBox ); m_add = new KPushButton( KGuiItem( TQString(), Amarok::icon( "queue_track" ) ), buttonBox ); m_clear = new KPushButton( KGuiItem( TQString(), Amarok::icon( "playlist_clear" ) ), buttonBox ); TQToolTip::add( m_up, i18n( "Move up" ) ); TQToolTip::add( m_down, i18n( "Move down" ) ); TQToolTip::add( m_remove, i18n( "Remove" ) ); TQToolTip::add( m_add, i18n( "Enqueue track" ) ); TQToolTip::add( m_clear, i18n( "Clear queue" ) ); m_up->setEnabled( false ); m_down->setEnabled( false ); m_remove->setEnabled( false ); m_add->setEnabled( false ); m_clear->setEnabled( false ); connect( m_up, TQT_SIGNAL( clicked() ), m_listview, TQT_SLOT( moveSelectedUp() ) ); connect( m_down, TQT_SIGNAL( clicked() ), m_listview, TQT_SLOT( moveSelectedDown() ) ); connect( m_remove, TQT_SIGNAL( clicked() ), this, TQT_SLOT( removeSelected() ) ); connect( m_add, TQT_SIGNAL( clicked() ), this, TQT_SLOT( addItems() ) ); connect( m_clear, TQT_SIGNAL( clicked() ), m_listview, TQT_SLOT( clear() ) ); Playlist *pl = Playlist::instance(); connect( pl, TQT_SIGNAL( selectionChanged() ), TQT_SLOT( updateButtons() ) ); connect( m_listview, TQT_SIGNAL( selectionChanged() ), TQT_SLOT( updateButtons() ) ); connect( pl, TQT_SIGNAL( queueChanged(const PLItemList &, const PLItemList &) ), TQT_SLOT( changeQueuedItems(const PLItemList &, const PLItemList &) ) ); connect( this, TQT_SIGNAL( applyClicked()), TQT_SLOT( applyNow() ) ); connect( m_listview, TQT_SIGNAL( changed() ), this, TQT_SLOT ( changed() ) ); s_instance->enableButtonApply(false); insertItems(); } QueueManager::~QueueManager() { s_instance = 0; } void QueueManager::applyNow() { Playlist *pl = Playlist::instance(); pl->changeFromQueueManager( newQueue() ); s_instance->enableButtonApply(false); } void QueueManager::addItems( TQListViewItem *after ) { /* HACK!!!!! We can know which items where dragged since they should still be selected I do this, because: - Dragging items from the playlist provides urls - Providing urls, requires iterating through the entire list in order to find which item was selected. Possibly a very expensive task - worst case: O(n) - After a drag, those items are still selected in the playlist, so we can find out which PlaylistItems were dragged by selectedItems(); */ if( !after ) after = m_listview->lastChild(); TQPtrList list = Playlist::instance()->selectedItems(); bool item_added = false; for( TQListViewItem *item = list.first(); item; item = list.next() ) { #define item static_cast(item) TQValueList current = m_map.values(); if( current.find( item ) == current.end() ) //avoid duplication { TQString title = i18n("%1 - %2").arg( item->artist(), item->title() ); after = new QueueItem( m_listview, after, title ); m_map[ after ] = item; item_added = true; } #undef item } if( item_added ) emit m_listview->changed(); } void QueueManager::changeQueuedItems( const PLItemList &in, const PLItemList &out ) //SLOT { TQPtrListIterator it(in); for( it.toFirst(); it; ++it ) addQueuedItem( *it ); it = TQPtrListIterator(out); for( it.toFirst(); it; ++it ) removeQueuedItem( *it ); } void QueueManager::addQueuedItem( PlaylistItem *item ) { Playlist *pl = Playlist::instance(); if( !pl ) return; //should never happen const int index = pl->m_nextTracks.findRef( item ); TQListViewItem *after; if( !index ) after = 0; else { int find = m_listview->childCount(); if( index - 1 <= find ) find = index - 1; after = m_listview->itemAtIndex( find ); } TQValueList current = m_map.values(); TQValueListIterator newItem = current.find( item ); TQString title = i18n("%1 - %2").arg( item->artist(), item->title() ); if( newItem == current.end() ) //avoid duplication { after = new QueueItem( m_listview, after, title ); m_map[ after ] = item; } } void QueueManager::removeQueuedItem( PlaylistItem *item ) { Playlist *pl = Playlist::instance(); if( !pl ) return; //should never happen TQValueList current = m_map.values(); TQValueListIterator newItem = current.find( item ); TQString title = i18n("%1 - %2").arg( item->artist(), item->title() ); TQListViewItem *removableItem = m_listview->findItem( title, 0 ); if( removableItem ) { //Remove the key from the map, so we can re-queue the item TQMapIterator end( m_map.end() ); for( TQMapIterator it = m_map.begin(); it != end; ++it ) { if( it.data() == item ) { m_map.remove( it ); //Remove the item from the queuelist m_listview->takeItem( removableItem ); delete removableItem; return; } } } } /// Playlist uses this to determine the altered queue and reflect the changes. TQPtrList QueueManager::newQueue() { TQPtrList queue; for( TQListViewItem *key = m_listview->firstChild(); key; key = key->nextSibling() ) { queue.append( m_map[ key ] ); } return queue; } void QueueManager::insertItems() { TQPtrList list = Playlist::instance()->m_nextTracks; TQListViewItem *last = 0; for( PlaylistItem *item = list.first(); item; item = list.next() ) { TQString title = i18n("%1 - %2").arg( item->artist(), item->title() ); last = new QueueItem( m_listview, last, title ); m_map[ last ] = item; } updateButtons(); } void QueueManager::changed() // SLOT { s_instance->enableButtonApply(true); } void QueueManager::removeSelected() //SLOT { TQPtrList selected = m_listview->selectedItems(); bool item_removed = false; for( TQListViewItem *item = selected.first(); item; item = selected.next() ) { //Remove the key from the map, so we can re-queue the item TQMapIterator it = m_map.find( item ); m_map.remove( it ); //Remove the item from the queuelist m_listview->takeItem( item ); delete item; item_removed = true; } if( item_removed ) emit m_listview->changed(); } void QueueManager::updateButtons() //SLOT { const bool enablePL = !Playlist::instance()->selectedItems().isEmpty(); const bool emptyLV = m_listview->isEmpty(); const bool enableQL = m_listview->hasSelection() && !emptyLV; m_up->setEnabled( enableQL ); m_down->setEnabled( enableQL ); m_remove->setEnabled( enableQL ); m_add->setEnabled( enablePL ); m_clear->setEnabled( !emptyLV ); } #include "queuemanager.moc"