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"
19 #include "collection/CollectionManager.h"
21 #include "editfilterdialog.h"
22 #include "deviceconfiguredialog.h"
23 #include "expression.h"
24 #include "hintlineedit.h"
25 #include "MediaItem.h"
26 #include "MediaDevice.h"
27 #include "MediaDeviceCache.h"
28 #include "MediaDevicePluginManager.h"
29 #include "meta/file/File.h"
30 #include "mountpointmanager.h"
31 #include "playlist/PlaylistModel.h"
32 #include "pluginmanager.h"
34 #include "scriptmanager.h"
35 #include "scrobbler.h"
36 #include "searchwidget.h"
37 #include "ContextStatusBar.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 <k3tempfile.h>
71 #include <k3urldrag.h> //dragObject()
72 #include <KActionCollection>
73 #include <KApplication> //kapp
76 #include <KFileDialog>
78 #include <KIconLoader>
79 #include <KInputDialog>
83 #include <KMessageBox>
84 #include <KPushButton>
86 #include <KStandardDirs> //locate file
88 #include <solid/device.h>
89 #include <solid/deviceinterface.h>
90 #include <solid/devicenotifier.h>
91 #include <solid/portablemediaplayer.h>
92 #include <solid/storageaccess.h>
96 MediaBrowser
*MediaBrowser::s_instance
= 0;
98 bool MediaBrowser::isAvailable() //static
100 if( !MediaBrowser::instance() )
105 //to re-enable hiding, uncomment this and get rid of the return true above:
106 //return MediaBrowser::instance()->m_haveDevices;
109 class DummyMediaDevice
: public MediaDevice
112 DummyMediaDevice() : MediaDevice()
114 m_name
= i18n( "No Device Available" );
115 m_type
= "dummy-mediadevice";
116 m_udi
= "manual|DummyDevice|none|none";
118 void init( MediaBrowser
*browser
) { MediaDevice::init( browser
); }
119 virtual ~DummyMediaDevice() {}
120 virtual bool isConnected() { return false; }
121 virtual MediaItem
* trackExists(const Meta::TrackPtr
) { return 0; }
122 virtual bool lockDevice(bool) { return true; }
123 virtual void unlockDevice() {}
124 virtual bool openDevice( bool silent
)
128 //QString msg = i18n( "Sorry, you do not have a supported portable music player." );
129 //Amarok::ContextStatusBar::instance()->longMessage( msg, KDE::StatusBar::Sorry );
133 virtual bool closeDevice() { return false; }
134 virtual void synchronizeDevice() {}
135 virtual MediaItem
* copyTrackToDevice(const Meta::TrackPtr track
) { return 0; }
136 virtual int deleteItemFromDevice(MediaItem
*, int) { return -1; }
140 MediaBrowser::MediaBrowser( const char * /*name*/ )
142 , m_timer( new QTimer( this ) )
143 , m_currentDevice( 0 )
144 , m_waitForTranscode( false )
145 , m_quitting( false )
146 , m_connectAction( 0 )
147 , m_disconnectAction( 0 )
148 , m_customAction( 0 )
149 , m_configAction( 0 )
150 , m_transferAction( 0 )
154 m_timer
->setSingleShot( true );
156 m_toolbar
= new Browser::ToolBar( this );
157 m_toolbar
->setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Minimum
);
158 m_toolbar
->setToolButtonStyle( Qt::ToolButtonTextBesideIcon
);
160 //TODO: how to fix getButton
161 // perhaps the action can be referenced instead?
162 m_connectAction
= new KAction(KIcon("connect_creating"), i18n("Connect"), this);
163 connect(m_connectAction
, SIGNAL(triggered()), this, SLOT(connectClicked()));
164 m_toolbar
->addAction(m_connectAction
);
165 // m_toolbar->insertButton( "connect_creating", CONNECT, true, i18n("Connect") );
166 // m_toolbar->getButton(CONNECT)->setToolTip( i18n( "Connect media device" ) );
168 m_disconnectAction
= new KAction(KIcon("media-eject"), i18n("Disconnect"), this);
169 connect(m_disconnectAction
, SIGNAL(triggered()), this, SLOT(disconnectClicked()));
170 m_toolbar
->addAction(m_disconnectAction
);
171 // m_toolbar->insertButton( "media-eject", DISCONNECT, true, i18n("Disconnect") );
172 // m_toolbar->getButton(DISCONNECT)->setToolTip( i18n( "Disconnect media device" ) );
174 m_transferAction
= new KAction(KIcon("rebuild"), i18n("Transfer"), this);
175 connect(m_transferAction
, SIGNAL(triggered()), this, SLOT(transferClicked()));
176 m_toolbar
->addAction(m_transferAction
);
177 // m_toolbar->insertButton( "rebuild", TRANSFER, true, i18n("Transfer") );
178 // m_toolbar->getButton(TRANSFER)->setToolTip( i18n( "Transfer tracks to media device" ) );
180 m_toolbar
->addSeparator();
182 // m_toolbar->setIconText( KToolBar::IconTextRight, true );
183 m_customAction
= new KAction(KIcon( "add_playlist" ), i18n("custom"), this);
184 connect(m_customAction
, SIGNAL(triggered()), this, SLOT(customClicked()));
185 m_customAction
->setText( i18n("Special Device Functions") );
186 m_customAction
->setToolTip( i18n("Device-specific special functions or information") );
187 m_toolbar
->addAction(m_customAction
);
188 // m_toolbar->insertButton( Amarok::icon( "add_playlist" ), CUSTOM, SIGNAL( clicked() ), this, SLOT( customClicked() ), true, "custom" );
189 // m_toolbar->getButton(TRANSFER)->setToolTip( i18n( "Transfer tracks to media device" ) );
191 m_toolbar
->setToolButtonStyle( Qt::ToolButtonIconOnly
);
193 m_configAction
= new KAction(KIcon("configure"), i18n("Configure"), this);
194 connect(m_configAction
, SIGNAL(triggered()), this, SLOT(config()));
195 m_toolbar
->addAction(m_configAction
);
196 // m_toolbar->insertButton( Amarok::icon( "configure" ), CONFIGURE, true, i18n("Configure") );
197 // m_toolbar->getButton(CONFIGURE)->setToolTip( i18n( "Configure device" ) );
200 m_deviceCombo
= new KComboBox( this );
202 // searching/filtering
203 QToolBar
* searchToolBar
= new Browser::ToolBar( this );
204 searchToolBar
->setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Minimum
);
205 m_searchWidget
= new SearchWidget( searchToolBar
, this );
206 searchToolBar
->addWidget( m_searchWidget
);
207 connect( m_timer
, SIGNAL( timeout() ), SLOT( slotSetFilter() ) );
209 // connect to device cache
210 connect( MediaDeviceCache::instance(), SIGNAL( deviceAdded(const QString
&) ),
211 SLOT( deviceAdded(const QString
&) ) );
212 connect( MediaDeviceCache::instance(), SIGNAL( deviceRemoved(const QString
&) ),
213 SLOT( deviceRemoved(const QString
&) ) );
216 // we always have a dummy device
217 m_pluginName
[ i18n( "Disable" ) ] = "dummy-mediadevice";
218 m_pluginAmarokName
["dummy-mediadevice"] = i18n( "Disable" );
219 m_pluginName
[ i18n( "Do not handle" ) ] = "ignore";
220 m_pluginAmarokName
["ignore"] = i18n( "Do not handle" );
221 // query available device plugins
222 m_plugins
= PluginManager::query( "[X-KDE-Amarok-plugintype] == 'mediadevice'" );
223 for( KService::List::ConstIterator it
= m_plugins
.begin(); it
!= m_plugins
.end(); ++it
) {
224 // Save name properties in QMap for lookup
225 m_pluginName
[(*it
)->name()] = (*it
)->property( "X-KDE-Amarok-name" ).toString();
226 m_pluginAmarokName
[(*it
)->property( "X-KDE-Amarok-name" ).toString()] = (*it
)->name();
229 m_views
= new KVBox( this );
230 m_queue
= new MediaQueue( this );
231 m_progressBox
= new KHBox( this );
232 m_progress
= new QProgressBar( m_progressBox
);
233 m_cancelButton
= new KPushButton( KIcon( Amarok::icon( "cancel" ) ), i18n("Cancel"), m_progressBox
);
236 m_stats
= new SpaceLabel(this);
238 m_progressBox
->hide();
240 MediaDevice
*dev
= new DummyMediaDevice();
243 activateDevice( 0, false );
245 queue()->load( Amarok::saveLocation() + "transferlist.xml" );
246 queue()->computeSize();
248 setFocusProxy( m_queue
);
252 MediaDeviceCache::instance()->refreshCache();
253 foreach( QString udi
, MediaDeviceCache::instance()->getAll() )
256 connect( m_deviceCombo
, SIGNAL( activated( int ) ), SLOT( activateDevice( int ) ) );
258 connect( m_cancelButton
, SIGNAL( clicked() ), SLOT( cancelClicked() ) );
259 connect( pApp
, SIGNAL( prepareToQuit() ), SLOT( prepareToQuit() ) );
260 //TODO port to meta & new collection interface
261 //connect( CollectionDB::instance(), SIGNAL( tagsChanged( const MetaBundle& ) ),
262 // SLOT( tagsChanged( const MetaBundle& ) ) );
264 //TODO: If we will be supporting manually adding devices, probably need the following section
265 /*m_haveDevices = false;
266 QMap<QString,QString> savedDevices = Amarok::config( "MediaBrowser" ).entryMap();
267 for( QMap<QString,QString>::Iterator it = savedDevices.begin();
268 it != savedDevices.end();
271 if( it.data() != "deleted" && it.data() != "ignore" )
273 m_haveDevices = true;
281 MediaBrowser::blockQuit() const
283 for( QList
<MediaDevice
*>::const_iterator it
= m_devices
.begin();
284 it
!= m_devices
.end();
287 if( *it
&& (*it
)->isConnected() )
295 MediaBrowser::tagsChanged( const Meta::TrackPtr newTrack
)
297 if (newTrack
.isNull())
300 m_itemMapMutex
.lock();
301 debug() << "tags changed for " << newTrack
->url();
302 ItemMap::iterator it
= m_itemMap
.find( newTrack
->url() );
303 if( it
!= m_itemMap
.end() )
305 MediaItem
*item
= *it
;
306 m_itemMapMutex
.unlock();
309 item
->device()->tagsChanged( item
, newTrack
);
313 // it's an item on the transfer queue
314 item
->setMeta( Meta::DataPtr::staticCast(newTrack
) );
316 QString text
= item
->meta()->prettyName();
317 if( !item
->m_playlistName
.isNull() )
319 text
+= " (" + item
->m_playlistName
+ ')';
321 item
->setText( 0, text
);
326 m_itemMapMutex
.unlock();
331 MediaBrowser::getMeta( const KUrl
&url
) const
333 QMutexLocker
locker( &m_itemMapMutex
);
335 ItemMap::const_iterator it
= m_itemMap
.find( url
.url() );
336 if( it
== m_itemMap
.end() )
337 return Meta::TrackPtr();
339 return Meta::TrackPtr::dynamicCast( (*it
)->meta() );
343 MediaBrowser::getProxyUrl( const KUrl
& daapUrl
) const
347 MediaDevice
* dc
= findChildren
<MediaDevice
*>( "DaapClient" ).first();
349 url
= dc
->getProxyUrl( daapUrl
);
354 MediaBrowser::deviceFromId( const QString
&id
) const
356 for( QList
<MediaDevice
*>::const_iterator it
= m_devices
.constBegin();
357 it
!= m_devices
.constEnd();
360 if( (*it
)->udi() == id
)
368 MediaBrowser::activateDevice( const MediaDevice
*dev
)
371 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
372 it
!= m_devices
.end();
377 activateDevice( index
);
385 MediaBrowser::activateDevice( int index
, bool skipDummy
)
387 if( m_currentDevice
&& m_currentDevice
->customAction() )
389 m_toolbar
->removeAction( m_currentDevice
->customAction() );
394 foreach( MediaDevice
*md
, m_devices
)
396 if( md
&& md
->view() )
402 m_currentDevice
= m_devices
.last();
409 if( index
>= m_devices
.count() )
411 if( !m_devices
.isEmpty() )
412 m_currentDevice
= m_devices
.last();
416 queue()->computeSize();
421 m_currentDevice
= m_devices
[index
];
422 if( m_currentDevice
&& m_currentDevice
->view() )
424 m_currentDevice
->view()->show();
425 if( m_currentDevice
->customAction() )
427 m_toolbar
->setToolButtonStyle( Qt::ToolButtonTextBesideIcon
);
428 m_toolbar
->addAction( m_currentDevice
->customAction() );
433 m_deviceCombo
->setCurrentIndex( index
-1 );
436 queue()->computeSize();
441 MediaBrowser::addDevice( MediaDevice
*device
)
443 m_devices
.append( device
);
445 device
->loadConfig();
447 if( device
->autoConnect() )
449 device
->connectDevice( true );
457 MediaBrowser::removeDevice( MediaDevice
*device
)
461 debug() << "remove device: type=" << device
->type();
463 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
464 it
!= m_devices
.end();
469 bool current
= ( (*it
)->udi() == m_currentDevice
->udi() );
470 m_devices
.erase( it
);
472 activateDevice( 0, false );
477 if( device
->isConnected() )
479 if( device
->disconnectDevice( false /* don't run post-disconnect command */ ) )
480 unloadDevicePlugin( device
);
483 debug() << "Cannot remove device because disconnect failed";
484 Amarok::ContextStatusBar::instance()->longMessage(
485 i18n( "Cannot remove device because disconnect failed" ),
486 KDE::StatusBar::Warning
);
490 unloadDevicePlugin( device
);
496 MediaBrowser::updateDevices()
499 m_deviceCombo
->clear();
501 foreach( MediaDevice
* device
, m_devices
)
503 if( m_devices
.count() > 1 && dynamic_cast<DummyMediaDevice
*>(device
) )
505 QString name
= device
->name();
506 if( !device
->deviceNode().isEmpty() )
508 name
= i18n( "%1 at %2", name
, device
->deviceNode() );
510 if( device
->hasMountPoint() && !device
->mountPoint().isEmpty() )
512 name
+= i18n( " (mounted at %1)", device
->mountPoint() );
514 m_deviceCombo
->addItem( name
, i
);
515 if( !m_currentDevice
|| device
->udi() == m_currentDevice
->udi() )
517 m_deviceCombo
->setCurrentItem( name
);
520 m_deviceCombo
->setEnabled( m_devices
.count() > 1 );
521 m_haveDevices
= m_devices
.count() > 1;
525 MediaBrowser::deviceNames() const
529 for( QList
<MediaDevice
*>::const_iterator it
= m_devices
.constBegin();
530 it
!= m_devices
.constEnd();
533 QString name
= (*it
)->name();
541 MediaBrowser::deviceSwitch( const QString
&name
)
544 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
545 it
!= m_devices
.end();
548 if( (*it
)->name() == name
)
550 activateDevice( index
, false );
560 MediaBrowser::transcodingFinished( const QString
&src
, const QString
&dst
)
562 KUrl srcJob
= KUrl( m_transcodeSrc
);
563 KUrl srcResult
= KUrl( src
);
565 if( srcJob
.path() == srcResult
.path() )
567 m_transcodedUrl
= KUrl( dst
);
568 m_waitForTranscode
= false;
572 debug() << "transcoding for " << src
<< " finished, "
573 << "but we are waiting for " << m_transcodeSrc
<< " -- aborting";
574 m_waitForTranscode
= false;
579 MediaBrowser::transcode( const KUrl
&src
, const QString
&filetype
)
581 const ScriptManager
* const sm
= ScriptManager::instance();
583 if( sm
->transcodeScriptRunning().isEmpty() )
585 debug() << "cannot transcode with no transcoder registered";
589 m_waitForTranscode
= true;
590 m_transcodeSrc
= src
.url();
591 m_transcodedUrl
= KUrl();
592 ScriptManager::instance()->notifyTranscode( src
.url(), filetype
);
594 while( m_waitForTranscode
&& !sm
->transcodeScriptRunning().isEmpty() )
597 kapp
->processEvents( QEventLoop::AllEvents
);
600 return m_transcodedUrl
;
605 MediaBrowser::slotSetFilterTimeout() //SLOT
607 m_timer
->start( 280 ); //stops the timer for us first
611 MediaBrowser::slotSetFilter() //SLOT
615 if( m_currentDevice
)
616 m_currentDevice
->view()->setFilter( m_searchWidget
->lineEdit()->text() );
620 MediaBrowser::slotSetFilter( const QString
&text
)
622 m_searchWidget
->lineEdit()->setText( text
);
627 MediaBrowser::slotEditFilter()
629 EditFilterDialog
*fd
= new EditFilterDialog( this, true, m_searchWidget
->lineEdit()->text() );
630 connect( fd
, SIGNAL(filterChanged(const QString
&)), SLOT(slotSetFilter(const QString
&)) );
632 m_searchWidget
->lineEdit()->setText( fd
->filter() );
637 MediaBrowser::prepareToQuit()
639 m_waitForTranscode
= false;
641 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
642 it
!= m_devices
.end();
645 if( (*it
)->isConnected() )
646 (*it
)->disconnectDevice( false /* don't unmount */ );
650 MediaBrowser::~MediaBrowser()
652 debug() << "having to remove " << m_devices
.count() << " devices";
653 while( !m_devices
.isEmpty() )
655 removeDevice( m_devices
.last() );
658 queue()->save( Amarok::saveLocation() + "transferlist.xml" );
660 delete m_deviceCombo
;
664 MediaView::MediaView( QWidget
* parent
, MediaDevice
*device
)
665 : K3ListView( parent
)
670 setSelectionMode( Q3ListView::Extended
);
671 setItemsMovable( false );
672 setShowSortIndicator( true );
673 setFullWidth( true );
674 setRootIsDecorated( true );
675 setDragEnabled( true );
676 setDropVisualizer( true ); //the visualizer (a line marker) is drawn when dragging over tracks
677 setDropHighlighter( true ); //and the highligther (a focus rect) is drawn when dragging over playlists
678 setDropVisualizerWidth( 3 );
679 setAcceptDrops( true );
682 addColumn( i18n( "Remote Media" ) );
684 KActionCollection
* ac
= new KActionCollection( this );
685 KStandardAction::selectAll( this, SLOT( selectAll() ), ac
);
687 connect( this, SIGNAL( contextMenuRequested( Q3ListViewItem
*, const QPoint
&, int ) ),
688 this, SLOT( rmbPressed( Q3ListViewItem
*, const QPoint
&, int ) ) );
690 connect( this, SIGNAL( itemRenamed( Q3ListViewItem
* ) ),
691 this, SLOT( renameItem( Q3ListViewItem
* ) ) );
693 connect( this, SIGNAL( expanded( Q3ListViewItem
* ) ),
694 this, SLOT( slotExpand( Q3ListViewItem
* ) ) );
696 connect( this, SIGNAL( returnPressed( Q3ListViewItem
* ) ),
697 this, SLOT( invokeItem( Q3ListViewItem
* ) ) );
699 connect( this, SIGNAL( doubleClicked( Q3ListViewItem
*, const QPoint
&, int ) ),
700 this, SLOT( invokeItem( Q3ListViewItem
*, const QPoint
&, int ) ) );
704 MediaView::keyPressEvent( QKeyEvent
*e
)
706 if( e
->key() == Qt::Key_Delete
)
707 m_device
->deleteFromDevice();
709 K3ListView::keyPressEvent( e
);
713 MediaView::invokeItem( Q3ListViewItem
* i
, const QPoint
& point
, int column
) //SLOT
718 QPoint p
= mapFromGlobal( point
);
719 if ( p
.x() > header()->sectionPos( header()->mapToIndex( 0 ) ) + treeStepSize() * ( i
->depth() + ( rootIsDecorated() ? 1 : 0) ) + itemMargin()
720 || p
.x() < header()->sectionPos( header()->mapToIndex( 0 ) ) )
726 MediaView::invokeItem( Q3ListViewItem
*i
)
728 MediaItem
*item
= static_cast<MediaItem
*>( i
);
732 Meta::TrackList tracks
= CollectionManager::instance()->tracksForUrls(
733 nodeBuildDragList( item
) );
734 The::playlistModel()->insertOptioned( tracks
, Playlist::AppendAndPlay
);
738 MediaView::renameItem( Q3ListViewItem
*item
)
740 m_device
->renameItem( item
);
744 MediaView::slotExpand( Q3ListViewItem
*item
)
746 m_device
->expandItem( item
);
750 MediaView::~MediaView()
756 MediaView::dragObject()
758 KUrl::List urls
= nodeBuildDragList( 0 );
759 K3MultipleDrag
*md
= new K3MultipleDrag( viewport() );
760 md
->addDragObject( K3ListView::dragObject() );
761 K3URLDrag
* ud
= new K3URLDrag( urls
, viewport() );
762 md
->addDragObject( ud
);
763 md
->setPixmap( CollectionDB::createDragPixmap( urls
),
764 QPoint( CollectionDB::DRAGPIXMAP_OFFSET_X
, CollectionDB::DRAGPIXMAP_OFFSET_Y
) );
770 MediaView::nodeBuildDragList( MediaItem
* item
, int flags
)
777 fi
= static_cast<MediaItem
*>(firstChild());
784 if( fi
->isVisible() )
786 if ( fi
->isSelected() || !(flags
& OnlySelected
) )
788 if( fi
->isLeafItem() || fi
->type() == MediaItem::DIRECTORY
)
792 if(fi
->childCount() )
793 items
+= nodeBuildDragList( static_cast<MediaItem
*>(fi
->firstChild()), None
);
798 if ( fi
->childCount() )
799 items
+= nodeBuildDragList( static_cast<MediaItem
*>(fi
->firstChild()), OnlySelected
);
802 fi
= static_cast<MediaItem
*>(fi
->nextSibling());
808 MediaView::getSelectedLeaves( MediaItem
*parent
, QList
<MediaItem
*> *list
, int flags
)
812 list
= new QList
<MediaItem
*>;
816 it
= static_cast<MediaItem
*>(firstChild());
818 it
= static_cast<MediaItem
*>(parent
->firstChild());
820 for( ; it
; it
= static_cast<MediaItem
*>(it
->nextSibling()))
822 if( it
->isVisible() )
824 if( it
->childCount() && !( it
->type() == MediaItem::DIRECTORY
&& it
->isSelected() ) )
827 if( it
->isSelected() )
829 numFiles
+= getSelectedLeaves(it
, list
, f
);
831 if( it
->isSelected() || !(flags
& OnlySelected
) )
833 if( it
->type() == MediaItem::TRACK
||
834 it
->type() == MediaItem::DIRECTORY
||
835 it
->type() == MediaItem::PODCASTITEM
||
836 it
->type() == MediaItem::PLAYLISTITEM
||
837 it
->type() == MediaItem::INVISIBLE
||
838 it
->type() == MediaItem::ORPHANED
)
840 if( flags
& OnlyPlayed
)
842 if( it
->played() > 0 )
848 if( ( it
->isLeafItem() && (!(flags
& OnlyPlayed
) || it
->played()>0) )
849 || it
->type() == MediaItem::DIRECTORY
)
859 MediaView::acceptDrag( QDropEvent
*e
) const
861 if( e
->source() == MediaBrowser::queue()->viewport() )
864 return e
->source() == viewport()
865 || e
->mimeData()->hasFormat( "amarok-sql" )
866 || KUrl::List::canDecode( e
->mimeData() );
870 MediaView::contentsDropEvent( QDropEvent
*e
)
872 cleanDropVisualizer();
873 cleanItemHighlighter();
875 if(e
->source() == viewport())
877 const QPoint p
= contentsToViewport( e
->pos() );
878 MediaItem
*item
= static_cast<MediaItem
*>(itemAt( p
));
880 if( !item
&& MediaBrowser::instance()->currentDevice()->m_type
!= "generic-mediadevice" )
883 QList
<MediaItem
*> items
;
885 if( !item
|| item
->type() == MediaItem::DIRECTORY
||
886 item
->type() == MediaItem::TRACK
)
888 QList
<MediaItem
*> items
;
889 getSelectedLeaves( 0, &items
);
890 m_device
->addToDirectory( item
, items
);
892 else if( item
->type() == MediaItem::PLAYLIST
)
894 MediaItem
*list
= item
;
895 MediaItem
*after
= 0;
896 for(MediaItem
*it
= static_cast<MediaItem
*>(item
->firstChild());
898 it
= static_cast<MediaItem
*>(it
->nextSibling()))
901 getSelectedLeaves( 0, &items
);
902 m_device
->addToPlaylist( list
, after
, items
);
904 else if( item
->type() == MediaItem::PLAYLISTITEM
)
906 MediaItem
*list
= static_cast<MediaItem
*>(item
->parent());
907 MediaItem
*after
= 0;
908 for(MediaItem
*it
= static_cast<MediaItem
*>(item
->parent()->firstChild());
910 it
= static_cast<MediaItem
*>(it
->nextSibling()))
917 getSelectedLeaves( 0, &items
);
918 m_device
->addToPlaylist( list
, after
, items
);
920 else if( item
->type() == MediaItem::PLAYLISTSROOT
)
922 QList
<MediaItem
*> items
;
923 getSelectedLeaves( 0, &items
);
924 QString
base( i18n("New Playlist") );
927 while( item
->findItem(name
) )
931 name
= base
+ ' ' + num
;
934 MediaItem
*pl
= m_device
->newPlaylist(name
, item
, items
);
935 ensureItemVisible(pl
);
941 if( e
->mimeData()->hasFormat( "amarok-sql" ) )
943 QString
data( e
->mimeData()->data( "amarok-sql" ) );
944 QString playlist
= data
.section( "\n", 0, 0 );
945 QString query
= data
.section( "\n", 1 );
946 QStringList values
= CollectionDB::instance()->query( query
);
947 KUrl::List list
= CollectionDB::instance()->URLsFromSqlDrag( values
);
948 MediaBrowser::queue()->addUrls( list
, playlist
);
950 else if ( KUrl::List::canDecode( e
->mimeData() ) )
952 KUrl::List list
= KUrl::List::fromMimeData( e
->mimeData() );
953 MediaBrowser::queue()->addUrls( list
);
959 MediaView::viewportPaintEvent( QPaintEvent
*e
)
961 K3ListView::viewportPaintEvent( e
);
963 // Superimpose bubble help:
965 if ( !MediaBrowser::instance()->currentDevice() || !MediaBrowser::instance()->currentDevice()->isConnected() )
967 QPainter
p( viewport() );
969 Q3SimpleRichText
t( i18n(
971 "<h3>Media Device Browser</h3>"
972 "Configure your media device and then "
973 "click the Connect button to access your media device. "
974 "Drag and drop files to enqueue them for transfer."
975 "</div>" ), QApplication::font() );
977 t
.setWidth( width() - 50 );
979 const uint w
= t
.width() + 20;
980 const uint h
= t
.height() + 20;
982 p
.setBrush( palette().background() );
983 p
.drawRoundRect( 15, 15, w
, h
, (8*200)/w
, (8*200)/h
);
984 t
.draw( &p
, 20, 20, QRect(), palette() );
986 MediaBrowser::instance()->updateButtons();
990 MediaView::rmbPressed( Q3ListViewItem
*item
, const QPoint
&p
, int i
)
992 if( m_device
->isConnected() )
993 m_device
->rmbPressed( item
, p
, i
);
997 MediaView::newDirectory( MediaItem
*parent
)
1000 const QString name
= KInputDialog::getText(i18n("Add Directory"), i18n("Directory Name:"), QString(), &ok
, this);
1002 if( ok
&& !name
.isEmpty() )
1004 m_device
->newDirectory( name
, parent
);
1011 MediaBrowser::deviceAdded( const QString
&udi
)
1014 debug() << "deviceAdded called with a udi of: " << udi
;
1015 if( MediaDeviceCache::instance()->deviceType( udi
) == MediaDeviceCache::SolidVolumeType
&&
1016 !MediaDeviceCache::instance()->isGenericEnabled( udi
) )
1018 debug() << "device is a generic volume but not enabled for use";
1021 MediaDevice
*md
= loadDevicePlugin( udi
);
1025 if( m_currentDevice
== *(m_devices
.begin()) || m_currentDevice
== *(m_devices
.end()) )
1026 activateDevice( m_devices
.count()-1, false );
1031 MediaBrowser::deviceRemoved( const QString
&udi
)
1033 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
1034 it
!= m_devices
.end();
1037 if( (*it
)->m_udi
== udi
)
1039 if( (*it
)->isConnected() )
1041 if( (*it
)->disconnectDevice() )
1042 removeDevice( *it
);
1043 Amarok::ContextStatusBar::instance()->longMessage(
1044 i18n( "The device %1 was removed before it was disconnected. "
1045 "In order to avoid possible data loss, press the \"Disconnect\" "
1046 "button before disconnecting the device.", (*it
)->name() ),
1047 KDE::StatusBar::Warning
);
1050 removeDevice( *it
);
1057 MediaBrowser::loadDevicePlugin( const QString
&udi
)
1065 if( MediaDeviceCache::instance()->deviceType( udi
) == MediaDeviceCache::SolidPMPType
)
1067 debug() << "udi " << udi
<< " detected as a Solid PMP device";
1068 Solid::Device
solidDevice( udi
);
1070 Solid::PortableMediaPlayer
* pmp
= solidDevice
.as
<Solid::PortableMediaPlayer
>();
1074 debug() << "Failed to convert Solid device to PortableMediaPlayer";
1077 if( pmp
->supportedProtocols().size() == 0 )
1079 debug() << "Portable Media Player " << udi
<< " does not support any protocols";
1083 foreach( const QString
&supported
, pmp
->supportedProtocols() )
1084 debug() << "Device supports protocol " << supported
;
1086 protocol
= pmp
->supportedProtocols()[0];
1087 if( protocol
== "storage" )
1088 protocol
= "generic";
1089 if( protocol
== "pde" )
1092 protocol
+= "-mediadevice";
1093 name
= solidDevice
.vendor() + " - " + solidDevice
.product();
1095 else if( MediaDeviceCache::instance()->deviceType( udi
) == MediaDeviceCache::SolidVolumeType
)
1097 debug() << "udi " << " detected as Solid volume device";
1098 Solid::Device
solidDevice( udi
);
1100 Solid::StorageAccess
* ssa
= solidDevice
.as
<Solid::StorageAccess
>();
1103 debug() << "Failed to convert Solid device to StorageAccess";
1107 protocol
= "generic-mediadevice";
1108 name
= solidDevice
.parent().vendor() + " - " + solidDevice
.parent().product();
1109 mountPoint
= ssa
->filePath();
1111 else if( MediaDeviceCache::instance()->deviceType( udi
) == MediaDeviceCache::ManualType
)
1113 debug() << "udi " << udi
<< " detected as manual device";
1114 KConfigGroup config
= Amarok::config( "PortableDevices" );
1115 protocol
= config
.readEntry( udi
, QString() );
1117 if( protocol
.isEmpty() )
1119 debug() << "Found no plugin in amarokrc for " << udi
;
1123 QStringList sl
= udi
.split( "|" );
1128 QString query
= "[X-KDE-Amarok-plugintype] == 'mediadevice' and [X-KDE-Amarok-name] == '%1'";
1129 debug() << "query is " << query
;
1130 Amarok::Plugin
*plugin
= PluginManager::createFromQuery( query
.arg( protocol
) );
1134 debug() << "Returning plugin!";
1135 MediaDevice
*device
= static_cast<MediaDevice
*>( plugin
);
1136 device
->init( this );
1137 device
->m_udi
= udi
;
1138 device
->m_name
= name
;
1139 device
->m_type
= protocol
;
1140 device
->m_mountPoint
= mountPoint
;
1144 debug() << "no plugin for " << protocol
;
1149 MediaBrowser::pluginSelected( const QString
&udi
, const QString
&plugin
)
1152 if( !plugin
.isEmpty() )
1154 debug() << "Device udi is " << udi
<< " and plugin selected is: " << plugin
;
1155 if( MediaDeviceCache::instance()->deviceType( udi
) == MediaDeviceCache::ManualType
)
1156 Amarok::config( "PortableDevices" ).writeEntry( udi
, plugin
);
1158 bool success
= true;
1159 foreach( MediaDevice
* device
, m_devices
)
1161 debug() << "plugin = " << plugin
<< ", device->type() = " << device
->type();
1162 if( device
->udi() == udi
)
1164 if( device
->type() == plugin
)
1166 debug() << "removing matching device";
1167 if( device
->isConnected() )
1169 if( device
->disconnectDevice( false ) )
1170 removeDevice( device
);
1175 removeDevice( device
);
1186 debug() << "Cannot change plugin while operation is in progress" << endl
;
1187 Amarok::ContextStatusBar::instance()->longMessage(
1188 i18n( "Cannot change plugin while operation is in progress" ),
1189 KDE::StatusBar::Warning
);
1193 debug() << "Device udi is " << udi
<< " and you opted not to use a plugin";
1197 MediaBrowser::showPluginManager()
1199 MediaDevicePluginManagerDialog
* mpm
= new MediaDevicePluginManagerDialog();
1205 MediaBrowser::unloadDevicePlugin( MediaDevice
*device
)
1212 disconnect( device
); // disconnect all signals
1214 if( dynamic_cast<DummyMediaDevice
*>(device
) )
1220 PluginManager::unload( device
);
1225 MediaBrowser::config()
1227 if( m_deviceCombo
->currentText() == "No Device Available" )
1229 //Amarok::ContextStatusBar::instance()->longMessage( i18n( "No device selected to configure." ),
1230 // KDE::StatusBar::Sorry );
1231 showPluginManager();
1235 DeviceConfigureDialog
* dcd
= new DeviceConfigureDialog( m_currentDevice
);
1237 bool successful
= dcd
->successful();
1243 MediaBrowser::updateButtons()
1245 if( !connectAction() || !disconnectAction() || !transferAction() )
1248 if( m_currentDevice
)
1250 transferAction()->setVisible( m_currentDevice
->m_transfer
);
1251 customAction()->setVisible( m_currentDevice
->m_customButton
);
1252 configAction()->setVisible( m_currentDevice
->m_configure
);
1254 connectAction()->setEnabled( !m_currentDevice
->isConnected() );
1255 disconnectAction()->setEnabled( m_currentDevice
->isConnected() );
1256 transferAction()->setEnabled( m_currentDevice
->isConnected() && m_queue
->childCount() > 0 );
1257 if( customAction() )
1258 customAction()->setEnabled( true );
1262 connectAction()->setEnabled( false );
1263 disconnectAction()->setEnabled( false );
1264 transferAction()->setEnabled( false );
1265 if( customAction() )
1266 customAction()->setEnabled( false );
1271 MediaBrowser::updateStats()
1276 KIO::filesize_t queued
= m_queue
->totalSize();
1278 QString text
= i18np( " 1 track in queue", " %1 tracks in queue", m_queue
->childCount() );
1279 if(m_queue
->childCount() > 0)
1281 text
+= i18n(" (%1)", KIO::convertSize( queued
) );
1284 KIO::filesize_t total
, avail
;
1285 if( m_currentDevice
&& m_currentDevice
->getCapacity(&total
, &avail
) )
1287 text
+= i18n( " - %1 of %2 available", KIO::convertSize( avail
), KIO::convertSize( total
) );
1289 m_stats
->m_used
= total
-avail
;
1290 m_stats
->m_total
= total
;
1291 m_stats
->m_scheduled
= queued
;
1295 m_stats
->m_used
= 0;
1296 m_stats
->m_total
= 0;
1297 m_stats
->m_scheduled
= queued
;
1300 m_stats
->setText(text
);
1301 m_stats
->setToolTip( text
);
1306 MediaView::setFilter( const QString
&filter
, MediaItem
*parent
)
1308 //TODO port to meta, for now never filter.
1311 bool advanced = ExpressionParser::isAdvancedExpression( filter );
1312 QList<int> defaultColumns;
1313 defaultColumns << MetaBundle::Album;
1314 defaultColumns << MetaBundle::Title;
1315 defaultColumns << MetaBundle::Artist;
1322 it = static_cast<MediaItem *>(firstChild());
1326 it = static_cast<MediaItem *>(parent->firstChild());
1329 bool childrenVisible = false;
1330 for( ; it; it = static_cast<MediaItem *>(it->nextSibling()))
1332 bool visible = true;
1333 if(it->isLeafItem())
1337 ParsedExpression parsed = ExpressionParser::parse( filter );
1338 visible = it->bundle() && it->bundle()->matchesParsedExpression( parsed, defaultColumns );
1342 visible = it->bundle() && it->bundle()->matchesSimpleExpression( filter, defaultColumns );
1347 visible = setFilter(filter, it);
1348 if(it->type()==MediaItem::PLAYLISTSROOT || it->type()==MediaItem::PLAYLIST)
1352 else if(it->type()==MediaItem::DIRECTORY)
1355 QStringList list = filter.split( " ", QString::SkipEmptyParts );
1356 for( QStringList::iterator i = list.begin();
1360 if( !(*it).text(0).contains( *i ) )
1370 if( filter.isEmpty() )
1372 it->setVisible( visible );
1374 childrenVisible = true;
1377 if( root && m_device )
1378 m_device->updateRootItems();
1380 return childrenVisible;
1385 MediaQueue::syncPlaylist( const QString
&name
, const QString
&query
, bool loading
)
1387 MediaItem
* item
= new MediaItem( this, lastItem() );
1388 item
->setType( MediaItem::PLAYLIST
);
1389 item
->setExpandable( false );
1390 item
->setData( query
);
1391 item
->m_playlistName
= name
;
1392 item
->setText( 0, name
);
1393 item
->m_flags
|= MediaItem::SmartPlaylist
;
1394 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1401 MediaQueue::syncPlaylist( const QString
&name
, const KUrl
&url
, bool loading
)
1403 MediaItem
* item
= new MediaItem( this, lastItem() );
1404 item
->setType( MediaItem::PLAYLIST
);
1405 item
->setExpandable( false );
1406 item
->setData( url
.url() );
1407 item
->m_playlistName
= name
;
1408 item
->setText( 0, name
);
1409 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1416 MediaQueue::addUrl( const KUrl
& url2
, Meta::TrackPtr meta
, const QString
&playlistName
)
1418 KUrl url
= Amarok::mostLocalURL( url2
);
1421 // if( PlaylistFile::isPlaylistFile( url ) )
1423 // QString name = url.path().section( "/", -1 ).section( ".", 0, -2 ).replace( "_", " " );
1424 // PlaylistFile playlist( url.path() );
1426 // if( playlist.isError() )
1428 // Amarok::ContextStatusBar::instance()->longMessage( i18n( "Failed to load playlist: %1", url.path() ),
1429 // KDE::StatusBar::Sorry );
1433 // for( BundleList::iterator it = playlist.bundles().begin();
1434 // it != playlist.bundles().end();
1437 // addUrl( (*it).url(), 0, name );
1441 if( url
.protocol() == "file" && QFileInfo( url
.path() ).isDir() )
1444 // KUrl::List urls = Amarok::recursiveUrlExpand( url );
1445 // oldForeachType( KUrl::List, urls )
1450 if( playlistName
.isNull() )
1452 for( MediaItem
*it
= static_cast<MediaItem
*>(firstChild());
1454 it
= static_cast<MediaItem
*>(it
->nextSibling()) )
1456 if( it
->url() == url
)
1458 Amarok::ContextStatusBar::instance()->shortMessage(
1459 i18n( "Track already queued for transfer: %1", url
.url() ) );
1466 meta
= Meta::TrackPtr( new MetaFile::Track( url
) );
1468 MediaItem
* item
= new MediaItem( this, lastItem() );
1469 item
->setExpandable( false );
1470 item
->setDropEnabled( true );
1471 item
->setMeta( Meta::DataPtr::staticCast(meta
) );
1472 //TODO: podcast meta
1473 //if(bundle->podcastBundle() )
1475 // item->setType( MediaItem::PODCASTITEM );
1477 item
->m_playlistName
= playlistName
;
1479 QString text
= item
->meta()->prettyName();
1480 if( !item
->m_playlistName
.isNull() )
1482 text
+= " (" + item
->m_playlistName
+ ')';
1484 item
->setText( 0, text
);
1486 m_parent
->updateButtons();
1487 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1488 addItemToSize( item
);
1493 MediaQueue::addUrl( const KUrl
&url
, MediaItem
*item
)
1496 MediaItem
*newitem
= new MediaItem( this, lastItem() );
1497 newitem
->setExpandable( false );
1498 newitem
->setDropEnabled( true );
1499 Meta::TrackPtr meta
= Meta::TrackPtr::dynamicCast(item
->meta());
1501 //KUrl filepath(url);
1502 //filepath.addPath( meta->filename() );
1503 //bundle->setUrl( filepath );
1504 newitem
->m_device
= item
->m_device
;
1505 //if(bundle->podcastBundle() )
1507 // item->setType( MediaItem::PODCASTITEM );
1509 QString text
= item
->meta()->prettyName();
1510 if( !item
->m_playlistName
.isEmpty() )
1512 text
+= " (" + item
->m_playlistName
+ ')';
1514 newitem
->setText( 0, text
);
1515 newitem
->setMeta( Meta::DataPtr::staticCast(meta
) );
1516 m_parent
->updateButtons();
1517 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1518 addItemToSize( item
);
1524 MediaQueue::addUrls( const KUrl::List urls
, const QString
&playlistName
)
1526 KUrl::List::ConstIterator it
= urls
.begin();
1527 for ( ; it
!= urls
.end(); ++it
)
1528 addUrl( *it
, Meta::TrackPtr(), playlistName
);
1534 MediaQueue::URLsAdded()
1536 m_parent
->updateStats();
1537 m_parent
->updateButtons();
1538 if( m_parent
->currentDevice()
1539 && m_parent
->currentDevice()->isConnected()
1540 && m_parent
->currentDevice()->asynchronousTransfer()
1541 && !m_parent
->currentDevice()->isTransferring() )
1542 m_parent
->currentDevice()->transferFiles();
1544 save( Amarok::saveLocation() + "transferlist.xml" );
1548 MediaQueue::dragObject()
1551 for( Q3ListViewItem
*it
= firstChild(); it
; it
= it
->nextSibling() )
1553 if( it
->isVisible() && it
->isSelected() && static_cast<MediaItem
*>(it
) )
1554 urls
+= static_cast<MediaItem
*>(it
)->url();
1557 K3MultipleDrag
*md
= new K3MultipleDrag( viewport() );
1558 Q3DragObject
*d
= K3ListView::dragObject();
1559 K3URLDrag
* urldrag
= new K3URLDrag( urls
, viewport() );
1560 md
->addDragObject( d
);
1561 md
->addDragObject( urldrag
);
1562 md
->setPixmap( CollectionDB::createDragPixmap( urls
),
1563 QPoint( CollectionDB::DRAGPIXMAP_OFFSET_X
, CollectionDB::DRAGPIXMAP_OFFSET_Y
) );
1568 MediaBrowser::cancelClicked()
1572 m_waitForTranscode
= false;
1573 if( m_currentDevice
)
1574 m_currentDevice
->abortTransfer();
1578 MediaBrowser::transferClicked()
1580 transferAction()->setEnabled( false );
1582 && m_currentDevice
->isConnected()
1583 && !m_currentDevice
->isTransferring() )
1585 if( !m_currentDevice
->hasTransferDialog() )
1586 m_currentDevice
->transferFiles();
1589 m_currentDevice
->runTransferDialog();
1590 //may not work with non-TransferDialog-class object, but maybe some run time introspection could solve it?
1591 if( m_currentDevice
->getTransferDialog() &&
1592 ( reinterpret_cast<TransferDialog
*>(m_currentDevice
->getTransferDialog()))->isAccepted() )
1593 m_currentDevice
->transferFiles();
1598 m_currentDevice
->m_transferDir
= m_currentDevice
->mountPoint();
1602 MediaBrowser::connectClicked()
1604 bool haveToConfig
= false;
1605 // it was just clicked, so isOn() == true.
1606 if( m_currentDevice
&& !m_currentDevice
->isConnected() )
1608 haveToConfig
= !m_currentDevice
->connectDevice();
1611 haveToConfig
|= !m_currentDevice
;
1612 haveToConfig
|= ( m_currentDevice
&& !m_currentDevice
->isConnected() );
1614 if ( !m_currentDevice
->needsManualConfig() )
1615 haveToConfig
= false;
1617 if( haveToConfig
&& m_devices
.at( 0 ) == m_currentDevice
)
1619 if( config() && m_currentDevice
&& !m_currentDevice
->isConnected() )
1620 m_currentDevice
->connectDevice();
1629 MediaBrowser::disconnectClicked()
1631 if( m_currentDevice
&& m_currentDevice
->isTransferring() )
1633 int action
= KMessageBox::questionYesNoCancel( MediaBrowser::instance(),
1634 i18n( "Transfer in progress. Finish or stop after current track?" ),
1635 i18n( "Stop Transfer?" ),
1636 KGuiItem(i18n("&Finish"), "goto-page"),
1637 KGuiItem(i18n("&Stop"), "media-eject") );
1638 if( action
== KMessageBox::Cancel
)
1642 else if( action
== KMessageBox::Yes
)
1644 m_currentDevice
->scheduleDisconnect();
1649 transferAction()->setEnabled( false );
1650 disconnectAction()->setEnabled( false );
1652 if( m_currentDevice
)
1654 m_currentDevice
->disconnectDevice( true );
1663 MediaBrowser::customClicked()
1665 if( m_currentDevice
)
1666 m_currentDevice
->customClicked();
1670 MediaQueue::save( const QString
&path
)
1674 if( !file
.open( QIODevice::WriteOnly
) ) return;
1676 QDomDocument newdoc
;
1677 QDomElement transferlist
= newdoc
.createElement( "playlist" );
1678 transferlist
.setAttribute( "product", "Amarok" );
1679 transferlist
.setAttribute( "version", APP_VERSION
);
1680 newdoc
.appendChild( transferlist
);
1682 for( const MediaItem
*item
= static_cast<MediaItem
*>( firstChild() );
1684 item
= static_cast<MediaItem
*>( item
->nextSibling() ) )
1686 QDomElement i
= newdoc
.createElement("item");
1687 i
.setAttribute("url", item
->url().url());
1691 QDomElement attr
= newdoc
.createElement( "Title" );
1692 QDomText t
= newdoc
.createTextNode( item
->meta()->name() );
1693 attr
.appendChild( t
);
1694 i
.appendChild( attr
);
1695 /* TODO port to meta
1696 attr = newdoc.createElement( "Artist" );
1697 t = newdoc.createTextNode( item->bundle()->artist() );
1698 attr.appendChild( t );
1699 i.appendChild( attr );
1701 attr = newdoc.createElement( "Album" );
1702 t = newdoc.createTextNode( item->bundle()->album() );
1703 attr.appendChild( t );
1704 i.appendChild( attr );
1706 attr = newdoc.createElement( "Year" );
1707 t = newdoc.createTextNode( QString::number( item->bundle()->year() ) );
1708 attr.appendChild( t );
1709 i.appendChild( attr );
1711 attr = newdoc.createElement( "Comment" );
1712 t = newdoc.createTextNode( item->bundle()->comment() );
1713 attr.appendChild( t );
1714 i.appendChild( attr );
1716 attr = newdoc.createElement( "Genre" );
1717 t = newdoc.createTextNode( item->bundle()->genre() );
1718 attr.appendChild( t );
1719 i.appendChild( attr );
1721 attr = newdoc.createElement( "Track" );
1722 t = newdoc.createTextNode( QString::number( item->bundle()->track() ) );
1723 attr.appendChild( t );
1724 i.appendChild( attr );
1728 if(item
->type() == MediaItem::PODCASTITEM
)
1730 i
.setAttribute( "podcast", "1" );
1733 /*TODO if(item->type() == MediaItem::PODCASTITEM
1734 && item->bundle()->podcastBundle())
1736 PodcastEpisodeBundle *peb = item->bundle()->podcastBundle();
1737 QDomElement attr = newdoc.createElement( "PodcastDescription" );
1738 QDomText t = newdoc.createTextNode( peb->description() );
1739 attr.appendChild( t );
1740 i.appendChild( attr );
1742 attr = newdoc.createElement( "PodcastAuthor" );
1743 t = newdoc.createTextNode( peb->author() );
1744 attr.appendChild( t );
1745 i.appendChild( attr );
1747 attr = newdoc.createElement( "PodcastRSS" );
1748 t = newdoc.createTextNode( peb->parent().url() );
1749 attr.appendChild( t );
1750 i.appendChild( attr );
1752 attr = newdoc.createElement( "PodcastURL" );
1753 t = newdoc.createTextNode( peb->url().url() );
1754 attr.appendChild( t );
1755 i.appendChild( attr );
1758 if( !item
->m_playlistName
.isEmpty() )
1760 i
.setAttribute( "playlist", item
->m_playlistName
);
1763 if(item
->type() == MediaItem::PLAYLIST
)
1765 i
.setAttribute( "playlistdata", item
->data() );
1766 if( item
->flags() & MediaItem::SmartPlaylist
)
1767 i
.setAttribute( "smartplaylist", "1" );
1770 transferlist
.appendChild( i
);
1773 QTextStream
stream( &file
);
1774 stream
.setCodec( QTextCodec::codecForName( "UTF-8" ) );
1775 stream
.setAutoDetectUnicode( true );
1776 stream
<< "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1777 stream
<< newdoc
.toString();
1782 MediaQueue::load( const QString
& filename
)
1784 QFile
file( filename
);
1785 if( !file
.open( QIODevice::ReadOnly
) ) {
1791 QTextStream
stream( &file
);
1792 stream
.setCodec( QTextCodec::codecForName( "UTF-8" ) );
1793 stream
.setAutoDetectUnicode( true );
1798 if( !d
.setContent( stream
.readAll(), &er
, &l
, &c
) ) { // return error values
1799 Amarok::ContextStatusBar::instance()->longMessageThreadSafe( i18n(
1800 //TODO add a link to the path to the playlist
1801 "The XML in the transferlist was invalid. Please report this as a bug to the Amarok "
1802 "developers. Thank you." ), KDE::StatusBar::Error
);
1803 error() << "[TRANSFERLISTLOADER]: Error loading xml file: " << filename
<< "(" << er
<< ")"
1804 << " at line " << l
<< ", column " << c
;
1808 QList
<QDomNode
> nodes
;
1809 const QString
ITEM( "item" ); //so we don't construct this QString all the time
1810 for( QDomNode n
= d
.namedItem( "playlist" ).firstChild(); !n
.isNull(); n
= n
.nextSibling() )
1812 if( n
.nodeName() != ITEM
) continue;
1814 QDomElement elem
= n
.toElement();
1815 if( !elem
.isNull() )
1818 if( !elem
.hasAttribute( "url" ) )
1822 KUrl
url(elem
.attribute("url"));
1825 //bool podcast = elem.hasAttribute( "podcast" );
1826 //PodcastEpisodeBundle peb;
1827 //if( url.isLocalFile() )
1828 // peb.setLocalURL( url );
1829 Meta::TrackPtr track
= Meta::TrackPtr( new MetaFile::Track( url
) );
1831 for(QDomNode node
= elem
.firstChild();
1833 node
= node
.nextSibling())
1835 if(node
.firstChild().isNull())
1839 //if(node.nodeName() == "Title" )
1840 // bundle->setTitle(node.firstChild().toText().nodeValue());
1841 //else if(node.nodeName() == "Artist" )
1842 // bundle->setArtist(node.firstChild().toText().nodeValue());
1843 //else if(node.nodeName() == "Album" )
1844 // bundle->setAlbum(node.firstChild().toText().nodeValue());
1845 //else if(node.nodeName() == "Year" )
1846 // bundle->setYear(node.firstChild().toText().nodeValue().toUInt());
1847 //else if(node.nodeName() == "Genre" )
1848 // bundle->setGenre(node.firstChild().toText().nodeValue());
1849 //else if(node.nodeName() == "Comment" )
1850 // bundle->setComment(node.firstChild().toText().nodeValue());
1851 //else if(node.nodeName() == "PodcastDescription" )
1852 // peb.setDescription( node.firstChild().toText().nodeValue() );
1853 //else if(node.nodeName() == "PodcastAuthor" )
1854 // peb.setAuthor( node.firstChild().toText().nodeValue() );
1855 //else if(node.nodeName() == "PodcastRSS" )
1856 // peb.setParent( KUrl( node.firstChild().toText().nodeValue() ) );
1857 //else if(node.nodeName() == "PodcastURL" )
1858 // peb.setUrl( KUrl( node.firstChild().toText().nodeValue() ) );
1864 // bundle->setPodcastBundle( peb );
1867 QString playlist
= elem
.attribute( "playlist" );
1868 QString playlistdata
= elem
.attribute( "playlistdata" );
1869 if( !playlistdata
.isEmpty() )
1871 QString smart
= elem
.attribute( "smartplaylist" );
1872 if( smart
.isEmpty() )
1873 syncPlaylist( playlist
, KUrl( playlistdata
), true );
1875 syncPlaylist( playlist
, playlistdata
, true );
1878 addUrl( url
, track
, playlist
);
1884 MediaQueue::MediaQueue(MediaBrowser
*parent
)
1885 : K3ListView( parent
), m_parent( parent
)
1887 setFixedHeight( 200 );
1888 setSelectionMode( Q3ListView::Extended
);
1889 setItemsMovable( true );
1890 setDragEnabled( true );
1891 setShowSortIndicator( false );
1893 setFullWidth( true );
1894 setRootIsDecorated( false );
1895 setDropVisualizer( true ); //the visualizer (a line marker) is drawn when dragging over tracks
1896 setDropHighlighter( true ); //and the highligther (a focus rect) is drawn when dragging over playlists
1897 setDropVisualizerWidth( 3 );
1898 setAcceptDrops( true );
1899 addColumn( i18n( "Transfer Queue" ) );
1903 KActionCollection
* ac
= new KActionCollection( this );
1904 KStandardAction::selectAll( this, SLOT( selectAll() ), ac
);
1906 connect( this, SIGNAL( contextMenuRequested( Q3ListViewItem
*, const QPoint
&, int ) ),
1907 SLOT( slotShowContextMenu( Q3ListViewItem
*, const QPoint
&, int ) ) );
1908 connect( this, SIGNAL( dropped(QDropEvent
*, Q3ListViewItem
*, Q3ListViewItem
*) ),
1909 SLOT( slotDropped(QDropEvent
*, Q3ListViewItem
*, Q3ListViewItem
*) ) );
1913 MediaQueue::acceptDrag( QDropEvent
*e
) const
1916 return e
->source() == viewport()
1917 || e
->mimeData()->hasFormat( "amarok-sql" )
1918 || KUrl::List::canDecode( e
->mimeData() );
1922 MediaQueue::slotDropped( QDropEvent
* e
, Q3ListViewItem
* parent
, Q3ListViewItem
* after
)
1924 if( e
->source() != viewport() )
1926 if( e
->mimeData()->hasFormat( "amarok-sql" ) )
1928 QString
data( e
->mimeData()->data( "amarok-sql" ) );
1929 QString playlist
= data
.section( "\n", 0, 0 );
1930 QString query
= data
.section( "\n", 1 );
1931 QStringList values
= CollectionDB::instance()->query( query
);
1932 KUrl::List list
= CollectionDB::instance()->URLsFromSqlDrag( values
);
1933 addUrls( list
, playlist
);
1935 else if ( KUrl::List::canDecode( e
->mimeData() ) )
1937 KUrl::List list
= KUrl::List::fromMimeData( e
->mimeData() );
1938 if (!list
.isEmpty() )
1942 else if( Q3ListViewItem
*i
= currentItem() )
1944 moveItem( i
, parent
, after
);
1949 MediaQueue::dropProxyEvent( QDropEvent
*e
)
1951 slotDropped( e
, 0, 0 );
1955 MediaQueue::findPath( QString path
)
1957 for( Q3ListViewItem
*item
= firstChild();
1959 item
= item
->nextSibling())
1961 if(static_cast<MediaItem
*>(item
)->url().path() == path
)
1962 return static_cast<MediaItem
*>(item
);
1969 MediaQueue::computeSize() const
1972 for( Q3ListViewItem
*it
= firstChild();
1974 it
= it
->nextSibling())
1976 MediaItem
*item
= static_cast<MediaItem
*>(it
);
1978 if( item
&& item
->meta() &&
1979 ( !m_parent
->currentDevice()
1980 || !m_parent
->currentDevice()->isConnected()
1981 || !m_parent
->currentDevice()->trackExists(Meta::TrackPtr::dynamicCast(item
->meta())) ) )
1982 m_totalSize
+= ((item
->size()+1023)/1024)*1024;
1987 MediaQueue::totalSize() const
1993 MediaQueue::addItemToSize( const MediaItem
*item
) const
1995 if( item
&& item
->meta() &&
1996 ( !m_parent
->currentDevice()
1997 || !m_parent
->currentDevice()->isConnected()
1998 || !m_parent
->currentDevice()->trackExists(Meta::TrackPtr::dynamicCast(item
->meta())) ) )
1999 m_totalSize
+= ((item
->size()+1023)/1024)*1024;
2003 MediaQueue::subtractItemFromSize( const MediaItem
*item
, bool unconditionally
) const
2005 if( item
&& item
->meta() &&
2006 ( !m_parent
->currentDevice()
2007 || !m_parent
->currentDevice()->isConnected()
2008 || (unconditionally
|| !m_parent
->currentDevice()->trackExists(Meta::TrackPtr::dynamicCast(item
->meta()))) ) )
2009 m_totalSize
-= ((item
->size()+1023)/1024)*1024;
2013 MediaQueue::removeSelected()
2015 QList
<Q3ListViewItem
*> selected
= selectedItems();
2017 QListIterator
<Q3ListViewItem
*> iter( selected
);
2018 while( iter
.hasNext() )
2020 Q3ListViewItem
*item
= iter
.next();
2021 if( !(static_cast<MediaItem
*>(item
)->flags() & MediaItem::Transferring
) )
2023 subtractItemFromSize( static_cast<MediaItem
*>(item
) );
2025 if( m_parent
->currentDevice() && m_parent
->currentDevice()->isTransferring() )
2027 MediaBrowser::instance()->m_progress
->setRange( 0, MediaBrowser::instance()->m_progress
->maximum() - 1 );
2032 MediaBrowser::instance()->updateStats();
2033 MediaBrowser::instance()->updateButtons();
2038 MediaQueue::keyPressEvent( QKeyEvent
*e
)
2040 if( e
->key() == Qt::Key_Delete
)
2043 K3ListView::keyPressEvent( e
);
2047 MediaQueue::itemCountChanged()
2049 if( childCount() == 0 )
2051 else if( !isShown() )
2056 MediaQueue::slotShowContextMenu( Q3ListViewItem
* item
, const QPoint
& point
, int )
2061 Q3PopupMenu
menu( this );
2063 enum Actions
{ REMOVE_SELECTED
, CLEAR_ALL
, START_TRANSFER
};
2066 menu
.insertItem( KIcon( Amarok::icon( "remove_from_playlist" ) ), i18n( "&Remove From Queue" ), REMOVE_SELECTED
);
2068 menu
.insertItem( KIcon( Amarok::icon( "playlist_clear" ) ), i18n( "&Clear Queue" ), CLEAR_ALL
);
2069 menu
.insertItem( KIcon( Amarok::icon( "playlist_refresh" ) ), i18n( "&Start Transfer" ), START_TRANSFER
);
2070 menu
.setItemEnabled( START_TRANSFER
,
2071 MediaBrowser::instance()->currentDevice() &&
2072 MediaBrowser::instance()->currentDevice()->isConnected() &&
2073 MediaBrowser::instance()->currentDevice()->m_transfer
);
2075 switch( menu
.exec( point
) )
2077 case REMOVE_SELECTED
:
2083 case START_TRANSFER
:
2084 MediaBrowser::instance()->transferClicked();
2090 MediaQueue::clearItems()
2097 m_parent
->updateStats();
2098 m_parent
->updateButtons();
2103 #include "mediabrowser.moc"