1 // (c) 2004 Christian Muehlhaeuser <chris@chris.de>
2 // (c) 2005-2006 Martin Aumueller <aumuell@reserv.at>
3 // (c) 2005 Seb Ruiz <ruiz@kde.org>
4 // (c) 2006 T.R.Shashwath <trshash84@gmail.com>
5 // See COPYING file for licensing information
8 #define DEBUG_PREFIX "MediaBrowser"
10 #include "mediabrowser.h"
12 #include "config-amarok.h"
15 #include "amarokconfig.h"
17 #include "browserToolBar.h"
18 #include "collectiondb.h"
20 #include "editfilterdialog.h"
21 #include "deviceconfiguredialog.h"
22 #include "expression.h"
23 #include "hintlineedit.h"
24 #include "MediaItem.h"
25 #include "MediaDevice.h"
26 #include "MediaDeviceCache.h"
28 //#include "mediumpluginmanager.h"
29 #include "metabundle.h"
30 #include "mountpointmanager.h"
31 #include "playlist/PlaylistModel.h"
32 #include "pluginmanager.h"
33 #include "podcastbundle.h"
34 #include "scriptmanager.h"
35 #include "scrobbler.h"
36 #include "searchwidget.h"
37 #include "statusbar.h"
38 #include "transferdialog.h"
39 #include "TheInstances.h"
42 #include <Q3PopupMenu>
43 #include <q3simplerichtext.h>
48 #include <QDomDocument>
49 #include <QDomElement>
58 #include <QListIterator>
61 #include <QPaintEvent>
63 #include <QProgressBar>
64 #include <QRadioButton>
66 #include <QToolButton>
67 #include <QToolTip> //QToolTip::add()
69 #include <k3multipledrag.h>
70 #include <k3process.h>
71 #include <k3tempfile.h>
72 #include <k3urldrag.h> //dragObject()
73 #include <KActionCollection>
74 #include <KApplication> //kapp
77 #include <KFileDialog>
79 #include <KIconLoader>
80 #include <KInputDialog>
84 #include <KMessageBox>
85 #include <KPushButton>
87 #include <KStandardDirs> //locate file
89 #include <solid/device.h>
90 #include <solid/deviceinterface.h>
91 #include <solid/devicenotifier.h>
92 #include <solid/portablemediaplayer.h>
95 MediaBrowser
*MediaBrowser::s_instance
= 0;
97 bool MediaBrowser::isAvailable() //static
99 if( !MediaBrowser::instance() )
104 //to re-enable hiding, uncomment this and get rid of the return true above:
105 //return MediaBrowser::instance()->m_haveDevices;
108 class DummyMediaDevice
: public MediaDevice
111 DummyMediaDevice() : MediaDevice()
113 m_name
= i18n( "No Device Available" );
114 m_type
= "dummy-mediadevice";
115 m_uid
= "manual|DummyDevice|none|none";
117 void init( MediaBrowser
*browser
) { MediaDevice::init( browser
); }
118 virtual ~DummyMediaDevice() {}
119 virtual bool isConnected() { return false; }
120 virtual MediaItem
* trackExists(const MetaBundle
&) { return 0; }
121 virtual bool lockDevice(bool) { return true; }
122 virtual void unlockDevice() {}
123 virtual bool openDevice( bool silent
)
127 //QString msg = i18n( "Sorry, you do not have a supported portable music player." );
128 //Amarok::StatusBar::instance()->longMessage( msg, KDE::StatusBar::Sorry );
132 virtual bool closeDevice() { return false; }
133 virtual void synchronizeDevice() {}
134 virtual MediaItem
* copyTrackToDevice(const MetaBundle
&) { return 0; }
135 virtual int deleteItemFromDevice(MediaItem
*, int) { return -1; }
139 MediaBrowser::MediaBrowser( const char * /*name*/ )
141 , m_timer( new QTimer( this ) )
142 , m_currentDevice( 0 )
143 , m_waitForTranscode( false )
144 , m_quitting( false )
145 , m_connectAction( 0 )
146 , m_disconnectAction( 0 )
147 , m_customAction( 0 )
148 , m_configAction( 0 )
149 , m_transferAction( 0 )
153 m_timer
->setSingleShot( true );
155 m_toolbar
= new Browser::ToolBar( this );
156 m_toolbar
->setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Minimum
);
157 m_toolbar
->setToolButtonStyle( Qt::ToolButtonTextBesideIcon
);
159 //TODO: how to fix getButton
160 // perhaps the action can be referenced instead?
161 m_connectAction
= new KAction(KIcon("connect_creating"), i18n("Connect"), this);
162 connect(m_connectAction
, SIGNAL(triggered()), this, SLOT(connectClicked()));
163 m_toolbar
->addAction(m_connectAction
);
164 // m_toolbar->insertButton( "connect_creating", CONNECT, true, i18n("Connect") );
165 // m_toolbar->getButton(CONNECT)->setToolTip( i18n( "Connect media device" ) );
167 m_disconnectAction
= new KAction(KIcon("media-eject"), i18n("Disconnect"), this);
168 connect(m_disconnectAction
, SIGNAL(triggered()), this, SLOT(disconnectClicked()));
169 m_toolbar
->addAction(m_disconnectAction
);
170 // m_toolbar->insertButton( "media-eject", DISCONNECT, true, i18n("Disconnect") );
171 // m_toolbar->getButton(DISCONNECT)->setToolTip( i18n( "Disconnect media device" ) );
173 m_transferAction
= new KAction(KIcon("rebuild"), i18n("Transfer"), this);
174 connect(m_transferAction
, SIGNAL(triggered()), this, SLOT(transferClicked()));
175 m_toolbar
->addAction(m_transferAction
);
176 // m_toolbar->insertButton( "rebuild", TRANSFER, true, i18n("Transfer") );
177 // m_toolbar->getButton(TRANSFER)->setToolTip( i18n( "Transfer tracks to media device" ) );
179 m_toolbar
->addSeparator();
181 // m_toolbar->setIconText( KToolBar::IconTextRight, true );
182 m_customAction
= new KAction(KIcon( "add_playlist" ), i18n("custom"), this);
183 connect(m_customAction
, SIGNAL(triggered()), this, SLOT(customClicked()));
184 m_customAction
->setText( i18n("Special Device Functions") );
185 m_customAction
->setToolTip( i18n("Device-specific special functions or information") );
186 m_toolbar
->addAction(m_customAction
);
187 // m_toolbar->insertButton( Amarok::icon( "add_playlist" ), CUSTOM, SIGNAL( clicked() ), this, SLOT( customClicked() ), true, "custom" );
188 // m_toolbar->getButton(TRANSFER)->setToolTip( i18n( "Transfer tracks to media device" ) );
190 m_toolbar
->setToolButtonStyle( Qt::ToolButtonIconOnly
);
192 m_configAction
= new KAction(KIcon("configure"), i18n("Configure"), this);
193 connect(m_configAction
, SIGNAL(triggered()), this, SLOT(config()));
194 m_toolbar
->addAction(m_configAction
);
195 // m_toolbar->insertButton( Amarok::icon( "configure" ), CONFIGURE, true, i18n("Configure") );
196 // m_toolbar->getButton(CONFIGURE)->setToolTip( i18n( "Configure device" ) );
199 m_deviceCombo
= new KComboBox( this );
201 // searching/filtering
202 QToolBar
* searchToolBar
= new Browser::ToolBar( this );
203 searchToolBar
->setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Minimum
);
204 m_searchWidget
= new SearchWidget( searchToolBar
, this );
205 searchToolBar
->addWidget( m_searchWidget
);
206 connect( m_timer
, SIGNAL( timeout() ), SLOT( slotSetFilter() ) );
208 // connect to device cache
209 connect( MediaDeviceCache::instance(), SIGNAL( deviceAdded(const QString
&) ),
210 SLOT( deviceAdded(const QString
&) ) );
211 connect( MediaDeviceCache::instance(), SIGNAL( deviceRemoved(const QString
&) ),
212 SLOT( deviceRemoved(const QString
&) ) );
215 // we always have a dummy device
216 m_pluginName
[ i18n( "Disable" ) ] = "dummy-mediadevice";
217 m_pluginAmarokName
["dummy-mediadevice"] = i18n( "Disable" );
218 m_pluginName
[ i18n( "Do not handle" ) ] = "ignore";
219 m_pluginAmarokName
["ignore"] = i18n( "Do not handle" );
220 // query available device plugins
221 m_plugins
= PluginManager::query( "[X-KDE-Amarok-plugintype] == 'mediadevice'" );
222 for( KService::List::ConstIterator it
= m_plugins
.begin(); it
!= m_plugins
.end(); ++it
) {
223 // Save name properties in QMap for lookup
224 m_pluginName
[(*it
)->name()] = (*it
)->property( "X-KDE-Amarok-name" ).toString();
225 m_pluginAmarokName
[(*it
)->property( "X-KDE-Amarok-name" ).toString()] = (*it
)->name();
228 m_views
= new KVBox( this );
229 m_queue
= new MediaQueue( this );
230 m_progressBox
= new KHBox( this );
231 m_progress
= new QProgressBar( m_progressBox
);
232 m_cancelButton
= new KPushButton( KIcon( Amarok::icon( "cancel" ) ), i18n("Cancel"), m_progressBox
);
235 m_stats
= new SpaceLabel(this);
237 m_progressBox
->hide();
239 MediaDevice
*dev
= new DummyMediaDevice();
242 activateDevice( 0, false );
244 queue()->load( Amarok::saveLocation() + "transferlist.xml" );
245 queue()->computeSize();
247 setFocusProxy( m_queue
);
251 MediaDeviceCache::instance()->refreshCache();
252 foreach( QString udi
, MediaDeviceCache::instance()->getAll() )
255 //TODO: Take generic storage devices into account too -- or do we rely on the
256 //Solid backend to tell us if it's a PMP with "storage" type?
258 connect( m_deviceCombo
, SIGNAL( activated( int ) ), SLOT( activateDevice( int ) ) );
260 connect( m_cancelButton
, SIGNAL( clicked() ), SLOT( cancelClicked() ) );
261 connect( pApp
, SIGNAL( prepareToQuit() ), SLOT( prepareToQuit() ) );
262 connect( CollectionDB::instance(), SIGNAL( tagsChanged( const MetaBundle
& ) ),
263 SLOT( tagsChanged( const MetaBundle
& ) ) );
265 //TODO: If we will be supporting manually adding devices, probably need the following section
266 /*m_haveDevices = false;
267 QMap<QString,QString> savedDevices = Amarok::config( "MediaBrowser" ).entryMap();
268 for( QMap<QString,QString>::Iterator it = savedDevices.begin();
269 it != savedDevices.end();
272 if( it.data() != "deleted" && it.data() != "ignore" )
274 m_haveDevices = true;
282 MediaBrowser::blockQuit() const
284 for( QList
<MediaDevice
*>::const_iterator it
= m_devices
.begin();
285 it
!= m_devices
.end();
288 if( *it
&& (*it
)->isConnected() )
296 MediaBrowser::tagsChanged( const MetaBundle
&bundle
)
298 m_itemMapMutex
.lock();
299 debug() << "tags changed for " << bundle
.url().url();
300 ItemMap::iterator it
= m_itemMap
.find( bundle
.url().url() );
301 if( it
!= m_itemMap
.end() )
303 MediaItem
*item
= *it
;
304 m_itemMapMutex
.unlock();
307 item
->device()->tagsChanged( item
, bundle
);
311 // it's an item on the transfer queue
312 item
->setBundle( new MetaBundle( bundle
) );
314 QString text
= item
->bundle()->prettyTitle();
315 if( text
.isEmpty() || (!item
->bundle()->isValidMedia() && !item
->bundle()->podcastBundle()) )
316 text
= item
->bundle()->url().prettyUrl();
317 if( !item
->m_playlistName
.isNull() )
319 text
+= " (" + item
->m_playlistName
+ ')';
321 item
->setText( 0, text
);
326 m_itemMapMutex
.unlock();
331 MediaBrowser::getBundle( const KUrl
&url
, MetaBundle
*bundle
) const
333 QMutexLocker
locker( &m_itemMapMutex
);
334 ItemMap::const_iterator it
= m_itemMap
.find( url
.url() );
335 if( it
== m_itemMap
.end() )
339 *bundle
= *(*it
)->bundle();
345 MediaBrowser::getProxyUrl( const KUrl
& daapUrl
) const
349 MediaDevice
* dc
= findChildren
<MediaDevice
*>( "DaapClient" ).first();
351 url
= dc
->getProxyUrl( daapUrl
);
356 MediaBrowser::deviceFromId( const QString
&id
) const
358 for( QList
<MediaDevice
*>::const_iterator it
= m_devices
.constBegin();
359 it
!= m_devices
.end();
362 if( (*it
)->uid() == id
)
370 MediaBrowser::activateDevice( const MediaDevice
*dev
)
373 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
374 it
!= m_devices
.end();
379 activateDevice( index
);
387 MediaBrowser::activateDevice( int index
, bool skipDummy
)
389 if( m_currentDevice
&& m_currentDevice
->customAction() )
391 m_toolbar
->removeAction( m_currentDevice
->customAction() );
396 foreach( MediaDevice
*md
, m_devices
)
398 if( md
&& md
->view() )
404 m_currentDevice
= m_devices
.last();
411 if( index
>= m_devices
.count() )
413 if( !m_devices
.isEmpty() )
414 m_currentDevice
= m_devices
.last();
418 queue()->computeSize();
423 m_currentDevice
= m_devices
[index
];
424 if( m_currentDevice
&& m_currentDevice
->view() )
426 m_currentDevice
->view()->show();
427 if( m_currentDevice
->customAction() )
429 m_toolbar
->setToolButtonStyle( Qt::ToolButtonTextBesideIcon
);
430 m_toolbar
->addAction( m_currentDevice
->customAction() );
435 m_deviceCombo
->setCurrentIndex( index
-1 );
438 queue()->computeSize();
443 MediaBrowser::addDevice( MediaDevice
*device
)
445 m_devices
.append( device
);
447 device
->loadConfig();
449 if( device
->autoConnect() )
451 device
->connectDevice( true );
459 MediaBrowser::removeDevice( MediaDevice
*device
)
463 debug() << "remove device: type=" << device
->deviceType();
465 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
466 it
!= m_devices
.end();
471 bool current
= ( (*it
)->uid() == m_currentDevice
->uid() );
472 m_devices
.erase( it
);
474 activateDevice( 0, false );
479 if( device
->isConnected() )
481 if( device
->disconnectDevice( false /* don't run post-disconnect command */ ) )
482 unloadDevicePlugin( device
);
485 debug() << "Cannot remove device because disconnect failed";
486 Amarok::StatusBar::instance()->longMessage(
487 i18n( "Cannot remove device because disconnect failed" ),
488 KDE::StatusBar::Warning
);
492 unloadDevicePlugin( device
);
498 MediaBrowser::updateDevices()
500 m_deviceCombo
->clear();
502 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
503 it
!= m_devices
.end();
506 if( m_devices
.count() > 1 && dynamic_cast<DummyMediaDevice
*>(*it
) )
508 QString name
= (*it
)->name();
509 if( !(*it
)->deviceNode().isEmpty() )
511 name
= i18n( "%1 at %2", name
, (*it
)->deviceNode() );
513 if( (*it
)->hasMountPoint() && !(*it
)->mountPoint().isEmpty() )
515 name
+= i18n( " (mounted at %1)", (*it
)->mountPoint() );
517 m_deviceCombo
->addItem( name
, i
);
518 if( !m_currentDevice
|| (*it
)->uid() == m_currentDevice
->uid() )
520 m_deviceCombo
->setCurrentItem( name
);
524 m_deviceCombo
->setEnabled( m_devices
.count() > 1 );
525 m_haveDevices
= m_devices
.count() > 1;
529 MediaBrowser::deviceNames() const
533 for( QList
<MediaDevice
*>::const_iterator it
= m_devices
.constBegin();
534 it
!= m_devices
.constEnd();
537 QString name
= (*it
)->name();
545 MediaBrowser::deviceSwitch( const QString
&name
)
548 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
549 it
!= m_devices
.end();
552 if( (*it
)->name() == name
)
554 activateDevice( index
, false );
564 MediaBrowser::transcodingFinished( const QString
&src
, const QString
&dst
)
566 KUrl srcJob
= KUrl( m_transcodeSrc
);
567 KUrl srcResult
= KUrl( src
);
569 if( srcJob
.path() == srcResult
.path() )
571 m_transcodedUrl
= KUrl( dst
);
572 m_waitForTranscode
= false;
576 debug() << "transcoding for " << src
<< " finished, "
577 << "but we are waiting for " << m_transcodeSrc
<< " -- aborting";
578 m_waitForTranscode
= false;
583 MediaBrowser::transcode( const KUrl
&src
, const QString
&filetype
)
585 const ScriptManager
* const sm
= ScriptManager::instance();
587 if( sm
->transcodeScriptRunning().isEmpty() )
589 debug() << "cannot transcode with no transcoder registered";
593 m_waitForTranscode
= true;
594 m_transcodeSrc
= src
.url();
595 m_transcodedUrl
= KUrl();
596 ScriptManager::instance()->notifyTranscode( src
.url(), filetype
);
598 while( m_waitForTranscode
&& !sm
->transcodeScriptRunning().isEmpty() )
601 kapp
->processEvents( QEventLoop::AllEvents
);
604 return m_transcodedUrl
;
609 MediaBrowser::slotSetFilterTimeout() //SLOT
611 m_timer
->start( 280 ); //stops the timer for us first
615 MediaBrowser::slotSetFilter() //SLOT
619 if( m_currentDevice
)
620 m_currentDevice
->view()->setFilter( m_searchWidget
->lineEdit()->text() );
624 MediaBrowser::slotSetFilter( const QString
&text
)
626 m_searchWidget
->lineEdit()->setText( text
);
631 MediaBrowser::slotEditFilter()
633 EditFilterDialog
*fd
= new EditFilterDialog( this, true, m_searchWidget
->lineEdit()->text() );
634 connect( fd
, SIGNAL(filterChanged(const QString
&)), SLOT(slotSetFilter(const QString
&)) );
636 m_searchWidget
->lineEdit()->setText( fd
->filter() );
641 MediaBrowser::prepareToQuit()
643 m_waitForTranscode
= false;
645 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
646 it
!= m_devices
.end();
649 if( (*it
)->isConnected() )
650 (*it
)->disconnectDevice( false /* don't unmount */ );
654 MediaBrowser::~MediaBrowser()
656 debug() << "having to remove " << m_devices
.count() << " devices";
657 while( !m_devices
.isEmpty() )
659 removeDevice( m_devices
.last() );
662 queue()->save( Amarok::saveLocation() + "transferlist.xml" );
664 delete m_deviceCombo
;
668 MediaView::MediaView( QWidget
* parent
, MediaDevice
*device
)
669 : K3ListView( parent
)
674 setSelectionMode( Q3ListView::Extended
);
675 setItemsMovable( false );
676 setShowSortIndicator( true );
677 setFullWidth( true );
678 setRootIsDecorated( true );
679 setDragEnabled( true );
680 setDropVisualizer( true ); //the visualizer (a line marker) is drawn when dragging over tracks
681 setDropHighlighter( true ); //and the highligther (a focus rect) is drawn when dragging over playlists
682 setDropVisualizerWidth( 3 );
683 setAcceptDrops( true );
686 addColumn( i18n( "Remote Media" ) );
688 KActionCollection
* ac
= new KActionCollection( this );
689 KStandardAction::selectAll( this, SLOT( selectAll() ), ac
);
691 connect( this, SIGNAL( contextMenuRequested( Q3ListViewItem
*, const QPoint
&, int ) ),
692 this, SLOT( rmbPressed( Q3ListViewItem
*, const QPoint
&, int ) ) );
694 connect( this, SIGNAL( itemRenamed( Q3ListViewItem
* ) ),
695 this, SLOT( renameItem( Q3ListViewItem
* ) ) );
697 connect( this, SIGNAL( expanded( Q3ListViewItem
* ) ),
698 this, SLOT( slotExpand( Q3ListViewItem
* ) ) );
700 connect( this, SIGNAL( returnPressed( Q3ListViewItem
* ) ),
701 this, SLOT( invokeItem( Q3ListViewItem
* ) ) );
703 connect( this, SIGNAL( doubleClicked( Q3ListViewItem
*, const QPoint
&, int ) ),
704 this, SLOT( invokeItem( Q3ListViewItem
*, const QPoint
&, int ) ) );
708 MediaView::keyPressEvent( QKeyEvent
*e
)
710 if( e
->key() == Qt::Key_Delete
)
711 m_device
->deleteFromDevice();
713 K3ListView::keyPressEvent( e
);
717 MediaView::invokeItem( Q3ListViewItem
* i
, const QPoint
& point
, int column
) //SLOT
722 QPoint p
= mapFromGlobal( point
);
723 if ( p
.x() > header()->sectionPos( header()->mapToIndex( 0 ) ) + treeStepSize() * ( i
->depth() + ( rootIsDecorated() ? 1 : 0) ) + itemMargin()
724 || p
.x() < header()->sectionPos( header()->mapToIndex( 0 ) ) )
730 MediaView::invokeItem( Q3ListViewItem
*i
)
732 MediaItem
*item
= static_cast<MediaItem
*>( i
);
736 KUrl::List urls
= nodeBuildDragList( item
);
737 The::playlistModel()->insertMedia( urls
, Playlist::AppendAndPlay
);
741 MediaView::renameItem( Q3ListViewItem
*item
)
743 m_device
->renameItem( item
);
747 MediaView::slotExpand( Q3ListViewItem
*item
)
749 m_device
->expandItem( item
);
753 MediaView::~MediaView()
759 MediaView::dragObject()
761 KUrl::List urls
= nodeBuildDragList( 0 );
762 K3MultipleDrag
*md
= new K3MultipleDrag( viewport() );
763 md
->addDragObject( K3ListView::dragObject() );
764 K3URLDrag
* ud
= new K3URLDrag( urls
, viewport() );
765 md
->addDragObject( ud
);
766 md
->setPixmap( CollectionDB::createDragPixmap( urls
),
767 QPoint( CollectionDB::DRAGPIXMAP_OFFSET_X
, CollectionDB::DRAGPIXMAP_OFFSET_Y
) );
773 MediaView::nodeBuildDragList( MediaItem
* item
, int flags
)
780 fi
= static_cast<MediaItem
*>(firstChild());
787 if( fi
->isVisible() )
789 if ( fi
->isSelected() || !(flags
& OnlySelected
) )
791 if( fi
->isLeafItem() || fi
->type() == MediaItem::DIRECTORY
)
795 if(fi
->childCount() )
796 items
+= nodeBuildDragList( static_cast<MediaItem
*>(fi
->firstChild()), None
);
801 if ( fi
->childCount() )
802 items
+= nodeBuildDragList( static_cast<MediaItem
*>(fi
->firstChild()), OnlySelected
);
805 fi
= static_cast<MediaItem
*>(fi
->nextSibling());
811 MediaView::getSelectedLeaves( MediaItem
*parent
, QList
<MediaItem
*> *list
, int flags
)
815 list
= new QList
<MediaItem
*>;
819 it
= static_cast<MediaItem
*>(firstChild());
821 it
= static_cast<MediaItem
*>(parent
->firstChild());
823 for( ; it
; it
= static_cast<MediaItem
*>(it
->nextSibling()))
825 if( it
->isVisible() )
827 if( it
->childCount() && !( it
->type() == MediaItem::DIRECTORY
&& it
->isSelected() ) )
830 if( it
->isSelected() )
832 numFiles
+= getSelectedLeaves(it
, list
, f
);
834 if( it
->isSelected() || !(flags
& OnlySelected
) )
836 if( it
->type() == MediaItem::TRACK
||
837 it
->type() == MediaItem::DIRECTORY
||
838 it
->type() == MediaItem::PODCASTITEM
||
839 it
->type() == MediaItem::PLAYLISTITEM
||
840 it
->type() == MediaItem::INVISIBLE
||
841 it
->type() == MediaItem::ORPHANED
)
843 if( flags
& OnlyPlayed
)
845 if( it
->played() > 0 )
851 if( ( it
->isLeafItem() && (!(flags
& OnlyPlayed
) || it
->played()>0) )
852 || it
->type() == MediaItem::DIRECTORY
)
862 MediaView::acceptDrag( QDropEvent
*e
) const
864 if( e
->source() == MediaBrowser::queue()->viewport() )
867 return e
->source() == viewport()
868 || e
->mimeData()->hasFormat( "amarok-sql" )
869 || KUrl::List::canDecode( e
->mimeData() );
873 MediaView::contentsDropEvent( QDropEvent
*e
)
875 cleanDropVisualizer();
876 cleanItemHighlighter();
878 if(e
->source() == viewport())
880 const QPoint p
= contentsToViewport( e
->pos() );
881 MediaItem
*item
= static_cast<MediaItem
*>(itemAt( p
));
883 if( !item
&& MediaBrowser::instance()->currentDevice()->m_type
!= "generic-mediadevice" )
886 QList
<MediaItem
*> items
;
888 if( !item
|| item
->type() == MediaItem::DIRECTORY
||
889 item
->type() == MediaItem::TRACK
)
891 QList
<MediaItem
*> items
;
892 getSelectedLeaves( 0, &items
);
893 m_device
->addToDirectory( item
, items
);
895 else if( item
->type() == MediaItem::PLAYLIST
)
897 MediaItem
*list
= item
;
898 MediaItem
*after
= 0;
899 for(MediaItem
*it
= static_cast<MediaItem
*>(item
->firstChild());
901 it
= static_cast<MediaItem
*>(it
->nextSibling()))
904 getSelectedLeaves( 0, &items
);
905 m_device
->addToPlaylist( list
, after
, items
);
907 else if( item
->type() == MediaItem::PLAYLISTITEM
)
909 MediaItem
*list
= static_cast<MediaItem
*>(item
->parent());
910 MediaItem
*after
= 0;
911 for(MediaItem
*it
= static_cast<MediaItem
*>(item
->parent()->firstChild());
913 it
= static_cast<MediaItem
*>(it
->nextSibling()))
920 getSelectedLeaves( 0, &items
);
921 m_device
->addToPlaylist( list
, after
, items
);
923 else if( item
->type() == MediaItem::PLAYLISTSROOT
)
925 QList
<MediaItem
*> items
;
926 getSelectedLeaves( 0, &items
);
927 QString
base( i18n("New Playlist") );
930 while( item
->findItem(name
) )
934 name
= base
+ ' ' + num
;
937 MediaItem
*pl
= m_device
->newPlaylist(name
, item
, items
);
938 ensureItemVisible(pl
);
944 if( e
->mimeData()->hasFormat( "amarok-sql" ) )
946 QString
data( e
->mimeData()->data( "amarok-sql" ) );
947 QString playlist
= data
.section( "\n", 0, 0 );
948 QString query
= data
.section( "\n", 1 );
949 QStringList values
= CollectionDB::instance()->query( query
);
950 KUrl::List list
= CollectionDB::instance()->URLsFromSqlDrag( values
);
951 MediaBrowser::queue()->addUrls( list
, playlist
);
953 else if ( KUrl::List::canDecode( e
->mimeData() ) )
955 KUrl::List list
= KUrl::List::fromMimeData( e
->mimeData() );
956 MediaBrowser::queue()->addUrls( list
);
962 MediaView::viewportPaintEvent( QPaintEvent
*e
)
964 K3ListView::viewportPaintEvent( e
);
966 // Superimpose bubble help:
968 if ( !MediaBrowser::instance()->currentDevice() || !MediaBrowser::instance()->currentDevice()->isConnected() )
970 QPainter
p( viewport() );
972 Q3SimpleRichText
t( i18n(
974 "<h3>Media Device Browser</h3>"
975 "Configure your media device and then "
976 "click the Connect button to access your media device. "
977 "Drag and drop files to enqueue them for transfer."
978 "</div>" ), QApplication::font() );
980 t
.setWidth( width() - 50 );
982 const uint w
= t
.width() + 20;
983 const uint h
= t
.height() + 20;
985 p
.setBrush( palette().background() );
986 p
.drawRoundRect( 15, 15, w
, h
, (8*200)/w
, (8*200)/h
);
987 t
.draw( &p
, 20, 20, QRect(), palette() );
989 MediaBrowser::instance()->updateButtons();
993 MediaView::rmbPressed( Q3ListViewItem
*item
, const QPoint
&p
, int i
)
995 if( m_device
->isConnected() )
996 m_device
->rmbPressed( item
, p
, i
);
1000 MediaView::newDirectory( MediaItem
*parent
)
1003 const QString name
= KInputDialog::getText(i18n("Add Directory"), i18n("Directory Name:"), QString(), &ok
, this);
1005 if( ok
&& !name
.isEmpty() )
1007 m_device
->newDirectory( name
, parent
);
1014 MediaBrowser::deviceAdded( const QString
&udi
)
1017 debug() << "deviceAdded called with a udi of: " << udi
;
1018 MediaDevice
*md
= loadDevicePlugin( udi
);
1022 if( m_currentDevice
== *(m_devices
.begin()) || m_currentDevice
== *(m_devices
.end()) )
1023 activateDevice( m_devices
.count()-1, false );
1028 MediaBrowser::deviceRemoved( const QString
&udi
)
1030 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
1031 it
!= m_devices
.end();
1034 if( (*it
)->m_uid
== udi
)
1036 if( (*it
)->isConnected() )
1038 if( (*it
)->disconnectDevice() )
1039 removeDevice( *it
);
1040 Amarok::StatusBar::instance()->longMessage(
1041 i18n( "The device %1 was removed before it was disconnected. "
1042 "In order to avoid possible data loss, press the \"Disconnect\" "
1043 "button before disconnecting the device.", (*it
)->name() ),
1044 KDE::StatusBar::Warning
);
1047 removeDevice( *it
);
1054 MediaBrowser::loadDevicePlugin( const QString
&udi
)
1062 if( MediaDeviceCache::instance()->deviceType( udi
) == MediaDeviceCache::SolidType
)
1064 debug() << "udi " << udi
<< " detected as a Solid device";
1065 Solid::Device
solidDevice( udi
);
1067 Solid::PortableMediaPlayer
* pmp
= solidDevice
.as
<Solid::PortableMediaPlayer
>();
1069 //TODO: Generic storage? need to set mount point if so...
1072 debug() << "Failed to convert Solid device to PortableMediaPlayer";
1075 if( pmp
->supportedProtocols().size() == 0 )
1077 debug() << "Portable Media Player " << udi
<< " does not support any protocols";
1081 foreach( QString supported
, pmp
->supportedProtocols() )
1082 debug() << "Device supports protocol " << supported
;
1084 protocol
= pmp
->supportedProtocols()[0];
1085 if( protocol
== "storage" )
1086 protocol
= "generic";
1087 if( protocol
== "pde" )
1090 name
= solidDevice
.vendor() + " - " + solidDevice
.product();
1092 else if( MediaDeviceCache::instance()->deviceType( udi
) == MediaDeviceCache::ManualType
)
1094 QString handler
= Amarok::config( "PortableDevices" ).readEntry( udi
, QString() );
1095 if( handler
.isEmpty() || !handler
.startsWith( "manual" ) )
1097 //this shouldn't happen, as manually adding the device should create the necessary entry
1098 debug() << "Something's wrong, trying to manually load a non-existant or non-manual manual device";
1101 QStringList sl
= handler
.split( "|" );
1102 if( sl
[0] != "manual" )
1109 protocol
+= "-mediadevice";
1110 QString query
= "[X-KDE-Amarok-plugintype] == 'mediadevice' and [X-KDE-Amarok-name] == '%1'";
1111 Amarok::Plugin
*plugin
= PluginManager::createFromQuery( query
.arg( protocol
) );
1115 debug() << "Returning plugin!";
1116 MediaDevice
*device
= static_cast<MediaDevice
*>( plugin
);
1117 device
->init( this );
1118 device
->m_uid
= udi
;
1119 device
->m_name
= name
;
1120 device
->m_type
= protocol
;
1121 device
->m_mountPoint
= mountPoint
;
1125 debug() << "no plugin for " << protocol
;
1130 MediaBrowser::unloadDevicePlugin( MediaDevice
*device
)
1137 disconnect( device
); // disconnect all signals
1139 if( dynamic_cast<DummyMediaDevice
*>(device
) )
1145 PluginManager::unload( device
);
1150 MediaBrowser::config()
1152 if( m_deviceCombo
->currentText() == "No Device Selected" )
1154 Amarok::StatusBar::instance()->longMessage( i18n( "No device selected to configure." ),
1155 KDE::StatusBar::Sorry
);
1159 DeviceConfigureDialog
* dcd
= new DeviceConfigureDialog( m_currentDevice
);
1161 bool successful
= dcd
->successful();
1167 MediaBrowser::updateButtons()
1169 if( !connectAction() || !disconnectAction() || !transferAction() )
1172 if( m_currentDevice
)
1174 transferAction()->setVisible( m_currentDevice
->m_transfer
);
1175 customAction()->setVisible( m_currentDevice
->m_customButton
);
1176 configAction()->setVisible( m_currentDevice
->m_configure
);
1178 connectAction()->setEnabled( !m_currentDevice
->isConnected() );
1179 disconnectAction()->setEnabled( m_currentDevice
->isConnected() );
1180 transferAction()->setEnabled( m_currentDevice
->isConnected() && m_queue
->childCount() > 0 );
1181 if( customAction() )
1182 customAction()->setEnabled( true );
1186 connectAction()->setEnabled( false );
1187 disconnectAction()->setEnabled( false );
1188 transferAction()->setEnabled( false );
1189 if( customAction() )
1190 customAction()->setEnabled( false );
1195 MediaBrowser::updateStats()
1200 KIO::filesize_t queued
= m_queue
->totalSize();
1202 QString text
= i18np( " 1 track in queue", " %1 tracks in queue", m_queue
->childCount() );
1203 if(m_queue
->childCount() > 0)
1205 text
+= i18n(" (%1)", KIO::convertSize( queued
) );
1208 KIO::filesize_t total
, avail
;
1209 if( m_currentDevice
&& m_currentDevice
->getCapacity(&total
, &avail
) )
1211 text
+= i18n( " - %1 of %2 available", KIO::convertSize( avail
), KIO::convertSize( total
) );
1213 m_stats
->m_used
= total
-avail
;
1214 m_stats
->m_total
= total
;
1215 m_stats
->m_scheduled
= queued
;
1219 m_stats
->m_used
= 0;
1220 m_stats
->m_total
= 0;
1221 m_stats
->m_scheduled
= queued
;
1224 m_stats
->setText(text
);
1225 m_stats
->setToolTip( text
);
1230 MediaView::setFilter( const QString
&filter
, MediaItem
*parent
)
1232 bool advanced
= ExpressionParser::isAdvancedExpression( filter
);
1233 QList
<int> defaultColumns
;
1234 defaultColumns
<< MetaBundle::Album
;
1235 defaultColumns
<< MetaBundle::Title
;
1236 defaultColumns
<< MetaBundle::Artist
;
1243 it
= static_cast<MediaItem
*>(firstChild());
1247 it
= static_cast<MediaItem
*>(parent
->firstChild());
1250 bool childrenVisible
= false;
1251 for( ; it
; it
= static_cast<MediaItem
*>(it
->nextSibling()))
1253 bool visible
= true;
1254 if(it
->isLeafItem())
1258 ParsedExpression parsed
= ExpressionParser::parse( filter
);
1259 visible
= it
->bundle() && it
->bundle()->matchesParsedExpression( parsed
, defaultColumns
);
1263 visible
= it
->bundle() && it
->bundle()->matchesSimpleExpression( filter
, defaultColumns
);
1268 visible
= setFilter(filter
, it
);
1269 if(it
->type()==MediaItem::PLAYLISTSROOT
|| it
->type()==MediaItem::PLAYLIST
)
1273 else if(it
->type()==MediaItem::DIRECTORY
)
1276 QStringList list
= filter
.split( " ", QString::SkipEmptyParts
);
1277 for( QStringList::iterator i
= list
.begin();
1281 if( !(*it
).text(0).contains( *i
) )
1291 if( filter
.isEmpty() )
1293 it
->setVisible( visible
);
1295 childrenVisible
= true;
1298 if( root
&& m_device
)
1299 m_device
->updateRootItems();
1301 return childrenVisible
;
1305 MediaQueue::syncPlaylist( const QString
&name
, const QString
&query
, bool loading
)
1307 MediaItem
* item
= new MediaItem( this, lastItem() );
1308 item
->setType( MediaItem::PLAYLIST
);
1309 item
->setExpandable( false );
1310 item
->setData( query
);
1311 item
->m_playlistName
= name
;
1312 item
->setText( 0, name
);
1313 item
->m_flags
|= MediaItem::SmartPlaylist
;
1314 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1321 MediaQueue::syncPlaylist( const QString
&name
, const KUrl
&url
, bool loading
)
1323 MediaItem
* item
= new MediaItem( this, lastItem() );
1324 item
->setType( MediaItem::PLAYLIST
);
1325 item
->setExpandable( false );
1326 item
->setData( url
.url() );
1327 item
->m_playlistName
= name
;
1328 item
->setText( 0, name
);
1329 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1336 MediaQueue::addUrl( const KUrl
& url2
, MetaBundle
*bundle
, const QString
&playlistName
)
1338 KUrl url
= Amarok::mostLocalURL( url2
);
1341 // if( PlaylistFile::isPlaylistFile( url ) )
1343 // QString name = url.path().section( "/", -1 ).section( ".", 0, -2 ).replace( "_", " " );
1344 // PlaylistFile playlist( url.path() );
1346 // if( playlist.isError() )
1348 // Amarok::StatusBar::instance()->longMessage( i18n( "Failed to load playlist: %1", url.path() ),
1349 // KDE::StatusBar::Sorry );
1353 // for( BundleList::iterator it = playlist.bundles().begin();
1354 // it != playlist.bundles().end();
1357 // addUrl( (*it).url(), 0, name );
1361 if( url
.protocol() == "file" && QFileInfo( url
.path() ).isDir() )
1364 // KUrl::List urls = Amarok::recursiveUrlExpand( url );
1365 // oldForeachType( KUrl::List, urls )
1370 if( playlistName
.isNull() )
1372 for( MediaItem
*it
= static_cast<MediaItem
*>(firstChild());
1374 it
= static_cast<MediaItem
*>(it
->nextSibling()) )
1376 if( it
->url() == url
)
1378 Amarok::StatusBar::instance()->shortMessage(
1379 i18n( "Track already queued for transfer: %1", url
.url() ) );
1386 bundle
= new MetaBundle( url
);
1388 MediaItem
* item
= new MediaItem( this, lastItem() );
1389 item
->setExpandable( false );
1390 item
->setDropEnabled( true );
1391 item
->setBundle( bundle
);
1392 if(bundle
->podcastBundle() )
1394 item
->setType( MediaItem::PODCASTITEM
);
1396 item
->m_playlistName
= playlistName
;
1398 QString text
= item
->bundle()->prettyTitle();
1399 if( text
.isEmpty() || (!item
->bundle()->isValidMedia() && !item
->bundle()->podcastBundle()) )
1400 text
= item
->bundle()->url().prettyUrl();
1401 if( !item
->m_playlistName
.isNull() )
1403 text
+= " (" + item
->m_playlistName
+ ')';
1405 item
->setText( 0, text
);
1407 m_parent
->updateButtons();
1408 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1409 addItemToSize( item
);
1414 MediaQueue::addUrl( const KUrl
&url
, MediaItem
*item
)
1417 MediaItem
*newitem
= new MediaItem( this, lastItem() );
1418 newitem
->setExpandable( false );
1419 newitem
->setDropEnabled( true );
1420 MetaBundle
*bundle
= new MetaBundle( *item
->bundle() );
1422 filepath
.addPath( bundle
->filename() );
1423 bundle
->setUrl( filepath
);
1424 newitem
->m_device
= item
->m_device
;
1425 if(bundle
->podcastBundle() )
1427 item
->setType( MediaItem::PODCASTITEM
);
1429 QString text
= item
->bundle()->prettyTitle();
1430 if( text
.isEmpty() || (!item
->bundle()->isValidMedia() && !item
->bundle()->podcastBundle()) )
1431 text
= item
->bundle()->url().prettyUrl();
1432 if( !item
->m_playlistName
.isEmpty() )
1434 text
+= " (" + item
->m_playlistName
+ ')';
1436 newitem
->setText( 0, text
);
1437 newitem
->setBundle( bundle
);
1438 m_parent
->updateButtons();
1439 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1440 addItemToSize( item
);
1446 MediaQueue::addUrls( const KUrl::List urls
, const QString
&playlistName
)
1448 KUrl::List::ConstIterator it
= urls
.begin();
1449 for ( ; it
!= urls
.end(); ++it
)
1450 addUrl( *it
, 0, playlistName
);
1456 MediaQueue::URLsAdded()
1458 m_parent
->updateStats();
1459 m_parent
->updateButtons();
1460 if( m_parent
->currentDevice()
1461 && m_parent
->currentDevice()->isConnected()
1462 && m_parent
->currentDevice()->asynchronousTransfer()
1463 && !m_parent
->currentDevice()->isTransferring() )
1464 m_parent
->currentDevice()->transferFiles();
1466 save( Amarok::saveLocation() + "transferlist.xml" );
1470 MediaQueue::dragObject()
1473 for( Q3ListViewItem
*it
= firstChild(); it
; it
= it
->nextSibling() )
1475 if( it
->isVisible() && it
->isSelected() && static_cast<MediaItem
*>(it
) )
1476 urls
+= static_cast<MediaItem
*>(it
)->url();
1479 K3MultipleDrag
*md
= new K3MultipleDrag( viewport() );
1480 Q3DragObject
*d
= K3ListView::dragObject();
1481 K3URLDrag
* urldrag
= new K3URLDrag( urls
, viewport() );
1482 md
->addDragObject( d
);
1483 md
->addDragObject( urldrag
);
1484 md
->setPixmap( CollectionDB::createDragPixmap( urls
),
1485 QPoint( CollectionDB::DRAGPIXMAP_OFFSET_X
, CollectionDB::DRAGPIXMAP_OFFSET_Y
) );
1490 MediaBrowser::cancelClicked()
1494 m_waitForTranscode
= false;
1495 if( m_currentDevice
)
1496 m_currentDevice
->abortTransfer();
1500 MediaBrowser::transferClicked()
1502 transferAction()->setEnabled( false );
1504 && m_currentDevice
->isConnected()
1505 && !m_currentDevice
->isTransferring() )
1507 if( !m_currentDevice
->hasTransferDialog() )
1508 m_currentDevice
->transferFiles();
1511 m_currentDevice
->runTransferDialog();
1512 //may not work with non-TransferDialog-class object, but maybe some run time introspection could solve it?
1513 if( m_currentDevice
->getTransferDialog() &&
1514 ( reinterpret_cast<TransferDialog
*>(m_currentDevice
->getTransferDialog()))->isAccepted() )
1515 m_currentDevice
->transferFiles();
1520 m_currentDevice
->m_transferDir
= m_currentDevice
->mountPoint();
1524 MediaBrowser::connectClicked()
1526 bool haveToConfig
= false;
1527 // it was just clicked, so isOn() == true.
1528 if( m_currentDevice
&& !m_currentDevice
->isConnected() )
1530 haveToConfig
= !m_currentDevice
->connectDevice();
1533 haveToConfig
|= !m_currentDevice
;
1534 haveToConfig
|= ( m_currentDevice
&& !m_currentDevice
->isConnected() );
1536 if ( !m_currentDevice
->needsManualConfig() )
1537 haveToConfig
= false;
1539 if( haveToConfig
&& m_devices
.at( 0 ) == m_currentDevice
)
1541 if( config() && m_currentDevice
&& !m_currentDevice
->isConnected() )
1542 m_currentDevice
->connectDevice();
1552 MediaBrowser::disconnectClicked()
1554 if( m_currentDevice
&& m_currentDevice
->isTransferring() )
1556 int action
= KMessageBox::questionYesNoCancel( MediaBrowser::instance(),
1557 i18n( "Transfer in progress. Finish or stop after current track?" ),
1558 i18n( "Stop Transfer?" ),
1559 KGuiItem(i18n("&Finish"), "goto-page"),
1560 KGuiItem(i18n("&Stop"), "media-eject") );
1561 if( action
== KMessageBox::Cancel
)
1565 else if( action
== KMessageBox::Yes
)
1567 m_currentDevice
->scheduleDisconnect();
1572 transferAction()->setEnabled( false );
1573 disconnectAction()->setEnabled( false );
1575 if( m_currentDevice
)
1577 m_currentDevice
->disconnectDevice( true );
1586 MediaBrowser::customClicked()
1588 if( m_currentDevice
)
1589 m_currentDevice
->customClicked();
1593 MediaItem::syncStatsFromPath( const QString
&url
)
1598 // copy Amarok rating, play count and last played time to device
1599 int rating
= CollectionDB::instance()->getSongRating( url
)*10;
1601 setRating( rating
);
1602 int playcount
= CollectionDB::instance()->getPlayCount( url
);
1603 if( playcount
> played() )
1604 setPlayCount( playcount
);
1605 QDateTime lastplay
= CollectionDB::instance()->getLastPlay( url
);
1606 if( lastplay
> playTime() )
1607 setLastPlayed( lastplay
.toTime_t() );
1611 MediaQueue::save( const QString
&path
)
1615 if( !file
.open( QIODevice::WriteOnly
) ) return;
1617 QDomDocument newdoc
;
1618 QDomElement transferlist
= newdoc
.createElement( "playlist" );
1619 transferlist
.setAttribute( "product", "Amarok" );
1620 transferlist
.setAttribute( "version", APP_VERSION
);
1621 newdoc
.appendChild( transferlist
);
1623 for( const MediaItem
*item
= static_cast<MediaItem
*>( firstChild() );
1625 item
= static_cast<MediaItem
*>( item
->nextSibling() ) )
1627 QDomElement i
= newdoc
.createElement("item");
1628 i
.setAttribute("url", item
->url().url());
1630 if( item
->bundle() )
1632 QDomElement attr
= newdoc
.createElement( "Title" );
1633 QDomText t
= newdoc
.createTextNode( item
->bundle()->title() );
1634 attr
.appendChild( t
);
1635 i
.appendChild( attr
);
1637 attr
= newdoc
.createElement( "Artist" );
1638 t
= newdoc
.createTextNode( item
->bundle()->artist() );
1639 attr
.appendChild( t
);
1640 i
.appendChild( attr
);
1642 attr
= newdoc
.createElement( "Album" );
1643 t
= newdoc
.createTextNode( item
->bundle()->album() );
1644 attr
.appendChild( t
);
1645 i
.appendChild( attr
);
1647 attr
= newdoc
.createElement( "Year" );
1648 t
= newdoc
.createTextNode( QString::number( item
->bundle()->year() ) );
1649 attr
.appendChild( t
);
1650 i
.appendChild( attr
);
1652 attr
= newdoc
.createElement( "Comment" );
1653 t
= newdoc
.createTextNode( item
->bundle()->comment() );
1654 attr
.appendChild( t
);
1655 i
.appendChild( attr
);
1657 attr
= newdoc
.createElement( "Genre" );
1658 t
= newdoc
.createTextNode( item
->bundle()->genre() );
1659 attr
.appendChild( t
);
1660 i
.appendChild( attr
);
1662 attr
= newdoc
.createElement( "Track" );
1663 t
= newdoc
.createTextNode( QString::number( item
->bundle()->track() ) );
1664 attr
.appendChild( t
);
1665 i
.appendChild( attr
);
1668 if(item
->type() == MediaItem::PODCASTITEM
)
1670 i
.setAttribute( "podcast", "1" );
1673 if(item
->type() == MediaItem::PODCASTITEM
1674 && item
->bundle()->podcastBundle())
1676 PodcastEpisodeBundle
*peb
= item
->bundle()->podcastBundle();
1677 QDomElement attr
= newdoc
.createElement( "PodcastDescription" );
1678 QDomText t
= newdoc
.createTextNode( peb
->description() );
1679 attr
.appendChild( t
);
1680 i
.appendChild( attr
);
1682 attr
= newdoc
.createElement( "PodcastAuthor" );
1683 t
= newdoc
.createTextNode( peb
->author() );
1684 attr
.appendChild( t
);
1685 i
.appendChild( attr
);
1687 attr
= newdoc
.createElement( "PodcastRSS" );
1688 t
= newdoc
.createTextNode( peb
->parent().url() );
1689 attr
.appendChild( t
);
1690 i
.appendChild( attr
);
1692 attr
= newdoc
.createElement( "PodcastURL" );
1693 t
= newdoc
.createTextNode( peb
->url().url() );
1694 attr
.appendChild( t
);
1695 i
.appendChild( attr
);
1698 if( !item
->m_playlistName
.isEmpty() )
1700 i
.setAttribute( "playlist", item
->m_playlistName
);
1703 if(item
->type() == MediaItem::PLAYLIST
)
1705 i
.setAttribute( "playlistdata", item
->data() );
1706 if( item
->flags() & MediaItem::SmartPlaylist
)
1707 i
.setAttribute( "smartplaylist", "1" );
1710 transferlist
.appendChild( i
);
1713 QTextStream
stream( &file
);
1714 stream
.setCodec( QTextCodec::codecForName( "UTF-8" ) );
1715 stream
.setAutoDetectUnicode( true );
1716 stream
<< "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1717 stream
<< newdoc
.toString();
1722 MediaQueue::load( const QString
& filename
)
1724 QFile
file( filename
);
1725 if( !file
.open( QIODevice::ReadOnly
) ) {
1731 QTextStream
stream( &file
);
1732 stream
.setCodec( QTextCodec::codecForName( "UTF-8" ) );
1733 stream
.setAutoDetectUnicode( true );
1738 if( !d
.setContent( stream
.readAll(), &er
, &l
, &c
) ) { // return error values
1739 Amarok::StatusBar::instance()->longMessageThreadSafe( i18n(
1740 //TODO add a link to the path to the playlist
1741 "The XML in the transferlist was invalid. Please report this as a bug to the Amarok "
1742 "developers. Thank you." ), KDE::StatusBar::Error
);
1743 error() << "[TRANSFERLISTLOADER]: Error loading xml file: " << filename
<< "(" << er
<< ")"
1744 << " at line " << l
<< ", column " << c
;
1748 QList
<QDomNode
> nodes
;
1749 const QString
ITEM( "item" ); //so we don't construct this QString all the time
1750 for( QDomNode n
= d
.namedItem( "playlist" ).firstChild(); !n
.isNull(); n
= n
.nextSibling() )
1752 if( n
.nodeName() != ITEM
) continue;
1754 QDomElement elem
= n
.toElement();
1755 if( !elem
.isNull() )
1758 if( !elem
.hasAttribute( "url" ) )
1762 KUrl
url(elem
.attribute("url"));
1764 bool podcast
= elem
.hasAttribute( "podcast" );
1765 PodcastEpisodeBundle peb
;
1766 if( url
.isLocalFile() )
1767 peb
.setLocalURL( url
);
1768 MetaBundle
*bundle
= new MetaBundle( url
);
1769 for(QDomNode node
= elem
.firstChild();
1771 node
= node
.nextSibling())
1773 if(node
.firstChild().isNull())
1776 if(node
.nodeName() == "Title" )
1777 bundle
->setTitle(node
.firstChild().toText().nodeValue());
1778 else if(node
.nodeName() == "Artist" )
1779 bundle
->setArtist(node
.firstChild().toText().nodeValue());
1780 else if(node
.nodeName() == "Album" )
1781 bundle
->setAlbum(node
.firstChild().toText().nodeValue());
1782 else if(node
.nodeName() == "Year" )
1783 bundle
->setYear(node
.firstChild().toText().nodeValue().toUInt());
1784 else if(node
.nodeName() == "Genre" )
1785 bundle
->setGenre(node
.firstChild().toText().nodeValue());
1786 else if(node
.nodeName() == "Comment" )
1787 bundle
->setComment(node
.firstChild().toText().nodeValue());
1788 else if(node
.nodeName() == "PodcastDescription" )
1789 peb
.setDescription( node
.firstChild().toText().nodeValue() );
1790 else if(node
.nodeName() == "PodcastAuthor" )
1791 peb
.setAuthor( node
.firstChild().toText().nodeValue() );
1792 else if(node
.nodeName() == "PodcastRSS" )
1793 peb
.setParent( KUrl( node
.firstChild().toText().nodeValue() ) );
1794 else if(node
.nodeName() == "PodcastURL" )
1795 peb
.setUrl( KUrl( node
.firstChild().toText().nodeValue() ) );
1800 bundle
->setPodcastBundle( peb
);
1803 QString playlist
= elem
.attribute( "playlist" );
1804 QString playlistdata
= elem
.attribute( "playlistdata" );
1805 if( !playlistdata
.isEmpty() )
1807 QString smart
= elem
.attribute( "smartplaylist" );
1808 if( smart
.isEmpty() )
1809 syncPlaylist( playlist
, KUrl( playlistdata
), true );
1811 syncPlaylist( playlist
, playlistdata
, true );
1814 addUrl( url
, bundle
, playlist
);
1820 MediaQueue::MediaQueue(MediaBrowser
*parent
)
1821 : K3ListView( parent
), m_parent( parent
)
1823 setFixedHeight( 200 );
1824 setSelectionMode( Q3ListView::Extended
);
1825 setItemsMovable( true );
1826 setDragEnabled( true );
1827 setShowSortIndicator( false );
1829 setFullWidth( true );
1830 setRootIsDecorated( false );
1831 setDropVisualizer( true ); //the visualizer (a line marker) is drawn when dragging over tracks
1832 setDropHighlighter( true ); //and the highligther (a focus rect) is drawn when dragging over playlists
1833 setDropVisualizerWidth( 3 );
1834 setAcceptDrops( true );
1835 addColumn( i18n( "Transfer Queue" ) );
1839 KActionCollection
* ac
= new KActionCollection( this );
1840 KStandardAction::selectAll( this, SLOT( selectAll() ), ac
);
1842 connect( this, SIGNAL( contextMenuRequested( Q3ListViewItem
*, const QPoint
&, int ) ),
1843 SLOT( slotShowContextMenu( Q3ListViewItem
*, const QPoint
&, int ) ) );
1844 connect( this, SIGNAL( dropped(QDropEvent
*, Q3ListViewItem
*, Q3ListViewItem
*) ),
1845 SLOT( slotDropped(QDropEvent
*, Q3ListViewItem
*, Q3ListViewItem
*) ) );
1849 MediaQueue::acceptDrag( QDropEvent
*e
) const
1852 return e
->source() == viewport()
1853 || e
->mimeData()->hasFormat( "amarok-sql" )
1854 || KUrl::List::canDecode( e
->mimeData() );
1858 MediaQueue::slotDropped( QDropEvent
* e
, Q3ListViewItem
* parent
, Q3ListViewItem
* after
)
1860 if( e
->source() != viewport() )
1862 if( e
->mimeData()->hasFormat( "amarok-sql" ) )
1864 QString
data( e
->mimeData()->data( "amarok-sql" ) );
1865 QString playlist
= data
.section( "\n", 0, 0 );
1866 QString query
= data
.section( "\n", 1 );
1867 QStringList values
= CollectionDB::instance()->query( query
);
1868 KUrl::List list
= CollectionDB::instance()->URLsFromSqlDrag( values
);
1869 addUrls( list
, playlist
);
1871 else if ( KUrl::List::canDecode( e
->mimeData() ) )
1873 KUrl::List list
= KUrl::List::fromMimeData( e
->mimeData() );
1874 if (!list
.isEmpty() )
1878 else if( Q3ListViewItem
*i
= currentItem() )
1880 moveItem( i
, parent
, after
);
1885 MediaQueue::dropProxyEvent( QDropEvent
*e
)
1887 slotDropped( e
, 0, 0 );
1891 MediaQueue::findPath( QString path
)
1893 for( Q3ListViewItem
*item
= firstChild();
1895 item
= item
->nextSibling())
1897 if(static_cast<MediaItem
*>(item
)->url().path() == path
)
1898 return static_cast<MediaItem
*>(item
);
1905 MediaQueue::computeSize() const
1908 for( Q3ListViewItem
*it
= firstChild();
1910 it
= it
->nextSibling())
1912 MediaItem
*item
= static_cast<MediaItem
*>(it
);
1914 if( item
&& item
->bundle() &&
1915 ( !m_parent
->currentDevice()
1916 || !m_parent
->currentDevice()->isConnected()
1917 || !m_parent
->currentDevice()->trackExists(*item
->bundle()) ) )
1918 m_totalSize
+= ((item
->size()+1023)/1024)*1024;
1923 MediaQueue::totalSize() const
1929 MediaQueue::addItemToSize( const MediaItem
*item
) const
1931 if( item
&& item
->bundle() &&
1932 ( !m_parent
->currentDevice()
1933 || !m_parent
->currentDevice()->isConnected()
1934 || !m_parent
->currentDevice()->trackExists(*item
->bundle()) ) )
1935 m_totalSize
+= ((item
->size()+1023)/1024)*1024;
1939 MediaQueue::subtractItemFromSize( const MediaItem
*item
, bool unconditionally
) const
1941 if( item
&& item
->bundle() &&
1942 ( !m_parent
->currentDevice()
1943 || !m_parent
->currentDevice()->isConnected()
1944 || (unconditionally
|| !m_parent
->currentDevice()->trackExists(*item
->bundle())) ) )
1945 m_totalSize
-= ((item
->size()+1023)/1024)*1024;
1949 MediaQueue::removeSelected()
1951 QList
<Q3ListViewItem
*> selected
= selectedItems();
1953 QListIterator
<Q3ListViewItem
*> iter( selected
);
1954 while( iter
.hasNext() )
1956 Q3ListViewItem
*item
= iter
.next();
1957 if( !(static_cast<MediaItem
*>(item
)->flags() & MediaItem::Transferring
) )
1959 subtractItemFromSize( static_cast<MediaItem
*>(item
) );
1961 if( m_parent
->currentDevice() && m_parent
->currentDevice()->isTransferring() )
1963 MediaBrowser::instance()->m_progress
->setRange( 0, MediaBrowser::instance()->m_progress
->maximum() - 1 );
1968 MediaBrowser::instance()->updateStats();
1969 MediaBrowser::instance()->updateButtons();
1974 MediaQueue::keyPressEvent( QKeyEvent
*e
)
1976 if( e
->key() == Qt::Key_Delete
)
1979 K3ListView::keyPressEvent( e
);
1983 MediaQueue::itemCountChanged()
1985 if( childCount() == 0 )
1987 else if( !isShown() )
1992 MediaQueue::slotShowContextMenu( Q3ListViewItem
* item
, const QPoint
& point
, int )
1997 Q3PopupMenu
menu( this );
1999 enum Actions
{ REMOVE_SELECTED
, CLEAR_ALL
, START_TRANSFER
};
2002 menu
.insertItem( KIcon( Amarok::icon( "remove_from_playlist" ) ), i18n( "&Remove From Queue" ), REMOVE_SELECTED
);
2004 menu
.insertItem( KIcon( Amarok::icon( "playlist_clear" ) ), i18n( "&Clear Queue" ), CLEAR_ALL
);
2005 menu
.insertItem( KIcon( Amarok::icon( "playlist_refresh" ) ), i18n( "&Start Transfer" ), START_TRANSFER
);
2006 menu
.setItemEnabled( START_TRANSFER
,
2007 MediaBrowser::instance()->currentDevice() &&
2008 MediaBrowser::instance()->currentDevice()->isConnected() &&
2009 MediaBrowser::instance()->currentDevice()->m_transfer
);
2011 switch( menu
.exec( point
) )
2013 case REMOVE_SELECTED
:
2019 case START_TRANSFER
:
2020 MediaBrowser::instance()->transferClicked();
2026 MediaQueue::clearItems()
2033 m_parent
->updateStats();
2034 m_parent
->updateButtons();
2039 #include "mediabrowser.moc"