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"
27 #include "MediaDevicePluginManager.h"
28 #include "metabundle.h"
29 #include "mountpointmanager.h"
30 #include "playlist/PlaylistModel.h"
31 #include "pluginmanager.h"
32 #include "podcastbundle.h"
33 #include "scriptmanager.h"
34 #include "scrobbler.h"
35 #include "searchwidget.h"
36 #include "statusbar.h"
37 #include "transferdialog.h"
38 #include "TheInstances.h"
41 #include <Q3PopupMenu>
42 #include <q3simplerichtext.h>
47 #include <QDomDocument>
48 #include <QDomElement>
57 #include <QListIterator>
60 #include <QPaintEvent>
62 #include <QProgressBar>
63 #include <QRadioButton>
65 #include <QToolButton>
66 #include <QToolTip> //QToolTip::add()
68 #include <k3multipledrag.h>
69 #include <k3process.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>
94 MediaBrowser
*MediaBrowser::s_instance
= 0;
96 bool MediaBrowser::isAvailable() //static
98 if( !MediaBrowser::instance() )
103 //to re-enable hiding, uncomment this and get rid of the return true above:
104 //return MediaBrowser::instance()->m_haveDevices;
107 class DummyMediaDevice
: public MediaDevice
110 DummyMediaDevice() : MediaDevice()
112 m_name
= i18n( "No Device Available" );
113 m_type
= "dummy-mediadevice";
114 m_uid
= "manual|DummyDevice|none|none";
116 void init( MediaBrowser
*browser
) { MediaDevice::init( browser
); }
117 virtual ~DummyMediaDevice() {}
118 virtual bool isConnected() { return false; }
119 virtual MediaItem
* trackExists(const MetaBundle
&) { return 0; }
120 virtual bool lockDevice(bool) { return true; }
121 virtual void unlockDevice() {}
122 virtual bool openDevice( bool silent
)
126 //QString msg = i18n( "Sorry, you do not have a supported portable music player." );
127 //Amarok::StatusBar::instance()->longMessage( msg, KDE::StatusBar::Sorry );
131 virtual bool closeDevice() { return false; }
132 virtual void synchronizeDevice() {}
133 virtual MediaItem
* copyTrackToDevice(const MetaBundle
&) { return 0; }
134 virtual int deleteItemFromDevice(MediaItem
*, int) { return -1; }
138 MediaBrowser::MediaBrowser( const char * /*name*/ )
140 , m_timer( new QTimer( this ) )
141 , m_currentDevice( 0 )
142 , m_waitForTranscode( false )
143 , m_quitting( false )
144 , m_connectAction( 0 )
145 , m_disconnectAction( 0 )
146 , m_customAction( 0 )
147 , m_configAction( 0 )
148 , m_transferAction( 0 )
152 m_timer
->setSingleShot( true );
154 m_toolbar
= new Browser::ToolBar( this );
155 m_toolbar
->setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Minimum
);
156 m_toolbar
->setToolButtonStyle( Qt::ToolButtonTextBesideIcon
);
158 //TODO: how to fix getButton
159 // perhaps the action can be referenced instead?
160 m_connectAction
= new KAction(KIcon("connect_creating"), i18n("Connect"), this);
161 connect(m_connectAction
, SIGNAL(triggered()), this, SLOT(connectClicked()));
162 m_toolbar
->addAction(m_connectAction
);
163 // m_toolbar->insertButton( "connect_creating", CONNECT, true, i18n("Connect") );
164 // m_toolbar->getButton(CONNECT)->setToolTip( i18n( "Connect media device" ) );
166 m_disconnectAction
= new KAction(KIcon("media-eject"), i18n("Disconnect"), this);
167 connect(m_disconnectAction
, SIGNAL(triggered()), this, SLOT(disconnectClicked()));
168 m_toolbar
->addAction(m_disconnectAction
);
169 // m_toolbar->insertButton( "media-eject", DISCONNECT, true, i18n("Disconnect") );
170 // m_toolbar->getButton(DISCONNECT)->setToolTip( i18n( "Disconnect media device" ) );
172 m_transferAction
= new KAction(KIcon("rebuild"), i18n("Transfer"), this);
173 connect(m_transferAction
, SIGNAL(triggered()), this, SLOT(transferClicked()));
174 m_toolbar
->addAction(m_transferAction
);
175 // m_toolbar->insertButton( "rebuild", TRANSFER, true, i18n("Transfer") );
176 // m_toolbar->getButton(TRANSFER)->setToolTip( i18n( "Transfer tracks to media device" ) );
178 m_toolbar
->addSeparator();
180 // m_toolbar->setIconText( KToolBar::IconTextRight, true );
181 m_customAction
= new KAction(KIcon( "add_playlist" ), i18n("custom"), this);
182 connect(m_customAction
, SIGNAL(triggered()), this, SLOT(customClicked()));
183 m_customAction
->setText( i18n("Special Device Functions") );
184 m_customAction
->setToolTip( i18n("Device-specific special functions or information") );
185 m_toolbar
->addAction(m_customAction
);
186 // m_toolbar->insertButton( Amarok::icon( "add_playlist" ), CUSTOM, SIGNAL( clicked() ), this, SLOT( customClicked() ), true, "custom" );
187 // m_toolbar->getButton(TRANSFER)->setToolTip( i18n( "Transfer tracks to media device" ) );
189 m_toolbar
->setToolButtonStyle( Qt::ToolButtonIconOnly
);
191 m_configAction
= new KAction(KIcon("configure"), i18n("Configure"), this);
192 connect(m_configAction
, SIGNAL(triggered()), this, SLOT(config()));
193 m_toolbar
->addAction(m_configAction
);
194 // m_toolbar->insertButton( Amarok::icon( "configure" ), CONFIGURE, true, i18n("Configure") );
195 // m_toolbar->getButton(CONFIGURE)->setToolTip( i18n( "Configure device" ) );
198 m_deviceCombo
= new KComboBox( this );
200 // searching/filtering
201 QToolBar
* searchToolBar
= new Browser::ToolBar( this );
202 searchToolBar
->setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Minimum
);
203 m_searchWidget
= new SearchWidget( searchToolBar
, this );
204 searchToolBar
->addWidget( m_searchWidget
);
205 connect( m_timer
, SIGNAL( timeout() ), SLOT( slotSetFilter() ) );
207 // connect to device cache
208 connect( MediaDeviceCache::instance(), SIGNAL( deviceAdded(const QString
&) ),
209 SLOT( deviceAdded(const QString
&) ) );
210 connect( MediaDeviceCache::instance(), SIGNAL( deviceRemoved(const QString
&) ),
211 SLOT( deviceRemoved(const QString
&) ) );
214 // we always have a dummy device
215 m_pluginName
[ i18n( "Disable" ) ] = "dummy-mediadevice";
216 m_pluginAmarokName
["dummy-mediadevice"] = i18n( "Disable" );
217 m_pluginName
[ i18n( "Do not handle" ) ] = "ignore";
218 m_pluginAmarokName
["ignore"] = i18n( "Do not handle" );
219 // query available device plugins
220 m_plugins
= PluginManager::query( "[X-KDE-Amarok-plugintype] == 'mediadevice'" );
221 for( KService::List::ConstIterator it
= m_plugins
.begin(); it
!= m_plugins
.end(); ++it
) {
222 // Save name properties in QMap for lookup
223 m_pluginName
[(*it
)->name()] = (*it
)->property( "X-KDE-Amarok-name" ).toString();
224 m_pluginAmarokName
[(*it
)->property( "X-KDE-Amarok-name" ).toString()] = (*it
)->name();
227 m_views
= new KVBox( this );
228 m_queue
= new MediaQueue( this );
229 m_progressBox
= new KHBox( this );
230 m_progress
= new QProgressBar( m_progressBox
);
231 m_cancelButton
= new KPushButton( KIcon( Amarok::icon( "cancel" ) ), i18n("Cancel"), m_progressBox
);
234 m_stats
= new SpaceLabel(this);
236 m_progressBox
->hide();
238 MediaDevice
*dev
= new DummyMediaDevice();
241 activateDevice( 0, false );
243 queue()->load( Amarok::saveLocation() + "transferlist.xml" );
244 queue()->computeSize();
246 setFocusProxy( m_queue
);
250 MediaDeviceCache::instance()->refreshCache();
251 foreach( QString uid
, MediaDeviceCache::instance()->getAll() )
254 //TODO: Take generic storage devices into account too -- or do we rely on the
255 //Solid backend to tell us if it's a PMP with "storage" type?
257 connect( m_deviceCombo
, SIGNAL( activated( int ) ), SLOT( activateDevice( int ) ) );
259 connect( m_cancelButton
, SIGNAL( clicked() ), SLOT( cancelClicked() ) );
260 connect( pApp
, SIGNAL( prepareToQuit() ), SLOT( prepareToQuit() ) );
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 MetaBundle
&bundle
)
297 m_itemMapMutex
.lock();
298 debug() << "tags changed for " << bundle
.url().url();
299 ItemMap::iterator it
= m_itemMap
.find( bundle
.url().url() );
300 if( it
!= m_itemMap
.end() )
302 MediaItem
*item
= *it
;
303 m_itemMapMutex
.unlock();
306 item
->device()->tagsChanged( item
, bundle
);
310 // it's an item on the transfer queue
311 item
->setBundle( new MetaBundle( bundle
) );
313 QString text
= item
->bundle()->prettyTitle();
314 if( text
.isEmpty() || (!item
->bundle()->isValidMedia() && !item
->bundle()->podcastBundle()) )
315 text
= item
->bundle()->url().prettyUrl();
316 if( !item
->m_playlistName
.isNull() )
318 text
+= " (" + item
->m_playlistName
+ ')';
320 item
->setText( 0, text
);
325 m_itemMapMutex
.unlock();
330 MediaBrowser::getBundle( const KUrl
&url
, MetaBundle
*bundle
) const
332 QMutexLocker
locker( &m_itemMapMutex
);
333 ItemMap::const_iterator it
= m_itemMap
.find( url
.url() );
334 if( it
== m_itemMap
.end() )
338 *bundle
= *(*it
)->bundle();
344 MediaBrowser::getProxyUrl( const KUrl
& daapUrl
) const
348 MediaDevice
* dc
= findChildren
<MediaDevice
*>( "DaapClient" ).first();
350 url
= dc
->getProxyUrl( daapUrl
);
355 MediaBrowser::deviceFromId( const QString
&id
) const
357 for( QList
<MediaDevice
*>::const_iterator it
= m_devices
.constBegin();
358 it
!= m_devices
.constEnd();
361 if( (*it
)->uid() == id
)
369 MediaBrowser::activateDevice( const MediaDevice
*dev
)
372 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
373 it
!= m_devices
.end();
378 activateDevice( index
);
386 MediaBrowser::activateDevice( int index
, bool skipDummy
)
388 if( m_currentDevice
&& m_currentDevice
->customAction() )
390 m_toolbar
->removeAction( m_currentDevice
->customAction() );
395 foreach( MediaDevice
*md
, m_devices
)
397 if( md
&& md
->view() )
403 m_currentDevice
= m_devices
.last();
410 if( index
>= m_devices
.count() )
412 if( !m_devices
.isEmpty() )
413 m_currentDevice
= m_devices
.last();
417 queue()->computeSize();
422 m_currentDevice
= m_devices
[index
];
423 if( m_currentDevice
&& m_currentDevice
->view() )
425 m_currentDevice
->view()->show();
426 if( m_currentDevice
->customAction() )
428 m_toolbar
->setToolButtonStyle( Qt::ToolButtonTextBesideIcon
);
429 m_toolbar
->addAction( m_currentDevice
->customAction() );
434 m_deviceCombo
->setCurrentIndex( index
-1 );
437 queue()->computeSize();
442 MediaBrowser::addDevice( MediaDevice
*device
)
444 m_devices
.append( device
);
446 device
->loadConfig();
448 if( device
->autoConnect() )
450 device
->connectDevice( true );
458 MediaBrowser::removeDevice( MediaDevice
*device
)
462 debug() << "remove device: type=" << device
->deviceType();
464 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
465 it
!= m_devices
.end();
470 bool current
= ( (*it
)->uid() == m_currentDevice
->uid() );
471 m_devices
.erase( it
);
473 activateDevice( 0, false );
478 if( device
->isConnected() )
480 if( device
->disconnectDevice( false /* don't run post-disconnect command */ ) )
481 unloadDevicePlugin( device
);
484 debug() << "Cannot remove device because disconnect failed";
485 Amarok::StatusBar::instance()->longMessage(
486 i18n( "Cannot remove device because disconnect failed" ),
487 KDE::StatusBar::Warning
);
491 unloadDevicePlugin( device
);
497 MediaBrowser::updateDevices()
499 m_deviceCombo
->clear();
501 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
502 it
!= m_devices
.end();
505 if( m_devices
.count() > 1 && dynamic_cast<DummyMediaDevice
*>(*it
) )
507 QString name
= (*it
)->name();
508 if( !(*it
)->deviceNode().isEmpty() )
510 name
= i18n( "%1 at %2", name
, (*it
)->deviceNode() );
512 if( (*it
)->hasMountPoint() && !(*it
)->mountPoint().isEmpty() )
514 name
+= i18n( " (mounted at %1)", (*it
)->mountPoint() );
516 m_deviceCombo
->addItem( name
, i
);
517 if( !m_currentDevice
|| (*it
)->uid() == m_currentDevice
->uid() )
519 m_deviceCombo
->setCurrentItem( name
);
523 m_deviceCombo
->setEnabled( m_devices
.count() > 1 );
524 m_haveDevices
= m_devices
.count() > 1;
528 MediaBrowser::deviceNames() const
532 for( QList
<MediaDevice
*>::const_iterator it
= m_devices
.constBegin();
533 it
!= m_devices
.constEnd();
536 QString name
= (*it
)->name();
544 MediaBrowser::deviceSwitch( const QString
&name
)
547 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
548 it
!= m_devices
.end();
551 if( (*it
)->name() == name
)
553 activateDevice( index
, false );
563 MediaBrowser::transcodingFinished( const QString
&src
, const QString
&dst
)
565 KUrl srcJob
= KUrl( m_transcodeSrc
);
566 KUrl srcResult
= KUrl( src
);
568 if( srcJob
.path() == srcResult
.path() )
570 m_transcodedUrl
= KUrl( dst
);
571 m_waitForTranscode
= false;
575 debug() << "transcoding for " << src
<< " finished, "
576 << "but we are waiting for " << m_transcodeSrc
<< " -- aborting";
577 m_waitForTranscode
= false;
582 MediaBrowser::transcode( const KUrl
&src
, const QString
&filetype
)
584 const ScriptManager
* const sm
= ScriptManager::instance();
586 if( sm
->transcodeScriptRunning().isEmpty() )
588 debug() << "cannot transcode with no transcoder registered";
592 m_waitForTranscode
= true;
593 m_transcodeSrc
= src
.url();
594 m_transcodedUrl
= KUrl();
595 ScriptManager::instance()->notifyTranscode( src
.url(), filetype
);
597 while( m_waitForTranscode
&& !sm
->transcodeScriptRunning().isEmpty() )
600 kapp
->processEvents( QEventLoop::AllEvents
);
603 return m_transcodedUrl
;
608 MediaBrowser::slotSetFilterTimeout() //SLOT
610 m_timer
->start( 280 ); //stops the timer for us first
614 MediaBrowser::slotSetFilter() //SLOT
618 if( m_currentDevice
)
619 m_currentDevice
->view()->setFilter( m_searchWidget
->lineEdit()->text() );
623 MediaBrowser::slotSetFilter( const QString
&text
)
625 m_searchWidget
->lineEdit()->setText( text
);
630 MediaBrowser::slotEditFilter()
632 EditFilterDialog
*fd
= new EditFilterDialog( this, true, m_searchWidget
->lineEdit()->text() );
633 connect( fd
, SIGNAL(filterChanged(const QString
&)), SLOT(slotSetFilter(const QString
&)) );
635 m_searchWidget
->lineEdit()->setText( fd
->filter() );
640 MediaBrowser::prepareToQuit()
642 m_waitForTranscode
= false;
644 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
645 it
!= m_devices
.end();
648 if( (*it
)->isConnected() )
649 (*it
)->disconnectDevice( false /* don't unmount */ );
653 MediaBrowser::~MediaBrowser()
655 debug() << "having to remove " << m_devices
.count() << " devices";
656 while( !m_devices
.isEmpty() )
658 removeDevice( m_devices
.last() );
661 queue()->save( Amarok::saveLocation() + "transferlist.xml" );
663 delete m_deviceCombo
;
667 MediaView::MediaView( QWidget
* parent
, MediaDevice
*device
)
668 : K3ListView( parent
)
673 setSelectionMode( Q3ListView::Extended
);
674 setItemsMovable( false );
675 setShowSortIndicator( true );
676 setFullWidth( true );
677 setRootIsDecorated( true );
678 setDragEnabled( true );
679 setDropVisualizer( true ); //the visualizer (a line marker) is drawn when dragging over tracks
680 setDropHighlighter( true ); //and the highligther (a focus rect) is drawn when dragging over playlists
681 setDropVisualizerWidth( 3 );
682 setAcceptDrops( true );
685 addColumn( i18n( "Remote Media" ) );
687 KActionCollection
* ac
= new KActionCollection( this );
688 KStandardAction::selectAll( this, SLOT( selectAll() ), ac
);
690 connect( this, SIGNAL( contextMenuRequested( Q3ListViewItem
*, const QPoint
&, int ) ),
691 this, SLOT( rmbPressed( Q3ListViewItem
*, const QPoint
&, int ) ) );
693 connect( this, SIGNAL( itemRenamed( Q3ListViewItem
* ) ),
694 this, SLOT( renameItem( Q3ListViewItem
* ) ) );
696 connect( this, SIGNAL( expanded( Q3ListViewItem
* ) ),
697 this, SLOT( slotExpand( Q3ListViewItem
* ) ) );
699 connect( this, SIGNAL( returnPressed( Q3ListViewItem
* ) ),
700 this, SLOT( invokeItem( Q3ListViewItem
* ) ) );
702 connect( this, SIGNAL( doubleClicked( Q3ListViewItem
*, const QPoint
&, int ) ),
703 this, SLOT( invokeItem( Q3ListViewItem
*, const QPoint
&, int ) ) );
707 MediaView::keyPressEvent( QKeyEvent
*e
)
709 if( e
->key() == Qt::Key_Delete
)
710 m_device
->deleteFromDevice();
712 K3ListView::keyPressEvent( e
);
716 MediaView::invokeItem( Q3ListViewItem
* i
, const QPoint
& point
, int column
) //SLOT
721 QPoint p
= mapFromGlobal( point
);
722 if ( p
.x() > header()->sectionPos( header()->mapToIndex( 0 ) ) + treeStepSize() * ( i
->depth() + ( rootIsDecorated() ? 1 : 0) ) + itemMargin()
723 || p
.x() < header()->sectionPos( header()->mapToIndex( 0 ) ) )
729 MediaView::invokeItem( Q3ListViewItem
*i
)
731 MediaItem
*item
= static_cast<MediaItem
*>( i
);
735 KUrl::List urls
= nodeBuildDragList( item
);
736 The::playlistModel()->insertMedia( urls
, Playlist::AppendAndPlay
);
740 MediaView::renameItem( Q3ListViewItem
*item
)
742 m_device
->renameItem( item
);
746 MediaView::slotExpand( Q3ListViewItem
*item
)
748 m_device
->expandItem( item
);
752 MediaView::~MediaView()
758 MediaView::dragObject()
760 KUrl::List urls
= nodeBuildDragList( 0 );
761 K3MultipleDrag
*md
= new K3MultipleDrag( viewport() );
762 md
->addDragObject( K3ListView::dragObject() );
763 K3URLDrag
* ud
= new K3URLDrag( urls
, viewport() );
764 md
->addDragObject( ud
);
765 md
->setPixmap( CollectionDB::createDragPixmap( urls
),
766 QPoint( CollectionDB::DRAGPIXMAP_OFFSET_X
, CollectionDB::DRAGPIXMAP_OFFSET_Y
) );
772 MediaView::nodeBuildDragList( MediaItem
* item
, int flags
)
779 fi
= static_cast<MediaItem
*>(firstChild());
786 if( fi
->isVisible() )
788 if ( fi
->isSelected() || !(flags
& OnlySelected
) )
790 if( fi
->isLeafItem() || fi
->type() == MediaItem::DIRECTORY
)
794 if(fi
->childCount() )
795 items
+= nodeBuildDragList( static_cast<MediaItem
*>(fi
->firstChild()), None
);
800 if ( fi
->childCount() )
801 items
+= nodeBuildDragList( static_cast<MediaItem
*>(fi
->firstChild()), OnlySelected
);
804 fi
= static_cast<MediaItem
*>(fi
->nextSibling());
810 MediaView::getSelectedLeaves( MediaItem
*parent
, QList
<MediaItem
*> *list
, int flags
)
814 list
= new QList
<MediaItem
*>;
818 it
= static_cast<MediaItem
*>(firstChild());
820 it
= static_cast<MediaItem
*>(parent
->firstChild());
822 for( ; it
; it
= static_cast<MediaItem
*>(it
->nextSibling()))
824 if( it
->isVisible() )
826 if( it
->childCount() && !( it
->type() == MediaItem::DIRECTORY
&& it
->isSelected() ) )
829 if( it
->isSelected() )
831 numFiles
+= getSelectedLeaves(it
, list
, f
);
833 if( it
->isSelected() || !(flags
& OnlySelected
) )
835 if( it
->type() == MediaItem::TRACK
||
836 it
->type() == MediaItem::DIRECTORY
||
837 it
->type() == MediaItem::PODCASTITEM
||
838 it
->type() == MediaItem::PLAYLISTITEM
||
839 it
->type() == MediaItem::INVISIBLE
||
840 it
->type() == MediaItem::ORPHANED
)
842 if( flags
& OnlyPlayed
)
844 if( it
->played() > 0 )
850 if( ( it
->isLeafItem() && (!(flags
& OnlyPlayed
) || it
->played()>0) )
851 || it
->type() == MediaItem::DIRECTORY
)
861 MediaView::acceptDrag( QDropEvent
*e
) const
863 if( e
->source() == MediaBrowser::queue()->viewport() )
866 return e
->source() == viewport()
867 || e
->mimeData()->hasFormat( "amarok-sql" )
868 || KUrl::List::canDecode( e
->mimeData() );
872 MediaView::contentsDropEvent( QDropEvent
*e
)
874 cleanDropVisualizer();
875 cleanItemHighlighter();
877 if(e
->source() == viewport())
879 const QPoint p
= contentsToViewport( e
->pos() );
880 MediaItem
*item
= static_cast<MediaItem
*>(itemAt( p
));
882 if( !item
&& MediaBrowser::instance()->currentDevice()->m_type
!= "generic-mediadevice" )
885 QList
<MediaItem
*> items
;
887 if( !item
|| item
->type() == MediaItem::DIRECTORY
||
888 item
->type() == MediaItem::TRACK
)
890 QList
<MediaItem
*> items
;
891 getSelectedLeaves( 0, &items
);
892 m_device
->addToDirectory( item
, items
);
894 else if( item
->type() == MediaItem::PLAYLIST
)
896 MediaItem
*list
= item
;
897 MediaItem
*after
= 0;
898 for(MediaItem
*it
= static_cast<MediaItem
*>(item
->firstChild());
900 it
= static_cast<MediaItem
*>(it
->nextSibling()))
903 getSelectedLeaves( 0, &items
);
904 m_device
->addToPlaylist( list
, after
, items
);
906 else if( item
->type() == MediaItem::PLAYLISTITEM
)
908 MediaItem
*list
= static_cast<MediaItem
*>(item
->parent());
909 MediaItem
*after
= 0;
910 for(MediaItem
*it
= static_cast<MediaItem
*>(item
->parent()->firstChild());
912 it
= static_cast<MediaItem
*>(it
->nextSibling()))
919 getSelectedLeaves( 0, &items
);
920 m_device
->addToPlaylist( list
, after
, items
);
922 else if( item
->type() == MediaItem::PLAYLISTSROOT
)
924 QList
<MediaItem
*> items
;
925 getSelectedLeaves( 0, &items
);
926 QString
base( i18n("New Playlist") );
929 while( item
->findItem(name
) )
933 name
= base
+ ' ' + num
;
936 MediaItem
*pl
= m_device
->newPlaylist(name
, item
, items
);
937 ensureItemVisible(pl
);
943 if( e
->mimeData()->hasFormat( "amarok-sql" ) )
945 QString
data( e
->mimeData()->data( "amarok-sql" ) );
946 QString playlist
= data
.section( "\n", 0, 0 );
947 QString query
= data
.section( "\n", 1 );
948 QStringList values
= CollectionDB::instance()->query( query
);
949 KUrl::List list
= CollectionDB::instance()->URLsFromSqlDrag( values
);
950 MediaBrowser::queue()->addUrls( list
, playlist
);
952 else if ( KUrl::List::canDecode( e
->mimeData() ) )
954 KUrl::List list
= KUrl::List::fromMimeData( e
->mimeData() );
955 MediaBrowser::queue()->addUrls( list
);
961 MediaView::viewportPaintEvent( QPaintEvent
*e
)
963 K3ListView::viewportPaintEvent( e
);
965 // Superimpose bubble help:
967 if ( !MediaBrowser::instance()->currentDevice() || !MediaBrowser::instance()->currentDevice()->isConnected() )
969 QPainter
p( viewport() );
971 Q3SimpleRichText
t( i18n(
973 "<h3>Media Device Browser</h3>"
974 "Configure your media device and then "
975 "click the Connect button to access your media device. "
976 "Drag and drop files to enqueue them for transfer."
977 "</div>" ), QApplication::font() );
979 t
.setWidth( width() - 50 );
981 const uint w
= t
.width() + 20;
982 const uint h
= t
.height() + 20;
984 p
.setBrush( palette().background() );
985 p
.drawRoundRect( 15, 15, w
, h
, (8*200)/w
, (8*200)/h
);
986 t
.draw( &p
, 20, 20, QRect(), palette() );
988 MediaBrowser::instance()->updateButtons();
992 MediaView::rmbPressed( Q3ListViewItem
*item
, const QPoint
&p
, int i
)
994 if( m_device
->isConnected() )
995 m_device
->rmbPressed( item
, p
, i
);
999 MediaView::newDirectory( MediaItem
*parent
)
1002 const QString name
= KInputDialog::getText(i18n("Add Directory"), i18n("Directory Name:"), QString(), &ok
, this);
1004 if( ok
&& !name
.isEmpty() )
1006 m_device
->newDirectory( name
, parent
);
1013 MediaBrowser::deviceAdded( const QString
&uid
)
1016 debug() << "deviceAdded called with a uid of: " << uid
;
1017 MediaDevice
*md
= loadDevicePlugin( uid
);
1021 if( m_currentDevice
== *(m_devices
.begin()) || m_currentDevice
== *(m_devices
.end()) )
1022 activateDevice( m_devices
.count()-1, false );
1027 MediaBrowser::deviceRemoved( const QString
&uid
)
1029 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
1030 it
!= m_devices
.end();
1033 if( (*it
)->m_uid
== uid
)
1035 if( (*it
)->isConnected() )
1037 if( (*it
)->disconnectDevice() )
1038 removeDevice( *it
);
1039 Amarok::StatusBar::instance()->longMessage(
1040 i18n( "The device %1 was removed before it was disconnected. "
1041 "In order to avoid possible data loss, press the \"Disconnect\" "
1042 "button before disconnecting the device.", (*it
)->name() ),
1043 KDE::StatusBar::Warning
);
1046 removeDevice( *it
);
1053 MediaBrowser::loadDevicePlugin( const QString
&uid
)
1061 if( MediaDeviceCache::instance()->deviceType( uid
) == MediaDeviceCache::SolidType
)
1063 debug() << "uid " << uid
<< " detected as a Solid device";
1064 Solid::Device
solidDevice( uid
);
1066 Solid::PortableMediaPlayer
* pmp
= solidDevice
.as
<Solid::PortableMediaPlayer
>();
1068 //TODO: Generic storage? need to set mount point if so...
1071 debug() << "Failed to convert Solid device to PortableMediaPlayer";
1074 if( pmp
->supportedProtocols().size() == 0 )
1076 debug() << "Portable Media Player " << uid
<< " does not support any protocols";
1080 foreach( QString supported
, pmp
->supportedProtocols() )
1081 debug() << "Device supports protocol " << supported
;
1083 protocol
= pmp
->supportedProtocols()[0];
1084 if( protocol
== "storage" )
1085 protocol
= "generic";
1086 if( protocol
== "pde" )
1089 name
= solidDevice
.vendor() + " - " + solidDevice
.product();
1091 else if( MediaDeviceCache::instance()->deviceType( uid
) == MediaDeviceCache::ManualType
)
1093 QString handler
= Amarok::config( "PortableDevices" ).readEntry( uid
, QString() );
1094 if( handler
.isEmpty() || !handler
.startsWith( "manual" ) )
1096 //this shouldn't happen, as manually adding the device should create the necessary entry
1097 debug() << "Something's wrong, trying to manually load a non-existant or non-manual manual device";
1100 QStringList sl
= handler
.split( "|" );
1101 if( sl
[0] != "manual" )
1108 protocol
+= "-mediadevice";
1109 QString query
= "[X-KDE-Amarok-plugintype] == 'mediadevice' and [X-KDE-Amarok-name] == '%1'";
1110 Amarok::Plugin
*plugin
= PluginManager::createFromQuery( query
.arg( protocol
) );
1114 debug() << "Returning plugin!";
1115 MediaDevice
*device
= static_cast<MediaDevice
*>( plugin
);
1116 device
->init( this );
1117 device
->m_uid
= uid
;
1118 device
->m_name
= name
;
1119 device
->m_type
= protocol
;
1120 device
->m_mountPoint
= mountPoint
;
1124 debug() << "no plugin for " << protocol
;
1129 MediaBrowser::pluginSelected( const QString
&uid
, const QString
&plugin
)
1132 if( !plugin
.isEmpty() )
1134 debug() << "Device uid is " << uid
<< " and plugin selected is: " << plugin
;
1135 Amarok::config( "PortableDevices" ).writeEntry( uid
, plugin
);
1137 bool success
= true;
1138 foreach( MediaDevice
* device
, m_devices
)
1140 if( device
->uid() == uid
)
1142 debug() << "removing matching device";
1143 if( device
->isConnected() )
1145 if( device
->disconnectDevice( false ) )
1146 removeDevice( device
);
1151 removeDevice( device
);
1162 debug() << "Cannot change plugin while operation is in progress" << endl
;
1163 Amarok::StatusBar::instance()->longMessage(
1164 i18n( "Cannot change plugin while operation is in progress" ),
1165 KDE::StatusBar::Warning
);
1169 debug() << "Device uid is " << uid
<< " and you opted not to use a plugin";
1173 MediaBrowser::showPluginManager()
1175 MediaDevicePluginManagerDialog
* mpm
= new MediaDevicePluginManagerDialog();
1181 MediaBrowser::configSelectPlugin( int index )
1185 if( m_currentDevice == m_devices.begin() )
1187 AmarokConfig::setDeviceType( m_pluginName[m_configPluginCombo->currentText()] );
1189 else if( currentDevice() )
1191 KConfig *config = Amarok::config( "MediaBrowser" );
1192 config->writeEntry( currentDevice()->uniqueId(), m_pluginName[m_configPluginCombo->currentText()] );
1195 if( !currentDevice() )
1196 activateDevice( 0, false );
1198 if( !currentDevice() )
1201 if( m_pluginName[m_configPluginCombo->currentText()] != currentDevice()->deviceType() )
1203 MediaDevice *dev = currentDevice();
1204 dev->removeConfigElements( m_configBox );
1205 if( dev->isConnected() )
1207 dev->disconnectDevice( false );
1209 unloadDevicePlugin( dev );
1210 *m_currentDevice = loadDevicePlugin( AmarokConfig::deviceType() );
1211 if( !*m_currentDevice )
1213 *m_currentDevice = new DummyMediaDevice();
1214 if( AmarokConfig::deviceType() != "dummy-mediadevice" )
1216 QString msg = i18n( "The requested media device could not be loaded" );
1217 Amarok::StatusBar::instance()->shortMessage( msg );
1220 dev = currentDevice();
1224 m_configBox->hide();
1225 dev->addConfigElements( m_configBox );
1226 m_configBox->show();
1228 dev->view()->show();
1230 if( dev->autoConnect() )
1232 dev->connectDevice( true );
1241 MediaBrowser::unloadDevicePlugin( MediaDevice
*device
)
1248 disconnect( device
); // disconnect all signals
1250 if( dynamic_cast<DummyMediaDevice
*>(device
) )
1256 PluginManager::unload( device
);
1261 MediaBrowser::config()
1263 if( m_deviceCombo
->currentText() == "No Device Selected" )
1265 //Amarok::StatusBar::instance()->longMessage( i18n( "No device selected to configure." ),
1266 // KDE::StatusBar::Sorry );
1267 showPluginManager();
1271 DeviceConfigureDialog
* dcd
= new DeviceConfigureDialog( m_currentDevice
);
1273 bool successful
= dcd
->successful();
1279 MediaBrowser::updateButtons()
1281 if( !connectAction() || !disconnectAction() || !transferAction() )
1284 if( m_currentDevice
)
1286 transferAction()->setVisible( m_currentDevice
->m_transfer
);
1287 customAction()->setVisible( m_currentDevice
->m_customButton
);
1288 configAction()->setVisible( m_currentDevice
->m_configure
);
1290 connectAction()->setEnabled( !m_currentDevice
->isConnected() );
1291 disconnectAction()->setEnabled( m_currentDevice
->isConnected() );
1292 transferAction()->setEnabled( m_currentDevice
->isConnected() && m_queue
->childCount() > 0 );
1293 if( customAction() )
1294 customAction()->setEnabled( true );
1298 connectAction()->setEnabled( false );
1299 disconnectAction()->setEnabled( false );
1300 transferAction()->setEnabled( false );
1301 if( customAction() )
1302 customAction()->setEnabled( false );
1307 MediaBrowser::updateStats()
1312 KIO::filesize_t queued
= m_queue
->totalSize();
1314 QString text
= i18np( " 1 track in queue", " %1 tracks in queue", m_queue
->childCount() );
1315 if(m_queue
->childCount() > 0)
1317 text
+= i18n(" (%1)", KIO::convertSize( queued
) );
1320 KIO::filesize_t total
, avail
;
1321 if( m_currentDevice
&& m_currentDevice
->getCapacity(&total
, &avail
) )
1323 text
+= i18n( " - %1 of %2 available", KIO::convertSize( avail
), KIO::convertSize( total
) );
1325 m_stats
->m_used
= total
-avail
;
1326 m_stats
->m_total
= total
;
1327 m_stats
->m_scheduled
= queued
;
1331 m_stats
->m_used
= 0;
1332 m_stats
->m_total
= 0;
1333 m_stats
->m_scheduled
= queued
;
1336 m_stats
->setText(text
);
1337 m_stats
->setToolTip( text
);
1342 MediaView::setFilter( const QString
&filter
, MediaItem
*parent
)
1344 bool advanced
= ExpressionParser::isAdvancedExpression( filter
);
1345 QList
<int> defaultColumns
;
1346 defaultColumns
<< MetaBundle::Album
;
1347 defaultColumns
<< MetaBundle::Title
;
1348 defaultColumns
<< MetaBundle::Artist
;
1355 it
= static_cast<MediaItem
*>(firstChild());
1359 it
= static_cast<MediaItem
*>(parent
->firstChild());
1362 bool childrenVisible
= false;
1363 for( ; it
; it
= static_cast<MediaItem
*>(it
->nextSibling()))
1365 bool visible
= true;
1366 if(it
->isLeafItem())
1370 ParsedExpression parsed
= ExpressionParser::parse( filter
);
1371 visible
= it
->bundle() && it
->bundle()->matchesParsedExpression( parsed
, defaultColumns
);
1375 visible
= it
->bundle() && it
->bundle()->matchesSimpleExpression( filter
, defaultColumns
);
1380 visible
= setFilter(filter
, it
);
1381 if(it
->type()==MediaItem::PLAYLISTSROOT
|| it
->type()==MediaItem::PLAYLIST
)
1385 else if(it
->type()==MediaItem::DIRECTORY
)
1388 QStringList list
= filter
.split( " ", QString::SkipEmptyParts
);
1389 for( QStringList::iterator i
= list
.begin();
1393 if( !(*it
).text(0).contains( *i
) )
1403 if( filter
.isEmpty() )
1405 it
->setVisible( visible
);
1407 childrenVisible
= true;
1410 if( root
&& m_device
)
1411 m_device
->updateRootItems();
1413 return childrenVisible
;
1417 MediaQueue::syncPlaylist( const QString
&name
, const QString
&query
, bool loading
)
1419 MediaItem
* item
= new MediaItem( this, lastItem() );
1420 item
->setType( MediaItem::PLAYLIST
);
1421 item
->setExpandable( false );
1422 item
->setData( query
);
1423 item
->m_playlistName
= name
;
1424 item
->setText( 0, name
);
1425 item
->m_flags
|= MediaItem::SmartPlaylist
;
1426 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1433 MediaQueue::syncPlaylist( const QString
&name
, const KUrl
&url
, bool loading
)
1435 MediaItem
* item
= new MediaItem( this, lastItem() );
1436 item
->setType( MediaItem::PLAYLIST
);
1437 item
->setExpandable( false );
1438 item
->setData( url
.url() );
1439 item
->m_playlistName
= name
;
1440 item
->setText( 0, name
);
1441 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1448 MediaQueue::addUrl( const KUrl
& url2
, MetaBundle
*bundle
, const QString
&playlistName
)
1450 KUrl url
= Amarok::mostLocalURL( url2
);
1453 // if( PlaylistFile::isPlaylistFile( url ) )
1455 // QString name = url.path().section( "/", -1 ).section( ".", 0, -2 ).replace( "_", " " );
1456 // PlaylistFile playlist( url.path() );
1458 // if( playlist.isError() )
1460 // Amarok::StatusBar::instance()->longMessage( i18n( "Failed to load playlist: %1", url.path() ),
1461 // KDE::StatusBar::Sorry );
1465 // for( BundleList::iterator it = playlist.bundles().begin();
1466 // it != playlist.bundles().end();
1469 // addUrl( (*it).url(), 0, name );
1473 if( url
.protocol() == "file" && QFileInfo( url
.path() ).isDir() )
1476 // KUrl::List urls = Amarok::recursiveUrlExpand( url );
1477 // oldForeachType( KUrl::List, urls )
1482 if( playlistName
.isNull() )
1484 for( MediaItem
*it
= static_cast<MediaItem
*>(firstChild());
1486 it
= static_cast<MediaItem
*>(it
->nextSibling()) )
1488 if( it
->url() == url
)
1490 Amarok::StatusBar::instance()->shortMessage(
1491 i18n( "Track already queued for transfer: %1", url
.url() ) );
1498 bundle
= new MetaBundle( url
);
1500 MediaItem
* item
= new MediaItem( this, lastItem() );
1501 item
->setExpandable( false );
1502 item
->setDropEnabled( true );
1503 item
->setBundle( bundle
);
1504 if(bundle
->podcastBundle() )
1506 item
->setType( MediaItem::PODCASTITEM
);
1508 item
->m_playlistName
= playlistName
;
1510 QString text
= item
->bundle()->prettyTitle();
1511 if( text
.isEmpty() || (!item
->bundle()->isValidMedia() && !item
->bundle()->podcastBundle()) )
1512 text
= item
->bundle()->url().prettyUrl();
1513 if( !item
->m_playlistName
.isNull() )
1515 text
+= " (" + item
->m_playlistName
+ ')';
1517 item
->setText( 0, text
);
1519 m_parent
->updateButtons();
1520 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1521 addItemToSize( item
);
1526 MediaQueue::addUrl( const KUrl
&url
, MediaItem
*item
)
1529 MediaItem
*newitem
= new MediaItem( this, lastItem() );
1530 newitem
->setExpandable( false );
1531 newitem
->setDropEnabled( true );
1532 MetaBundle
*bundle
= new MetaBundle( *item
->bundle() );
1534 filepath
.addPath( bundle
->filename() );
1535 bundle
->setUrl( filepath
);
1536 newitem
->m_device
= item
->m_device
;
1537 if(bundle
->podcastBundle() )
1539 item
->setType( MediaItem::PODCASTITEM
);
1541 QString text
= item
->bundle()->prettyTitle();
1542 if( text
.isEmpty() || (!item
->bundle()->isValidMedia() && !item
->bundle()->podcastBundle()) )
1543 text
= item
->bundle()->url().prettyUrl();
1544 if( !item
->m_playlistName
.isEmpty() )
1546 text
+= " (" + item
->m_playlistName
+ ')';
1548 newitem
->setText( 0, text
);
1549 newitem
->setBundle( bundle
);
1550 m_parent
->updateButtons();
1551 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1552 addItemToSize( item
);
1558 MediaQueue::addUrls( const KUrl::List urls
, const QString
&playlistName
)
1560 KUrl::List::ConstIterator it
= urls
.begin();
1561 for ( ; it
!= urls
.end(); ++it
)
1562 addUrl( *it
, 0, playlistName
);
1568 MediaQueue::URLsAdded()
1570 m_parent
->updateStats();
1571 m_parent
->updateButtons();
1572 if( m_parent
->currentDevice()
1573 && m_parent
->currentDevice()->isConnected()
1574 && m_parent
->currentDevice()->asynchronousTransfer()
1575 && !m_parent
->currentDevice()->isTransferring() )
1576 m_parent
->currentDevice()->transferFiles();
1578 save( Amarok::saveLocation() + "transferlist.xml" );
1582 MediaQueue::dragObject()
1585 for( Q3ListViewItem
*it
= firstChild(); it
; it
= it
->nextSibling() )
1587 if( it
->isVisible() && it
->isSelected() && static_cast<MediaItem
*>(it
) )
1588 urls
+= static_cast<MediaItem
*>(it
)->url();
1591 K3MultipleDrag
*md
= new K3MultipleDrag( viewport() );
1592 Q3DragObject
*d
= K3ListView::dragObject();
1593 K3URLDrag
* urldrag
= new K3URLDrag( urls
, viewport() );
1594 md
->addDragObject( d
);
1595 md
->addDragObject( urldrag
);
1596 md
->setPixmap( CollectionDB::createDragPixmap( urls
),
1597 QPoint( CollectionDB::DRAGPIXMAP_OFFSET_X
, CollectionDB::DRAGPIXMAP_OFFSET_Y
) );
1602 MediaBrowser::cancelClicked()
1606 m_waitForTranscode
= false;
1607 if( m_currentDevice
)
1608 m_currentDevice
->abortTransfer();
1612 MediaBrowser::transferClicked()
1614 transferAction()->setEnabled( false );
1616 && m_currentDevice
->isConnected()
1617 && !m_currentDevice
->isTransferring() )
1619 if( !m_currentDevice
->hasTransferDialog() )
1620 m_currentDevice
->transferFiles();
1623 m_currentDevice
->runTransferDialog();
1624 //may not work with non-TransferDialog-class object, but maybe some run time introspection could solve it?
1625 if( m_currentDevice
->getTransferDialog() &&
1626 ( reinterpret_cast<TransferDialog
*>(m_currentDevice
->getTransferDialog()))->isAccepted() )
1627 m_currentDevice
->transferFiles();
1632 m_currentDevice
->m_transferDir
= m_currentDevice
->mountPoint();
1636 MediaBrowser::connectClicked()
1638 bool haveToConfig
= false;
1639 // it was just clicked, so isOn() == true.
1640 if( m_currentDevice
&& !m_currentDevice
->isConnected() )
1642 haveToConfig
= !m_currentDevice
->connectDevice();
1645 haveToConfig
|= !m_currentDevice
;
1646 haveToConfig
|= ( m_currentDevice
&& !m_currentDevice
->isConnected() );
1648 if ( !m_currentDevice
->needsManualConfig() )
1649 haveToConfig
= false;
1651 if( haveToConfig
&& m_devices
.at( 0 ) == m_currentDevice
)
1653 if( config() && m_currentDevice
&& !m_currentDevice
->isConnected() )
1654 m_currentDevice
->connectDevice();
1663 MediaBrowser::disconnectClicked()
1665 if( m_currentDevice
&& m_currentDevice
->isTransferring() )
1667 int action
= KMessageBox::questionYesNoCancel( MediaBrowser::instance(),
1668 i18n( "Transfer in progress. Finish or stop after current track?" ),
1669 i18n( "Stop Transfer?" ),
1670 KGuiItem(i18n("&Finish"), "goto-page"),
1671 KGuiItem(i18n("&Stop"), "media-eject") );
1672 if( action
== KMessageBox::Cancel
)
1676 else if( action
== KMessageBox::Yes
)
1678 m_currentDevice
->scheduleDisconnect();
1683 transferAction()->setEnabled( false );
1684 disconnectAction()->setEnabled( false );
1686 if( m_currentDevice
)
1688 m_currentDevice
->disconnectDevice( true );
1697 MediaBrowser::customClicked()
1699 if( m_currentDevice
)
1700 m_currentDevice
->customClicked();
1704 MediaQueue::save( const QString
&path
)
1708 if( !file
.open( QIODevice::WriteOnly
) ) return;
1710 QDomDocument newdoc
;
1711 QDomElement transferlist
= newdoc
.createElement( "playlist" );
1712 transferlist
.setAttribute( "product", "Amarok" );
1713 transferlist
.setAttribute( "version", APP_VERSION
);
1714 newdoc
.appendChild( transferlist
);
1716 for( const MediaItem
*item
= static_cast<MediaItem
*>( firstChild() );
1718 item
= static_cast<MediaItem
*>( item
->nextSibling() ) )
1720 QDomElement i
= newdoc
.createElement("item");
1721 i
.setAttribute("url", item
->url().url());
1723 if( item
->bundle() )
1725 QDomElement attr
= newdoc
.createElement( "Title" );
1726 QDomText t
= newdoc
.createTextNode( item
->bundle()->title() );
1727 attr
.appendChild( t
);
1728 i
.appendChild( attr
);
1730 attr
= newdoc
.createElement( "Artist" );
1731 t
= newdoc
.createTextNode( item
->bundle()->artist() );
1732 attr
.appendChild( t
);
1733 i
.appendChild( attr
);
1735 attr
= newdoc
.createElement( "Album" );
1736 t
= newdoc
.createTextNode( item
->bundle()->album() );
1737 attr
.appendChild( t
);
1738 i
.appendChild( attr
);
1740 attr
= newdoc
.createElement( "Year" );
1741 t
= newdoc
.createTextNode( QString::number( item
->bundle()->year() ) );
1742 attr
.appendChild( t
);
1743 i
.appendChild( attr
);
1745 attr
= newdoc
.createElement( "Comment" );
1746 t
= newdoc
.createTextNode( item
->bundle()->comment() );
1747 attr
.appendChild( t
);
1748 i
.appendChild( attr
);
1750 attr
= newdoc
.createElement( "Genre" );
1751 t
= newdoc
.createTextNode( item
->bundle()->genre() );
1752 attr
.appendChild( t
);
1753 i
.appendChild( attr
);
1755 attr
= newdoc
.createElement( "Track" );
1756 t
= newdoc
.createTextNode( QString::number( item
->bundle()->track() ) );
1757 attr
.appendChild( t
);
1758 i
.appendChild( attr
);
1761 if(item
->type() == MediaItem::PODCASTITEM
)
1763 i
.setAttribute( "podcast", "1" );
1766 if(item
->type() == MediaItem::PODCASTITEM
1767 && item
->bundle()->podcastBundle())
1769 PodcastEpisodeBundle
*peb
= item
->bundle()->podcastBundle();
1770 QDomElement attr
= newdoc
.createElement( "PodcastDescription" );
1771 QDomText t
= newdoc
.createTextNode( peb
->description() );
1772 attr
.appendChild( t
);
1773 i
.appendChild( attr
);
1775 attr
= newdoc
.createElement( "PodcastAuthor" );
1776 t
= newdoc
.createTextNode( peb
->author() );
1777 attr
.appendChild( t
);
1778 i
.appendChild( attr
);
1780 attr
= newdoc
.createElement( "PodcastRSS" );
1781 t
= newdoc
.createTextNode( peb
->parent().url() );
1782 attr
.appendChild( t
);
1783 i
.appendChild( attr
);
1785 attr
= newdoc
.createElement( "PodcastURL" );
1786 t
= newdoc
.createTextNode( peb
->url().url() );
1787 attr
.appendChild( t
);
1788 i
.appendChild( attr
);
1791 if( !item
->m_playlistName
.isEmpty() )
1793 i
.setAttribute( "playlist", item
->m_playlistName
);
1796 if(item
->type() == MediaItem::PLAYLIST
)
1798 i
.setAttribute( "playlistdata", item
->data() );
1799 if( item
->flags() & MediaItem::SmartPlaylist
)
1800 i
.setAttribute( "smartplaylist", "1" );
1803 transferlist
.appendChild( i
);
1806 QTextStream
stream( &file
);
1807 stream
.setCodec( QTextCodec::codecForName( "UTF-8" ) );
1808 stream
.setAutoDetectUnicode( true );
1809 stream
<< "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1810 stream
<< newdoc
.toString();
1815 MediaQueue::load( const QString
& filename
)
1817 QFile
file( filename
);
1818 if( !file
.open( QIODevice::ReadOnly
) ) {
1824 QTextStream
stream( &file
);
1825 stream
.setCodec( QTextCodec::codecForName( "UTF-8" ) );
1826 stream
.setAutoDetectUnicode( true );
1831 if( !d
.setContent( stream
.readAll(), &er
, &l
, &c
) ) { // return error values
1832 Amarok::StatusBar::instance()->longMessageThreadSafe( i18n(
1833 //TODO add a link to the path to the playlist
1834 "The XML in the transferlist was invalid. Please report this as a bug to the Amarok "
1835 "developers. Thank you." ), KDE::StatusBar::Error
);
1836 error() << "[TRANSFERLISTLOADER]: Error loading xml file: " << filename
<< "(" << er
<< ")"
1837 << " at line " << l
<< ", column " << c
;
1841 QList
<QDomNode
> nodes
;
1842 const QString
ITEM( "item" ); //so we don't construct this QString all the time
1843 for( QDomNode n
= d
.namedItem( "playlist" ).firstChild(); !n
.isNull(); n
= n
.nextSibling() )
1845 if( n
.nodeName() != ITEM
) continue;
1847 QDomElement elem
= n
.toElement();
1848 if( !elem
.isNull() )
1851 if( !elem
.hasAttribute( "url" ) )
1855 KUrl
url(elem
.attribute("url"));
1857 bool podcast
= elem
.hasAttribute( "podcast" );
1858 PodcastEpisodeBundle peb
;
1859 if( url
.isLocalFile() )
1860 peb
.setLocalURL( url
);
1861 MetaBundle
*bundle
= new MetaBundle( url
);
1862 for(QDomNode node
= elem
.firstChild();
1864 node
= node
.nextSibling())
1866 if(node
.firstChild().isNull())
1869 if(node
.nodeName() == "Title" )
1870 bundle
->setTitle(node
.firstChild().toText().nodeValue());
1871 else if(node
.nodeName() == "Artist" )
1872 bundle
->setArtist(node
.firstChild().toText().nodeValue());
1873 else if(node
.nodeName() == "Album" )
1874 bundle
->setAlbum(node
.firstChild().toText().nodeValue());
1875 else if(node
.nodeName() == "Year" )
1876 bundle
->setYear(node
.firstChild().toText().nodeValue().toUInt());
1877 else if(node
.nodeName() == "Genre" )
1878 bundle
->setGenre(node
.firstChild().toText().nodeValue());
1879 else if(node
.nodeName() == "Comment" )
1880 bundle
->setComment(node
.firstChild().toText().nodeValue());
1881 else if(node
.nodeName() == "PodcastDescription" )
1882 peb
.setDescription( node
.firstChild().toText().nodeValue() );
1883 else if(node
.nodeName() == "PodcastAuthor" )
1884 peb
.setAuthor( node
.firstChild().toText().nodeValue() );
1885 else if(node
.nodeName() == "PodcastRSS" )
1886 peb
.setParent( KUrl( node
.firstChild().toText().nodeValue() ) );
1887 else if(node
.nodeName() == "PodcastURL" )
1888 peb
.setUrl( KUrl( node
.firstChild().toText().nodeValue() ) );
1893 bundle
->setPodcastBundle( peb
);
1896 QString playlist
= elem
.attribute( "playlist" );
1897 QString playlistdata
= elem
.attribute( "playlistdata" );
1898 if( !playlistdata
.isEmpty() )
1900 QString smart
= elem
.attribute( "smartplaylist" );
1901 if( smart
.isEmpty() )
1902 syncPlaylist( playlist
, KUrl( playlistdata
), true );
1904 syncPlaylist( playlist
, playlistdata
, true );
1907 addUrl( url
, bundle
, playlist
);
1913 MediaQueue::MediaQueue(MediaBrowser
*parent
)
1914 : K3ListView( parent
), m_parent( parent
)
1916 setFixedHeight( 200 );
1917 setSelectionMode( Q3ListView::Extended
);
1918 setItemsMovable( true );
1919 setDragEnabled( true );
1920 setShowSortIndicator( false );
1922 setFullWidth( true );
1923 setRootIsDecorated( false );
1924 setDropVisualizer( true ); //the visualizer (a line marker) is drawn when dragging over tracks
1925 setDropHighlighter( true ); //and the highligther (a focus rect) is drawn when dragging over playlists
1926 setDropVisualizerWidth( 3 );
1927 setAcceptDrops( true );
1928 addColumn( i18n( "Transfer Queue" ) );
1932 KActionCollection
* ac
= new KActionCollection( this );
1933 KStandardAction::selectAll( this, SLOT( selectAll() ), ac
);
1935 connect( this, SIGNAL( contextMenuRequested( Q3ListViewItem
*, const QPoint
&, int ) ),
1936 SLOT( slotShowContextMenu( Q3ListViewItem
*, const QPoint
&, int ) ) );
1937 connect( this, SIGNAL( dropped(QDropEvent
*, Q3ListViewItem
*, Q3ListViewItem
*) ),
1938 SLOT( slotDropped(QDropEvent
*, Q3ListViewItem
*, Q3ListViewItem
*) ) );
1942 MediaQueue::acceptDrag( QDropEvent
*e
) const
1945 return e
->source() == viewport()
1946 || e
->mimeData()->hasFormat( "amarok-sql" )
1947 || KUrl::List::canDecode( e
->mimeData() );
1951 MediaQueue::slotDropped( QDropEvent
* e
, Q3ListViewItem
* parent
, Q3ListViewItem
* after
)
1953 if( e
->source() != viewport() )
1955 if( e
->mimeData()->hasFormat( "amarok-sql" ) )
1957 QString
data( e
->mimeData()->data( "amarok-sql" ) );
1958 QString playlist
= data
.section( "\n", 0, 0 );
1959 QString query
= data
.section( "\n", 1 );
1960 QStringList values
= CollectionDB::instance()->query( query
);
1961 KUrl::List list
= CollectionDB::instance()->URLsFromSqlDrag( values
);
1962 addUrls( list
, playlist
);
1964 else if ( KUrl::List::canDecode( e
->mimeData() ) )
1966 KUrl::List list
= KUrl::List::fromMimeData( e
->mimeData() );
1967 if (!list
.isEmpty() )
1971 else if( Q3ListViewItem
*i
= currentItem() )
1973 moveItem( i
, parent
, after
);
1978 MediaQueue::dropProxyEvent( QDropEvent
*e
)
1980 slotDropped( e
, 0, 0 );
1984 MediaQueue::findPath( QString path
)
1986 for( Q3ListViewItem
*item
= firstChild();
1988 item
= item
->nextSibling())
1990 if(static_cast<MediaItem
*>(item
)->url().path() == path
)
1991 return static_cast<MediaItem
*>(item
);
1998 MediaQueue::computeSize() const
2001 for( Q3ListViewItem
*it
= firstChild();
2003 it
= it
->nextSibling())
2005 MediaItem
*item
= static_cast<MediaItem
*>(it
);
2007 if( item
&& item
->bundle() &&
2008 ( !m_parent
->currentDevice()
2009 || !m_parent
->currentDevice()->isConnected()
2010 || !m_parent
->currentDevice()->trackExists(*item
->bundle()) ) )
2011 m_totalSize
+= ((item
->size()+1023)/1024)*1024;
2016 MediaQueue::totalSize() const
2022 MediaQueue::addItemToSize( const MediaItem
*item
) const
2024 if( item
&& item
->bundle() &&
2025 ( !m_parent
->currentDevice()
2026 || !m_parent
->currentDevice()->isConnected()
2027 || !m_parent
->currentDevice()->trackExists(*item
->bundle()) ) )
2028 m_totalSize
+= ((item
->size()+1023)/1024)*1024;
2032 MediaQueue::subtractItemFromSize( const MediaItem
*item
, bool unconditionally
) const
2034 if( item
&& item
->bundle() &&
2035 ( !m_parent
->currentDevice()
2036 || !m_parent
->currentDevice()->isConnected()
2037 || (unconditionally
|| !m_parent
->currentDevice()->trackExists(*item
->bundle())) ) )
2038 m_totalSize
-= ((item
->size()+1023)/1024)*1024;
2042 MediaQueue::removeSelected()
2044 QList
<Q3ListViewItem
*> selected
= selectedItems();
2046 QListIterator
<Q3ListViewItem
*> iter( selected
);
2047 while( iter
.hasNext() )
2049 Q3ListViewItem
*item
= iter
.next();
2050 if( !(static_cast<MediaItem
*>(item
)->flags() & MediaItem::Transferring
) )
2052 subtractItemFromSize( static_cast<MediaItem
*>(item
) );
2054 if( m_parent
->currentDevice() && m_parent
->currentDevice()->isTransferring() )
2056 MediaBrowser::instance()->m_progress
->setRange( 0, MediaBrowser::instance()->m_progress
->maximum() - 1 );
2061 MediaBrowser::instance()->updateStats();
2062 MediaBrowser::instance()->updateButtons();
2067 MediaQueue::keyPressEvent( QKeyEvent
*e
)
2069 if( e
->key() == Qt::Key_Delete
)
2072 K3ListView::keyPressEvent( e
);
2076 MediaQueue::itemCountChanged()
2078 if( childCount() == 0 )
2080 else if( !isShown() )
2085 MediaQueue::slotShowContextMenu( Q3ListViewItem
* item
, const QPoint
& point
, int )
2090 Q3PopupMenu
menu( this );
2092 enum Actions
{ REMOVE_SELECTED
, CLEAR_ALL
, START_TRANSFER
};
2095 menu
.insertItem( KIcon( Amarok::icon( "remove_from_playlist" ) ), i18n( "&Remove From Queue" ), REMOVE_SELECTED
);
2097 menu
.insertItem( KIcon( Amarok::icon( "playlist_clear" ) ), i18n( "&Clear Queue" ), CLEAR_ALL
);
2098 menu
.insertItem( KIcon( Amarok::icon( "playlist_refresh" ) ), i18n( "&Start Transfer" ), START_TRANSFER
);
2099 menu
.setItemEnabled( START_TRANSFER
,
2100 MediaBrowser::instance()->currentDevice() &&
2101 MediaBrowser::instance()->currentDevice()->isConnected() &&
2102 MediaBrowser::instance()->currentDevice()->m_transfer
);
2104 switch( menu
.exec( point
) )
2106 case REMOVE_SELECTED
:
2112 case START_TRANSFER
:
2113 MediaBrowser::instance()->transferClicked();
2119 MediaQueue::clearItems()
2126 m_parent
->updateStats();
2127 m_parent
->updateButtons();
2132 #include "mediabrowser.moc"