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"
26 #include "mediumpluginmanager.h"
27 #include "metabundle.h"
28 #include "mountpointmanager.h"
29 #include "playlist/PlaylistModel.h"
30 #include "pluginmanager.h"
31 #include "podcastbundle.h"
32 #include "scriptmanager.h"
33 #include "scrobbler.h"
34 #include "searchwidget.h"
35 #include "statusbar.h"
36 #include "transferdialog.h"
37 #include "TheInstances.h"
40 #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 SpaceLabel
: public QLabel
{
109 SpaceLabel(QWidget
*parent
)
112 m_total
= m_used
= m_scheduled
= 0;
115 void paintEvent(QPaintEvent
*e
)
118 p
.fillRect(e
->rect(), palette().brush(QColorGroup::Background
));
122 int used
= int(float(m_used
)/float(m_total
)*width());
123 int scheduled
= int(float(m_used
+ m_scheduled
)/float(m_total
)*width());
127 QColor
blueish(70,120,255);
128 if(e
->rect().left() < used
)
131 if(e
->rect().right() < right
)
132 right
= e
->rect().right();
133 p
.fillRect(e
->rect().left(), e
->rect().top(),
134 used
, e
->rect().bottom()+1, QBrush(blueish
, Qt::SolidPattern
));
140 QColor
sched(70, 230, 120);
141 if(m_used
+ m_scheduled
> m_total
- m_total
/200)
143 sched
.setRgb( 255, 120, 120 );
145 int left
= e
->rect().left();
148 int right
= e
->rect().right();
149 if(scheduled
< right
)
151 p
.fillRect(left
, e
->rect().top(), right
, e
->rect().bottom()+1, QBrush(sched
, Qt::SolidPattern
));
154 if(m_used
+ m_scheduled
< m_total
)
156 QColor
grey(180, 180, 180);
157 int left
= e
->rect().left();
160 int right
= e
->rect().right();
161 p
.fillRect(left
, e
->rect().top(), right
, e
->rect().bottom()+1, palette().brush(QColorGroup::Background
));
164 QLabel::paintEvent(e
);
167 KIO::filesize_t m_total
;
168 KIO::filesize_t m_used
;
169 KIO::filesize_t m_scheduled
;
172 class DummyMediaDevice
: public MediaDevice
175 DummyMediaDevice() : MediaDevice()
177 m_name
= i18n( "No Device Available" );
178 m_type
= "dummy-mediadevice";
179 m_medium
= Medium( "DummyDevice", "DummyDevice" );
181 void init( MediaBrowser
*browser
) { MediaDevice::init( browser
); }
182 virtual ~DummyMediaDevice() {}
183 virtual bool isConnected() { return false; }
184 virtual MediaItem
* trackExists(const MetaBundle
&) { return 0; }
185 virtual bool lockDevice(bool) { return true; }
186 virtual void unlockDevice() {}
187 virtual bool openDevice( bool silent
)
191 //QString msg = i18n( "Sorry, you do not have a supported portable music player." );
192 //Amarok::StatusBar::instance()->longMessage( msg, KDE::StatusBar::Sorry );
196 virtual bool closeDevice() { return false; }
197 virtual void synchronizeDevice() {}
198 virtual MediaItem
* copyTrackToDevice(const MetaBundle
&) { return 0; }
199 virtual int deleteItemFromDevice(MediaItem
*, int) { return -1; }
203 MediaBrowser::MediaBrowser( const char * /*name*/ )
205 , m_timer( new QTimer( this ) )
206 , m_currentDevice( 0 )
207 , m_waitForTranscode( false )
208 , m_quitting( false )
209 , m_connectAction( 0 )
210 , m_disconnectAction( 0 )
211 , m_customAction( 0 )
212 , m_configAction( 0 )
213 , m_transferAction( 0 )
217 QVBoxLayout
*layout
= new QVBoxLayout
;
219 layout
->setSpacing( 4 );
221 m_timer
->setSingleShot( true );
223 m_toolbar
= new Browser::ToolBar( this );
224 m_toolbar
->setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Minimum
);
225 m_toolbar
->setToolButtonStyle( Qt::ToolButtonTextBesideIcon
);
227 //TODO: how to fix getButton
228 m_connectAction
= new KAction(KIcon("connect_creating"), i18n("Connect"), this);
229 connect(m_connectAction
, SIGNAL(triggered()), this, SLOT(connectClicked()));
230 m_toolbar
->addAction(m_connectAction
);
231 // m_toolbar->insertButton( "connect_creating", CONNECT, true, i18n("Connect") );
232 // m_toolbar->getButton(CONNECT)->setToolTip( i18n( "Connect media device" ) );
234 m_disconnectAction
= new KAction(KIcon("media-eject"), i18n("Disconnect"), this);
235 connect(m_disconnectAction
, SIGNAL(triggered()), this, SLOT(disconnectClicked()));
236 m_toolbar
->addAction(m_disconnectAction
);
237 // m_toolbar->insertButton( "media-eject", DISCONNECT, true, i18n("Disconnect") );
238 // m_toolbar->getButton(DISCONNECT)->setToolTip( i18n( "Disconnect media device" ) );
240 m_transferAction
= new KAction(KIcon("rebuild"), i18n("Transfer"), this);
241 connect(m_transferAction
, SIGNAL(triggered()), this, SLOT(transferClicked()));
242 m_toolbar
->addAction(m_transferAction
);
243 // m_toolbar->insertButton( "rebuild", TRANSFER, true, i18n("Transfer") );
244 // m_toolbar->getButton(TRANSFER)->setToolTip( i18n( "Transfer tracks to media device" ) );
246 m_toolbar
->addSeparator();
248 // m_toolbar->setIconText( KToolBar::IconTextRight, true );
249 m_customAction
= new KAction(KIcon( "add_playlist" ), i18n("custom"), this);
250 connect(m_customAction
, SIGNAL(triggered()), this, SLOT(custom()));
251 m_toolbar
->addAction(m_customAction
);
252 // m_toolbar->insertButton( Amarok::icon( "add_playlist" ), CUSTOM, SIGNAL( clicked() ), this, SLOT( customClicked() ), true, "custom" );
253 // m_toolbar->getButton(TRANSFER)->setToolTip( i18n( "Transfer tracks to media device" ) );
255 m_toolbar
->setToolButtonStyle( Qt::ToolButtonIconOnly
);
257 m_configAction
= new KAction(KIcon("configure"), i18n("Configure"), this);
258 connect(m_configAction
, SIGNAL(triggered()), this, SLOT(config()));
259 m_toolbar
->addAction(m_configAction
);
260 // m_toolbar->insertButton( Amarok::icon( "configure" ), CONFIGURE, true, i18n("Configure") );
261 // m_toolbar->getButton(CONFIGURE)->setToolTip( i18n( "Configure device" ) );
264 m_deviceCombo
= new KComboBox();
265 layout
->addWidget( m_deviceCombo
);
267 // searching/filtering
268 QToolBar
* searchToolBar
= new Browser::ToolBar( this );
269 searchToolBar
->setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Minimum
);
270 m_searchWidget
= new SearchWidget( searchToolBar
, this );
271 searchToolBar
->addWidget( m_searchWidget
);
272 connect( m_timer
, SIGNAL( timeout() ), SLOT( slotSetFilter() ) );
273 // connect( m_searchEdit, SIGNAL( textChanged( const QString& ) ), SLOT( slotSetFilterTimeout() ) );
274 // connect( m_searchEdit, SIGNAL( returnPressed() ), SLOT( slotSetFilter() ) );
276 // connect to device manager
277 connect( Solid::DeviceNotifier::instance(), SIGNAL( deviceAdded(const QString
&) ),
278 SLOT( deviceAdded(const QString
&) ) );
279 connect( Solid::DeviceNotifier::instance(), SIGNAL( deviceRemoved(const QString
&) ),
280 SLOT( deviceRemoved(const QString
&) ) );
283 // we always have a dummy device
284 m_pluginName
[ i18n( "Disable" ) ] = "dummy-mediadevice";
285 m_pluginAmarokName
["dummy-mediadevice"] = i18n( "Disable" );
286 m_pluginName
[ i18n( "Do not handle" ) ] = "ignore";
287 m_pluginAmarokName
["ignore"] = i18n( "Do not handle" );
288 // query available device plugins
289 m_plugins
= PluginManager::query( "[X-KDE-Amarok-plugintype] == 'mediadevice'" );
290 for( KService::List::ConstIterator it
= m_plugins
.begin(); it
!= m_plugins
.end(); ++it
) {
291 // Save name properties in QMap for lookup
292 m_pluginName
[(*it
)->name()] = (*it
)->property( "X-KDE-Amarok-name" ).toString();
293 m_pluginAmarokName
[(*it
)->property( "X-KDE-Amarok-name" ).toString()] = (*it
)->name();
296 m_views
= new QWidget( this );
297 m_queue
= new MediaQueue( this );
298 m_progressBox
= new KHBox();
299 layout
->addWidget( m_progressBox
);
300 m_progress
= new QProgressBar( m_progressBox
);
301 m_cancelButton
= new KPushButton( KIcon( Amarok::icon( "cancel" ) ), i18n("Cancel"), m_progressBox
);
304 m_stats
= new SpaceLabel(this);
306 m_progressBox
->hide();
308 MediaDevice
*dev
= new DummyMediaDevice();
310 dev
->setUid( "DummyDevice" );
312 activateDevice( 0, false );
313 queue()->load( Amarok::saveLocation() + "transferlist.xml" );
314 queue()->computeSize();
316 setFocusProxy( m_queue
);
320 QList
<Solid::Device
> pmpList
= Solid::Device::listFromType( Solid::DeviceInterface::PortableMediaPlayer
);
321 foreach( Solid::Device device
, pmpList
)
322 deviceAdded( device
.udi() );
324 //TODO: Take generic storage devices into account too -- or do we rely on the
325 //Solid backend to tell us if it's a PMP with "storage" type?
327 connect( m_deviceCombo
, SIGNAL( activated( int ) ), SLOT( activateDevice( int ) ) );
329 connect( m_cancelButton
, SIGNAL( clicked() ), SLOT( cancelClicked() ) );
330 connect( pApp
, SIGNAL( prepareToQuit() ), SLOT( prepareToQuit() ) );
331 connect( CollectionDB::instance(), SIGNAL( tagsChanged( const MetaBundle
& ) ),
332 SLOT( tagsChanged( const MetaBundle
& ) ) );
334 //TODO: If we will be supporting manually adding devices, probably need the following section
335 /*m_haveDevices = false;
336 QMap<QString,QString> savedDevices = Amarok::config( "MediaBrowser" ).entryMap();
337 for( QMap<QString,QString>::Iterator it = savedDevices.begin();
338 it != savedDevices.end();
341 if( it.data() != "deleted" && it.data() != "ignore" )
343 m_haveDevices = true;
348 emit
availabilityChanged( !pmpList
.isEmpty() );
350 this->setLayout( layout
);
354 MediaBrowser::blockQuit() const
356 for( QList
<MediaDevice
*>::const_iterator it
= m_devices
.begin();
357 it
!= m_devices
.end();
360 if( *it
&& (*it
)->isConnected() )
368 MediaBrowser::tagsChanged( const MetaBundle
&bundle
)
370 m_itemMapMutex
.lock();
371 debug() << "tags changed for " << bundle
.url().url();
372 ItemMap::iterator it
= m_itemMap
.find( bundle
.url().url() );
373 if( it
!= m_itemMap
.end() )
375 MediaItem
*item
= *it
;
376 m_itemMapMutex
.unlock();
379 item
->device()->tagsChanged( item
, bundle
);
383 // it's an item on the transfer queue
384 item
->setBundle( new MetaBundle( bundle
) );
386 QString text
= item
->bundle()->prettyTitle();
387 if( text
.isEmpty() || (!item
->bundle()->isValidMedia() && !item
->bundle()->podcastBundle()) )
388 text
= item
->bundle()->url().prettyUrl();
389 if( !item
->m_playlistName
.isNull() )
391 text
+= " (" + item
->m_playlistName
+ ')';
393 item
->setText( 0, text
);
398 m_itemMapMutex
.unlock();
403 MediaBrowser::getBundle( const KUrl
&url
, MetaBundle
*bundle
) const
405 QMutexLocker
locker( &m_itemMapMutex
);
406 ItemMap::const_iterator it
= m_itemMap
.find( url
.url() );
407 if( it
== m_itemMap
.end() )
411 *bundle
= *(*it
)->bundle();
417 MediaBrowser::getProxyUrl( const KUrl
& daapUrl
) const
421 MediaDevice
* dc
= findChildren
<MediaDevice
*>( "DaapClient" ).first();
423 url
= dc
->getProxyUrl( daapUrl
);
428 MediaBrowser::deviceFromId( const QString
&id
) const
430 for( QList
<MediaDevice
*>::const_iterator it
= m_devices
.constBegin();
431 it
!= m_devices
.end();
434 if( (*it
)->uniqueId() == id
)
442 MediaBrowser::activateDevice( const MediaDevice
*dev
)
445 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
446 it
!= m_devices
.end();
451 activateDevice( index
);
459 MediaBrowser::activateDevice( int index
, bool skipDummy
)
461 if( m_currentDevice
&& m_currentDevice
->customAction() )
463 m_toolbar
->removeAction( m_currentDevice
->customAction() );
468 foreach( MediaDevice
*md
, m_devices
)
470 if( md
&& md
->view() )
476 m_currentDevice
= m_devices
.last();
483 if( index
>= m_devices
.count() )
485 if( !m_devices
.isEmpty() )
486 m_currentDevice
= m_devices
.last();
490 queue()->computeSize();
495 m_currentDevice
= m_devices
[index
];
496 if( m_currentDevice
&& m_currentDevice
->view() )
498 m_currentDevice
->view()->show();
499 if( m_currentDevice
->customAction() )
501 m_toolbar
->setToolButtonStyle( Qt::ToolButtonTextBesideIcon
);
502 m_toolbar
->addAction( m_currentDevice
->customAction() );
507 m_deviceCombo
->setCurrentIndex( index
-1 );
510 queue()->computeSize();
515 MediaBrowser::addDevice( MediaDevice
*device
)
517 m_devices
.append( device
);
519 device
->loadConfig();
521 if( device
->autoConnect() )
523 device
->connectDevice( true );
531 MediaBrowser::removeDevice( MediaDevice
*device
)
535 debug() << "remove device: type=" << device
->deviceType();
537 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
538 it
!= m_devices
.end();
543 bool current
= ( (*it
)->uid() == m_currentDevice
->uid() );
544 m_devices
.erase( it
);
546 activateDevice( 0, false );
551 if( device
->isConnected() )
553 if( device
->disconnectDevice( false /* don't run post-disconnect command */ ) )
554 unloadDevicePlugin( device
);
557 debug() << "Cannot remove device because disconnect failed";
558 Amarok::StatusBar::instance()->longMessage(
559 i18n( "Cannot remove device because disconnect failed" ),
560 KDE::StatusBar::Warning
);
564 unloadDevicePlugin( device
);
570 MediaBrowser::updateDevices()
572 m_deviceCombo
->clear();
574 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
575 it
!= m_devices
.end();
578 if( m_devices
.count() > 1 && dynamic_cast<DummyMediaDevice
*>(*it
) )
580 QString name
= (*it
)->name();
581 if( !(*it
)->deviceNode().isEmpty() )
583 name
= i18n( "%1 at %2", name
, (*it
)->deviceNode() );
585 if( (*it
)->hasMountPoint() && !(*it
)->mountPoint().isEmpty() )
587 name
+= i18n( " (mounted at %1)", (*it
)->mountPoint() );
589 m_deviceCombo
->addItem( name
, i
);
590 if( !m_currentDevice
|| (*it
)->uid() == m_currentDevice
->uid() )
592 m_deviceCombo
->setCurrentItem( name
);
596 m_deviceCombo
->setEnabled( m_devices
.count() > 1 );
597 m_haveDevices
= m_devices
.count() > 1;
598 emit
availabilityChanged( m_haveDevices
);
602 MediaBrowser::deviceNames() const
606 for( QList
<MediaDevice
*>::const_iterator it
= m_devices
.constBegin();
607 it
!= m_devices
.constEnd();
610 QString name
= (*it
)->name();
618 MediaBrowser::deviceSwitch( const QString
&name
)
621 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
622 it
!= m_devices
.end();
625 if( (*it
)->name() == name
)
627 activateDevice( index
, false );
637 MediaBrowser::transcodingFinished( const QString
&src
, const QString
&dst
)
639 KUrl srcJob
= KUrl( m_transcodeSrc
);
640 KUrl srcResult
= KUrl( src
);
642 if( srcJob
.path() == srcResult
.path() )
644 m_transcodedUrl
= KUrl( dst
);
645 m_waitForTranscode
= false;
649 debug() << "transcoding for " << src
<< " finished, "
650 << "but we are waiting for " << m_transcodeSrc
<< " -- aborting";
651 m_waitForTranscode
= false;
656 MediaBrowser::transcode( const KUrl
&src
, const QString
&filetype
)
658 const ScriptManager
* const sm
= ScriptManager::instance();
660 if( sm
->transcodeScriptRunning().isEmpty() )
662 debug() << "cannot transcode with no transcoder registered";
666 m_waitForTranscode
= true;
667 m_transcodeSrc
= src
.url();
668 m_transcodedUrl
= KUrl();
669 ScriptManager::instance()->notifyTranscode( src
.url(), filetype
);
671 while( m_waitForTranscode
&& !sm
->transcodeScriptRunning().isEmpty() )
674 kapp
->processEvents( QEventLoop::AllEvents
);
677 return m_transcodedUrl
;
682 MediaBrowser::slotSetFilterTimeout() //SLOT
684 m_timer
->start( 280 ); //stops the timer for us first
688 MediaBrowser::slotSetFilter() //SLOT
692 if( m_currentDevice
)
693 m_currentDevice
->view()->setFilter( m_searchWidget
->lineEdit()->text() );
697 MediaBrowser::slotSetFilter( const QString
&text
)
699 m_searchWidget
->lineEdit()->setText( text
);
704 MediaBrowser::slotEditFilter()
706 EditFilterDialog
*fd
= new EditFilterDialog( this, true, m_searchWidget
->lineEdit()->text() );
707 connect( fd
, SIGNAL(filterChanged(const QString
&)), SLOT(slotSetFilter(const QString
&)) );
709 m_searchWidget
->lineEdit()->setText( fd
->filter() );
714 MediaBrowser::prepareToQuit()
716 m_waitForTranscode
= false;
718 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
719 it
!= m_devices
.end();
722 if( (*it
)->isConnected() )
723 (*it
)->disconnectDevice( false /* don't unmount */ );
727 MediaBrowser::~MediaBrowser()
729 debug() << "having to remove " << m_devices
.count() << " devices";
730 while( !m_devices
.isEmpty() )
732 removeDevice( m_devices
.last() );
735 queue()->save( Amarok::saveLocation() + "transferlist.xml" );
737 delete m_deviceCombo
;
741 MediaView::MediaView( QWidget
* parent
, MediaDevice
*device
)
742 : K3ListView( parent
)
747 setSelectionMode( Q3ListView::Extended
);
748 setItemsMovable( false );
749 setShowSortIndicator( true );
750 setFullWidth( true );
751 setRootIsDecorated( true );
752 setDragEnabled( true );
753 setDropVisualizer( true ); //the visualizer (a line marker) is drawn when dragging over tracks
754 setDropHighlighter( true ); //and the highligther (a focus rect) is drawn when dragging over playlists
755 setDropVisualizerWidth( 3 );
756 setAcceptDrops( true );
759 addColumn( i18n( "Remote Media" ) );
761 KActionCollection
* ac
= new KActionCollection( this );
762 KStandardAction::selectAll( this, SLOT( selectAll() ), ac
);
764 connect( this, SIGNAL( contextMenuRequested( Q3ListViewItem
*, const QPoint
&, int ) ),
765 this, SLOT( rmbPressed( Q3ListViewItem
*, const QPoint
&, int ) ) );
767 connect( this, SIGNAL( itemRenamed( Q3ListViewItem
* ) ),
768 this, SLOT( renameItem( Q3ListViewItem
* ) ) );
770 connect( this, SIGNAL( expanded( Q3ListViewItem
* ) ),
771 this, SLOT( slotExpand( Q3ListViewItem
* ) ) );
773 connect( this, SIGNAL( returnPressed( Q3ListViewItem
* ) ),
774 this, SLOT( invokeItem( Q3ListViewItem
* ) ) );
776 connect( this, SIGNAL( doubleClicked( Q3ListViewItem
*, const QPoint
&, int ) ),
777 this, SLOT( invokeItem( Q3ListViewItem
*, const QPoint
&, int ) ) );
781 MediaView::keyPressEvent( QKeyEvent
*e
)
783 if( e
->key() == Qt::Key_Delete
)
784 m_device
->deleteFromDevice();
786 K3ListView::keyPressEvent( e
);
790 MediaView::invokeItem( Q3ListViewItem
* i
, const QPoint
& point
, int column
) //SLOT
795 QPoint p
= mapFromGlobal( point
);
796 if ( p
.x() > header()->sectionPos( header()->mapToIndex( 0 ) ) + treeStepSize() * ( i
->depth() + ( rootIsDecorated() ? 1 : 0) ) + itemMargin()
797 || p
.x() < header()->sectionPos( header()->mapToIndex( 0 ) ) )
803 MediaView::invokeItem( Q3ListViewItem
*i
)
805 MediaItem
*item
= static_cast<MediaItem
*>( i
);
809 KUrl::List urls
= nodeBuildDragList( item
);
810 The::playlistModel()->insertMedia( urls
, Playlist::AppendAndPlay
);
814 MediaView::renameItem( Q3ListViewItem
*item
)
816 m_device
->renameItem( item
);
820 MediaView::slotExpand( Q3ListViewItem
*item
)
822 m_device
->expandItem( item
);
826 MediaView::~MediaView()
832 MediaView::dragObject()
834 KUrl::List urls
= nodeBuildDragList( 0 );
835 K3MultipleDrag
*md
= new K3MultipleDrag( viewport() );
836 md
->addDragObject( K3ListView::dragObject() );
837 K3URLDrag
* ud
= new K3URLDrag( urls
, viewport() );
838 md
->addDragObject( ud
);
839 md
->setPixmap( CollectionDB::createDragPixmap( urls
),
840 QPoint( CollectionDB::DRAGPIXMAP_OFFSET_X
, CollectionDB::DRAGPIXMAP_OFFSET_Y
) );
846 MediaView::nodeBuildDragList( MediaItem
* item
, int flags
)
853 fi
= static_cast<MediaItem
*>(firstChild());
860 if( fi
->isVisible() )
862 if ( fi
->isSelected() || !(flags
& OnlySelected
) )
864 if( fi
->isLeafItem() || fi
->type() == MediaItem::DIRECTORY
)
868 if(fi
->childCount() )
869 items
+= nodeBuildDragList( static_cast<MediaItem
*>(fi
->firstChild()), None
);
874 if ( fi
->childCount() )
875 items
+= nodeBuildDragList( static_cast<MediaItem
*>(fi
->firstChild()), OnlySelected
);
878 fi
= static_cast<MediaItem
*>(fi
->nextSibling());
884 MediaView::getSelectedLeaves( MediaItem
*parent
, Q3PtrList
<MediaItem
> *list
, int flags
)
888 list
= new Q3PtrList
<MediaItem
>;
892 it
= static_cast<MediaItem
*>(firstChild());
894 it
= static_cast<MediaItem
*>(parent
->firstChild());
896 for( ; it
; it
= static_cast<MediaItem
*>(it
->nextSibling()))
898 if( it
->isVisible() )
900 if( it
->childCount() && !( it
->type() == MediaItem::DIRECTORY
&& it
->isSelected() ) )
903 if( it
->isSelected() )
905 numFiles
+= getSelectedLeaves(it
, list
, f
);
907 if( it
->isSelected() || !(flags
& OnlySelected
) )
909 if( it
->type() == MediaItem::TRACK
||
910 it
->type() == MediaItem::DIRECTORY
||
911 it
->type() == MediaItem::PODCASTITEM
||
912 it
->type() == MediaItem::PLAYLISTITEM
||
913 it
->type() == MediaItem::INVISIBLE
||
914 it
->type() == MediaItem::ORPHANED
)
916 if( flags
& OnlyPlayed
)
918 if( it
->played() > 0 )
924 if( ( it
->isLeafItem() && (!(flags
& OnlyPlayed
) || it
->played()>0) )
925 || it
->type() == MediaItem::DIRECTORY
)
935 MediaView::acceptDrag( QDropEvent
*e
) const
937 if( e
->source() == MediaBrowser::queue()->viewport() )
940 return e
->source() == viewport()
941 || e
->mimeData()->hasFormat( "amarok-sql" )
942 || KUrl::List::canDecode( e
->mimeData() );
946 MediaView::contentsDropEvent( QDropEvent
*e
)
948 cleanDropVisualizer();
949 cleanItemHighlighter();
951 if(e
->source() == viewport())
953 const QPoint p
= contentsToViewport( e
->pos() );
954 MediaItem
*item
= static_cast<MediaItem
*>(itemAt( p
));
956 if( !item
&& MediaBrowser::instance()->currentDevice()->m_type
!= "generic-mediadevice" )
959 Q3PtrList
<MediaItem
> items
;
961 if( !item
|| item
->type() == MediaItem::DIRECTORY
||
962 item
->type() == MediaItem::TRACK
)
964 Q3PtrList
<MediaItem
> items
;
965 getSelectedLeaves( 0, &items
);
966 m_device
->addToDirectory( item
, items
);
968 else if( item
->type() == MediaItem::PLAYLIST
)
970 MediaItem
*list
= item
;
971 MediaItem
*after
= 0;
972 for(MediaItem
*it
= static_cast<MediaItem
*>(item
->firstChild());
974 it
= static_cast<MediaItem
*>(it
->nextSibling()))
977 getSelectedLeaves( 0, &items
);
978 m_device
->addToPlaylist( list
, after
, items
);
980 else if( item
->type() == MediaItem::PLAYLISTITEM
)
982 MediaItem
*list
= static_cast<MediaItem
*>(item
->parent());
983 MediaItem
*after
= 0;
984 for(MediaItem
*it
= static_cast<MediaItem
*>(item
->parent()->firstChild());
986 it
= static_cast<MediaItem
*>(it
->nextSibling()))
993 getSelectedLeaves( 0, &items
);
994 m_device
->addToPlaylist( list
, after
, items
);
996 else if( item
->type() == MediaItem::PLAYLISTSROOT
)
998 Q3PtrList
<MediaItem
> items
;
999 getSelectedLeaves( 0, &items
);
1000 QString
base( i18n("New Playlist") );
1001 QString name
= base
;
1003 while( item
->findItem(name
) )
1007 name
= base
+ ' ' + num
;
1010 MediaItem
*pl
= m_device
->newPlaylist(name
, item
, items
);
1011 ensureItemVisible(pl
);
1017 if( e
->mimeData()->hasFormat( "amarok-sql" ) )
1019 QString
data( e
->mimeData()->data( "amarok-sql" ) );
1020 QString playlist
= data
.section( "\n", 0, 0 );
1021 QString query
= data
.section( "\n", 1 );
1022 QStringList values
= CollectionDB::instance()->query( query
);
1023 KUrl::List list
= CollectionDB::instance()->URLsFromSqlDrag( values
);
1024 MediaBrowser::queue()->addUrls( list
, playlist
);
1026 else if ( KUrl::List::canDecode( e
->mimeData() ) )
1028 KUrl::List list
= KUrl::List::fromMimeData( e
->mimeData() );
1029 MediaBrowser::queue()->addUrls( list
);
1035 MediaView::viewportPaintEvent( QPaintEvent
*e
)
1037 K3ListView::viewportPaintEvent( e
);
1039 // Superimpose bubble help:
1041 if ( !MediaBrowser::instance()->currentDevice() || !MediaBrowser::instance()->currentDevice()->isConnected() )
1043 QPainter
p( viewport() );
1045 Q3SimpleRichText
t( i18n(
1046 "<div align=center>"
1047 "<h3>Media Device Browser</h3>"
1048 "Configure your media device and then "
1049 "click the Connect button to access your media device. "
1050 "Drag and drop files to enqueue them for transfer."
1051 "</div>" ), QApplication::font() );
1053 t
.setWidth( width() - 50 );
1055 const uint w
= t
.width() + 20;
1056 const uint h
= t
.height() + 20;
1058 p
.setBrush( palette().background() );
1059 p
.drawRoundRect( 15, 15, w
, h
, (8*200)/w
, (8*200)/h
);
1060 t
.draw( &p
, 20, 20, QRect(), palette() );
1062 MediaBrowser::instance()->updateButtons();
1066 MediaView::rmbPressed( Q3ListViewItem
*item
, const QPoint
&p
, int i
)
1068 if( m_device
->isConnected() )
1069 m_device
->rmbPressed( item
, p
, i
);
1073 MediaView::newDirectory( MediaItem
*parent
)
1076 const QString name
= KInputDialog::getText(i18n("Add Directory"), i18n("Directory Name:"), QString(), &ok
, this);
1078 if( ok
&& !name
.isEmpty() )
1080 m_device
->newDirectory( name
, parent
);
1087 MediaBrowser::deviceAdded( const QString
&udi
)
1089 MediaDevice
*md
= loadDevicePlugin( udi
);
1093 if( m_currentDevice
== *(m_devices
.begin()) || m_currentDevice
== *(m_devices
.end()) )
1094 activateDevice( m_devices
.count()-1, false );
1099 MediaBrowser::deviceRemoved( const QString
&udi
)
1101 for( QList
<MediaDevice
*>::iterator it
= m_devices
.begin();
1102 it
!= m_devices
.end();
1105 if( (*it
)->m_uid
== udi
)
1107 if( (*it
)->isConnected() )
1109 if( (*it
)->disconnectDevice() )
1110 removeDevice( *it
);
1111 Amarok::StatusBar::instance()->longMessage(
1112 i18n( "The device %1 was removed before it was disconnected. "
1113 "In order to avoid possible data loss, press the \"Disconnect\" "
1114 "button before disconnecting the device.", (*it
)->name() ),
1115 KDE::StatusBar::Warning
);
1118 removeDevice( *it
);
1125 MediaBrowser::loadDevicePlugin( const QString
&udi
)
1129 Solid::Device
solidDevice( udi
);
1131 Solid::PortableMediaPlayer
* pmp
= solidDevice
.as
<Solid::PortableMediaPlayer
>();
1133 //TODO: Generic storage?
1136 debug() << "Failed to convert Solid device to PortableMediaPlayer";
1139 if( pmp
->supportedProtocols().size() == 0 )
1141 debug() << "Portable Media Player " << udi
<< " does not support any protocols";
1145 QString protocol
= pmp
->supportedProtocols()[0];
1146 if( protocol
== "storage" )
1147 protocol
= "generic";
1148 if( protocol
== "pde" )
1151 protocol
+= "-mediadevice";
1152 QString query
= "[X-KDE-Amarok-plugintype] == 'mediadevice' and [X-KDE-Amarok-name] == '%1'";
1153 Amarok::Plugin
*plugin
= PluginManager::createFromQuery( query
.arg( protocol
) );
1157 debug() << "Returning plugin!";
1158 MediaDevice
*device
= static_cast<MediaDevice
*>( plugin
);
1159 device
->init( this );
1160 device
->m_uid
= solidDevice
.udi();
1161 device
->m_name
= solidDevice
.product();
1162 device
->m_type
= protocol
;
1166 debug() << "no plugin for " << protocol
;
1171 MediaBrowser::unloadDevicePlugin( MediaDevice
*device
)
1178 disconnect( device
); // disconnect all signals
1180 if( dynamic_cast<DummyMediaDevice
*>(device
) )
1186 PluginManager::unload( device
);
1191 MediaBrowser::config()
1193 if( m_deviceCombo
->currentText() == "No Device Selected" )
1195 Amarok::StatusBar::instance()->longMessage( i18n( "No device selected to configure." ),
1196 KDE::StatusBar::Sorry
);
1200 DeviceConfigureDialog
* dcd
= new DeviceConfigureDialog( m_currentDevice
->m_medium
);
1202 bool successful
= dcd
->successful();
1208 MediaBrowser::updateButtons()
1210 if( !connectAction() || !disconnectAction() || !transferAction() )
1213 if( m_currentDevice
)
1215 transferAction()->setVisible( m_currentDevice
->m_transfer
);
1216 customAction()->setVisible( m_currentDevice
->m_customButton
);
1217 configAction()->setVisible( m_currentDevice
->m_configure
);
1219 connectAction()->setEnabled( !m_currentDevice
->isConnected() );
1220 disconnectAction()->setEnabled( m_currentDevice
->isConnected() );
1221 transferAction()->setEnabled( m_currentDevice
->isConnected() && m_queue
->childCount() > 0 );
1222 if( customAction() )
1223 customAction()->setEnabled( true );
1227 connectAction()->setEnabled( false );
1228 disconnectAction()->setEnabled( false );
1229 transferAction()->setEnabled( false );
1230 if( customAction() )
1231 customAction()->setEnabled( false );
1236 MediaBrowser::updateStats()
1241 KIO::filesize_t queued
= m_queue
->totalSize();
1243 QString text
= i18np( "1 track in queue", "%1 tracks in queue", m_queue
->childCount() );
1244 if(m_queue
->childCount() > 0)
1246 text
+= i18n(" (%1)", KIO::convertSize( queued
) );
1249 KIO::filesize_t total
, avail
;
1250 if( m_currentDevice
&& m_currentDevice
->getCapacity(&total
, &avail
) )
1252 text
+= i18n( " - %1 of %2 available", KIO::convertSize( avail
), KIO::convertSize( total
) );
1254 m_stats
->m_used
= total
-avail
;
1255 m_stats
->m_total
= total
;
1256 m_stats
->m_scheduled
= queued
;
1260 m_stats
->m_used
= 0;
1261 m_stats
->m_total
= 0;
1262 m_stats
->m_scheduled
= queued
;
1265 m_stats
->setText(text
);
1266 m_stats
->setToolTip( text
);
1271 MediaView::setFilter( const QString
&filter
, MediaItem
*parent
)
1273 bool advanced
= ExpressionParser::isAdvancedExpression( filter
);
1274 QList
<int> defaultColumns
;
1275 defaultColumns
<< MetaBundle::Album
;
1276 defaultColumns
<< MetaBundle::Title
;
1277 defaultColumns
<< MetaBundle::Artist
;
1284 it
= static_cast<MediaItem
*>(firstChild());
1288 it
= static_cast<MediaItem
*>(parent
->firstChild());
1291 bool childrenVisible
= false;
1292 for( ; it
; it
= static_cast<MediaItem
*>(it
->nextSibling()))
1294 bool visible
= true;
1295 if(it
->isLeafItem())
1299 ParsedExpression parsed
= ExpressionParser::parse( filter
);
1300 visible
= it
->bundle() && it
->bundle()->matchesParsedExpression( parsed
, defaultColumns
);
1304 visible
= it
->bundle() && it
->bundle()->matchesSimpleExpression( filter
, defaultColumns
);
1309 visible
= setFilter(filter
, it
);
1310 if(it
->type()==MediaItem::PLAYLISTSROOT
|| it
->type()==MediaItem::PLAYLIST
)
1314 else if(it
->type()==MediaItem::DIRECTORY
)
1317 QStringList list
= filter
.split( " ", QString::SkipEmptyParts
);
1318 for( QStringList::iterator i
= list
.begin();
1322 if( !(*it
).text(0).contains( *i
) )
1332 if( filter
.isEmpty() )
1334 it
->setVisible( visible
);
1336 childrenVisible
= true;
1339 if( root
&& m_device
)
1340 m_device
->updateRootItems();
1342 return childrenVisible
;
1345 MediaDevice::MediaDevice()
1347 , m_name( QString() )
1348 , m_hasMountPoint( true )
1349 , m_autoDeletePodcasts( false )
1350 , m_syncStats( false )
1351 , m_transcode( false )
1352 , m_transcodeAlways( false )
1353 , m_transcodeRemove( false )
1357 , m_uid( QString() )
1359 , m_requireMount( false )
1360 , m_canceled( false )
1361 , m_transferring( false )
1362 , m_deleting( false )
1363 , m_deferredDisconnect( false )
1364 , m_scheduledDisconnect( false )
1365 , m_transfer( true )
1366 , m_configure( true )
1367 , m_customButton( false )
1368 , m_playlistItem( 0 )
1369 , m_podcastItem( 0 )
1370 , m_invisibleItem( 0 )
1372 , m_orphanedItem( 0 )
1374 sysProc
= new K3ShellProcess(); Q_CHECK_PTR(sysProc
);
1377 void MediaDevice::init( MediaBrowser
* parent
)
1381 // m_view = new MediaView( m_parent->m_views, this );
1385 MediaDevice::~MediaDevice()
1392 MediaDevice::isSpecialItem( MediaItem
*item
)
1394 return (item
== m_playlistItem
) ||
1395 (item
== m_podcastItem
) ||
1396 (item
== m_invisibleItem
) ||
1397 (item
== m_staleItem
) ||
1398 (item
== m_orphanedItem
);
1402 MediaDevice::loadConfig()
1404 m_transcode
= configBool( "Transcode" );
1405 m_transcodeAlways
= configBool( "TranscodeAlways" );
1406 m_transcodeRemove
= configBool( "TranscodeRemove" );
1407 m_preconnectcmd
= configString( "PreConnectCommand" );
1408 if( m_preconnectcmd
.isEmpty() )
1409 m_preconnectcmd
= configString( "MountCommand" );
1410 m_postdisconnectcmd
= configString( "PostDisconnectCommand" );
1411 if( m_postdisconnectcmd
.isEmpty() )
1412 m_postdisconnectcmd
= configString( "UmountCommand" );
1413 if( m_requireMount
&& m_postdisconnectcmd
.isEmpty() )
1414 m_postdisconnectcmd
= "kdeeject -q %d";
1418 MediaDevice::configString( const QString
&name
, const QString
&defValue
)
1420 QString configName
= "MediaDevice";
1421 if( !uniqueId().isEmpty() )
1422 configName
+= '_' + uniqueId();
1423 KConfigGroup config
= Amarok::config( configName
);
1424 return config
.readEntry( name
, defValue
);
1428 MediaDevice::setConfigString( const QString
&name
, const QString
&value
)
1430 QString configName
= "MediaDevice";
1431 if( !uniqueId().isEmpty() )
1432 configName
+= '_' + uniqueId();
1433 KConfigGroup config
= Amarok::config( configName
);
1434 config
.writeEntry( name
, value
);
1438 MediaDevice::configBool( const QString
&name
, bool defValue
)
1440 QString configName
= "MediaDevice";
1441 if( !uniqueId().isEmpty() )
1442 configName
+= '_' + uniqueId();
1443 KConfigGroup config
= Amarok::config( configName
);
1444 return config
.readEntry( name
, defValue
);
1448 MediaDevice::setConfigBool( const QString
&name
, bool value
)
1450 QString configName
= "MediaDevice";
1451 if( !uniqueId().isEmpty() )
1452 configName
+= '_' + uniqueId();
1453 KConfigGroup config
= Amarok::config( configName
);
1454 config
.writeEntry( name
, value
);
1464 MediaDevice::hideProgress()
1466 m_parent
->m_progressBox
->hide();
1470 MediaDevice::updateRootItems()
1473 m_podcastItem
->setVisible(m_podcastItem
->childCount() > 0);
1475 m_invisibleItem
->setVisible(m_invisibleItem
->childCount() > 0);
1477 m_staleItem
->setVisible(m_staleItem
->childCount() > 0);
1479 m_orphanedItem
->setVisible(m_orphanedItem
->childCount() > 0);
1483 MediaQueue::syncPlaylist( const QString
&name
, const QString
&query
, bool loading
)
1485 MediaItem
* item
= new MediaItem( this, lastItem() );
1486 item
->setType( MediaItem::PLAYLIST
);
1487 item
->setExpandable( false );
1488 item
->setData( query
);
1489 item
->m_playlistName
= name
;
1490 item
->setText( 0, name
);
1491 item
->m_flags
|= MediaItem::SmartPlaylist
;
1492 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1499 MediaQueue::syncPlaylist( const QString
&name
, const KUrl
&url
, bool loading
)
1501 MediaItem
* item
= new MediaItem( this, lastItem() );
1502 item
->setType( MediaItem::PLAYLIST
);
1503 item
->setExpandable( false );
1504 item
->setData( url
.url() );
1505 item
->m_playlistName
= name
;
1506 item
->setText( 0, name
);
1507 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1514 MediaDevice::bundlesToSync( const QString
&name
, const KUrl
&url
)
1517 // BundleList bundles;
1518 // if( !PlaylistFile::isPlaylistFile( url ) )
1520 // Amarok::StatusBar::instance()->longMessage( i18n( "Not a playlist file: %1", url.path() ),
1521 // KDE::StatusBar::Sorry );
1525 // PlaylistFile playlist( url.path() );
1526 // if( playlist.isError() )
1528 // Amarok::StatusBar::instance()->longMessage( i18n( "Failed to load playlist: %1", url.path() ),
1529 // KDE::StatusBar::Sorry );
1533 // for( BundleList::iterator it = playlist.bundles().begin();
1534 // it != playlist.bundles().end();
1537 // bundles += MetaBundle( (*it).url() );
1539 // preparePlaylistForSync( name, bundles );
1544 MediaDevice::bundlesToSync( const QString
&name
, const QString
&query
)
1546 const QStringList values
= CollectionDB::instance()->query( query
);
1549 for( QStringList::const_iterator it
= values
.begin(); it
!= values
.end(); ++it
)
1550 bundles
+= CollectionDB::instance()->bundleFromQuery( &it
);
1551 preparePlaylistForSync( name
, bundles
);
1556 MediaDevice::preparePlaylistForSync( const QString
&name
, const BundleList
&bundles
)
1558 if( ! m_playlistItem
) // might be syncing a new playlist from the playlist browser
1560 MediaItem
*pl
= m_playlistItem
->findItem( name
);
1563 MediaItem
*next
= 0;
1564 for( MediaItem
*it
= static_cast<MediaItem
*>(pl
->firstChild());
1568 next
= static_cast<MediaItem
*>(it
->nextSibling());
1569 const MetaBundle
*bundle
= (*it
).bundle();
1572 if( isOnOtherPlaylist( name
, *bundle
) )
1574 if( isInBundleList( bundles
, *bundle
) )
1576 deleteItemFromDevice( it
);
1578 deleteItemFromDevice( pl
, None
);
1584 MediaDevice::bundleMatch( const MetaBundle
&b1
, const MetaBundle
&b2
)
1586 if( b1
.track() != b2
.track() )
1588 if( b1
.title() != b2
.title() )
1590 if( b1
.album() != b2
.album() )
1592 if( b1
.artist() != b2
.artist() )
1595 if( b1
.discNumber() != b2
.discNumber() )
1597 if( b1
.composer() != b2
.composer() )
1605 MediaDevice::isInBundleList( const BundleList
&bundles
, const MetaBundle
&b
)
1607 for( BundleList::const_iterator it
= bundles
.begin();
1608 it
!= bundles
.end();
1611 if( bundleMatch( b
, *it
) )
1619 MediaDevice::isOnOtherPlaylist( const QString
&playlistToAvoid
, const MetaBundle
&bundle
)
1621 for( MediaItem
*it
= static_cast<MediaItem
*>(m_playlistItem
->firstChild());
1623 it
= static_cast<MediaItem
*>(it
->nextSibling()) )
1625 if( it
->text( 0 ) == playlistToAvoid
)
1627 if( isOnPlaylist( *it
, bundle
) )
1635 MediaDevice::isOnPlaylist( const MediaItem
&playlist
, const MetaBundle
&bundle
)
1637 for( MediaItem
*it
= static_cast<MediaItem
*>(playlist
.firstChild());
1639 it
= static_cast<MediaItem
*>(it
->nextSibling()) )
1641 const MetaBundle
*b
= (*it
).bundle();
1644 if( bundleMatch( *b
, bundle
) )
1652 MediaQueue::addUrl( const KUrl
& url2
, MetaBundle
*bundle
, const QString
&playlistName
)
1654 KUrl url
= Amarok::mostLocalURL( url2
);
1657 // if( PlaylistFile::isPlaylistFile( url ) )
1659 // QString name = url.path().section( "/", -1 ).section( ".", 0, -2 ).replace( "_", " " );
1660 // PlaylistFile playlist( url.path() );
1662 // if( playlist.isError() )
1664 // Amarok::StatusBar::instance()->longMessage( i18n( "Failed to load playlist: %1", url.path() ),
1665 // KDE::StatusBar::Sorry );
1669 // for( BundleList::iterator it = playlist.bundles().begin();
1670 // it != playlist.bundles().end();
1673 // addUrl( (*it).url(), 0, name );
1677 if( url
.protocol() == "file" && QFileInfo( url
.path() ).isDir() )
1680 // KUrl::List urls = Amarok::recursiveUrlExpand( url );
1681 // oldForeachType( KUrl::List, urls )
1686 if( playlistName
.isNull() )
1688 for( MediaItem
*it
= static_cast<MediaItem
*>(firstChild());
1690 it
= static_cast<MediaItem
*>(it
->nextSibling()) )
1692 if( it
->url() == url
)
1694 Amarok::StatusBar::instance()->shortMessage(
1695 i18n( "Track already queued for transfer: %1", url
.url() ) );
1702 bundle
= new MetaBundle( url
);
1704 MediaItem
* item
= new MediaItem( this, lastItem() );
1705 item
->setExpandable( false );
1706 item
->setDropEnabled( true );
1707 item
->setBundle( bundle
);
1708 if(bundle
->podcastBundle() )
1710 item
->setType( MediaItem::PODCASTITEM
);
1712 item
->m_playlistName
= playlistName
;
1714 QString text
= item
->bundle()->prettyTitle();
1715 if( text
.isEmpty() || (!item
->bundle()->isValidMedia() && !item
->bundle()->podcastBundle()) )
1716 text
= item
->bundle()->url().prettyUrl();
1717 if( !item
->m_playlistName
.isNull() )
1719 text
+= " (" + item
->m_playlistName
+ ')';
1721 item
->setText( 0, text
);
1723 m_parent
->updateButtons();
1724 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1725 addItemToSize( item
);
1730 MediaQueue::addUrl( const KUrl
&url
, MediaItem
*item
)
1733 MediaItem
*newitem
= new MediaItem( this, lastItem() );
1734 newitem
->setExpandable( false );
1735 newitem
->setDropEnabled( true );
1736 MetaBundle
*bundle
= new MetaBundle( *item
->bundle() );
1738 filepath
.addPath( bundle
->filename() );
1739 bundle
->setUrl( filepath
);
1740 newitem
->m_device
= item
->m_device
;
1741 if(bundle
->podcastBundle() )
1743 item
->setType( MediaItem::PODCASTITEM
);
1745 QString text
= item
->bundle()->prettyTitle();
1746 if( text
.isEmpty() || (!item
->bundle()->isValidMedia() && !item
->bundle()->podcastBundle()) )
1747 text
= item
->bundle()->url().prettyUrl();
1748 if( !item
->m_playlistName
.isEmpty() )
1750 text
+= " (" + item
->m_playlistName
+ ')';
1752 newitem
->setText( 0, text
);
1753 newitem
->setBundle( bundle
);
1754 m_parent
->updateButtons();
1755 m_parent
->m_progress
->setRange( 0, m_parent
->m_progress
->maximum() + 1 );
1756 addItemToSize( item
);
1762 MediaQueue::addUrls( const KUrl::List urls
, const QString
&playlistName
)
1764 KUrl::List::ConstIterator it
= urls
.begin();
1765 for ( ; it
!= urls
.end(); ++it
)
1766 addUrl( *it
, 0, playlistName
);
1772 MediaQueue::URLsAdded()
1774 m_parent
->updateStats();
1775 m_parent
->updateButtons();
1776 if( m_parent
->currentDevice()
1777 && m_parent
->currentDevice()->isConnected()
1778 && m_parent
->currentDevice()->asynchronousTransfer()
1779 && !m_parent
->currentDevice()->isTransferring() )
1780 m_parent
->currentDevice()->transferFiles();
1782 save( Amarok::saveLocation() + "transferlist.xml" );
1786 MediaDevice::copyTrackFromDevice( MediaItem
*item
)
1788 debug() << "copyTrackFromDevice: not copying " << item
->url() << ": not implemented";
1792 MediaQueue::dragObject()
1795 for( Q3ListViewItem
*it
= firstChild(); it
; it
= it
->nextSibling() )
1797 if( it
->isVisible() && it
->isSelected() && static_cast<MediaItem
*>(it
) )
1798 urls
+= static_cast<MediaItem
*>(it
)->url();
1801 K3MultipleDrag
*md
= new K3MultipleDrag( viewport() );
1802 Q3DragObject
*d
= K3ListView::dragObject();
1803 K3URLDrag
* urldrag
= new K3URLDrag( urls
, viewport() );
1804 md
->addDragObject( d
);
1805 md
->addDragObject( urldrag
);
1806 md
->setPixmap( CollectionDB::createDragPixmap( urls
),
1807 QPoint( CollectionDB::DRAGPIXMAP_OFFSET_X
, CollectionDB::DRAGPIXMAP_OFFSET_Y
) );
1812 MediaDevice::replaceVariables( const QString
&cmd
)
1814 QString result
= cmd
;
1815 result
.replace( "%d", deviceNode() );
1816 result
.replace( "%m", mountPoint() );
1820 int MediaDevice::runPreConnectCommand()
1822 if( m_preconnectcmd
.isEmpty() )
1825 QString cmd
= replaceVariables( m_preconnectcmd
);
1827 debug() << "running pre-connect command: [" << cmd
<< "]";
1829 debug() << "pre-connect: e=" << e
;
1833 int MediaDevice::runPostDisconnectCommand()
1835 if( m_postdisconnectcmd
.isEmpty() )
1838 QString cmd
= replaceVariables( m_postdisconnectcmd
);
1839 debug() << "running post-disconnect command: [" << cmd
<< "]";
1841 debug() << "post-disconnect: e=" << e
;
1846 int MediaDevice::sysCall( const QString
&command
)
1848 if ( sysProc
->isRunning() ) return -1;
1850 sysProc
->clearArguments();
1851 (*sysProc
) << command
;
1852 if (!sysProc
->start( K3Process::Block
, K3Process::AllOutput
))
1853 kFatal() << i18n("could not execute %1", command
.toLocal8Bit().data());
1855 return (sysProc
->exitStatus());
1859 MediaDevice::abortTransfer()
1861 setCanceled( true );
1866 MediaDevice::kioCopyTrack( const KUrl
&src
, const KUrl
&dst
)
1870 KIO::FileCopyJob
*job
= KIO::file_copy( src
, dst
,
1871 -1 /* permissions */,
1872 false /* overwrite */,
1874 false /* show progress */ );
1875 connect( job
, SIGNAL( result( KIO::Job
* ) ),
1876 this, SLOT( fileTransferred( KIO::Job
* ) ) );
1878 bool tryToRemove
= false;
1883 job
->kill( KJob::EmitResult
);
1890 kapp
->processEvents( QEventLoop::ExcludeUserInputEvents
);
1899 Amarok::StatusBar::instance()->longMessage(
1900 i18n( "Media Device: Copying %1 to %2 failed" )
1901 .arg( src
.prettyUrl(), dst
.prettyUrl() ),
1902 KDE::StatusBar::Error
);
1906 MetaBundle
bundle2(dst
);
1907 if(!bundle2
.isValidMedia() && bundle2
.filesize()==MetaBundle::Undetermined
)
1910 // probably s.th. went wrong
1911 Amarok::StatusBar::instance()->longMessage(
1912 i18n( "Media Device: Reading tags from %1 failed", dst
.prettyUrl() ),
1913 KDE::StatusBar::Error
);
1920 QFile::remove( dst
.path() );
1928 MediaDevice::fileTransferred( KIO::Job
*job
) //SLOT
1932 m_copyFailed
= true;
1933 debug() << "file transfer failed: " << job
->errorText();
1937 m_copyFailed
= false;
1944 MediaBrowser::cancelClicked()
1948 m_waitForTranscode
= false;
1949 if( m_currentDevice
)
1950 m_currentDevice
->abortTransfer();
1954 MediaBrowser::transferClicked()
1956 transferAction()->setEnabled( false );
1958 && m_currentDevice
->isConnected()
1959 && !m_currentDevice
->isTransferring() )
1961 if( !m_currentDevice
->hasTransferDialog() )
1962 m_currentDevice
->transferFiles();
1965 m_currentDevice
->runTransferDialog();
1966 //may not work with non-TransferDialog-class object, but maybe some run time introspection could solve it?
1967 if( m_currentDevice
->getTransferDialog() &&
1968 ( reinterpret_cast<TransferDialog
*>(m_currentDevice
->getTransferDialog()))->isAccepted() )
1969 m_currentDevice
->transferFiles();
1974 m_currentDevice
->m_transferDir
= m_currentDevice
->m_medium
.mountPoint();
1978 MediaBrowser::connectClicked()
1980 bool haveToConfig
= false;
1981 // it was just clicked, so isOn() == true.
1982 if( m_currentDevice
&& !m_currentDevice
->isConnected() )
1984 haveToConfig
= !m_currentDevice
->connectDevice();
1987 haveToConfig
|= !m_currentDevice
;
1988 haveToConfig
|= ( m_currentDevice
&& !m_currentDevice
->isConnected() );
1990 if ( !m_currentDevice
->needsManualConfig() )
1991 haveToConfig
= false;
1993 if( haveToConfig
&& m_devices
.at( 0 ) == m_currentDevice
)
1995 if( config() && m_currentDevice
&& !m_currentDevice
->isConnected() )
1996 m_currentDevice
->connectDevice();
2006 MediaBrowser::disconnectClicked()
2008 if( m_currentDevice
&& m_currentDevice
->isTransferring() )
2010 int action
= KMessageBox::questionYesNoCancel( MediaBrowser::instance(),
2011 i18n( "Transfer in progress. Finish or stop after current track?" ),
2012 i18n( "Stop Transfer?" ),
2013 KGuiItem(i18n("&Finish"), "goto-page"),
2014 KGuiItem(i18n("&Stop"), "media-eject") );
2015 if( action
== KMessageBox::Cancel
)
2019 else if( action
== KMessageBox::Yes
)
2021 m_currentDevice
->scheduleDisconnect();
2026 transferAction()->setEnabled( false );
2027 disconnectAction()->setEnabled( false );
2029 if( m_currentDevice
)
2031 m_currentDevice
->disconnectDevice( true );
2040 MediaBrowser::customClicked()
2042 if( m_currentDevice
)
2043 m_currentDevice
->customClicked();
2047 MediaDevice::connectDevice( bool silent
)
2049 if( !lockDevice( true ) )
2052 runPreConnectCommand();
2053 openDevice( silent
);
2056 && MediaBrowser::instance()->currentDevice() != this
2057 && MediaBrowser::instance()->currentDevice()
2058 && !MediaBrowser::instance()->currentDevice()->isConnected() )
2060 MediaBrowser::instance()->activateDevice( this );
2062 m_parent
->updateStats();
2063 m_parent
->updateButtons();
2065 if( !isConnected() )
2073 syncStatsFromDevice( 0 );
2074 Scrobbler::instance()->m_submitter
->syncComplete();
2077 // delete podcasts already played
2078 if( m_autoDeletePodcasts
&& m_podcastItem
)
2080 Q3PtrList
<MediaItem
> list
;
2081 //NOTE we assume that currentItem is the main target
2082 int numFiles
= m_view
->getSelectedLeaves( m_podcastItem
, &list
, MediaView::OnlyPlayed
);
2086 m_parent
->m_stats
->setText( i18np( "1 track to be deleted", "%1 tracks to be deleted", numFiles
) );
2088 setProgress( 0, numFiles
);
2090 int numDeleted
= deleteItemFromDevice( m_podcastItem
, true );
2092 if( numDeleted
< 0 )
2094 Amarok::StatusBar::instance()->longMessage(
2095 i18n( "Failed to purge podcasts already played" ),
2096 KDE::StatusBar::Sorry
);
2098 else if( numDeleted
> 0 )
2100 Amarok::StatusBar::instance()->shortMessage(
2101 i18np( "Purged 1 podcasts already played",
2102 "Purged %1 podcasts already played",
2106 synchronizeDevice();
2108 QTimer::singleShot( 1500, m_parent
->m_progressBox
, SLOT(hide()) );
2109 m_parent
->queue()->computeSize();
2110 m_parent
->updateStats();
2117 if( m_deferredDisconnect
)
2119 m_deferredDisconnect
= false;
2120 disconnectDevice( m_runDisconnectHook
);
2123 Amarok::StatusBar::instance()->shortMessage( i18n( "Device successfully connected" ) );
2125 m_parent
->updateDevices();
2131 MediaDevice::disconnectDevice( bool postDisconnectHook
)
2137 debug() << "disconnecting: hook=" << postDisconnectHook
;
2139 if( !lockDevice( true ) )
2141 m_runDisconnectHook
= postDisconnectHook
;
2142 m_deferredDisconnect
= true;
2143 debug() << "disconnecting: locked";
2146 debug() << "disconnecting: ok";
2150 syncStatsToDevice();
2156 m_parent
->updateStats();
2159 if( postDisconnectHook
&& runPostDisconnectCommand() != 0 )
2161 Amarok::StatusBar::instance()->longMessage(
2162 i18n( "Post-disconnect command failed, before removing device, please make sure that it is safe to do so." ),
2163 KDE::StatusBar::Information
);
2167 Amarok::StatusBar::instance()->shortMessage( i18n( "Device successfully disconnected" ) );
2169 m_parent
->updateDevices();
2175 MediaDevice::syncStatsFromDevice( MediaItem
*root
)
2177 MediaItem
*it
= static_cast<MediaItem
*>( m_view
->firstChild() );
2180 it
= static_cast<MediaItem
*>( root
->firstChild() );
2183 kapp
->processEvents( QEventLoop::ExcludeUserInputEvents
);
2185 for( ; it
; it
= static_cast<MediaItem
*>( it
->nextSibling() ) )
2187 switch( it
->type() )
2189 case MediaItem::TRACK
:
2190 if( !it
->parent() || static_cast<MediaItem
*>( it
->parent() )->type() != MediaItem::PLAYLIST
)
2192 const MetaBundle
*bundle
= it
->bundle();
2193 for( int i
=0; i
<it
->recentlyPlayed(); i
++ )
2195 // submit to last.fm
2196 if( bundle
->length() > 30
2197 && !bundle
->artist().isEmpty() && bundle
->artist() != i18n( "Unknown" )
2198 && !bundle
->title().isEmpty() && bundle
->title() != i18n( "Unknown" ) )
2200 // don't submit tracks shorter than 30 sec or w/o artist/title
2201 debug() << "scrobbling " << bundle
->artist() << " - " << bundle
->title();
2202 SubmitItem
*sit
= new SubmitItem( bundle
->artist(), bundle
->album(), bundle
->title(), bundle
->length(), false /* fake time */ );
2203 Scrobbler::instance()->m_submitter
->submitItem( sit
);
2206 // increase Amarok playcount
2207 QString url
= CollectionDB::instance()->getURL( *bundle
);
2208 if( !url
.isEmpty() )
2210 QDateTime t
= it
->playTime();
2211 CollectionDB::instance()->addSongPercentage( url
, 100, "mediadevice", t
.isValid() ? &t
: 0 );
2212 debug() << "played " << url
;
2216 if( it
->ratingChanged() )
2218 // copy rating from media device to Amarok
2219 QString url
= CollectionDB::instance()->getURL( *bundle
);
2220 debug() << "rating changed " << url
<< ": " << it
->rating()/10;
2221 if( !url
.isEmpty() )
2223 CollectionDB::instance()->setSongRating( url
, it
->rating()/10 );
2224 it
->setRating( it
->rating() ); // prevent setting it again next time
2229 case MediaItem::PODCASTITEM
:
2230 if( !it
->parent() || static_cast<MediaItem
*>( it
->parent() )->type() != MediaItem::PLAYLIST
)
2232 const MetaBundle
*bundle
= it
->bundle();
2233 if( it
->played() || it
->recentlyPlayed() )
2235 if( PodcastEpisodeBundle
*peb
= bundle
->podcastBundle() )
2237 debug() << "marking podcast episode as played: " << peb
->url();
2239 // if( PlaylistBrowser::instance() )
2241 // PodcastEpisode *p = PlaylistBrowser::instance()->findPodcastEpisode( peb->url(), peb->parent() );
2243 // p->setListened();
2245 // debug() << "did not find podcast episode: " << peb->url() << " from " << peb->parent();
2253 syncStatsFromDevice( it
);
2260 MediaItem::syncStatsFromPath( const QString
&url
)
2265 // copy Amarok rating, play count and last played time to device
2266 int rating
= CollectionDB::instance()->getSongRating( url
)*10;
2268 setRating( rating
);
2269 int playcount
= CollectionDB::instance()->getPlayCount( url
);
2270 if( playcount
> played() )
2271 setPlayCount( playcount
);
2272 QDateTime lastplay
= CollectionDB::instance()->getLastPlay( url
);
2273 if( lastplay
> playTime() )
2274 setLastPlayed( lastplay
.toTime_t() );
2278 MediaDevice::syncStatsToDevice( MediaItem
*root
)
2280 MediaItem
*it
= static_cast<MediaItem
*>( m_view
->firstChild() );
2283 it
= static_cast<MediaItem
*>( root
->firstChild() );
2286 kapp
->processEvents( QEventLoop::ExcludeUserInputEvents
);
2288 for( ; it
; it
= static_cast<MediaItem
*>( it
->nextSibling() ) )
2290 switch( it
->type() )
2292 case MediaItem::TRACK
:
2293 if( !it
->parent() || static_cast<MediaItem
*>( it
->parent() )->type() != MediaItem::PLAYLIST
)
2295 const MetaBundle
*bundle
= it
->bundle();
2296 QString url
= CollectionDB::instance()->getURL( *bundle
);
2297 it
->syncStatsFromPath( url
);
2301 case MediaItem::PODCASTITEM
:
2302 if( !it
->parent() || static_cast<MediaItem
*>( it
->parent() )->type() != MediaItem::PLAYLIST
)
2304 const MetaBundle
*bundle
= it
->bundle();
2305 if( PodcastEpisodeBundle
*peb
= bundle
->podcastBundle() )
2308 // if( PlaylistBrowser::instance() )
2310 // PodcastEpisode *p = PlaylistBrowser::instance()->findPodcastEpisode( peb->url(), peb->parent() );
2312 // it->setListened( !p->isNew() );
2319 syncStatsToDevice( it
);
2326 MediaDevice::transferFiles()
2328 if( !lockDevice( true ) )
2333 setCanceled( false );
2335 m_transferring
= true;
2336 m_parent
->transferAction()->setEnabled( false );
2338 setProgress( 0, m_parent
->m_queue
->childCount() );
2340 // ok, let's copy the stuff to the device
2342 KUrl::List existing
, unplayable
;
2343 unsigned transcodeFail
= 0;
2344 // iterate through items
2345 MediaItem
*next
= static_cast<MediaItem
*>(m_parent
->m_queue
->firstChild());
2348 MediaItem
*transferredItem
= next
;
2349 transferredItem
->setFailed( false );
2350 transferredItem
->m_flags
|= MediaItem::Transferring
;
2351 next
= static_cast<MediaItem
*>( transferredItem
->nextSibling() );
2353 if( transferredItem
->device() )
2355 transferredItem
->device()->copyTrackFromDevice( transferredItem
);
2356 m_parent
->m_queue
->subtractItemFromSize( transferredItem
, true );
2357 delete transferredItem
;
2358 setProgress( progress() + 1 );
2359 m_parent
->m_queue
->itemCountChanged();
2360 kapp
->processEvents( QEventLoop::ExcludeUserInputEvents
);
2365 if( transferredItem
->type() == MediaItem::PLAYLIST
)
2367 if( transferredItem
->flags() & MediaItem::SmartPlaylist
)
2368 bundles
= bundlesToSync( transferredItem
->text( 0 ), transferredItem
->data() );
2370 bundles
= bundlesToSync( transferredItem
->text( 0 ), KUrl( transferredItem
->data() ) );
2372 else if( transferredItem
->bundle() )
2373 bundles
+= *transferredItem
->bundle();
2376 // this should not happen
2377 debug() << "invalid item in transfer queue";
2378 m_parent
->m_queue
->subtractItemFromSize( transferredItem
, true );
2379 delete transferredItem
;
2380 m_parent
->m_queue
->itemCountChanged();
2384 if( bundles
.count() > 1 )
2385 setProgress( progress(), MediaBrowser::instance()->m_progress
->maximum() + bundles
.count() - 1 );
2387 QString playlist
= transferredItem
->m_playlistName
;
2388 for( BundleList::const_iterator it
= bundles
.begin();
2389 it
!= bundles
.end();
2395 const MetaBundle
*bundle
= &(*it
);
2397 bool transcoding
= false;
2398 MediaItem
*item
= trackExists( *bundle
);
2399 if( item
&& playlist
.isEmpty() )
2401 Amarok::StatusBar::instance()->shortMessage( i18n( "Track already on media device: %1" ).
2402 arg( (*it
).url().prettyUrl() ),
2403 KDE::StatusBar::Sorry
);
2404 existing
+= (*it
).url();
2405 setProgress( progress() + 1 );
2408 else if( !item
) // the item does not yet exist on the media device
2410 if( m_transcode
&& ( !isPlayable( *bundle
) || m_transcodeAlways
) )
2412 QString preferred
= supportedFiletypes().isEmpty() ? "mp3" : supportedFiletypes().first();
2413 debug() << "transcoding " << bundle
->url() << " to " << preferred
;
2414 KUrl transcoded
= MediaBrowser::instance()->transcode( bundle
->url(), preferred
);
2417 if( transcoded
.isEmpty() )
2419 debug() << "transcoding failed";
2425 MetaBundle
*transcodedBundle
= new MetaBundle( transcoded
);
2426 transcodedBundle
->setArtist( bundle
->artist() );
2427 transcodedBundle
->setTitle( bundle
->title() );
2428 transcodedBundle
->setComposer( bundle
->composer() );
2429 transcodedBundle
->setAlbum( bundle
->album() );
2430 transcodedBundle
->setGenre( bundle
->genre() );
2431 transcodedBundle
->setComment( bundle
->comment() );
2432 transcodedBundle
->setYear( bundle
->year() );
2433 transcodedBundle
->setDiscNumber( bundle
->discNumber() );
2434 transcodedBundle
->setTrack( bundle
->track() );
2435 if( bundle
->podcastBundle() )
2437 transcodedBundle
->setPodcastBundle( *bundle
->podcastBundle() );
2438 transcodedBundle
->copyFrom( *bundle
->podcastBundle() );
2440 bundle
= transcodedBundle
;
2444 if( !isPlayable( *bundle
) )
2446 Amarok::StatusBar::instance()->shortMessage( i18n( "Track not playable on media device: %1", bundle
->url().path() ),
2447 KDE::StatusBar::Sorry
);
2448 unplayable
+= (*it
).url();
2449 transferredItem
->setFailed();
2455 setProgress( progress() + 1 );
2458 item
= copyTrackToDevice( *bundle
);
2461 if( !item
) // copyTrackToDevice() failed
2465 Amarok::StatusBar::instance()->longMessage(
2466 i18n( "Failed to copy track to media device: %1", bundle
->url().path() ),
2467 KDE::StatusBar::Sorry
);
2468 transferredItem
->setFailed();
2474 if( m_transcodeRemove
)
2475 QFile( bundle
->url().path() ).remove();
2486 setProgress( progress() + 1 );
2490 item
->syncStatsFromPath( (*it
).url().path() );
2492 if( m_playlistItem
&& !playlist
.isEmpty() )
2494 MediaItem
*pl
= m_playlistItem
->findItem( playlist
);
2497 Q3PtrList
<MediaItem
> items
;
2498 pl
= newPlaylist( playlist
, m_playlistItem
, items
);
2502 Q3PtrList
<MediaItem
> items
;
2503 items
.append( item
);
2504 addToPlaylist( pl
, pl
->lastChild(), items
);
2508 setProgress( progress() + 1 );
2511 transferredItem
->m_flags
&= ~MediaItem::Transferring
;
2515 m_parent
->updateStats();
2519 if( !(transferredItem
->flags() & MediaItem::Failed
) )
2521 m_parent
->m_queue
->subtractItemFromSize( transferredItem
, true );
2522 delete transferredItem
;
2523 m_parent
->m_queue
->itemCountChanged();
2525 m_parent
->updateStats();
2527 kapp
->processEvents( QEventLoop::ExcludeUserInputEvents
);
2529 synchronizeDevice();
2531 fileTransferFinished();
2534 if( unplayable
.count() > 0 )
2536 msg
= i18np( "One track not playable on media device",
2537 "%1 tracks not playable on media device", unplayable
.count() );
2539 if( existing
.count() > 0 )
2542 msg
= i18np( "One track already on media device",
2543 "%1 tracks already on media device", existing
.count() );
2545 msg
+= i18np( ", one track already on media device",
2546 ", %1 tracks already on media device", existing
.count() );
2548 if( transcodeFail
> 0 )
2551 msg
= i18np( "One track was not transcoded",
2552 "%1 tracks were not transcoded", transcodeFail
);
2554 msg
+= i18np( ", one track was not transcoded",
2555 ", %1 tracks were not transcoded", transcodeFail
);
2557 const ScriptManager
* const sm
= ScriptManager::instance();
2558 if( !sm
->transcodeScriptRunning().isEmpty() )
2559 msg
+= i18n( " (no transcode script running)" );
2562 if( unplayable
.count() + existing
.count() > 0 )
2564 QString longMsg
= i18n( "The following tracks were not transferred: ");
2565 for( KUrl::List::Iterator it
= existing
.begin();
2566 it
!= existing
.end();
2569 longMsg
+= "<br>" + (*it
).prettyUrl();
2571 for( KUrl::List::Iterator it
= unplayable
.begin();
2572 it
!= unplayable
.end();
2575 longMsg
+= "<br>" + (*it
).prettyUrl();
2577 Amarok::StatusBar::instance()->shortLongMessage( msg
, longMsg
, KDE::StatusBar::Sorry
);
2579 else if( !msg
.isEmpty() )
2581 Amarok::StatusBar::instance()->shortMessage( msg
, KDE::StatusBar::Sorry
);
2584 m_parent
->updateButtons();
2585 m_parent
->queue()->save( Amarok::saveLocation() + "transferlist.xml" );
2586 m_transferring
= false;
2588 if( m_deferredDisconnect
)
2590 m_deferredDisconnect
= false;
2591 disconnectDevice( m_runDisconnectHook
);
2593 else if( m_scheduledDisconnect
)
2595 disconnectDevice( true );
2597 m_scheduledDisconnect
= false;
2601 MediaDevice::progress() const
2603 return m_parent
->m_progress
->value();
2607 MediaDevice::setProgress( const int progress
, const int total
)
2610 m_parent
->m_progress
->setRange( 0, total
);
2611 m_parent
->m_progress
->setValue( progress
);
2612 m_parent
->m_progressBox
->show();
2616 MediaDevice::fileTransferFinished() //SLOT
2618 m_parent
->updateStats();
2619 m_parent
->m_progressBox
->hide();
2620 m_parent
->transferAction()->setEnabled( isConnected() && m_parent
->queue()->childCount() > 0 );
2626 MediaDevice::deleteFromDevice(MediaItem
*item
, int flags
)
2628 MediaItem
* fi
= item
;
2631 if ( !(flags
& Recursing
) )
2633 if( !lockDevice( true ) )
2636 setCanceled( false );
2640 Q3PtrList
<MediaItem
> list
;
2641 //NOTE we assume that currentItem is the main target
2642 int numFiles
= m_view
->getSelectedLeaves(item
, &list
, MediaView::OnlySelected
| ((flags
& OnlyPlayed
) ? MediaView::OnlyPlayed
: MediaView::None
) );
2644 m_parent
->m_stats
->setText( i18np( "1 track to be deleted", "%1 tracks to be deleted", numFiles
) );
2645 if( numFiles
> 0 && (flags
& DeleteTrack
) )
2647 int button
= KMessageBox::warningContinueCancel( m_parent
,
2648 i18np( "<p>You have selected 1 track to be <b>irreversibly</b> deleted.",
2649 "<p>You have selected %1 tracks to be <b>irreversibly</b> deleted.",
2653 KGuiItem(i18n("&Delete"),"edit-delete") );
2655 if ( button
!= KMessageBox::Continue
)
2657 m_parent
->queue()->computeSize();
2658 m_parent
->updateStats();
2664 if(!isTransferring())
2666 setProgress( 0, numFiles
);
2670 // don't return if numFiles==0: playlist items might be to delete
2673 fi
= static_cast<MediaItem
*>(m_view
->firstChild());
2678 MediaItem
*next
= static_cast<MediaItem
*>(fi
->nextSibling());
2685 if( !fi
->isVisible() )
2691 if( fi
->isSelected() )
2693 int ret
= deleteItemFromDevice(fi
, flags
);
2694 if( ret
>= 0 && count
>= 0 )
2701 if( fi
->childCount() )
2703 int ret
= deleteFromDevice( static_cast<MediaItem
*>(fi
->firstChild()), flags
| Recursing
);
2704 if( ret
>= 0 && count
>= 0 )
2710 m_parent
->updateStats();
2715 if(!(flags
& Recursing
))
2718 synchronizeDevice();
2722 if(!isTransferring())
2724 QTimer::singleShot( 1500, m_parent
->m_progressBox
, SLOT(hide()) );
2727 if( m_deferredDisconnect
)
2729 m_deferredDisconnect
= false;
2730 disconnectDevice( m_runDisconnectHook
);
2733 m_parent
->queue()->computeSize();
2734 m_parent
->updateStats();
2740 MediaDevice::purgeEmptyItems( MediaItem
*root
)
2745 it
= static_cast<MediaItem
*>(root
->firstChild());
2749 it
= static_cast<MediaItem
*>(m_view
->firstChild());
2752 MediaItem
*next
= 0;
2753 for( ; it
; it
=next
)
2755 next
= static_cast<MediaItem
*>(it
->nextSibling());
2756 purgeEmptyItems( it
);
2757 if( it
->childCount() == 0 &&
2758 (it
->type() == MediaItem::ARTIST
||
2759 it
->type() == MediaItem::ALBUM
||
2760 it
->type() == MediaItem::PODCASTCHANNEL
) )
2766 MediaQueue::save( const QString
&path
)
2770 if( !file
.open( QIODevice::WriteOnly
) ) return;
2772 QDomDocument newdoc
;
2773 QDomElement transferlist
= newdoc
.createElement( "playlist" );
2774 transferlist
.setAttribute( "product", "Amarok" );
2775 transferlist
.setAttribute( "version", APP_VERSION
);
2776 newdoc
.appendChild( transferlist
);
2778 for( const MediaItem
*item
= static_cast<MediaItem
*>( firstChild() );
2780 item
= static_cast<MediaItem
*>( item
->nextSibling() ) )
2782 QDomElement i
= newdoc
.createElement("item");
2783 i
.setAttribute("url", item
->url().url());
2785 if( item
->bundle() )
2787 QDomElement attr
= newdoc
.createElement( "Title" );
2788 QDomText t
= newdoc
.createTextNode( item
->bundle()->title() );
2789 attr
.appendChild( t
);
2790 i
.appendChild( attr
);
2792 attr
= newdoc
.createElement( "Artist" );
2793 t
= newdoc
.createTextNode( item
->bundle()->artist() );
2794 attr
.appendChild( t
);
2795 i
.appendChild( attr
);
2797 attr
= newdoc
.createElement( "Album" );
2798 t
= newdoc
.createTextNode( item
->bundle()->album() );
2799 attr
.appendChild( t
);
2800 i
.appendChild( attr
);
2802 attr
= newdoc
.createElement( "Year" );
2803 t
= newdoc
.createTextNode( QString::number( item
->bundle()->year() ) );
2804 attr
.appendChild( t
);
2805 i
.appendChild( attr
);
2807 attr
= newdoc
.createElement( "Comment" );
2808 t
= newdoc
.createTextNode( item
->bundle()->comment() );
2809 attr
.appendChild( t
);
2810 i
.appendChild( attr
);
2812 attr
= newdoc
.createElement( "Genre" );
2813 t
= newdoc
.createTextNode( item
->bundle()->genre() );
2814 attr
.appendChild( t
);
2815 i
.appendChild( attr
);
2817 attr
= newdoc
.createElement( "Track" );
2818 t
= newdoc
.createTextNode( QString::number( item
->bundle()->track() ) );
2819 attr
.appendChild( t
);
2820 i
.appendChild( attr
);
2823 if(item
->type() == MediaItem::PODCASTITEM
)
2825 i
.setAttribute( "podcast", "1" );
2828 if(item
->type() == MediaItem::PODCASTITEM
2829 && item
->bundle()->podcastBundle())
2831 PodcastEpisodeBundle
*peb
= item
->bundle()->podcastBundle();
2832 QDomElement attr
= newdoc
.createElement( "PodcastDescription" );
2833 QDomText t
= newdoc
.createTextNode( peb
->description() );
2834 attr
.appendChild( t
);
2835 i
.appendChild( attr
);
2837 attr
= newdoc
.createElement( "PodcastAuthor" );
2838 t
= newdoc
.createTextNode( peb
->author() );
2839 attr
.appendChild( t
);
2840 i
.appendChild( attr
);
2842 attr
= newdoc
.createElement( "PodcastRSS" );
2843 t
= newdoc
.createTextNode( peb
->parent().url() );
2844 attr
.appendChild( t
);
2845 i
.appendChild( attr
);
2847 attr
= newdoc
.createElement( "PodcastURL" );
2848 t
= newdoc
.createTextNode( peb
->url().url() );
2849 attr
.appendChild( t
);
2850 i
.appendChild( attr
);
2853 if( !item
->m_playlistName
.isEmpty() )
2855 i
.setAttribute( "playlist", item
->m_playlistName
);
2858 if(item
->type() == MediaItem::PLAYLIST
)
2860 i
.setAttribute( "playlistdata", item
->data() );
2861 if( item
->flags() & MediaItem::SmartPlaylist
)
2862 i
.setAttribute( "smartplaylist", "1" );
2865 transferlist
.appendChild( i
);
2868 QTextStream
stream( &file
);
2869 stream
.setCodec( QTextCodec::codecForName( "UTF-8" ) );
2870 stream
.setAutoDetectUnicode( true );
2871 stream
<< "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2872 stream
<< newdoc
.toString();
2877 MediaQueue::load( const QString
& filename
)
2879 QFile
file( filename
);
2880 if( !file
.open( QIODevice::ReadOnly
) ) {
2886 QTextStream
stream( &file
);
2887 stream
.setCodec( QTextCodec::codecForName( "UTF-8" ) );
2888 stream
.setAutoDetectUnicode( true );
2893 if( !d
.setContent( stream
.readAll(), &er
, &l
, &c
) ) { // return error values
2894 Amarok::StatusBar::instance()->longMessageThreadSafe( i18n(
2895 //TODO add a link to the path to the playlist
2896 "The XML in the transferlist was invalid. Please report this as a bug to the Amarok "
2897 "developers. Thank you." ), KDE::StatusBar::Error
);
2898 error() << "[TRANSFERLISTLOADER]: Error loading xml file: " << filename
<< "(" << er
<< ")"
2899 << " at line " << l
<< ", column " << c
;
2903 QList
<QDomNode
> nodes
;
2904 const QString
ITEM( "item" ); //so we don't construct this QString all the time
2905 for( QDomNode n
= d
.namedItem( "playlist" ).firstChild(); !n
.isNull(); n
= n
.nextSibling() )
2907 if( n
.nodeName() != ITEM
) continue;
2909 QDomElement elem
= n
.toElement();
2910 if( !elem
.isNull() )
2913 if( !elem
.hasAttribute( "url" ) )
2917 KUrl
url(elem
.attribute("url"));
2919 bool podcast
= elem
.hasAttribute( "podcast" );
2920 PodcastEpisodeBundle peb
;
2921 if( url
.isLocalFile() )
2922 peb
.setLocalURL( url
);
2923 MetaBundle
*bundle
= new MetaBundle( url
);
2924 for(QDomNode node
= elem
.firstChild();
2926 node
= node
.nextSibling())
2928 if(node
.firstChild().isNull())
2931 if(node
.nodeName() == "Title" )
2932 bundle
->setTitle(node
.firstChild().toText().nodeValue());
2933 else if(node
.nodeName() == "Artist" )
2934 bundle
->setArtist(node
.firstChild().toText().nodeValue());
2935 else if(node
.nodeName() == "Album" )
2936 bundle
->setAlbum(node
.firstChild().toText().nodeValue());
2937 else if(node
.nodeName() == "Year" )
2938 bundle
->setYear(node
.firstChild().toText().nodeValue().toUInt());
2939 else if(node
.nodeName() == "Genre" )
2940 bundle
->setGenre(node
.firstChild().toText().nodeValue());
2941 else if(node
.nodeName() == "Comment" )
2942 bundle
->setComment(node
.firstChild().toText().nodeValue());
2943 else if(node
.nodeName() == "PodcastDescription" )
2944 peb
.setDescription( node
.firstChild().toText().nodeValue() );
2945 else if(node
.nodeName() == "PodcastAuthor" )
2946 peb
.setAuthor( node
.firstChild().toText().nodeValue() );
2947 else if(node
.nodeName() == "PodcastRSS" )
2948 peb
.setParent( KUrl( node
.firstChild().toText().nodeValue() ) );
2949 else if(node
.nodeName() == "PodcastURL" )
2950 peb
.setUrl( KUrl( node
.firstChild().toText().nodeValue() ) );
2955 bundle
->setPodcastBundle( peb
);
2958 QString playlist
= elem
.attribute( "playlist" );
2959 QString playlistdata
= elem
.attribute( "playlistdata" );
2960 if( !playlistdata
.isEmpty() )
2962 QString smart
= elem
.attribute( "smartplaylist" );
2963 if( smart
.isEmpty() )
2964 syncPlaylist( playlist
, KUrl( playlistdata
), true );
2966 syncPlaylist( playlist
, playlistdata
, true );
2969 addUrl( url
, bundle
, playlist
);
2976 MediaDevice::isPlayable( const MetaBundle
&bundle
)
2978 if( supportedFiletypes().isEmpty() )
2981 QString type
= bundle
.url().path().section( ".", -1 ).toLower();
2982 return supportedFiletypes().contains( type
);
2986 MediaDevice::isPreferredFormat( const MetaBundle
&bundle
)
2988 if( supportedFiletypes().isEmpty() )
2991 QString type
= bundle
.url().path().section( ".", -1 ).toLower();
2992 return ( type
== supportedFiletypes().first() );
2996 MediaQueue::MediaQueue(MediaBrowser
*parent
)
2997 : K3ListView( parent
), m_parent( parent
)
2999 setFixedHeight( 200 );
3000 setSelectionMode( Q3ListView::Extended
);
3001 setItemsMovable( true );
3002 setDragEnabled( true );
3003 setShowSortIndicator( false );
3005 setFullWidth( true );
3006 setRootIsDecorated( false );
3007 setDropVisualizer( true ); //the visualizer (a line marker) is drawn when dragging over tracks
3008 setDropHighlighter( true ); //and the highligther (a focus rect) is drawn when dragging over playlists
3009 setDropVisualizerWidth( 3 );
3010 setAcceptDrops( true );
3011 addColumn( i18n( "Transfer Queue" ) );
3015 KActionCollection
* ac
= new KActionCollection( this );
3016 KStandardAction::selectAll( this, SLOT( selectAll() ), ac
);
3018 connect( this, SIGNAL( contextMenuRequested( Q3ListViewItem
*, const QPoint
&, int ) ),
3019 SLOT( slotShowContextMenu( Q3ListViewItem
*, const QPoint
&, int ) ) );
3020 connect( this, SIGNAL( dropped(QDropEvent
*, Q3ListViewItem
*, Q3ListViewItem
*) ),
3021 SLOT( slotDropped(QDropEvent
*, Q3ListViewItem
*, Q3ListViewItem
*) ) );
3025 MediaQueue::acceptDrag( QDropEvent
*e
) const
3028 return e
->source() == viewport()
3029 || e
->mimeData()->hasFormat( "amarok-sql" )
3030 || KUrl::List::canDecode( e
->mimeData() );
3034 MediaQueue::slotDropped( QDropEvent
* e
, Q3ListViewItem
* parent
, Q3ListViewItem
* after
)
3036 if( e
->source() != viewport() )
3038 if( e
->mimeData()->hasFormat( "amarok-sql" ) )
3040 QString
data( e
->mimeData()->data( "amarok-sql" ) );
3041 QString playlist
= data
.section( "\n", 0, 0 );
3042 QString query
= data
.section( "\n", 1 );
3043 QStringList values
= CollectionDB::instance()->query( query
);
3044 KUrl::List list
= CollectionDB::instance()->URLsFromSqlDrag( values
);
3045 addUrls( list
, playlist
);
3047 else if ( KUrl::List::canDecode( e
->mimeData() ) )
3049 KUrl::List list
= KUrl::List::fromMimeData( e
->mimeData() );
3050 if (!list
.isEmpty() )
3054 else if( Q3ListViewItem
*i
= currentItem() )
3056 moveItem( i
, parent
, after
);
3061 MediaQueue::dropProxyEvent( QDropEvent
*e
)
3063 slotDropped( e
, 0, 0 );
3067 MediaQueue::findPath( QString path
)
3069 for( Q3ListViewItem
*item
= firstChild();
3071 item
= item
->nextSibling())
3073 if(static_cast<MediaItem
*>(item
)->url().path() == path
)
3074 return static_cast<MediaItem
*>(item
);
3081 MediaQueue::computeSize() const
3084 for( Q3ListViewItem
*it
= firstChild();
3086 it
= it
->nextSibling())
3088 MediaItem
*item
= static_cast<MediaItem
*>(it
);
3090 if( item
&& item
->bundle() &&
3091 ( !m_parent
->currentDevice()
3092 || !m_parent
->currentDevice()->isConnected()
3093 || !m_parent
->currentDevice()->trackExists(*item
->bundle()) ) )
3094 m_totalSize
+= ((item
->size()+1023)/1024)*1024;
3099 MediaQueue::totalSize() const
3105 MediaQueue::addItemToSize( const MediaItem
*item
) const
3107 if( item
&& item
->bundle() &&
3108 ( !m_parent
->currentDevice()
3109 || !m_parent
->currentDevice()->isConnected()
3110 || !m_parent
->currentDevice()->trackExists(*item
->bundle()) ) )
3111 m_totalSize
+= ((item
->size()+1023)/1024)*1024;
3115 MediaQueue::subtractItemFromSize( const MediaItem
*item
, bool unconditionally
) const
3117 if( item
&& item
->bundle() &&
3118 ( !m_parent
->currentDevice()
3119 || !m_parent
->currentDevice()->isConnected()
3120 || (unconditionally
|| !m_parent
->currentDevice()->trackExists(*item
->bundle())) ) )
3121 m_totalSize
-= ((item
->size()+1023)/1024)*1024;
3125 MediaQueue::removeSelected()
3127 QList
<Q3ListViewItem
*> selected
= selectedItems();
3129 QListIterator
<Q3ListViewItem
*> iter( selected
);
3130 while( iter
.hasNext() )
3132 Q3ListViewItem
*item
= iter
.next();
3133 if( !(static_cast<MediaItem
*>(item
)->flags() & MediaItem::Transferring
) )
3135 subtractItemFromSize( static_cast<MediaItem
*>(item
) );
3137 if( m_parent
->currentDevice() && m_parent
->currentDevice()->isTransferring() )
3139 MediaBrowser::instance()->m_progress
->setRange( 0, MediaBrowser::instance()->m_progress
->maximum() - 1 );
3144 MediaBrowser::instance()->updateStats();
3145 MediaBrowser::instance()->updateButtons();
3150 MediaQueue::keyPressEvent( QKeyEvent
*e
)
3152 if( e
->key() == Qt::Key_Delete
)
3155 K3ListView::keyPressEvent( e
);
3159 MediaQueue::itemCountChanged()
3161 if( childCount() == 0 )
3163 else if( !isShown() )
3168 MediaQueue::slotShowContextMenu( Q3ListViewItem
* item
, const QPoint
& point
, int )
3173 Q3PopupMenu
menu( this );
3175 enum Actions
{ REMOVE_SELECTED
, CLEAR_ALL
, START_TRANSFER
};
3178 menu
.insertItem( KIcon( Amarok::icon( "remove_from_playlist" ) ), i18n( "&Remove From Queue" ), REMOVE_SELECTED
);
3180 menu
.insertItem( KIcon( Amarok::icon( "playlist_clear" ) ), i18n( "&Clear Queue" ), CLEAR_ALL
);
3181 menu
.insertItem( KIcon( Amarok::icon( "playlist_refresh" ) ), i18n( "&Start Transfer" ), START_TRANSFER
);
3182 menu
.setItemEnabled( START_TRANSFER
,
3183 MediaBrowser::instance()->currentDevice() &&
3184 MediaBrowser::instance()->currentDevice()->isConnected() &&
3185 MediaBrowser::instance()->currentDevice()->m_transfer
);
3187 switch( menu
.exec( point
) )
3189 case REMOVE_SELECTED
:
3195 case START_TRANSFER
:
3196 MediaBrowser::instance()->transferClicked();
3202 MediaQueue::clearItems()
3209 m_parent
->updateStats();
3210 m_parent
->updateButtons();
3215 #include "mediabrowser.moc"