|
|
|
// (c) 2004 Christian Muehlhaeuser <chris@chris.de>
|
|
|
|
// (c) 2005-2006 Martin Aumueller <aumuell@reserv.at>
|
|
|
|
// (c) 2005 Seb Ruiz <me@sebruiz.net>
|
|
|
|
// (c) 2006 T.R.Shashwath <trshash84@gmail.com>
|
|
|
|
// See COPYING file for licensing information
|
|
|
|
|
|
|
|
|
|
|
|
#define DEBUG_PREFIX "MediaBrowser"
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "amarok.h"
|
|
|
|
#include "amarokconfig.h"
|
|
|
|
#include "app.h"
|
|
|
|
#include "browserToolBar.h"
|
|
|
|
#include "clicklineedit.h"
|
|
|
|
#include "collectiondb.h"
|
|
|
|
#include "colorgenerator.h"
|
|
|
|
#include "contextbrowser.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "editfilterdialog.h"
|
|
|
|
#include "deviceconfiguredialog.h"
|
|
|
|
#include "mediadevicemanager.h"
|
|
|
|
#include "expression.h"
|
|
|
|
#include "hintlineedit.h"
|
|
|
|
#include "mediabrowser.h"
|
|
|
|
#include "medium.h"
|
|
|
|
#include "mediumpluginmanager.h"
|
|
|
|
#include "metabundle.h"
|
|
|
|
#include "mountpointmanager.h"
|
|
|
|
#include "playlist.h"
|
|
|
|
#include "playlistbrowser.h"
|
|
|
|
#include "playlistbrowseritem.h"
|
|
|
|
#include "playlistloader.h"
|
|
|
|
#include "pluginmanager.h"
|
|
|
|
#include "podcastbundle.h"
|
|
|
|
#include "scriptmanager.h"
|
|
|
|
#include "scrobbler.h"
|
|
|
|
#include "statusbar.h"
|
|
|
|
#include "transferdialog.h"
|
|
|
|
#include "browserToolBar.h"
|
|
|
|
|
|
|
|
#include <tqvbuttongroup.h>
|
|
|
|
#include <tqcheckbox.h>
|
|
|
|
#include <tqdatetime.h>
|
|
|
|
#include <tqdir.h>
|
|
|
|
#include <tqdom.h>
|
|
|
|
#include <tqfileinfo.h>
|
|
|
|
#include <tqgroupbox.h>
|
|
|
|
#include <tqheader.h>
|
|
|
|
#include <tqimage.h>
|
|
|
|
#include <tqlabel.h>
|
|
|
|
#include <tqobjectlist.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqradiobutton.h>
|
|
|
|
#include <tqsimplerichtext.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
#include <tqtooltip.h> //TQToolTip::add()
|
|
|
|
|
|
|
|
#include <kapplication.h> //kapp
|
|
|
|
#include <kcombobox.h>
|
|
|
|
#include <kdirlister.h>
|
|
|
|
#include <kfiledialog.h>
|
|
|
|
#include <kglobal.h>
|
|
|
|
#include <kiconloader.h>
|
|
|
|
#include <kinputdialog.h>
|
|
|
|
#include <kio/job.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kmessagebox.h>
|
|
|
|
#include <kmultipledrag.h>
|
|
|
|
#include <kpopupmenu.h>
|
|
|
|
#include <kprocess.h>
|
|
|
|
#include <kprogress.h>
|
|
|
|
#include <kpushbutton.h>
|
|
|
|
#include <krun.h>
|
|
|
|
#include <kstandarddirs.h> //locate file
|
|
|
|
#include <ktabbar.h>
|
|
|
|
#include <ktempfile.h>
|
|
|
|
#include <ktoolbarbutton.h> //ctor
|
|
|
|
#include <kurldrag.h> //dragObject()
|
|
|
|
#include <kactioncollection.h>
|
|
|
|
|
|
|
|
|
|
|
|
MediaBrowser *MediaBrowser::s_instance = 0;
|
|
|
|
|
|
|
|
TQPixmap *MediaItem::s_pixUnknown = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixArtist = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixAlbum = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixFile = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixTrack = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixPodcast = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixPlaylist = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixInvisible = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixStale = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixOrphaned = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixDirectory = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixRootItem = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixTransferFailed = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixTransferBegin = 0;
|
|
|
|
TQPixmap *MediaItem::s_pixTransferEnd = 0;
|
|
|
|
|
|
|
|
bool MediaBrowser::isAvailable() //static
|
|
|
|
{
|
|
|
|
if( !MediaBrowser::instance() )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
//to re-enable hiding, uncomment this and get rid of the return true above:
|
|
|
|
//return MediaBrowser::instance()->m_haveDevices;
|
|
|
|
}
|
|
|
|
|
|
|
|
class SpaceLabel : public TQLabel {
|
|
|
|
public:
|
|
|
|
SpaceLabel(TQWidget *parent)
|
|
|
|
: TQLabel(parent)
|
|
|
|
{
|
|
|
|
m_total = m_used = m_scheduled = 0;
|
|
|
|
setBackgroundMode(TQt::NoBackground);
|
|
|
|
}
|
|
|
|
|
|
|
|
void paintEvent(TQPaintEvent *e)
|
|
|
|
{
|
|
|
|
TQPainter p(this);
|
|
|
|
p.fillRect(e->rect(), colorGroup().brush(TQColorGroup::Background));
|
|
|
|
|
|
|
|
if(m_total > 0)
|
|
|
|
{
|
|
|
|
int used = int(float(m_used)/float(m_total)*width());
|
|
|
|
int scheduled = int(float(m_used + m_scheduled)/float(m_total)*width());
|
|
|
|
|
|
|
|
if(m_used > 0)
|
|
|
|
{
|
|
|
|
TQColor blueish(70,120,255);
|
|
|
|
if(e->rect().left() < used)
|
|
|
|
{
|
|
|
|
int right = used;
|
|
|
|
if(e->rect().right() < right)
|
|
|
|
right = e->rect().right();
|
|
|
|
p.fillRect(e->rect().left(), e->rect().top(),
|
|
|
|
used, e->rect().bottom()+1, TQBrush(blueish, TQt::SolidPattern));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_scheduled > 0)
|
|
|
|
{
|
|
|
|
TQColor sched(70, 230, 120);
|
|
|
|
if(m_used + m_scheduled > m_total - m_total/200)
|
|
|
|
{
|
|
|
|
sched.setRgb( 255, 120, 120 );
|
|
|
|
}
|
|
|
|
int left = e->rect().left();
|
|
|
|
if(used > left)
|
|
|
|
left = used;
|
|
|
|
int right = e->rect().right();
|
|
|
|
if(scheduled < right)
|
|
|
|
right = scheduled;
|
|
|
|
p.fillRect(left, e->rect().top(), right, e->rect().bottom()+1, TQBrush(sched, TQt::SolidPattern));
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_used + m_scheduled < m_total)
|
|
|
|
{
|
|
|
|
TQColor grey(180, 180, 180);
|
|
|
|
int left = e->rect().left();
|
|
|
|
if(scheduled > left)
|
|
|
|
left = scheduled;
|
|
|
|
int right = e->rect().right();
|
|
|
|
p.fillRect(left, e->rect().top(), right, e->rect().bottom()+1, colorGroup().brush(TQColorGroup::Background));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TQLabel::paintEvent(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
KIO::filesize_t m_total;
|
|
|
|
KIO::filesize_t m_used;
|
|
|
|
KIO::filesize_t m_scheduled;
|
|
|
|
};
|
|
|
|
|
|
|
|
class DummyMediaDevice : public MediaDevice
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
DummyMediaDevice() : MediaDevice()
|
|
|
|
{
|
|
|
|
m_name = i18n( "No Device Available" );
|
|
|
|
m_type = "dummy-mediadevice";
|
|
|
|
m_medium = Medium( "DummyDevice", "DummyDevice" );
|
|
|
|
}
|
|
|
|
void init( MediaBrowser *browser ) { MediaDevice::init( browser ); }
|
|
|
|
virtual ~DummyMediaDevice() {}
|
|
|
|
virtual bool isConnected() { return false; }
|
|
|
|
virtual MediaItem* trackExists(const MetaBundle&) { return 0; }
|
|
|
|
virtual bool lockDevice(bool) { return true; }
|
|
|
|
virtual void unlockDevice() {}
|
|
|
|
virtual bool openDevice( bool silent )
|
|
|
|
{
|
|
|
|
if( !silent )
|
|
|
|
{
|
|
|
|
//TQString msg = i18n( "Sorry, you do not have a supported portable music player." );
|
|
|
|
//Amarok::StatusBar::instance()->longMessage( msg, KDE::StatusBar::Sorry );
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
virtual bool closeDevice() { return false; }
|
|
|
|
virtual void synchronizeDevice() {}
|
|
|
|
virtual MediaItem* copyTrackToDevice(const MetaBundle&) { return 0; }
|
|
|
|
virtual int deleteItemFromDevice(MediaItem*, int) { return -1; }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
MediaBrowser::MediaBrowser( const char *name )
|
|
|
|
: TQVBox( 0, name )
|
|
|
|
, m_timer( new TQTimer( this ) )
|
|
|
|
, m_currentDevice( m_devices.end() )
|
|
|
|
, m_waitForTranscode( false )
|
|
|
|
, m_quitting( false )
|
|
|
|
{
|
|
|
|
s_instance = this;
|
|
|
|
|
|
|
|
// preload pixmaps used in browser
|
|
|
|
KIconLoader iconLoader;
|
|
|
|
MediaItem::s_pixUnknown = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "unknown" ), KIcon::Toolbar, KIcon::SizeSmall ));
|
|
|
|
MediaItem::s_pixTrack = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "playlist" ), KIcon::Toolbar, KIcon::SizeSmall ));
|
|
|
|
MediaItem::s_pixFile = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "sound" ), KIcon::Toolbar, KIcon::SizeSmall ) );
|
|
|
|
MediaItem::s_pixPodcast = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "podcast" ), KIcon::Toolbar, KIcon::SizeSmall ) );
|
|
|
|
MediaItem::s_pixPlaylist = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "playlist" ), KIcon::Toolbar, KIcon::SizeSmall ) );
|
|
|
|
MediaItem::s_pixRootItem = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "files2" ), KIcon::Toolbar, KIcon::SizeSmall ) );
|
|
|
|
// history
|
|
|
|
// favorites
|
|
|
|
// collection
|
|
|
|
// folder
|
|
|
|
// folder_red
|
|
|
|
// player_playlist_2
|
|
|
|
// cancel
|
|
|
|
// sound
|
|
|
|
MediaItem::s_pixArtist = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "personal" ), KIcon::Toolbar, KIcon::SizeSmall ) );
|
|
|
|
MediaItem::s_pixAlbum = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "cdrom_unmount" ), KIcon::Toolbar, KIcon::SizeSmall ) );
|
|
|
|
MediaItem::s_pixInvisible = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "cancel" ), KIcon::Toolbar, KIcon::SizeSmall ) );
|
|
|
|
MediaItem::s_pixStale = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "cancel" ), KIcon::Toolbar, KIcon::SizeSmall ) );
|
|
|
|
MediaItem::s_pixOrphaned = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "cancel" ), KIcon::Toolbar, KIcon::SizeSmall ) );
|
|
|
|
MediaItem::s_pixDirectory = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "folder" ), KIcon::Toolbar, KIcon::SizeSmall ) );
|
|
|
|
MediaItem::s_pixTransferBegin = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "play" ), KIcon::Toolbar, KIcon::SizeSmall ) );
|
|
|
|
MediaItem::s_pixTransferEnd = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "stop" ), KIcon::Toolbar, KIcon::SizeSmall ) );
|
|
|
|
MediaItem::s_pixTransferFailed = new TQPixmap(iconLoader.loadIcon( Amarok::icon( "cancel" ), KIcon::Toolbar, KIcon::SizeSmall ) );
|
|
|
|
|
|
|
|
setSpacing( 4 );
|
|
|
|
|
|
|
|
m_toolbar = new Browser::ToolBar( this );
|
|
|
|
m_toolbar->setIconText( KToolBar::IconTextRight, false );
|
|
|
|
|
|
|
|
m_toolbar->insertButton( "connect_creating", CONNECT, true, i18n("Connect") );
|
|
|
|
TQToolTip::add( m_toolbar->getButton(CONNECT), i18n( "Connect media device" ) );
|
|
|
|
|
|
|
|
m_toolbar->insertButton( "player_eject", DISCONNECT, true, i18n("Disconnect") );
|
|
|
|
TQToolTip::add( m_toolbar->getButton(DISCONNECT), i18n( "Disconnect media device" ) );
|
|
|
|
|
|
|
|
m_toolbar->insertButton( "rebuild", TRANSFER, true, i18n("Transfer") );
|
|
|
|
TQToolTip::add( m_toolbar->getButton(TRANSFER), i18n( "Transfer tracks to media device" ) );
|
|
|
|
|
|
|
|
m_toolbar->insertLineSeparator();
|
|
|
|
|
|
|
|
// m_toolbar->setIconText( KToolBar::IconTextRight, true );
|
|
|
|
m_toolbar->insertButton( Amarok::icon( "add_playlist" ), CUSTOM, TQT_SIGNAL( clicked() ), TQT_TQOBJECT(this), TQT_SLOT( customClicked() ), true, "custom" );
|
|
|
|
TQToolTip::add( m_toolbar->getButton(TRANSFER), i18n( "Transfer tracks to media device" ) );
|
|
|
|
|
|
|
|
m_toolbar->setIconText( KToolBar::IconOnly, false );
|
|
|
|
|
|
|
|
m_toolbar->insertButton( Amarok::icon( "configure" ), CONFIGURE, true, i18n("Configure") );
|
|
|
|
TQToolTip::add( m_toolbar->getButton(CONFIGURE), i18n( "Configure device" ) );
|
|
|
|
|
|
|
|
|
|
|
|
m_deviceCombo = new KComboBox( this );
|
|
|
|
|
|
|
|
// searching/filtering
|
|
|
|
{ //<Search LineEdit>
|
|
|
|
KToolBar* searchToolBar = new Browser::ToolBar( this );
|
|
|
|
KToolBarButton *button = new KToolBarButton( "locationbar_erase", 0, searchToolBar );
|
|
|
|
m_searchEdit = new ClickLineEdit( i18n( "Enter search terms here" ), searchToolBar );
|
|
|
|
KPushButton *filterButton = new KPushButton("...", searchToolBar, "filter");
|
|
|
|
searchToolBar->setStretchableWidget( m_searchEdit );
|
|
|
|
m_searchEdit->setFrame( TQFrame::Sunken );
|
|
|
|
|
|
|
|
connect( button, TQT_SIGNAL( clicked() ), m_searchEdit, TQT_SLOT( clear() ) );
|
|
|
|
connect( filterButton, TQT_SIGNAL( clicked() ), TQT_SLOT( slotEditFilter() ) );
|
|
|
|
|
|
|
|
TQToolTip::add( button, i18n( "Clear filter" ) );
|
|
|
|
TQToolTip::add( m_searchEdit, i18n( "Enter space-separated terms to search" ) );
|
|
|
|
TQToolTip::add( filterButton, i18n( "Click to edit filter" ) );
|
|
|
|
} //</Search LineEdit>
|
|
|
|
|
|
|
|
connect( m_timer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotSetFilter() ) );
|
|
|
|
connect( m_searchEdit, TQT_SIGNAL( textChanged( const TQString& ) ), TQT_SLOT( slotSetFilterTimeout() ) );
|
|
|
|
connect( m_searchEdit, TQT_SIGNAL( returnPressed() ), TQT_SLOT( slotSetFilter() ) );
|
|
|
|
|
|
|
|
// connect to device manager
|
|
|
|
connect( MediaDeviceManager::instance(), TQT_SIGNAL( mediumAdded(const Medium *, TQString) ),
|
|
|
|
TQT_SLOT( mediumAdded(const Medium *, TQString) ) );
|
|
|
|
connect( MediaDeviceManager::instance(), TQT_SIGNAL( mediumChanged(const Medium *, TQString) ),
|
|
|
|
TQT_SLOT( mediumChanged(const Medium *, TQString) ) );
|
|
|
|
connect( MediaDeviceManager::instance(), TQT_SIGNAL( mediumRemoved(const Medium *, TQString) ),
|
|
|
|
TQT_SLOT( mediumRemoved(const Medium *, TQString) ) );
|
|
|
|
|
|
|
|
|
|
|
|
// we always have a dummy device
|
|
|
|
m_pluginName[ i18n( "Disable" ) ] = "dummy-mediadevice";
|
|
|
|
m_pluginAmarokName["dummy-mediadevice"] = i18n( "Disable" );
|
|
|
|
m_pluginName[ i18n( "Do not handle" ) ] = "ignore";
|
|
|
|
m_pluginAmarokName["ignore"] = i18n( "Do not handle" );
|
|
|
|
// query available device plugins
|
|
|
|
m_plugins = PluginManager::query( "[X-KDE-Amarok-plugintype] == 'mediadevice'" );
|
|
|
|
for( KTrader::OfferList::ConstIterator it = m_plugins.begin(); it != m_plugins.end(); ++it ) {
|
|
|
|
// Save name properties in TQMap for lookup
|
|
|
|
m_pluginName[(*it)->name()] = (*it)->property( "X-KDE-Amarok-name" ).toString();
|
|
|
|
m_pluginAmarokName[(*it)->property( "X-KDE-Amarok-name" ).toString()] = (*it)->name();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_views = new TQVBox( this );
|
|
|
|
m_queue = new MediaQueue( this );
|
|
|
|
m_progressBox = new TQHBox( this );
|
|
|
|
m_progress = new KProgress( m_progressBox );
|
|
|
|
m_cancelButton = new KPushButton( SmallIconSet( Amarok::icon( "cancel" ) ), i18n("Cancel"), m_progressBox );
|
|
|
|
|
|
|
|
|
|
|
|
m_stats = new SpaceLabel(this);
|
|
|
|
|
|
|
|
m_progressBox->hide();
|
|
|
|
|
|
|
|
MediaDevice *dev = new DummyMediaDevice();
|
|
|
|
dev->init( this );
|
|
|
|
addDevice( dev );
|
|
|
|
activateDevice( 0, false );
|
|
|
|
queue()->load( Amarok::saveLocation() + "transferlist.xml" );
|
|
|
|
queue()->computeSize();
|
|
|
|
|
|
|
|
setFocusProxy( m_queue );
|
|
|
|
|
|
|
|
updateStats();
|
|
|
|
|
|
|
|
TQMap<TQString, Medium*> mmap = MediaDeviceManager::instance()->getMediumMap();
|
|
|
|
|
|
|
|
bool newflag = false;
|
|
|
|
//This deals with <strike>auto-detectable</strike> ALL devices!
|
|
|
|
for( TQMap<TQString, Medium*>::Iterator it = mmap.begin();
|
|
|
|
it != mmap.end();
|
|
|
|
it++ )
|
|
|
|
{
|
|
|
|
TQString handler = Amarok::config( "MediaBrowser" )->readEntry( (*it)->id() );
|
|
|
|
//debug() << "[MediaBrowser] (*it)->id() = " << (*it)->id() << ", handler = " << handler << endl;
|
|
|
|
if( handler.isEmpty() )
|
|
|
|
{
|
|
|
|
//this should probably never be the case with a manually added device, unless amarokrc's been messed with
|
|
|
|
Amarok::config( "MediaBrowser" )->writeEntry( (*it)->id(), "ignore" );
|
|
|
|
newflag = true;
|
|
|
|
mediumAdded( *it, (*it)->name(), true );
|
|
|
|
}
|
|
|
|
//and this definitely shouldn't!
|
|
|
|
else if( handler != "deleted" )
|
|
|
|
mediumAdded( *it, (*it)->name(), true );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( newflag )
|
|
|
|
Amarok::StatusBar::instance()->longMessageThreadSafe(
|
|
|
|
i18n("Amarok has detected new portable media devices.\n"
|
|
|
|
"Go to the \"Media Devices\" pane of the configuration\n"
|
|
|
|
"dialog to choose a plugin for these devices.") );
|
|
|
|
|
|
|
|
connect( m_toolbar->getButton(CONNECT), TQT_SIGNAL( clicked() ), TQT_SLOT( connectClicked() ) );
|
|
|
|
connect( m_toolbar->getButton(DISCONNECT), TQT_SIGNAL( clicked() ), TQT_SLOT( disconnectClicked() ) );
|
|
|
|
connect( m_toolbar->getButton(TRANSFER), TQT_SIGNAL( clicked() ), TQT_SLOT( transferClicked() ) );
|
|
|
|
connect( m_toolbar->getButton(CONFIGURE), TQT_SIGNAL( clicked() ), TQT_SLOT( config() ) );
|
|
|
|
|
|
|
|
connect( m_deviceCombo, TQT_SIGNAL( activated( int ) ), TQT_SLOT( activateDevice( int ) ) );
|
|
|
|
|
|
|
|
connect( m_cancelButton, TQT_SIGNAL( clicked() ), TQT_SLOT( cancelClicked() ) );
|
|
|
|
connect( pApp, TQT_SIGNAL( prepareToQuit() ), TQT_SLOT( prepareToQuit() ) );
|
|
|
|
connect( CollectionDB::instance(), TQT_SIGNAL( tagsChanged( const MetaBundle& ) ),
|
|
|
|
TQT_SLOT( tagsChanged( const MetaBundle& ) ) );
|
|
|
|
|
|
|
|
m_haveDevices = false;
|
|
|
|
TQMap<TQString,TQString> savedDevices = Amarok::config( "MediaBrowser" )->entryMap( "MediaBrowser" );
|
|
|
|
for( TQMap<TQString,TQString>::Iterator it = savedDevices.begin();
|
|
|
|
it != savedDevices.end();
|
|
|
|
++it )
|
|
|
|
{
|
|
|
|
if( it.data() != "deleted" && it.data() != "ignore" )
|
|
|
|
{
|
|
|
|
m_haveDevices = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
emit availabilityChanged( m_haveDevices );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaBrowser::blockQuit() const
|
|
|
|
{
|
|
|
|
for( TQValueList<MediaDevice *>::const_iterator it = m_devices.begin();
|
|
|
|
it != m_devices.end();
|
|
|
|
++it )
|
|
|
|
{
|
|
|
|
if( *it && (*it)->isConnected() )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::tagsChanged( const MetaBundle &bundle )
|
|
|
|
{
|
|
|
|
m_itemMapMutex.lock();
|
|
|
|
debug() << "tags changed for " << bundle.url().url() << endl;
|
|
|
|
ItemMap::iterator it = m_itemMap.find( bundle.url().url() );
|
|
|
|
if( it != m_itemMap.end() )
|
|
|
|
{
|
|
|
|
MediaItem *item = *it;
|
|
|
|
m_itemMapMutex.unlock();
|
|
|
|
if( item->device() )
|
|
|
|
{
|
|
|
|
item->device()->tagsChanged( item, bundle );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// it's an item on the transfer queue
|
|
|
|
item->setBundle( new MetaBundle( bundle ) );
|
|
|
|
|
|
|
|
TQString text = item->bundle()->prettyTitle();
|
|
|
|
if( text.isEmpty() || (!item->bundle()->isValidMedia() && !item->bundle()->podcastBundle()) )
|
|
|
|
text = item->bundle()->url().prettyURL();
|
|
|
|
if( !item->m_playlistName.isNull() )
|
|
|
|
{
|
|
|
|
text += " (" + item->m_playlistName + ')';
|
|
|
|
}
|
|
|
|
item->setText( 0, text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_itemMapMutex.unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaBrowser::getBundle( const KURL &url, MetaBundle *bundle ) const
|
|
|
|
{
|
|
|
|
TQMutexLocker locker( &m_itemMapMutex );
|
|
|
|
ItemMap::const_iterator it = m_itemMap.find( url.url() );
|
|
|
|
if( it == m_itemMap.end() )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if( bundle )
|
|
|
|
*bundle = TQDeepCopy<MetaBundle>( *(*it)->bundle() );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
KURL
|
|
|
|
MediaBrowser::getProxyUrl( const KURL& daapUrl ) const
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
KURL url;
|
|
|
|
MediaDevice* dc = dynamic_cast<MediaDevice*>( queryList( "DaapClient" )->getFirst() );
|
|
|
|
if( dc )
|
|
|
|
url = dc->getProxyUrl( daapUrl );
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaDevice *
|
|
|
|
MediaBrowser::currentDevice() const
|
|
|
|
{
|
|
|
|
TQValueList<MediaDevice *>::const_iterator current = m_currentDevice;
|
|
|
|
if( current != m_devices.constEnd() )
|
|
|
|
{
|
|
|
|
return *m_currentDevice;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaDevice *
|
|
|
|
MediaBrowser::deviceFromId( const TQString &id ) const
|
|
|
|
{
|
|
|
|
for( TQValueList<MediaDevice *>::const_iterator it = m_devices.constBegin();
|
|
|
|
it != m_devices.end();
|
|
|
|
it++ )
|
|
|
|
{
|
|
|
|
if( (*it)->uniqueId() == id )
|
|
|
|
return (*it);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::activateDevice( const MediaDevice *dev )
|
|
|
|
{
|
|
|
|
int index = 0;
|
|
|
|
for( TQValueList<MediaDevice *>::iterator it = m_devices.begin();
|
|
|
|
it != m_devices.end();
|
|
|
|
it++ )
|
|
|
|
{
|
|
|
|
if( *it == dev )
|
|
|
|
{
|
|
|
|
activateDevice( index );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::activateDevice( int index, bool skipDummy )
|
|
|
|
{
|
|
|
|
if( currentDevice() && currentDevice()->customAction() )
|
|
|
|
{
|
|
|
|
currentDevice()->customAction()->unplug( m_toolbar );
|
|
|
|
m_toolbar->hide();
|
|
|
|
m_toolbar->show();
|
|
|
|
}
|
|
|
|
|
|
|
|
for( TQValueList<MediaDevice *>::iterator it = m_devices.begin();
|
|
|
|
it != m_devices.end();
|
|
|
|
it++ )
|
|
|
|
{
|
|
|
|
(*it)->view()->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
if( index < 0 )
|
|
|
|
{
|
|
|
|
m_currentDevice = m_devices.end();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( skipDummy )
|
|
|
|
index++;
|
|
|
|
|
|
|
|
if( (uint)index >= m_devices.count() )
|
|
|
|
{
|
|
|
|
m_currentDevice = m_devices.end();
|
|
|
|
updateButtons();
|
|
|
|
queue()->computeSize();
|
|
|
|
updateStats();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_currentDevice = m_devices.at( index );
|
|
|
|
if( currentDevice() )
|
|
|
|
{
|
|
|
|
currentDevice()->view()->show();
|
|
|
|
if( currentDevice()->customAction() )
|
|
|
|
{
|
|
|
|
m_toolbar->setIconText( KToolBar::IconTextRight, false );
|
|
|
|
currentDevice()->customAction()->plug( m_toolbar );
|
|
|
|
m_toolbar->hide();
|
|
|
|
m_toolbar->show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_deviceCombo->setCurrentItem( index-1 );
|
|
|
|
|
|
|
|
updateButtons();
|
|
|
|
queue()->computeSize();
|
|
|
|
updateStats();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::addDevice( MediaDevice *device )
|
|
|
|
{
|
|
|
|
m_devices.append( device );
|
|
|
|
|
|
|
|
device->loadConfig();
|
|
|
|
|
|
|
|
if( device->autoConnect() )
|
|
|
|
{
|
|
|
|
device->connectDevice( true );
|
|
|
|
updateButtons();
|
|
|
|
}
|
|
|
|
|
|
|
|
updateDevices();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::removeDevice( MediaDevice *device )
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
|
|
|
|
debug() << "remove device: type=" << device->deviceType() << endl;
|
|
|
|
|
|
|
|
for( TQValueList<MediaDevice *>::iterator it = m_devices.begin();
|
|
|
|
it != m_devices.end();
|
|
|
|
it++ )
|
|
|
|
{
|
|
|
|
if( *it == device )
|
|
|
|
{
|
|
|
|
bool current = (it == m_currentDevice);
|
|
|
|
m_devices.remove( device );
|
|
|
|
if( current )
|
|
|
|
activateDevice( 0, false );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( device->isConnected() )
|
|
|
|
{
|
|
|
|
if( device->disconnectDevice( false /* don't run post-disconnect command */ ) )
|
|
|
|
unloadDevicePlugin( device );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
debug() << "Cannot remove device because disconnect failed" << endl;
|
|
|
|
Amarok::StatusBar::instance()->longMessage(
|
|
|
|
i18n( "Cannot remove device because disconnect failed" ),
|
|
|
|
KDE::StatusBar::Warning );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
unloadDevicePlugin( device );
|
|
|
|
|
|
|
|
updateDevices();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::updateDevices()
|
|
|
|
{
|
|
|
|
m_deviceCombo->clear();
|
|
|
|
uint i = 0;
|
|
|
|
for( TQValueList<MediaDevice *>::iterator it = m_devices.begin();
|
|
|
|
it != m_devices.end();
|
|
|
|
it++ )
|
|
|
|
{
|
|
|
|
if( m_devices.count() > 1 && dynamic_cast<DummyMediaDevice *>(*it) )
|
|
|
|
continue;
|
|
|
|
TQString name = (*it)->name();
|
|
|
|
if( !(*it)->deviceNode().isEmpty() )
|
|
|
|
{
|
|
|
|
name = i18n( "%1 at %2" ).arg( name, (*it)->deviceNode() );
|
|
|
|
}
|
|
|
|
if( (*it)->hasMountPoint() && !(*it)->mountPoint().isEmpty() )
|
|
|
|
{
|
|
|
|
name += i18n( " (mounted at %1)" ).arg( (*it)->mountPoint() );
|
|
|
|
}
|
|
|
|
m_deviceCombo->insertItem( name, i );
|
|
|
|
if( it == m_currentDevice )
|
|
|
|
{
|
|
|
|
m_deviceCombo->setCurrentItem( i );
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
m_deviceCombo->setEnabled( m_devices.count() > 1 );
|
|
|
|
m_haveDevices = m_devices.count() > 1;
|
|
|
|
emit availabilityChanged( m_haveDevices );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQStringList
|
|
|
|
MediaBrowser::deviceNames() const
|
|
|
|
{
|
|
|
|
TQStringList list;
|
|
|
|
|
|
|
|
for( TQValueList<MediaDevice *>::const_iterator it = m_devices.constBegin();
|
|
|
|
it != m_devices.constEnd();
|
|
|
|
it++ )
|
|
|
|
{
|
|
|
|
TQString name = (*it)->name();
|
|
|
|
list << name;
|
|
|
|
}
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaBrowser::deviceSwitch( const TQString &name )
|
|
|
|
{
|
|
|
|
int index = 0;
|
|
|
|
for( TQValueList<MediaDevice *>::iterator it = m_devices.begin();
|
|
|
|
it != m_devices.end();
|
|
|
|
it++ )
|
|
|
|
{
|
|
|
|
if( (*it)->name() == name )
|
|
|
|
{
|
|
|
|
activateDevice( index, false );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::transcodingFinished( const TQString &src, const TQString &dst )
|
|
|
|
{
|
|
|
|
KURL srcJob = KURL::fromPathOrURL( m_transcodeSrc );
|
|
|
|
KURL srcResult = KURL::fromPathOrURL( src );
|
|
|
|
|
|
|
|
if( srcJob.path() == srcResult.path() )
|
|
|
|
{
|
|
|
|
m_transcodedUrl = KURL::fromPathOrURL( dst );
|
|
|
|
m_waitForTranscode = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
debug() << "transcoding for " << src << " finished, "
|
|
|
|
<< "but we are waiting for " << m_transcodeSrc << " -- aborting" << endl;
|
|
|
|
m_waitForTranscode = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
KURL
|
|
|
|
MediaBrowser::transcode( const KURL &src, const TQString &filetype )
|
|
|
|
{
|
|
|
|
const ScriptManager* const sm = ScriptManager::instance();
|
|
|
|
|
|
|
|
if( sm->transcodeScriptRunning().isEmpty() )
|
|
|
|
{
|
|
|
|
debug() << "cannot transcode with no transcoder registered" << endl;
|
|
|
|
return KURL();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_waitForTranscode = true;
|
|
|
|
m_transcodeSrc = src.url();
|
|
|
|
m_transcodedUrl = KURL();
|
|
|
|
ScriptManager::instance()->notifyTranscode( src.url(), filetype );
|
|
|
|
|
|
|
|
while( m_waitForTranscode && sm->transcodeScriptRunning() != TQString() )
|
|
|
|
{
|
|
|
|
usleep( 10000 );
|
|
|
|
kapp->processEvents( 100 );
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_transcodedUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::slotSetFilterTimeout() //SLOT
|
|
|
|
{
|
|
|
|
m_timer->start( 280, true ); //stops the timer for us first
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::slotSetFilter() //SLOT
|
|
|
|
{
|
|
|
|
m_timer->stop();
|
|
|
|
|
|
|
|
if( currentDevice() )
|
|
|
|
currentDevice()->view()->setFilter( m_searchEdit->text() );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::slotSetFilter( const TQString &text )
|
|
|
|
{
|
|
|
|
m_searchEdit->setText( text );
|
|
|
|
slotSetFilter();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::slotEditFilter()
|
|
|
|
{
|
|
|
|
EditFilterDialog *fd = new EditFilterDialog( this, true, m_searchEdit->text() );
|
|
|
|
connect( fd, TQT_SIGNAL(filterChanged(const TQString &)), TQT_SLOT(slotSetFilter(const TQString &)) );
|
|
|
|
if( fd->exec() )
|
|
|
|
m_searchEdit->setText( fd->filter() );
|
|
|
|
delete fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::prepareToQuit()
|
|
|
|
{
|
|
|
|
m_waitForTranscode = false;
|
|
|
|
m_quitting = true;
|
|
|
|
for( TQValueList<MediaDevice *>::iterator it = m_devices.begin();
|
|
|
|
it != m_devices.end();
|
|
|
|
++it )
|
|
|
|
{
|
|
|
|
if( (*it)->isConnected() )
|
|
|
|
(*it)->disconnectDevice( false /* don't unmount */ );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaBrowser::~MediaBrowser()
|
|
|
|
{
|
|
|
|
debug() << "having to remove " << m_devices.count() << " devices" << endl;
|
|
|
|
while( !m_devices.isEmpty() )
|
|
|
|
{
|
|
|
|
removeDevice( m_devices.last() );
|
|
|
|
}
|
|
|
|
|
|
|
|
queue()->save( Amarok::saveLocation() + "transferlist.xml" );
|
|
|
|
|
|
|
|
delete m_deviceCombo;
|
|
|
|
delete m_queue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MediaItem::MediaItem( TQListView* parent )
|
|
|
|
: KListViewItem( parent )
|
|
|
|
{
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaItem::MediaItem( TQListViewItem* parent )
|
|
|
|
: KListViewItem( parent )
|
|
|
|
{
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaItem::MediaItem( TQListView* parent, TQListViewItem* after )
|
|
|
|
: KListViewItem( parent, after )
|
|
|
|
{
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaItem::MediaItem( TQListViewItem* parent, TQListViewItem* after )
|
|
|
|
: KListViewItem( parent, after )
|
|
|
|
{
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaItem::~MediaItem()
|
|
|
|
{
|
|
|
|
setBundle( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaItem::init()
|
|
|
|
{
|
|
|
|
m_bundle=0;
|
|
|
|
m_order=0;
|
|
|
|
m_type=UNKNOWN;
|
|
|
|
m_playlistName = TQString();
|
|
|
|
m_device=0;
|
|
|
|
m_flags=0;
|
|
|
|
setExpandable( false );
|
|
|
|
setDragEnabled( true );
|
|
|
|
setDropEnabled( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaItem::setBundle( MetaBundle *bundle )
|
|
|
|
{
|
|
|
|
MediaBrowser::instance()->m_itemMapMutex.lock();
|
|
|
|
if( m_bundle )
|
|
|
|
{
|
|
|
|
TQString itemUrl = url().url();
|
|
|
|
MediaBrowser::ItemMap::iterator it = MediaBrowser::instance()->m_itemMap.find( itemUrl );
|
|
|
|
if( it != MediaBrowser::instance()->m_itemMap.end() && *it == this )
|
|
|
|
MediaBrowser::instance()->m_itemMap.remove( itemUrl );
|
|
|
|
}
|
|
|
|
delete m_bundle;
|
|
|
|
m_bundle = bundle;
|
|
|
|
|
|
|
|
if( m_bundle )
|
|
|
|
{
|
|
|
|
TQString itemUrl = url().url();
|
|
|
|
MediaBrowser::ItemMap::iterator it = MediaBrowser::instance()->m_itemMap.find( itemUrl );
|
|
|
|
if( it == MediaBrowser::instance()->m_itemMap.end() )
|
|
|
|
MediaBrowser::instance()->m_itemMap[itemUrl] = this;
|
|
|
|
}
|
|
|
|
MediaBrowser::instance()->m_itemMapMutex.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MediaItem::paintCell( TQPainter *p, const TQColorGroup &cg, int column, int width, int align )
|
|
|
|
{
|
|
|
|
switch( type() )
|
|
|
|
{
|
|
|
|
case INVISIBLE:
|
|
|
|
case PODCASTSROOT:
|
|
|
|
case PLAYLISTSROOT:
|
|
|
|
case ORPHANEDROOT:
|
|
|
|
case STALEROOT:
|
|
|
|
{
|
|
|
|
TQFont font( p->font() );
|
|
|
|
font.setBold( true );
|
|
|
|
p->setFont( font );
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
KListViewItem::paintCell( p, cg, column, width, align );
|
|
|
|
}
|
|
|
|
|
|
|
|
const MetaBundle *
|
|
|
|
MediaItem::bundle() const
|
|
|
|
{
|
|
|
|
return m_bundle;
|
|
|
|
}
|
|
|
|
|
|
|
|
KURL
|
|
|
|
MediaItem::url() const
|
|
|
|
{
|
|
|
|
if( bundle() )
|
|
|
|
return bundle()->url();
|
|
|
|
else
|
|
|
|
return KURL();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaItem::isFileBacked() const
|
|
|
|
{
|
|
|
|
switch( type() )
|
|
|
|
{
|
|
|
|
case ARTIST:
|
|
|
|
case ALBUM:
|
|
|
|
case PODCASTSROOT:
|
|
|
|
case PODCASTCHANNEL:
|
|
|
|
case PLAYLISTSROOT:
|
|
|
|
case PLAYLIST:
|
|
|
|
case PLAYLISTITEM:
|
|
|
|
case INVISIBLEROOT:
|
|
|
|
case STALEROOT:
|
|
|
|
case STALE:
|
|
|
|
case ORPHANEDROOT:
|
|
|
|
return false;
|
|
|
|
|
|
|
|
case UNKNOWN:
|
|
|
|
case TRACK:
|
|
|
|
case ORPHANED:
|
|
|
|
case INVISIBLE:
|
|
|
|
case PODCASTITEM:
|
|
|
|
case DIRECTORY:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
long
|
|
|
|
MediaItem::size() const
|
|
|
|
{
|
|
|
|
if( !isFileBacked() )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if( bundle() )
|
|
|
|
return bundle()->filesize();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaItem::setType( Type type )
|
|
|
|
{
|
|
|
|
m_type=type;
|
|
|
|
|
|
|
|
setDragEnabled(true);
|
|
|
|
setDropEnabled(false);
|
|
|
|
|
|
|
|
switch(m_type)
|
|
|
|
{
|
|
|
|
case UNKNOWN:
|
|
|
|
setPixmap(0, *s_pixUnknown);
|
|
|
|
break;
|
|
|
|
case INVISIBLE:
|
|
|
|
case TRACK:
|
|
|
|
setPixmap(0, *s_pixFile);
|
|
|
|
break;
|
|
|
|
case PLAYLISTITEM:
|
|
|
|
setPixmap(0, *s_pixTrack);
|
|
|
|
setDropEnabled(true);
|
|
|
|
break;
|
|
|
|
case ARTIST:
|
|
|
|
setPixmap(0, *s_pixArtist);
|
|
|
|
break;
|
|
|
|
case ALBUM:
|
|
|
|
setPixmap(0, *s_pixAlbum);
|
|
|
|
break;
|
|
|
|
case PODCASTSROOT:
|
|
|
|
setPixmap(0, *s_pixRootItem);
|
|
|
|
break;
|
|
|
|
case PODCASTITEM:
|
|
|
|
case PODCASTCHANNEL:
|
|
|
|
setPixmap(0, *s_pixPodcast);
|
|
|
|
break;
|
|
|
|
case PLAYLIST:
|
|
|
|
setPixmap(0, *s_pixPlaylist);
|
|
|
|
setDropEnabled(true);
|
|
|
|
break;
|
|
|
|
case PLAYLISTSROOT:
|
|
|
|
setPixmap(0, *s_pixRootItem);
|
|
|
|
setDropEnabled( true );
|
|
|
|
break;
|
|
|
|
case INVISIBLEROOT:
|
|
|
|
setPixmap(0, *s_pixInvisible);
|
|
|
|
break;
|
|
|
|
case STALEROOT:
|
|
|
|
case STALE:
|
|
|
|
setPixmap(0, *s_pixStale);
|
|
|
|
break;
|
|
|
|
case ORPHANEDROOT:
|
|
|
|
case ORPHANED:
|
|
|
|
setPixmap(0, *s_pixOrphaned);
|
|
|
|
break;
|
|
|
|
case DIRECTORY:
|
|
|
|
setExpandable( true );
|
|
|
|
setDropEnabled( true );
|
|
|
|
setPixmap(0, *s_pixDirectory);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaItem::setFailed( bool failed )
|
|
|
|
{
|
|
|
|
if( failed )
|
|
|
|
{
|
|
|
|
m_flags &= ~MediaItem::Transferring;
|
|
|
|
m_flags |= MediaItem::Failed;
|
|
|
|
setPixmap(0, *MediaItem::s_pixTransferFailed);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_flags &= ~MediaItem::Failed;
|
|
|
|
if( m_type == PODCASTITEM )
|
|
|
|
setPixmap(0, *s_pixPodcast);
|
|
|
|
else if( m_type == PLAYLIST )
|
|
|
|
setPixmap(0, *s_pixPlaylist);
|
|
|
|
else
|
|
|
|
setPixmap(0, TQPixmap() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaItem *
|
|
|
|
MediaItem::lastChild() const
|
|
|
|
{
|
|
|
|
TQListViewItem *last = 0;
|
|
|
|
for( TQListViewItem *it = firstChild();
|
|
|
|
it;
|
|
|
|
it = it->nextSibling() )
|
|
|
|
{
|
|
|
|
last = it;
|
|
|
|
}
|
|
|
|
|
|
|
|
return dynamic_cast<MediaItem *>(last);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaItem::isLeafItem() const
|
|
|
|
{
|
|
|
|
switch(type())
|
|
|
|
{
|
|
|
|
case UNKNOWN:
|
|
|
|
return false;
|
|
|
|
|
|
|
|
case INVISIBLE:
|
|
|
|
case TRACK:
|
|
|
|
case PODCASTITEM:
|
|
|
|
case PLAYLISTITEM:
|
|
|
|
case STALE:
|
|
|
|
case ORPHANED:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case ARTIST:
|
|
|
|
case ALBUM:
|
|
|
|
case PODCASTSROOT:
|
|
|
|
case PODCASTCHANNEL:
|
|
|
|
case PLAYLISTSROOT:
|
|
|
|
case PLAYLIST:
|
|
|
|
case INVISIBLEROOT:
|
|
|
|
case STALEROOT:
|
|
|
|
case ORPHANEDROOT:
|
|
|
|
case DIRECTORY:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaItem *
|
|
|
|
MediaItem::findItem( const TQString &key, const MediaItem *after ) const
|
|
|
|
{
|
|
|
|
MediaItem *it = 0;
|
|
|
|
if( after )
|
|
|
|
it = dynamic_cast<MediaItem *>( after->nextSibling() );
|
|
|
|
else
|
|
|
|
it = dynamic_cast<MediaItem *>( firstChild() );
|
|
|
|
|
|
|
|
for( ; it; it = dynamic_cast<MediaItem *>(it->nextSibling()))
|
|
|
|
{
|
|
|
|
if(key == it->text(0))
|
|
|
|
return it;
|
|
|
|
if(key.isEmpty() && it->text(0).isEmpty())
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
MediaItem::compare( TQListViewItem *i, int col, bool ascending ) const
|
|
|
|
{
|
|
|
|
MediaItem *item = dynamic_cast<MediaItem *>(i);
|
|
|
|
if(item && col==0 && item->m_order != m_order)
|
|
|
|
return m_order-item->m_order;
|
|
|
|
else if( item && item->type() == MediaItem::ARTIST )
|
|
|
|
{
|
|
|
|
TQString key1 = key( col, ascending );
|
|
|
|
if( key1.startsWith( "the ", false ) )
|
|
|
|
key1 = key1.mid( 4 );
|
|
|
|
TQString key2 = i->key( col, ascending );
|
|
|
|
if( key2.startsWith( "the ", false ) )
|
|
|
|
key2 = key2.mid( 4 );
|
|
|
|
|
|
|
|
return key1.localeAwareCompare( key2 );
|
|
|
|
}
|
|
|
|
|
|
|
|
return KListViewItem::compare(i, col, ascending);
|
|
|
|
}
|
|
|
|
|
|
|
|
class MediaItemTip : public TQToolTip
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
MediaItemTip( TQListView *listview )
|
|
|
|
: TQToolTip( listview->viewport() )
|
|
|
|
, m_view( listview )
|
|
|
|
{}
|
|
|
|
virtual ~MediaItemTip() {}
|
|
|
|
protected:
|
|
|
|
virtual void maybeTip( const TQPoint &p )
|
|
|
|
{
|
|
|
|
MediaItem *i = dynamic_cast<MediaItem *>(m_view->itemAt( m_view->viewportToContents( p ) ) );
|
|
|
|
if( !i )
|
|
|
|
return;
|
|
|
|
|
|
|
|
TQString text;
|
|
|
|
switch( i->type() )
|
|
|
|
{
|
|
|
|
case MediaItem::TRACK:
|
|
|
|
{
|
|
|
|
const MetaBundle *b = i->bundle();
|
|
|
|
if( b )
|
|
|
|
{
|
|
|
|
if( b->track() )
|
|
|
|
text = TQString( "%1 - %2 (%3)" )
|
|
|
|
.arg( TQString::number(b->track()), b->title(), b->prettyLength() );
|
|
|
|
if( !b->genre().isEmpty() )
|
|
|
|
{
|
|
|
|
if( !text.isEmpty() )
|
|
|
|
text += "<br>";
|
|
|
|
text += TQString( "<i>Genre: %1</i>" )
|
|
|
|
.arg( b->genre() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MediaItem::PLAYLISTSROOT:
|
|
|
|
text = i18n( "Drag items here to create new playlist" );
|
|
|
|
break;
|
|
|
|
case MediaItem::PLAYLIST:
|
|
|
|
text = i18n( "Drag items here to append to this playlist" );
|
|
|
|
break;
|
|
|
|
case MediaItem::PLAYLISTITEM:
|
|
|
|
text = i18n( "Drag items here to insert before this item" );
|
|
|
|
break;
|
|
|
|
case MediaItem::INVISIBLEROOT:
|
|
|
|
case MediaItem::INVISIBLE:
|
|
|
|
text = i18n( "Not visible on media device" );
|
|
|
|
break;
|
|
|
|
case MediaItem::STALEROOT:
|
|
|
|
case MediaItem::STALE:
|
|
|
|
text = i18n( "In device database, but file is missing" );
|
|
|
|
break;
|
|
|
|
case MediaItem::ORPHANEDROOT:
|
|
|
|
case MediaItem::ORPHANED:
|
|
|
|
text = i18n( "File on device, but not in device database" );
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !text.isEmpty() && !text.isNull() )
|
|
|
|
tip( m_view->itemRect( i ), text );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQListView *m_view;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
MediaView::MediaView( TQWidget* parent, MediaDevice *device )
|
|
|
|
: KListView( parent )
|
|
|
|
, m_parent( parent )
|
|
|
|
, m_device( device )
|
|
|
|
{
|
|
|
|
hide();
|
|
|
|
setSelectionMode( TQListView::Extended );
|
|
|
|
setItemsMovable( false );
|
|
|
|
setShowSortIndicator( true );
|
|
|
|
setFullWidth( true );
|
|
|
|
setRootIsDecorated( true );
|
|
|
|
setDragEnabled( true );
|
|
|
|
setDropVisualizer( true ); //the visualizer (a line marker) is drawn when dragging over tracks
|
|
|
|
setDropHighlighter( true ); //and the highligther (a focus rect) is drawn when dragging over playlists
|
|
|
|
setDropVisualizerWidth( 3 );
|
|
|
|
setAcceptDrops( true );
|
|
|
|
|
|
|
|
header()->hide();
|
|
|
|
addColumn( i18n( "Remote Media" ) );
|
|
|
|
|
|
|
|
KActionCollection* ac = new KActionCollection( this );
|
|
|
|
KStdAction::selectAll( TQT_TQOBJECT(this), TQT_SLOT( selectAll() ), ac, "mediabrowser_select_all" );
|
|
|
|
|
|
|
|
connect( this, TQT_SIGNAL( contextMenuRequested( TQListViewItem*, const TQPoint&, int ) ),
|
|
|
|
this, TQT_SLOT( rmbPressed( TQListViewItem*, const TQPoint&, int ) ) );
|
|
|
|
|
|
|
|
connect( this, TQT_SIGNAL( itemRenamed( TQListViewItem* ) ),
|
|
|
|
this, TQT_SLOT( renameItem( TQListViewItem* ) ) );
|
|
|
|
|
|
|
|
connect( this, TQT_SIGNAL( expanded( TQListViewItem* ) ),
|
|
|
|
this, TQT_SLOT( slotExpand( TQListViewItem* ) ) );
|
|
|
|
|
|
|
|
connect( this, TQT_SIGNAL( returnPressed( TQListViewItem* ) ),
|
|
|
|
this, TQT_SLOT( invokeItem( TQListViewItem* ) ) );
|
|
|
|
|
|
|
|
connect( this, TQT_SIGNAL( doubleClicked( TQListViewItem*, const TQPoint&, int ) ),
|
|
|
|
this, TQT_SLOT( invokeItem( TQListViewItem*, const TQPoint &, int ) ) );
|
|
|
|
|
|
|
|
m_toolTip = new MediaItemTip( this );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaView::keyPressEvent( TQKeyEvent *e )
|
|
|
|
{
|
|
|
|
if( e->key() == Key_Delete )
|
|
|
|
m_device->deleteFromDevice();
|
|
|
|
else
|
|
|
|
KListView::keyPressEvent( e );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaView::invokeItem( TQListViewItem* i, const TQPoint& point, int column ) //SLOT
|
|
|
|
{
|
|
|
|
if( column == -1 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
TQPoint p = mapFromGlobal( point );
|
|
|
|
if ( p.x() > header()->sectionPos( header()->mapToIndex( 0 ) ) + treeStepSize() * ( i->depth() + ( rootIsDecorated() ? 1 : 0) ) + itemMargin()
|
|
|
|
|| p.x() < header()->sectionPos( header()->mapToIndex( 0 ) ) )
|
|
|
|
invokeItem( i );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaView::invokeItem( TQListViewItem *i )
|
|
|
|
{
|
|
|
|
MediaItem *item = dynamic_cast<MediaItem *>( i );
|
|
|
|
if( !item )
|
|
|
|
return;
|
|
|
|
|
|
|
|
KURL::List urls = nodeBuildDragList( item );
|
|
|
|
Playlist::instance()->insertMedia( urls, Playlist::DefaultOptions );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaView::renameItem( TQListViewItem *item )
|
|
|
|
{
|
|
|
|
m_device->renameItem( item );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaView::slotExpand( TQListViewItem *item )
|
|
|
|
{
|
|
|
|
m_device->expandItem( item );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MediaView::~MediaView()
|
|
|
|
{
|
|
|
|
delete m_toolTip;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQDragObject *
|
|
|
|
MediaView::dragObject()
|
|
|
|
{
|
|
|
|
KURL::List urls = nodeBuildDragList( 0 );
|
|
|
|
KMultipleDrag *md = new KMultipleDrag( viewport() );
|
|
|
|
md->addDragObject( KListView::dragObject() );
|
|
|
|
KURLDrag* ud = new KURLDrag( urls, viewport() );
|
|
|
|
md->addDragObject( ud );
|
|
|
|
md->setPixmap( CollectionDB::createDragPixmap( urls ),
|
|
|
|
TQPoint( CollectionDB::DRAGPIXMAP_OFFSET_X, CollectionDB::DRAGPIXMAP_OFFSET_Y ) );
|
|
|
|
return md;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
KURL::List
|
|
|
|
MediaView::nodeBuildDragList( MediaItem* item, int flags )
|
|
|
|
{
|
|
|
|
KURL::List items;
|
|
|
|
MediaItem* fi;
|
|
|
|
|
|
|
|
if ( !item )
|
|
|
|
{
|
|
|
|
fi = static_cast<MediaItem*>(firstChild());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fi = item;
|
|
|
|
|
|
|
|
while ( fi )
|
|
|
|
{
|
|
|
|
if( fi->isVisible() )
|
|
|
|
{
|
|
|
|
if ( fi->isSelected() || !(flags & OnlySelected ) )
|
|
|
|
{
|
|
|
|
if( fi->isLeafItem() || fi->type() == MediaItem::DIRECTORY )
|
|
|
|
items += fi->url();
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(fi->childCount() )
|
|
|
|
items += nodeBuildDragList( static_cast<MediaItem*>(fi->firstChild()), None );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( fi->childCount() )
|
|
|
|
items += nodeBuildDragList( static_cast<MediaItem*>(fi->firstChild()), OnlySelected );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fi = static_cast<MediaItem*>(fi->nextSibling());
|
|
|
|
}
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
MediaView::getSelectedLeaves( MediaItem *parent, TQPtrList<MediaItem> *list, int flags )
|
|
|
|
{
|
|
|
|
int numFiles = 0;
|
|
|
|
if( !list )
|
|
|
|
list = new TQPtrList<MediaItem>;
|
|
|
|
|
|
|
|
MediaItem *it;
|
|
|
|
if( !parent )
|
|
|
|
it = dynamic_cast<MediaItem *>(firstChild());
|
|
|
|
else
|
|
|
|
it = dynamic_cast<MediaItem *>(parent->firstChild());
|
|
|
|
|
|
|
|
for( ; it; it = dynamic_cast<MediaItem*>(it->nextSibling()))
|
|
|
|
{
|
|
|
|
if( it->isVisible() )
|
|
|
|
{
|
|
|
|
if( it->childCount() && !( it->type() == MediaItem::DIRECTORY && it->isSelected() ) )
|
|
|
|
{
|
|
|
|
int f = flags;
|
|
|
|
if( it->isSelected() )
|
|
|
|
f &= ~OnlySelected;
|
|
|
|
numFiles += getSelectedLeaves(it, list, f );
|
|
|
|
}
|
|
|
|
if( it->isSelected() || !(flags & OnlySelected) )
|
|
|
|
{
|
|
|
|
if( it->type() == MediaItem::TRACK ||
|
|
|
|
it->type() == MediaItem::DIRECTORY ||
|
|
|
|
it->type() == MediaItem::PODCASTITEM ||
|
|
|
|
it->type() == MediaItem::PLAYLISTITEM||
|
|
|
|
it->type() == MediaItem::INVISIBLE ||
|
|
|
|
it->type() == MediaItem::ORPHANED )
|
|
|
|
{
|
|
|
|
if( flags & OnlyPlayed )
|
|
|
|
{
|
|
|
|
if( it->played() > 0 )
|
|
|
|
numFiles++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
numFiles++;
|
|
|
|
}
|
|
|
|
if( ( it->isLeafItem() && (!(flags & OnlyPlayed) || it->played()>0) )
|
|
|
|
|| it->type() == MediaItem::DIRECTORY )
|
|
|
|
list->append( it );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return numFiles;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaView::acceptDrag( TQDropEvent *e ) const
|
|
|
|
{
|
|
|
|
if( e->source() == MediaBrowser::queue()->viewport() )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
TQString data;
|
|
|
|
TQCString subtype;
|
|
|
|
TQTextDrag::decode( e, data, subtype );
|
|
|
|
|
|
|
|
return e->source() == viewport()
|
|
|
|
|| subtype == "amarok-sql"
|
|
|
|
|| KURLDrag::canDecode( e );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaView::contentsDropEvent( TQDropEvent *e )
|
|
|
|
{
|
|
|
|
cleanDropVisualizer();
|
|
|
|
cleanItemHighlighter();
|
|
|
|
|
|
|
|
if(e->source() == viewport())
|
|
|
|
{
|
|
|
|
const TQPoint p = contentsToViewport( e->pos() );
|
|
|
|
MediaItem *item = dynamic_cast<MediaItem *>(itemAt( p ));
|
|
|
|
|
|
|
|
if( !item && MediaBrowser::instance()->currentDevice()->m_type != "generic-mediadevice" )
|
|
|
|
return;
|
|
|
|
|
|
|
|
TQPtrList<MediaItem> items;
|
|
|
|
|
|
|
|
if( !item || item->type() == MediaItem::DIRECTORY ||
|
|
|
|
item->type() == MediaItem::TRACK )
|
|
|
|
{
|
|
|
|
TQPtrList<MediaItem> items;
|
|
|
|
getSelectedLeaves( 0, &items );
|
|
|
|
m_device->addToDirectory( item, items );
|
|
|
|
}
|
|
|
|
else if( item->type() == MediaItem::PLAYLIST )
|
|
|
|
{
|
|
|
|
MediaItem *list = item;
|
|
|
|
MediaItem *after = 0;
|
|
|
|
for(MediaItem *it = dynamic_cast<MediaItem *>(item->firstChild());
|
|
|
|
it;
|
|
|
|
it = dynamic_cast<MediaItem *>(it->nextSibling()))
|
|
|
|
after = it;
|
|
|
|
|
|
|
|
getSelectedLeaves( 0, &items );
|
|
|
|
m_device->addToPlaylist( list, after, items );
|
|
|
|
}
|
|
|
|
else if( item->type() == MediaItem::PLAYLISTITEM )
|
|
|
|
{
|
|
|
|
MediaItem *list = dynamic_cast<MediaItem *>(item->parent());
|
|
|
|
MediaItem *after = 0;
|
|
|
|
for(MediaItem *it = dynamic_cast<MediaItem*>(item->parent()->firstChild());
|
|
|
|
it;
|
|
|
|
it = dynamic_cast<MediaItem *>(it->nextSibling()))
|
|
|
|
{
|
|
|
|
if(it == item)
|
|
|
|
break;
|
|
|
|
after = it;
|
|
|
|
}
|
|
|
|
|
|
|
|
getSelectedLeaves( 0, &items );
|
|
|
|
m_device->addToPlaylist( list, after, items );
|
|
|
|
}
|
|
|
|
else if( item->type() == MediaItem::PLAYLISTSROOT )
|
|
|
|
{
|
|
|
|
TQPtrList<MediaItem> items;
|
|
|
|
getSelectedLeaves( 0, &items );
|
|
|
|
TQString base( i18n("New Playlist") );
|
|
|
|
TQString name = base;
|
|
|
|
int i=1;
|
|
|
|
while( item->findItem(name) )
|
|
|
|
{
|
|
|
|
TQString num;
|
|
|
|
num.setNum(i);
|
|
|
|
name = base + ' ' + num;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
MediaItem *pl = m_device->newPlaylist(name, item, items);
|
|
|
|
ensureItemVisible(pl);
|
|
|
|
rename(pl, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TQString data;
|
|
|
|
TQCString subtype;
|
|
|
|
TQTextDrag::decode( e, data, subtype );
|
|
|
|
KURL::List list;
|
|
|
|
|
|
|
|
if( subtype == "amarok-sql" )
|
|
|
|
{
|
|
|
|
TQString playlist = data.section( "\n", 0, 0 );
|
|
|
|
TQString query = data.section( "\n", 1 );
|
|
|
|
TQStringList values = CollectionDB::instance()->query( query );
|
|
|
|
list = CollectionDB::instance()->URLsFromSqlDrag( values );
|
|
|
|
MediaBrowser::queue()->addURLs( list, playlist );
|
|
|
|
}
|
|
|
|
else if ( KURLDrag::decode( e, list ) )
|
|
|
|
{
|
|
|
|
MediaBrowser::queue()->addURLs( list );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaView::viewportPaintEvent( TQPaintEvent *e )
|
|
|
|
{
|
|
|
|
KListView::viewportPaintEvent( e );
|
|
|
|
|
|
|
|
// Superimpose bubble help:
|
|
|
|
|
|
|
|
if ( !MediaBrowser::instance()->currentDevice() || !MediaBrowser::instance()->currentDevice()->isConnected() )
|
|
|
|
{
|
|
|
|
TQPainter p( viewport() );
|
|
|
|
|
|
|
|
TQSimpleRichText t( i18n(
|
|
|
|
"<div align=center>"
|
|
|
|
"<h3>Media Device Browser</h3>"
|
|
|
|
"Configure your media device and then "
|
|
|
|
"click the Connect button to access your media device. "
|
|
|
|
"Drag and drop files to enqueue them for transfer."
|
|
|
|
"</div>" ), TQApplication::font() );
|
|
|
|
|
|
|
|
t.setWidth( width() - 50 );
|
|
|
|
|
|
|
|
const uint w = t.width() + 20;
|
|
|
|
const uint h = t.height() + 20;
|
|
|
|
|
|
|
|
p.setBrush( colorGroup().background() );
|
|
|
|
p.drawRoundRect( 15, 15, w, h, (8*200)/w, (8*200)/h );
|
|
|
|
t.draw( &p, 20, 20, TQRect(), colorGroup() );
|
|
|
|
}
|
|
|
|
MediaBrowser::instance()->updateButtons();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaView::rmbPressed( TQListViewItem *item, const TQPoint &p, int i )
|
|
|
|
{
|
|
|
|
if( m_device->isConnected() )
|
|
|
|
m_device->rmbPressed( item, p, i );
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaItem *
|
|
|
|
MediaView::newDirectory( MediaItem *parent )
|
|
|
|
{
|
|
|
|
bool ok;
|
|
|
|
const TQString name = KInputDialog::getText(i18n("Add Directory"), i18n("Directory Name:"), TQString(), &ok, this);
|
|
|
|
|
|
|
|
if( ok && !name.isEmpty() )
|
|
|
|
{
|
|
|
|
m_device->newDirectory( name, parent );
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::mediumAdded( const Medium *medium, TQString /*name*/, bool /*constructing*/ )
|
|
|
|
{
|
|
|
|
debug() << "mediumAdded: " << (medium? medium->properties():"null") << endl;
|
|
|
|
if( medium )
|
|
|
|
{
|
|
|
|
TQString handler = Amarok::config( "MediaBrowser" )->readEntry( medium->id() );
|
|
|
|
if( handler.isEmpty() )
|
|
|
|
{
|
|
|
|
//Some people complained about the dialog, boohoo
|
|
|
|
//Just disable it for now I guess
|
|
|
|
/*if( !constructing && medium->isAutodetected() )
|
|
|
|
{
|
|
|
|
MediumPluginManagerDialog *mpm = new MediumPluginManagerDialog();
|
|
|
|
mpm->exec();
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
//debug() << "id=" << medium->id() << ", handler=" << handler << endl;
|
|
|
|
MediaDevice *device = loadDevicePlugin( handler );
|
|
|
|
if( device )
|
|
|
|
{
|
|
|
|
device->m_medium = *medium;
|
|
|
|
addDevice( device );
|
|
|
|
if( m_currentDevice == m_devices.begin() || m_currentDevice == m_devices.end() )
|
|
|
|
activateDevice( m_devices.count()-1, false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::pluginSelected( const Medium *medium, const TQString plugin )
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
if( !plugin.isEmpty() )
|
|
|
|
{
|
|
|
|
debug() << "Medium id is " << medium->id() << " and plugin selected is: " << plugin << endl;
|
|
|
|
Amarok::config( "MediaBrowser" )->writeEntry( medium->id(), plugin );
|
|
|
|
|
|
|
|
bool success = true;
|
|
|
|
for( TQValueList<MediaDevice *>::iterator it = m_devices.begin();
|
|
|
|
it != m_devices.end();
|
|
|
|
it++ )
|
|
|
|
{
|
|
|
|
if( (*it)->uniqueId() == medium->id() )
|
|
|
|
{
|
|
|
|
debug() << "removing " << medium->deviceNode() << endl;
|
|
|
|
if( (*it)->isConnected() )
|
|
|
|
{
|
|
|
|
if( (*it)->disconnectDevice( false ) )
|
|
|
|
removeDevice( *it );
|
|
|
|
else
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
removeDevice( *it );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( success )
|
|
|
|
{
|
|
|
|
mediumAdded( medium, "doesntmatter", false );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
debug() << "Cannot change plugin while operation is in progress" << endl;
|
|
|
|
Amarok::StatusBar::instance()->longMessage(
|
|
|
|
i18n( "Cannot change plugin while operation is in progress" ),
|
|
|
|
KDE::StatusBar::Warning );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
debug() << "Medium id is " << medium->id() << " and you opted not to use a plugin" << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::showPluginManager()
|
|
|
|
{
|
|
|
|
MediumPluginManagerDialog* mpm = new MediumPluginManagerDialog();
|
|
|
|
mpm->exec();
|
|
|
|
delete mpm;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::mediumChanged( const Medium *medium, TQString /*name*/ )
|
|
|
|
{
|
|
|
|
if( medium )
|
|
|
|
{
|
|
|
|
for( TQValueList<MediaDevice *>::iterator it = m_devices.begin();
|
|
|
|
it != m_devices.end();
|
|
|
|
it++ )
|
|
|
|
{
|
|
|
|
if( (*it)->uniqueId() == medium->id() )
|
|
|
|
{
|
|
|
|
(*it)->m_medium = const_cast<Medium *>(medium);
|
|
|
|
if( !(*it)->isConnected() && medium->isMounted() )
|
|
|
|
(*it)->connectDevice();
|
|
|
|
#if 0
|
|
|
|
else if( (*it)->isConnected() && !medium->isMounted() )
|
|
|
|
{
|
|
|
|
Amarok::StatusBar::instance()->longMessage(
|
|
|
|
i18n( "The device %1 was unmounted before it was synchronized. "
|
|
|
|
"In order to avoid data loss, press the \"Disconnect\" button "
|
|
|
|
"before unmounting the device." ).arg( name ),
|
|
|
|
KDE::StatusBar::Warning );
|
|
|
|
//(*it)->disconnectDevice();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::mediumRemoved( const Medium *medium, TQString name )
|
|
|
|
{
|
|
|
|
if( medium )
|
|
|
|
{
|
|
|
|
for( TQValueList<MediaDevice *>::iterator it = m_devices.begin();
|
|
|
|
it != m_devices.end();
|
|
|
|
it++ )
|
|
|
|
{
|
|
|
|
if( (*it)->uniqueId() == medium->id() )
|
|
|
|
{
|
|
|
|
if( (*it)->isConnected() )
|
|
|
|
{
|
|
|
|
if( (*it)->disconnectDevice() )
|
|
|
|
removeDevice( *it );
|
|
|
|
Amarok::StatusBar::instance()->longMessage(
|
|
|
|
i18n( "The device %1 was removed before it was disconnected. "
|
|
|
|
"In order to avoid possible data loss, press the \"Disconnect\" "
|
|
|
|
"button before disconnecting the device." ).arg( name ),
|
|
|
|
KDE::StatusBar::Warning );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
removeDevice( *it );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaDevice *
|
|
|
|
MediaBrowser::loadDevicePlugin( const TQString &deviceType )
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
|
|
|
|
if( deviceType == "ignore" )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
TQString query = "[X-KDE-Amarok-plugintype] == 'mediadevice' and [X-KDE-Amarok-name] == '%1'";
|
|
|
|
Amarok::Plugin *plugin = PluginManager::createFromQuery( query.arg( deviceType ) );
|
|
|
|
|
|
|
|
if( plugin )
|
|
|
|
{
|
|
|
|
debug() << "Returning plugin!" << endl;
|
|
|
|
MediaDevice *device = static_cast<MediaDevice *>( plugin );
|
|
|
|
device->init( this );
|
|
|
|
device->m_type = deviceType;
|
|
|
|
return device;
|
|
|
|
}
|
|
|
|
|
|
|
|
debug() << "no plugin for " << deviceType << endl;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::unloadDevicePlugin( MediaDevice *device )
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
|
|
|
|
if( !device )
|
|
|
|
return;
|
|
|
|
|
|
|
|
disconnect( device ); // disconnect all signals
|
|
|
|
|
|
|
|
if( dynamic_cast<DummyMediaDevice *>(device) )
|
|
|
|
{
|
|
|
|
delete device;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PluginManager::unload( device );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaBrowser::config()
|
|
|
|
{
|
|
|
|
if( m_deviceCombo->currentText() == "No Device Selected" )
|
|
|
|
{
|
|
|
|
showPluginManager();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
DeviceConfigureDialog* dcd = new DeviceConfigureDialog( currentDevice()->m_medium );
|
|
|
|
dcd->exec();
|
|
|
|
bool successful = dcd->successful();
|
|
|
|
delete dcd;
|
|
|
|
return successful;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::configSelectPlugin( int index )
|
|
|
|
{
|
|
|
|
Q_UNUSED( index );
|
|
|
|
|
|
|
|
if( m_currentDevice == m_devices.begin() )
|
|
|
|
{
|
|
|
|
AmarokConfig::setDeviceType( m_pluginName[m_configPluginCombo->currentText()] );
|
|
|
|
}
|
|
|
|
else if( currentDevice() )
|
|
|
|
{
|
|
|
|
KConfig *config = Amarok::config( "MediaBrowser" );
|
|
|
|
config->writeEntry( currentDevice()->uniqueId(), m_pluginName[m_configPluginCombo->currentText()] );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !currentDevice() )
|
|
|
|
activateDevice( 0, false );
|
|
|
|
|
|
|
|
if( !currentDevice() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( m_pluginName[m_configPluginCombo->currentText()] != currentDevice()->deviceType() )
|
|
|
|
{
|
|
|
|
MediaDevice *dev = currentDevice();
|
|
|
|
dev->removeConfigElements( m_configBox );
|
|
|
|
if( dev->isConnected() )
|
|
|
|
{
|
|
|
|
dev->disconnectDevice( false );
|
|
|
|
}
|
|
|
|
unloadDevicePlugin( dev );
|
|
|
|
*m_currentDevice = loadDevicePlugin( AmarokConfig::deviceType() );
|
|
|
|
if( !*m_currentDevice )
|
|
|
|
{
|
|
|
|
*m_currentDevice = new DummyMediaDevice();
|
|
|
|
if( AmarokConfig::deviceType() != "dummy-mediadevice" )
|
|
|
|
{
|
|
|
|
TQString msg = i18n( "The requested media device could not be loaded" );
|
|
|
|
Amarok::StatusBar::instance()->shortMessage( msg );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dev = currentDevice();
|
|
|
|
dev->init( this );
|
|
|
|
dev->loadConfig();
|
|
|
|
|
|
|
|
m_configBox->hide();
|
|
|
|
dev->addConfigElements( m_configBox );
|
|
|
|
m_configBox->show();
|
|
|
|
|
|
|
|
dev->view()->show();
|
|
|
|
|
|
|
|
if( dev->autoConnect() )
|
|
|
|
{
|
|
|
|
dev->connectDevice( true );
|
|
|
|
updateButtons();
|
|
|
|
}
|
|
|
|
|
|
|
|
updateDevices();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::updateButtons()
|
|
|
|
{
|
|
|
|
if( !m_toolbar->getButton(CONNECT) ||
|
|
|
|
!m_toolbar->getButton(DISCONNECT) ||
|
|
|
|
!m_toolbar->getButton(TRANSFER) ) //TODO add CUSTOM
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( currentDevice() )
|
|
|
|
{
|
|
|
|
if( currentDevice()->m_transfer )
|
|
|
|
m_toolbar->showItem( TRANSFER );
|
|
|
|
else
|
|
|
|
m_toolbar->hideItem( TRANSFER );
|
|
|
|
|
|
|
|
if( currentDevice()->m_customButton )
|
|
|
|
m_toolbar->showItem( CUSTOM );
|
|
|
|
else
|
|
|
|
m_toolbar->hideItem( CUSTOM );
|
|
|
|
|
|
|
|
if( currentDevice()->m_configure )
|
|
|
|
m_toolbar->showItem( CONFIGURE );
|
|
|
|
else
|
|
|
|
m_toolbar->hideItem( CONFIGURE );
|
|
|
|
|
|
|
|
m_toolbar->getButton(CONNECT)->setEnabled( !currentDevice()->isConnected() );
|
|
|
|
m_toolbar->getButton(DISCONNECT)->setEnabled( currentDevice()->isConnected() );
|
|
|
|
m_toolbar->getButton(TRANSFER)->setEnabled( currentDevice()->isConnected() && m_queue->childCount() > 0 );
|
|
|
|
m_toolbar->getButton( CUSTOM )->setEnabled( true );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_toolbar->getButton( CONNECT )->setEnabled( false );
|
|
|
|
m_toolbar->getButton( DISCONNECT )->setEnabled( false );
|
|
|
|
m_toolbar->getButton( TRANSFER )->setEnabled( false );
|
|
|
|
m_toolbar->getButton( CUSTOM )->setEnabled( false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::updateStats()
|
|
|
|
{
|
|
|
|
if( !m_stats )
|
|
|
|
return;
|
|
|
|
|
|
|
|
KIO::filesize_t queued = m_queue->totalSize();
|
|
|
|
|
|
|
|
TQString text = i18n( "1 track in queue", "%n tracks in queue", m_queue->childCount() );
|
|
|
|
if(m_queue->childCount() > 0)
|
|
|
|
{
|
|
|
|
text += i18n(" (%1)").arg( KIO::convertSize( queued ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
KIO::filesize_t total, avail;
|
|
|
|
if( currentDevice() && currentDevice()->getCapacity(&total, &avail) )
|
|
|
|
{
|
|
|
|
text += i18n( " - %1 of %2 available" ).arg( KIO::convertSize( avail ) ).arg( KIO::convertSize( total ) );
|
|
|
|
|
|
|
|
m_stats->m_used = total-avail;
|
|
|
|
m_stats->m_total = total;
|
|
|
|
m_stats->m_scheduled = queued;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_stats->m_used = 0;
|
|
|
|
m_stats->m_total = 0;
|
|
|
|
m_stats->m_scheduled = queued;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_stats->setText(text);
|
|
|
|
TQToolTip::add( m_stats, text );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaView::setFilter( const TQString &filter, MediaItem *parent )
|
|
|
|
{
|
|
|
|
bool advanced = ExpressionParser::isAdvancedExpression( filter );
|
|
|
|
TQValueList<int> defaultColumns;
|
|
|
|
defaultColumns << MetaBundle::Album;
|
|
|
|
defaultColumns << MetaBundle::Title;
|
|
|
|
defaultColumns << MetaBundle::Artist;
|
|
|
|
|
|
|
|
bool root = false;
|
|
|
|
MediaItem *it;
|
|
|
|
if( !parent )
|
|
|
|
{
|
|
|
|
root = true;
|
|
|
|
it = dynamic_cast<MediaItem *>(firstChild());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
it = dynamic_cast<MediaItem *>(parent->firstChild());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool childrenVisible = false;
|
|
|
|
for( ; it; it = dynamic_cast<MediaItem *>(it->nextSibling()))
|
|
|
|
{
|
|
|
|
bool visible = true;
|
|
|
|
if(it->isLeafItem())
|
|
|
|
{
|
|
|
|
if( advanced )
|
|
|
|
{
|
|
|
|
ParsedExpression parsed = ExpressionParser::parse( filter );
|
|
|
|
visible = it->bundle() && it->bundle()->matchesParsedExpression( parsed, defaultColumns );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
visible = it->bundle() && it->bundle()->matchesSimpleExpression( filter, defaultColumns );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
visible = setFilter(filter, it);
|
|
|
|
if(it->type()==MediaItem::PLAYLISTSROOT || it->type()==MediaItem::PLAYLIST)
|
|
|
|
{
|
|
|
|
visible = true;
|
|
|
|
}
|
|
|
|
else if(it->type()==MediaItem::DIRECTORY)
|
|
|
|
{
|
|
|
|
bool match = true;
|
|
|
|
TQStringList list = TQStringList::split( " ", filter );
|
|
|
|
for( TQStringList::iterator i = list.begin();
|
|
|
|
i != list.end();
|
|
|
|
++i )
|
|
|
|
{
|
|
|
|
if( !(*it).text(0).contains( *i ) )
|
|
|
|
{
|
|
|
|
match = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( match )
|
|
|
|
visible = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( filter.isEmpty() )
|
|
|
|
visible = true;
|
|
|
|
it->setVisible( visible );
|
|
|
|
if(visible)
|
|
|
|
childrenVisible = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( root && m_device )
|
|
|
|
m_device->updateRootItems();
|
|
|
|
|
|
|
|
return childrenVisible;
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaDevice::MediaDevice()
|
|
|
|
: Amarok::Plugin()
|
|
|
|
, m_hasMountPoint( true )
|
|
|
|
, m_autoDeletePodcasts( false )
|
|
|
|
, m_syncStats( false )
|
|
|
|
, m_transcode( false )
|
|
|
|
, m_transcodeAlways( false )
|
|
|
|
, m_transcodeRemove( false )
|
|
|
|
, sysProc ( 0 )
|
|
|
|
, m_parent( 0 )
|
|
|
|
, m_view( 0 )
|
|
|
|
, m_wait( false )
|
|
|
|
, m_requireMount( false )
|
|
|
|
, m_canceled( false )
|
|
|
|
, m_transferring( false )
|
|
|
|
, m_deleting( false )
|
|
|
|
, m_deferredDisconnect( false )
|
|
|
|
, m_scheduledDisconnect( false )
|
|
|
|
, m_transfer( true )
|
|
|
|
, m_configure( true )
|
|
|
|
, m_customButton( false )
|
|
|
|
, m_playlistItem( 0 )
|
|
|
|
, m_podcastItem( 0 )
|
|
|
|
, m_invisibleItem( 0 )
|
|
|
|
, m_staleItem( 0 )
|
|
|
|
, m_orphanedItem( 0 )
|
|
|
|
{
|
|
|
|
sysProc = new KShellProcess(); Q_CHECK_PTR(sysProc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MediaDevice::init( MediaBrowser* parent )
|
|
|
|
{
|
|
|
|
m_parent = parent;
|
|
|
|
if( !m_view )
|
|
|
|
m_view = new MediaView( m_parent->m_views, this );
|
|
|
|
m_view->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaDevice::~MediaDevice()
|
|
|
|
{
|
|
|
|
delete m_view;
|
|
|
|
delete sysProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaDevice::isSpecialItem( MediaItem *item )
|
|
|
|
{
|
|
|
|
return (item == m_playlistItem) ||
|
|
|
|
(item == m_podcastItem) ||
|
|
|
|
(item == m_invisibleItem) ||
|
|
|
|
(item == m_staleItem) ||
|
|
|
|
(item == m_orphanedItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::loadConfig()
|
|
|
|
{
|
|
|
|
m_transcode = configBool( "Transcode" );
|
|
|
|
m_transcodeAlways = configBool( "TranscodeAlways" );
|
|
|
|
m_transcodeRemove = configBool( "TranscodeRemove" );
|
|
|
|
m_preconnectcmd = configString( "PreConnectCommand" );
|
|
|
|
if( m_preconnectcmd.isEmpty() )
|
|
|
|
m_preconnectcmd = configString( "MountCommand" );
|
|
|
|
m_postdisconnectcmd = configString( "PostDisconnectCommand" );
|
|
|
|
if( m_postdisconnectcmd.isEmpty() )
|
|
|
|
m_postdisconnectcmd = configString( "UmountCommand" );
|
|
|
|
if( m_requireMount && m_postdisconnectcmd.isEmpty() )
|
|
|
|
m_postdisconnectcmd = "kdeeject -q %d";
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString
|
|
|
|
MediaDevice::configString( const TQString &name, const TQString &defValue )
|
|
|
|
{
|
|
|
|
TQString configName = "MediaDevice";
|
|
|
|
if( !uniqueId().isEmpty() )
|
|
|
|
configName += '_' + uniqueId();
|
|
|
|
KConfig *config = Amarok::config( configName );
|
|
|
|
return config->readEntry( name, defValue );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::setConfigString( const TQString &name, const TQString &value )
|
|
|
|
{
|
|
|
|
TQString configName = "MediaDevice";
|
|
|
|
if( !uniqueId().isEmpty() )
|
|
|
|
configName += '_' + uniqueId();
|
|
|
|
KConfig *config = Amarok::config( configName );
|
|
|
|
config->writeEntry( name, value );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaDevice::configBool( const TQString &name, bool defValue )
|
|
|
|
{
|
|
|
|
TQString configName = "MediaDevice";
|
|
|
|
if( !uniqueId().isEmpty() )
|
|
|
|
configName += '_' + uniqueId();
|
|
|
|
KConfig *config = Amarok::config( configName );
|
|
|
|
return config->readBoolEntry( name, defValue );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::setConfigBool( const TQString &name, bool value )
|
|
|
|
{
|
|
|
|
TQString configName = "MediaDevice";
|
|
|
|
if( !uniqueId().isEmpty() )
|
|
|
|
configName += '_' + uniqueId();
|
|
|
|
KConfig *config = Amarok::config( configName );
|
|
|
|
config->writeEntry( name, value );
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaView *
|
|
|
|
MediaDevice::view()
|
|
|
|
{
|
|
|
|
return m_view;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::hideProgress()
|
|
|
|
{
|
|
|
|
m_parent->m_progressBox->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::updateRootItems()
|
|
|
|
{
|
|
|
|
if(m_podcastItem)
|
|
|
|
m_podcastItem->setVisible(m_podcastItem->childCount() > 0);
|
|
|
|
if(m_invisibleItem)
|
|
|
|
m_invisibleItem->setVisible(m_invisibleItem->childCount() > 0);
|
|
|
|
if(m_staleItem)
|
|
|
|
m_staleItem->setVisible(m_staleItem->childCount() > 0);
|
|
|
|
if(m_orphanedItem)
|
|
|
|
m_orphanedItem->setVisible(m_orphanedItem->childCount() > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::syncPlaylist( const TQString &name, const TQString &query, bool loading )
|
|
|
|
{
|
|
|
|
MediaItem* item = new MediaItem( this, lastItem() );
|
|
|
|
item->setType( MediaItem::PLAYLIST );
|
|
|
|
item->setExpandable( false );
|
|
|
|
item->setData( query );
|
|
|
|
item->m_playlistName = name;
|
|
|
|
item->setText( 0, name );
|
|
|
|
item->m_flags |= MediaItem::SmartPlaylist;
|
|
|
|
m_parent->m_progress->setTotalSteps( m_parent->m_progress->totalSteps() + 1 );
|
|
|
|
itemCountChanged();
|
|
|
|
if( !loading )
|
|
|
|
URLsAdded();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::syncPlaylist( const TQString &name, const KURL &url, bool loading )
|
|
|
|
{
|
|
|
|
MediaItem* item = new MediaItem( this, lastItem() );
|
|
|
|
item->setType( MediaItem::PLAYLIST );
|
|
|
|
item->setExpandable( false );
|
|
|
|
item->setData( url.url() );
|
|
|
|
item->m_playlistName = name;
|
|
|
|
item->setText( 0, name );
|
|
|
|
m_parent->m_progress->setTotalSteps( m_parent->m_progress->totalSteps() + 1 );
|
|
|
|
itemCountChanged();
|
|
|
|
if( !loading )
|
|
|
|
URLsAdded();
|
|
|
|
}
|
|
|
|
|
|
|
|
BundleList
|
|
|
|
MediaDevice::bundlesToSync( const TQString &name, const KURL &url )
|
|
|
|
{
|
|
|
|
BundleList bundles;
|
|
|
|
if( !PlaylistFile::isPlaylistFile( url ) )
|
|
|
|
{
|
|
|
|
Amarok::StatusBar::instance()->longMessage( i18n( "Not a playlist file: %1" ).arg( url.path() ),
|
|
|
|
KDE::StatusBar::Sorry );
|
|
|
|
return bundles;
|
|
|
|
}
|
|
|
|
|
|
|
|
PlaylistFile playlist( url.path() );
|
|
|
|
if( playlist.isError() )
|
|
|
|
{
|
|
|
|
Amarok::StatusBar::instance()->longMessage( i18n( "Failed to load playlist: %1" ).arg( url.path() ),
|
|
|
|
KDE::StatusBar::Sorry );
|
|
|
|
return bundles;
|
|
|
|
}
|
|
|
|
|
|
|
|
for( BundleList::iterator it = playlist.bundles().begin();
|
|
|
|
it != playlist.bundles().end();
|
|
|
|
++it )
|
|
|
|
{
|
|
|
|
bundles += MetaBundle( (*it).url() );
|
|
|
|
}
|
|
|
|
preparePlaylistForSync( name, bundles );
|
|
|
|
return bundles;
|
|
|
|
}
|
|
|
|
|
|
|
|
BundleList
|
|
|
|
MediaDevice::bundlesToSync( const TQString &name, const TQString &query )
|
|
|
|
{
|
|
|
|
const TQStringList values = CollectionDB::instance()->query( query );
|
|
|
|
|
|
|
|
BundleList bundles;
|
|
|
|
for( for_iterators( TQStringList, values ); it != end; ++it )
|
|
|
|
bundles += CollectionDB::instance()->bundleFromQuery( &it );
|
|
|
|
preparePlaylistForSync( name, bundles );
|
|
|
|
return bundles;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::preparePlaylistForSync( const TQString &name, const BundleList &bundles )
|
|
|
|
{
|
|
|
|
if( ! m_playlistItem ) // might be syncing a new playlist from the playlist browser
|
|
|
|
return;
|
|
|
|
MediaItem *pl = m_playlistItem->findItem( name );
|
|
|
|
if( pl )
|
|
|
|
{
|
|
|
|
MediaItem *next = 0;
|
|
|
|
for( MediaItem *it = static_cast<MediaItem *>(pl->firstChild());
|
|
|
|
it;
|
|
|
|
it = next )
|
|
|
|
{
|
|
|
|
next = static_cast<MediaItem *>(it->nextSibling());
|
|
|
|
const MetaBundle *bundle = (*it).bundle();
|
|
|
|
if( !bundle )
|
|
|
|
continue;
|
|
|
|
if( isOnOtherPlaylist( name, *bundle ) )
|
|
|
|
continue;
|
|
|
|
if( isInBundleList( bundles, *bundle ) )
|
|
|
|
continue;
|
|
|
|
deleteItemFromDevice( it );
|
|
|
|
}
|
|
|
|
deleteItemFromDevice( pl, None );
|
|
|
|
}
|
|
|
|
purgeEmptyItems();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaDevice::bundleMatch( const MetaBundle &b1, const MetaBundle &b2 )
|
|
|
|
{
|
|
|
|
if( b1.track() != b2.track() )
|
|
|
|
return false;
|
|
|
|
if( b1.title() != b2.title() )
|
|
|
|
return false;
|
|
|
|
if( b1.album() != b2.album() )
|
|
|
|
return false;
|
|
|
|
if( b1.artist() != b2.artist() )
|
|
|
|
return false;
|
|
|
|
#if 0
|
|
|
|
if( b1.discNumber() != b2.discNumber() )
|
|
|
|
return false;
|
|
|
|
if( b1.composer() != b2.composer() )
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaDevice::isInBundleList( const BundleList &bundles, const MetaBundle &b )
|
|
|
|
{
|
|
|
|
for( BundleList::const_iterator it = bundles.begin();
|
|
|
|
it != bundles.end();
|
|
|
|
++it )
|
|
|
|
{
|
|
|
|
if( bundleMatch( b, *it ) )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaDevice::isOnOtherPlaylist( const TQString &playlistToAvoid, const MetaBundle &bundle )
|
|
|
|
{
|
|
|
|
for( MediaItem *it = static_cast<MediaItem *>(m_playlistItem->firstChild());
|
|
|
|
it;
|
|
|
|
it = static_cast<MediaItem *>(it->nextSibling()) )
|
|
|
|
{
|
|
|
|
if( it->text( 0 ) == playlistToAvoid )
|
|
|
|
continue;
|
|
|
|
if( isOnPlaylist( *it, bundle ) )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaDevice::isOnPlaylist( const MediaItem &playlist, const MetaBundle &bundle )
|
|
|
|
{
|
|
|
|
for( MediaItem *it = static_cast<MediaItem *>(playlist.firstChild());
|
|
|
|
it;
|
|
|
|
it = static_cast<MediaItem *>(it->nextSibling()) )
|
|
|
|
{
|
|
|
|
const MetaBundle *b = (*it).bundle();
|
|
|
|
if( !b )
|
|
|
|
continue;
|
|
|
|
if( bundleMatch( *b, bundle ) )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::addURL( const KURL& url2, MetaBundle *bundle, const TQString &playlistName )
|
|
|
|
{
|
|
|
|
KURL url = Amarok::mostLocalURL( url2 );
|
|
|
|
|
|
|
|
if( PlaylistFile::isPlaylistFile( url ) )
|
|
|
|
{
|
|
|
|
TQString name = TQString(url.path().section( "/", -1 ).section( ".", 0, -2 )).replace( "_", " " );
|
|
|
|
PlaylistFile playlist( url.path() );
|
|
|
|
|
|
|
|
if( playlist.isError() )
|
|
|
|
{
|
|
|
|
Amarok::StatusBar::instance()->longMessage( i18n( "Failed to load playlist: %1" ).arg( url.path() ),
|
|
|
|
KDE::StatusBar::Sorry );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for( BundleList::iterator it = playlist.bundles().begin();
|
|
|
|
it != playlist.bundles().end();
|
|
|
|
++it )
|
|
|
|
{
|
|
|
|
addURL( (*it).url(), 0, name );
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if( ContextBrowser::hasContextProtocol( url ) )
|
|
|
|
{
|
|
|
|
KURL::List urls = ContextBrowser::expandURL( url );
|
|
|
|
|
|
|
|
for( KURL::List::iterator it = urls.begin();
|
|
|
|
it != urls.end();
|
|
|
|
++it )
|
|
|
|
{
|
|
|
|
addURL( *it );
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if( url.protocol() == "file" && TQFileInfo( url.path() ).isDir() )
|
|
|
|
{
|
|
|
|
KURL::List urls = Amarok::recursiveUrlExpand( url );
|
|
|
|
foreachType( KURL::List, urls )
|
|
|
|
addURL( *it );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( playlistName.isNull() )
|
|
|
|
{
|
|
|
|
for( MediaItem *it = static_cast<MediaItem *>(firstChild());
|
|
|
|
it;
|
|
|
|
it = static_cast<MediaItem *>(it->nextSibling()) )
|
|
|
|
{
|
|
|
|
if( it->url() == url )
|
|
|
|
{
|
|
|
|
Amarok::StatusBar::instance()->shortMessage(
|
|
|
|
i18n( "Track already queued for transfer: %1" ).arg( url.url() ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!bundle)
|
|
|
|
bundle = new MetaBundle( url );
|
|
|
|
|
|
|
|
MediaItem* item = new MediaItem( this, lastItem() );
|
|
|
|
item->setExpandable( false );
|
|
|
|
item->setDropEnabled( true );
|
|
|
|
item->setBundle( bundle );
|
|
|
|
if(bundle->podcastBundle() )
|
|
|
|
{
|
|
|
|
item->setType( MediaItem::PODCASTITEM );
|
|
|
|
}
|
|
|
|
item->m_playlistName = playlistName;
|
|
|
|
|
|
|
|
TQString text = item->bundle()->prettyTitle();
|
|
|
|
if( text.isEmpty() || (!item->bundle()->isValidMedia() && !item->bundle()->podcastBundle()) )
|
|
|
|
text = item->bundle()->url().prettyURL();
|
|
|
|
if( !item->m_playlistName.isNull() )
|
|
|
|
{
|
|
|
|
text += " (" + item->m_playlistName + ')';
|
|
|
|
}
|
|
|
|
item->setText( 0, text);
|
|
|
|
|
|
|
|
m_parent->updateButtons();
|
|
|
|
m_parent->m_progress->setTotalSteps( m_parent->m_progress->totalSteps() + 1 );
|
|
|
|
addItemToSize( item );
|
|
|
|
itemCountChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::addURL( const KURL &url, MediaItem *item )
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
MediaItem *newitem = new MediaItem( this, lastItem() );
|
|
|
|
newitem->setExpandable( false );
|
|
|
|
newitem->setDropEnabled( true );
|
|
|
|
MetaBundle *bundle = new MetaBundle( *item->bundle() );
|
|
|
|
KURL filepath(url);
|
|
|
|
filepath.addPath( bundle->filename() );
|
|
|
|
bundle->setUrl( filepath );
|
|
|
|
newitem->m_device = item->m_device;
|
|
|
|
if(bundle->podcastBundle() )
|
|
|
|
{
|
|
|
|
item->setType( MediaItem::PODCASTITEM );
|
|
|
|
}
|
|
|
|
TQString text = item->bundle()->prettyTitle();
|
|
|
|
if( text.isEmpty() || (!item->bundle()->isValidMedia() && !item->bundle()->podcastBundle()) )
|
|
|
|
text = item->bundle()->url().prettyURL();
|
|
|
|
if( item->m_playlistName != TQString() )
|
|
|
|
{
|
|
|
|
text += " (" + item->m_playlistName + ')';
|
|
|
|
}
|
|
|
|
newitem->setText( 0, text);
|
|
|
|
newitem->setBundle( bundle );
|
|
|
|
m_parent->updateButtons();
|
|
|
|
m_parent->m_progress->setTotalSteps( m_parent->m_progress->totalSteps() + 1 );
|
|
|
|
addItemToSize( item );
|
|
|
|
itemCountChanged();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::addURLs( const KURL::List urls, const TQString &playlistName )
|
|
|
|
{
|
|
|
|
KURL::List::ConstIterator it = urls.begin();
|
|
|
|
for ( ; it != urls.end(); ++it )
|
|
|
|
addURL( *it, 0, playlistName );
|
|
|
|
|
|
|
|
URLsAdded();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::URLsAdded()
|
|
|
|
{
|
|
|
|
m_parent->updateStats();
|
|
|
|
m_parent->updateButtons();
|
|
|
|
if( m_parent->currentDevice()
|
|
|
|
&& m_parent->currentDevice()->isConnected()
|
|
|
|
&& m_parent->currentDevice()->asynchronousTransfer()
|
|
|
|
&& !m_parent->currentDevice()->isTransferring() )
|
|
|
|
m_parent->currentDevice()->transferFiles();
|
|
|
|
|
|
|
|
save( Amarok::saveLocation() + "transferlist.xml" );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::copyTrackFromDevice( MediaItem *item )
|
|
|
|
{
|
|
|
|
debug() << "copyTrackFromDevice: not copying " << item->url() << ": not implemented" << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQDragObject *
|
|
|
|
MediaQueue::dragObject()
|
|
|
|
{
|
|
|
|
KURL::List urls;
|
|
|
|
for( TQListViewItem *it = firstChild(); it; it = it->nextSibling() )
|
|
|
|
{
|
|
|
|
if( it->isVisible() && it->isSelected() && dynamic_cast<MediaItem *>(it) )
|
|
|
|
urls += static_cast<MediaItem *>(it)->url();
|
|
|
|
}
|
|
|
|
|
|
|
|
KMultipleDrag *md = new KMultipleDrag( viewport() );
|
|
|
|
TQDragObject *d = KListView::dragObject();
|
|
|
|
KURLDrag* urldrag = new KURLDrag( urls, viewport() );
|
|
|
|
md->addDragObject( d );
|
|
|
|
md->addDragObject( urldrag );
|
|
|
|
md->setPixmap( CollectionDB::createDragPixmap( urls ),
|
|
|
|
TQPoint( CollectionDB::DRAGPIXMAP_OFFSET_X, CollectionDB::DRAGPIXMAP_OFFSET_Y ) );
|
|
|
|
return md;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString
|
|
|
|
MediaDevice::replaceVariables( const TQString &cmd )
|
|
|
|
{
|
|
|
|
TQString result = cmd;
|
|
|
|
result.replace( "%d", deviceNode() );
|
|
|
|
result.replace( "%m", mountPoint() );
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MediaDevice::runPreConnectCommand()
|
|
|
|
{
|
|
|
|
if( m_preconnectcmd.isEmpty() )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
TQString cmd = replaceVariables( m_preconnectcmd );
|
|
|
|
|
|
|
|
debug() << "running pre-connect command: [" << cmd << "]" << endl;
|
|
|
|
int e=sysCall(cmd);
|
|
|
|
debug() << "pre-connect: e=" << e << endl;
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MediaDevice::runPostDisconnectCommand()
|
|
|
|
{
|
|
|
|
if( m_postdisconnectcmd.isEmpty() )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
TQString cmd = replaceVariables( m_postdisconnectcmd );
|
|
|
|
debug() << "running post-disconnect command: [" << cmd << "]" << endl;
|
|
|
|
int e=sysCall(cmd);
|
|
|
|
debug() << "post-disconnect: e=" << e << endl;
|
|
|
|
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MediaDevice::sysCall( const TQString &command )
|
|
|
|
{
|
|
|
|
if ( sysProc->isRunning() ) return -1;
|
|
|
|
|
|
|
|
sysProc->clearArguments();
|
|
|
|
(*sysProc) << command;
|
|
|
|
if (!sysProc->start( KProcess::Block, KProcess::AllOutput ))
|
|
|
|
kdFatal() << i18n("could not execute %1").arg(command.local8Bit().data()) << endl;
|
|
|
|
|
|
|
|
return (sysProc->exitStatus());
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::abortTransfer()
|
|
|
|
{
|
|
|
|
setCanceled( true );
|
|
|
|
cancelTransfer();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaDevice::kioCopyTrack( const KURL &src, const KURL &dst )
|
|
|
|
{
|
|
|
|
m_wait = true;
|
|
|
|
|
|
|
|
KIO::FileCopyJob *job = KIO::file_copy( src, dst,
|
|
|
|
-1 /* permissions */,
|
|
|
|
false /* overwrite */,
|
|
|
|
false /* resume */,
|
|
|
|
false /* show progress */ );
|
|
|
|
connect( job, TQT_SIGNAL( result( KIO::Job * ) ),
|
|
|
|
this, TQT_SLOT( fileTransferred( KIO::Job * ) ) );
|
|
|
|
|
|
|
|
bool tryToRemove = false;
|
|
|
|
while ( m_wait )
|
|
|
|
{
|
|
|
|
if( isCanceled() )
|
|
|
|
{
|
|
|
|
job->kill( false /* still emit result */ );
|
|
|
|
tryToRemove = true;
|
|
|
|
m_wait = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
usleep(10000);
|
|
|
|
kapp->processEvents( 100 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !tryToRemove )
|
|
|
|
{
|
|
|
|
if(m_copyFailed)
|
|
|
|
{
|
|
|
|
tryToRemove = true;
|
|
|
|
Amarok::StatusBar::instance()->longMessage(
|
|
|
|
i18n( "Media Device: Copying %1 to %2 failed" )
|
|
|
|
.arg( src.prettyURL(), dst.prettyURL() ),
|
|
|
|
KDE::StatusBar::Error );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MetaBundle bundle2(dst);
|
|
|
|
if(!bundle2.isValidMedia() && bundle2.filesize()==MetaBundle::Undetermined)
|
|
|
|
{
|
|
|
|
tryToRemove = true;
|
|
|
|
// probably s.th. went wrong
|
|
|
|
Amarok::StatusBar::instance()->longMessage(
|
|
|
|
i18n( "Media Device: Reading tags from %1 failed" ).arg( dst.prettyURL() ),
|
|
|
|
KDE::StatusBar::Error );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( tryToRemove )
|
|
|
|
{
|
|
|
|
TQFile::remove( dst.path() );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::fileTransferred( KIO::Job *job ) //SLOT
|
|
|
|
{
|
|
|
|
if(job->error())
|
|
|
|
{
|
|
|
|
m_copyFailed = true;
|
|
|
|
debug() << "file transfer failed: " << job->errorText() << endl;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_copyFailed = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_wait = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::cancelClicked()
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
|
|
|
|
m_waitForTranscode = false;
|
|
|
|
if( currentDevice() )
|
|
|
|
currentDevice()->abortTransfer();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::transferClicked()
|
|
|
|
{
|
|
|
|
m_toolbar->getButton(TRANSFER)->setEnabled( false );
|
|
|
|
if( currentDevice()
|
|
|
|
&& currentDevice()->isConnected()
|
|
|
|
&& !currentDevice()->isTransferring() )
|
|
|
|
{
|
|
|
|
if( !currentDevice()->hasTransferDialog() )
|
|
|
|
currentDevice()->transferFiles();
|
|
|
|
else
|
|
|
|
{
|
|
|
|
currentDevice()->runTransferDialog();
|
|
|
|
//may not work with non-TransferDialog-class object, but maybe some run time introspection could solve it?
|
|
|
|
if( currentDevice()->getTransferDialog() &&
|
|
|
|
( reinterpret_cast<TransferDialog *>(currentDevice()->getTransferDialog()))->isAccepted() )
|
|
|
|
currentDevice()->transferFiles();
|
|
|
|
else
|
|
|
|
updateButtons();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
currentDevice()->m_transferDir = currentDevice()->m_medium.mountPoint();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::connectClicked()
|
|
|
|
{
|
|
|
|
bool haveToConfig = false;
|
|
|
|
// it was just clicked, so isOn() == true.
|
|
|
|
if( currentDevice() && !currentDevice()->isConnected() )
|
|
|
|
{
|
|
|
|
haveToConfig = !currentDevice()->connectDevice();
|
|
|
|
}
|
|
|
|
|
|
|
|
haveToConfig |= !currentDevice();
|
|
|
|
haveToConfig |= ( currentDevice() && !currentDevice()->isConnected() );
|
|
|
|
|
|
|
|
if ( !currentDevice()->needsManualConfig() )
|
|
|
|
haveToConfig = false;
|
|
|
|
|
|
|
|
if( haveToConfig && *m_devices.at( 0 ) == currentDevice() )
|
|
|
|
{
|
|
|
|
if( config() && currentDevice() && !currentDevice()->isConnected() )
|
|
|
|
currentDevice()->connectDevice();
|
|
|
|
}
|
|
|
|
|
|
|
|
updateDevices();
|
|
|
|
updateButtons();
|
|
|
|
updateStats();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::disconnectClicked()
|
|
|
|
{
|
|
|
|
if( currentDevice() && currentDevice()->isTransferring() )
|
|
|
|
{
|
|
|
|
int action = KMessageBox::questionYesNoCancel( MediaBrowser::instance(),
|
|
|
|
i18n( "Transfer in progress. Finish or stop after current track?" ),
|
|
|
|
i18n( "Stop Transfer?" ),
|
|
|
|
KGuiItem(i18n("&Finish"), "goto"),
|
|
|
|
KGuiItem(i18n("&Stop"), "player_eject") );
|
|
|
|
if( action == KMessageBox::Cancel )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if( action == KMessageBox::Yes )
|
|
|
|
{
|
|
|
|
currentDevice()->scheduleDisconnect();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_toolbar->getButton(TRANSFER)->setEnabled( false );
|
|
|
|
m_toolbar->getButton(DISCONNECT)->setEnabled( false );
|
|
|
|
|
|
|
|
if( currentDevice() )
|
|
|
|
{
|
|
|
|
currentDevice()->disconnectDevice( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
updateDevices();
|
|
|
|
updateButtons();
|
|
|
|
updateStats();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaBrowser::customClicked()
|
|
|
|
{
|
|
|
|
if( currentDevice() )
|
|
|
|
currentDevice()->customClicked();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaDevice::connectDevice( bool silent )
|
|
|
|
{
|
|
|
|
if( !lockDevice( true ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
runPreConnectCommand();
|
|
|
|
openDevice( silent );
|
|
|
|
|
|
|
|
if( isConnected()
|
|
|
|
&& MediaBrowser::instance()->currentDevice() != this
|
|
|
|
&& MediaBrowser::instance()->currentDevice()
|
|
|
|
&& !MediaBrowser::instance()->currentDevice()->isConnected() )
|
|
|
|
{
|
|
|
|
MediaBrowser::instance()->activateDevice( this );
|
|
|
|
}
|
|
|
|
m_parent->updateStats();
|
|
|
|
m_parent->updateButtons();
|
|
|
|
|
|
|
|
if( !isConnected() )
|
|
|
|
{
|
|
|
|
unlockDevice();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( m_syncStats )
|
|
|
|
{
|
|
|
|
syncStatsFromDevice( 0 );
|
|
|
|
Scrobbler::instance()->m_submitter->syncComplete();
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete podcasts already played
|
|
|
|
if( m_autoDeletePodcasts && m_podcastItem )
|
|
|
|
{
|
|
|
|
TQPtrList<MediaItem> list;
|
|
|
|
//NOTE we assume that currentItem is the main target
|
|
|
|
int numFiles = m_view->getSelectedLeaves( m_podcastItem, &list, MediaView::OnlyPlayed );
|
|
|
|
|
|
|
|
if(numFiles > 0)
|
|
|
|
{
|
|
|
|
m_parent->m_stats->setText( i18n( "1 track to be deleted", "%n tracks to be deleted", numFiles ) );
|
|
|
|
|
|
|
|
setProgress( 0, numFiles );
|
|
|
|
|
|
|
|
int numDeleted = deleteItemFromDevice( m_podcastItem, true );
|
|
|
|
purgeEmptyItems();
|
|
|
|
if( numDeleted < 0 )
|
|
|
|
{
|
|
|
|
Amarok::StatusBar::instance()->longMessage(
|
|
|
|
i18n( "Failed to purge podcasts already played" ),
|
|
|
|
KDE::StatusBar::Sorry );
|
|
|
|
}
|
|
|
|
else if( numDeleted > 0 )
|
|
|
|
{
|
|
|
|
Amarok::StatusBar::instance()->shortMessage(
|
|
|
|
i18n( "Purged 1 podcasts already played",
|
|
|
|
"Purged %n podcasts already played",
|
|
|
|
numDeleted ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
synchronizeDevice();
|
|
|
|
|
|
|
|
TQTimer::singleShot( 1500, m_parent->m_progressBox, TQT_SLOT(hide()) );
|
|
|
|
m_parent->queue()->computeSize();
|
|
|
|
m_parent->updateStats();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unlockDevice();
|
|
|
|
|
|
|
|
updateRootItems();
|
|
|
|
|
|
|
|
if( m_deferredDisconnect )
|
|
|
|
{
|
|
|
|
m_deferredDisconnect = false;
|
|
|
|
disconnectDevice( m_runDisconnectHook );
|
|
|
|
}
|
|
|
|
|
|
|
|
Amarok::StatusBar::instance()->shortMessage( i18n( "Device successfully connected" ) );
|
|
|
|
|
|
|
|
m_parent->updateDevices();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaDevice::disconnectDevice( bool postDisconnectHook )
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
|
|
|
|
abortTransfer();
|
|
|
|
|
|
|
|
debug() << "disconnecting: hook=" << postDisconnectHook << endl;
|
|
|
|
|
|
|
|
if( !lockDevice( true ) )
|
|
|
|
{
|
|
|
|
m_runDisconnectHook = postDisconnectHook;
|
|
|
|
m_deferredDisconnect = true;
|
|
|
|
debug() << "disconnecting: locked" << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
debug() << "disconnecting: ok" << endl;
|
|
|
|
|
|
|
|
if( m_syncStats )
|
|
|
|
{
|
|
|
|
syncStatsToDevice();
|
|
|
|
}
|
|
|
|
|
|
|
|
closeDevice();
|
|
|
|
unlockDevice();
|
|
|
|
|
|
|
|
m_parent->updateStats();
|
|
|
|
|
|
|
|
bool result = true;
|
|
|
|
if( postDisconnectHook && runPostDisconnectCommand() != 0 )
|
|
|
|
{
|
|
|
|
Amarok::StatusBar::instance()->longMessage(
|
|
|
|
i18n( "Post-disconnect command failed, before removing device, please make sure that it is safe to do so." ),
|
|
|
|
KDE::StatusBar::Information );
|
|
|
|
result = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Amarok::StatusBar::instance()->shortMessage( i18n( "Device successfully disconnected" ) );
|
|
|
|
|
|
|
|
m_parent->updateDevices();
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::syncStatsFromDevice( MediaItem *root )
|
|
|
|
{
|
|
|
|
MediaItem *it = static_cast<MediaItem *>( m_view->firstChild() );
|
|
|
|
if( root )
|
|
|
|
{
|
|
|
|
it = static_cast<MediaItem *>( root->firstChild() );
|
|
|
|
}
|
|
|
|
|
|
|
|
kapp->processEvents( 100 );
|
|
|
|
|
|
|
|
for( ; it; it = static_cast<MediaItem *>( it->nextSibling() ) )
|
|
|
|
{
|
|
|
|
switch( it->type() )
|
|
|
|
{
|
|
|
|
case MediaItem::TRACK:
|
|
|
|
if( !it->parent() || static_cast<MediaItem *>( it->parent() )->type() != MediaItem::PLAYLIST )
|
|
|
|
{
|
|
|
|
const MetaBundle *bundle = it->bundle();
|
|
|
|
for( int i=0; i<it->recentlyPlayed(); i++ )
|
|
|
|
{
|
|
|
|
// submit to last.fm
|
|
|
|
if( bundle->length() > 30
|
|
|
|
&& !bundle->artist().isEmpty() && bundle->artist() != i18n( "Unknown" )
|
|
|
|
&& !bundle->title().isEmpty() && bundle->title() != i18n( "Unknown" ) )
|
|
|
|
{
|
|
|
|
// don't submit tracks shorter than 30 sec or w/o artist/title
|
|
|
|
debug() << "scrobbling " << bundle->artist() << " - " << bundle->title() << endl;
|
|
|
|
SubmitItem *sit = new SubmitItem( bundle->artist(), bundle->album(), bundle->title(), bundle->length(), false /* fake time */ );
|
|
|
|
Scrobbler::instance()->m_submitter->submitItem( sit );
|
|
|
|
}
|
|
|
|
|
|
|
|
// increase Amarok playcount
|
|
|
|
TQString url = CollectionDB::instance()->getURL( *bundle );
|
|
|
|
if( url != TQString() )
|
|
|
|
{
|
|
|
|
TQDateTime t = it->playTime();
|
|
|
|
CollectionDB::instance()->addSongPercentage( url, 100, "mediadevice", t.isValid() ? &t : 0 );
|
|
|
|
debug() << "played " << url << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( it->ratingChanged() )
|
|
|
|
{
|
|
|
|
// copy rating from media device to Amarok
|
|
|
|
TQString url = CollectionDB::instance()->getURL( *bundle );
|
|
|
|
debug() << "rating changed " << url << ": " << it->rating()/10 << endl;
|
|
|
|
if( url != TQString() )
|
|
|
|
{
|
|
|
|
CollectionDB::instance()->setSongRating( url, it->rating()/10 );
|
|
|
|
it->setRating( it->rating() ); // prevent setting it again next time
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MediaItem::PODCASTITEM:
|
|
|
|
if( !it->parent() || static_cast<MediaItem *>( it->parent() )->type() != MediaItem::PLAYLIST )
|
|
|
|
{
|
|
|
|
const MetaBundle *bundle = it->bundle();
|
|
|
|
if( it->played() || it->recentlyPlayed() )
|
|
|
|
{
|
|
|
|
if( PodcastEpisodeBundle *peb = bundle->podcastBundle() )
|
|
|
|
{
|
|
|
|
debug() << "marking podcast episode as played: " << peb->url() << endl;
|
|
|
|
if( PlaylistBrowser::instance() )
|
|
|
|
{
|
|
|
|
PodcastEpisode *p = PlaylistBrowser::instance()->findPodcastEpisode( peb->url(), peb->parent() );
|
|
|
|
if ( p )
|
|
|
|
p->setListened();
|
|
|
|
else
|
|
|
|
debug() << "did not find podcast episode: " << peb->url() << " from " << peb->parent() << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
syncStatsFromDevice( it );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaItem::syncStatsFromPath( const TQString &url )
|
|
|
|
{
|
|
|
|
if( url.isEmpty() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// copy Amarok rating, play count and last played time to device
|
|
|
|
int rating = CollectionDB::instance()->getSongRating( url )*10;
|
|
|
|
if( rating )
|
|
|
|
setRating( rating );
|
|
|
|
int playcount = CollectionDB::instance()->getPlayCount( url );
|
|
|
|
if( playcount > played() )
|
|
|
|
setPlayCount( playcount );
|
|
|
|
TQDateTime lastplay = CollectionDB::instance()->getLastPlay( url );
|
|
|
|
if( lastplay > playTime() )
|
|
|
|
setLastPlayed( lastplay.toTime_t() );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::syncStatsToDevice( MediaItem *root )
|
|
|
|
{
|
|
|
|
MediaItem *it = static_cast<MediaItem *>( m_view->firstChild() );
|
|
|
|
if( root )
|
|
|
|
{
|
|
|
|
it = static_cast<MediaItem *>( root->firstChild() );
|
|
|
|
}
|
|
|
|
|
|
|
|
kapp->processEvents( 100 );
|
|
|
|
|
|
|
|
for( ; it; it = static_cast<MediaItem *>( it->nextSibling() ) )
|
|
|
|
{
|
|
|
|
switch( it->type() )
|
|
|
|
{
|
|
|
|
case MediaItem::TRACK:
|
|
|
|
if( !it->parent() || static_cast<MediaItem *>( it->parent() )->type() != MediaItem::PLAYLIST )
|
|
|
|
{
|
|
|
|
const MetaBundle *bundle = it->bundle();
|
|
|
|
TQString url = CollectionDB::instance()->getURL( *bundle );
|
|
|
|
it->syncStatsFromPath( url );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MediaItem::PODCASTITEM:
|
|
|
|
if( !it->parent() || static_cast<MediaItem *>( it->parent() )->type() != MediaItem::PLAYLIST )
|
|
|
|
{
|
|
|
|
const MetaBundle *bundle = it->bundle();
|
|
|
|
if( PodcastEpisodeBundle *peb = bundle->podcastBundle() )
|
|
|
|
{
|
|
|
|
if( PlaylistBrowser::instance() )
|
|
|
|
{
|
|
|
|
PodcastEpisode *p = PlaylistBrowser::instance()->findPodcastEpisode( peb->url(), peb->parent() );
|
|
|
|
if( p )
|
|
|
|
it->setListened( !p->isNew() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
syncStatsToDevice( it );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::transferFiles()
|
|
|
|
{
|
|
|
|
if( !lockDevice( true ) )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
setCanceled( false );
|
|
|
|
|
|
|
|
m_transferring = true;
|
|
|
|
m_parent->m_toolbar->getButton(MediaBrowser::TRANSFER)->setEnabled( false );
|
|
|
|
|
|
|
|
setProgress( 0, m_parent->m_queue->childCount() );
|
|
|
|
|
|
|
|
// ok, let's copy the stuff to the device
|
|
|
|
|
|
|
|
KURL::List existing, unplayable;
|
|
|
|
unsigned transcodeFail = 0;
|
|
|
|
// iterate through items
|
|
|
|
MediaItem *next = static_cast<MediaItem *>(m_parent->m_queue->firstChild());
|
|
|
|
while( next )
|
|
|
|
{
|
|
|
|
MediaItem *transferredItem = next;
|
|
|
|
transferredItem->setFailed( false );
|
|
|
|
transferredItem->m_flags |= MediaItem::Transferring;
|
|
|
|
next = static_cast<MediaItem *>( transferredItem->nextSibling() );
|
|
|
|
|
|
|
|
if( transferredItem->device() )
|
|
|
|
{
|
|
|
|
transferredItem->device()->copyTrackFromDevice( transferredItem );
|
|
|
|
m_parent->m_queue->subtractItemFromSize( transferredItem, true );
|
|
|
|
delete transferredItem;
|
|
|
|
setProgress( progress() + 1 );
|
|
|
|
m_parent->m_queue->itemCountChanged();
|
|
|
|
kapp->processEvents( 100 );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
BundleList bundles;
|
|
|
|
if( transferredItem->type() == MediaItem::PLAYLIST )
|
|
|
|
{
|
|
|
|
if( transferredItem->flags() & MediaItem::SmartPlaylist )
|
|
|
|
bundles = bundlesToSync( transferredItem->text( 0 ), transferredItem->data() );
|
|
|
|
else
|
|
|
|
bundles = bundlesToSync( transferredItem->text( 0 ), KURL::fromPathOrURL( transferredItem->data() ) );
|
|
|
|
}
|
|
|
|
else if( transferredItem->bundle() )
|
|
|
|
bundles += *transferredItem->bundle();
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// this should not happen
|
|
|
|
debug() << "invalid item in transfer queue" << endl;
|
|
|
|
m_parent->m_queue->subtractItemFromSize( transferredItem, true );
|
|
|
|
delete transferredItem;
|
|
|
|
m_parent->m_queue->itemCountChanged();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( bundles.count() > 1 )
|
|
|
|
setProgress( progress(), MediaBrowser::instance()->m_progress->totalSteps() + bundles.count() - 1 );
|
|
|
|
|
|
|
|
TQString playlist = transferredItem->m_playlistName;
|
|
|
|
for( BundleList::const_iterator it = bundles.begin();
|
|
|
|
it != bundles.end();
|
|
|
|
++it )
|
|
|
|
{
|
|
|
|
if( isCanceled() )
|
|
|
|
break;
|
|
|
|
|
|
|
|
const MetaBundle *bundle = &(*it);
|
|
|
|
|
|
|
|
bool transcoding = false;
|
|
|
|
MediaItem *item = trackExists( *bundle );
|
|
|
|
if( item && playlist.isEmpty() )
|
|
|
|
{
|
|
|
|
Amarok::StatusBar::instance()->shortMessage( i18n( "Track already on media device: %1" ).
|
|
|
|
arg( (*it).url().prettyURL() ),
|
|
|
|
KDE::StatusBar::Sorry );
|
|
|
|
existing += (*it).url();
|
|
|
|
setProgress( progress() + 1 );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if( !item ) // the item does not yet exist on the media device
|
|
|
|
{
|
|
|
|
if( m_transcode && ( !isPlayable( *bundle ) || m_transcodeAlways ) )
|
|
|
|
{
|
|
|
|
TQString preferred = supportedFiletypes().isEmpty() ? "mp3" : supportedFiletypes().first();
|
|
|
|
debug() << "transcoding " << bundle->url() << " to " << preferred << endl;
|
|
|
|
KURL transcoded = MediaBrowser::instance()->transcode( bundle->url(), preferred );
|
|
|
|
if( isCanceled() )
|
|
|
|
break;
|
|
|
|
if( transcoded.isEmpty() )
|
|
|
|
{
|
|
|
|
debug() << "transcoding failed" << endl;
|
|
|
|
transcodeFail++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
transcoding = true;
|
|
|
|
MetaBundle *transcodedBundle = new MetaBundle( transcoded );
|
|
|
|
transcodedBundle->setArtist( bundle->artist() );
|
|
|
|
transcodedBundle->setTitle( bundle->title() );
|
|
|
|
transcodedBundle->setComposer( bundle->composer() );
|
|
|
|
transcodedBundle->setAlbum( bundle->album() );
|
|
|
|
transcodedBundle->setGenre( bundle->genre() );
|
|
|
|
transcodedBundle->setComment( bundle->comment() );
|
|
|
|
transcodedBundle->setYear( bundle->year() );
|
|
|
|
transcodedBundle->setDiscNumber( bundle->discNumber() );
|
|
|
|
transcodedBundle->setTrack( bundle->track() );
|
|
|
|
if( bundle->podcastBundle() )
|
|
|
|
{
|
|
|
|
transcodedBundle->setPodcastBundle( *bundle->podcastBundle() );
|
|
|
|
transcodedBundle->copyFrom( *bundle->podcastBundle() );
|
|
|
|
//change the extension on the localUrl in the podcastbundle
|
|
|
|
//to make sure it ends up with the correct extension
|
|
|
|
//in the generic mediadevice.
|
|
|
|
KURL localUrl = transcodedBundle->podcastBundle()->localUrl();
|
|
|
|
TQString filename = TQFileInfo( localUrl.path() ).baseName() + '.' + preferred;
|
|
|
|
localUrl.setFileName( filename );
|
|
|
|
transcodedBundle->podcastBundle()->setLocalURL( localUrl );
|
|
|
|
}
|
|
|
|
bundle = transcodedBundle;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !isPlayable( *bundle ) )
|
|
|
|
{
|
|
|
|
Amarok::StatusBar::instance()->shortMessage( i18n( "Track not playable on media device: %1" ).arg( bundle->url().path() ),
|
|
|
|
KDE::StatusBar::Sorry );
|
|
|
|
unplayable += (*it).url();
|
|
|
|
transferredItem->setFailed();
|
|
|
|
if( transcoding )
|
|
|
|
{
|
|
|
|
delete bundle;
|
|
|
|
bundle = 0;
|
|
|
|
}
|
|
|
|
setProgress( progress() + 1 );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
item = copyTrackToDevice( *bundle );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !item ) // copyTrackToDevice() failed
|
|
|
|
{
|
|
|
|
if( !isCanceled() )
|
|
|
|
{
|
|
|
|
Amarok::StatusBar::instance()->longMessage(
|
|
|
|
i18n( "Failed to copy track to media device: %1" ).arg( bundle->url().path() ),
|
|
|
|
KDE::StatusBar::Sorry );
|
|
|
|
transferredItem->setFailed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( transcoding )
|
|
|
|
{
|
|
|
|
if( m_transcodeRemove )
|
|
|
|
TQFile( bundle->url().path() ).remove();
|
|
|
|
|
|
|
|
delete bundle;
|
|
|
|
bundle = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( isCanceled() )
|
|
|
|
break;
|
|
|
|
|
|
|
|
if( !item )
|
|
|
|
{
|
|
|
|
setProgress( progress() + 1 );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
item->syncStatsFromPath( (*it).url().path() );
|
|
|
|
|
|
|
|
if( m_playlistItem && !playlist.isEmpty() )
|
|
|
|
{
|
|
|
|
MediaItem *pl = m_playlistItem->findItem( playlist );
|
|
|
|
if( !pl )
|
|
|
|
{
|
|
|
|
TQPtrList<MediaItem> items;
|
|
|
|
pl = newPlaylist( playlist, m_playlistItem, items );
|
|
|
|
}
|
|
|
|
if( pl )
|
|
|
|
{
|
|
|
|
TQPtrList<MediaItem> items;
|
|
|
|
items.append( item );
|
|
|
|
addToPlaylist( pl, pl->lastChild(), items );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setProgress( progress() + 1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
transferredItem->m_flags &= ~MediaItem::Transferring;
|
|
|
|
|
|
|
|
if( isCanceled() )
|
|
|
|
{
|
|
|
|
m_parent->updateStats();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !(transferredItem->flags() & MediaItem::Failed) )
|
|
|
|
{
|
|
|
|
m_parent->m_queue->subtractItemFromSize( transferredItem, true );
|
|
|
|
delete transferredItem;
|
|
|
|
m_parent->m_queue->itemCountChanged();
|
|
|
|
}
|
|
|
|
m_parent->updateStats();
|
|
|
|
|
|
|
|
kapp->processEvents( 100 );
|
|
|
|
}
|
|
|
|
synchronizeDevice();
|
|
|
|
unlockDevice();
|
|
|
|
fileTransferFinished();
|
|
|
|
|
|
|
|
TQString msg;
|
|
|
|
if( unplayable.count() > 0 )
|
|
|
|
{
|
|
|
|
msg = i18n( "One track not playable on media device",
|
|
|
|
"%n tracks not playable on media device", unplayable.count() );
|
|
|
|
}
|
|
|
|
if( existing.count() > 0 )
|
|
|
|
{
|
|
|
|
if( msg.isEmpty() )
|
|
|
|
msg = i18n( "One track already on media device",
|
|
|
|
"%n tracks already on media device", existing.count() );
|
|
|
|
else
|
|
|
|
msg += i18n( ", one track already on media device",
|
|
|
|
", %n tracks already on media device", existing.count() );
|
|
|
|
}
|
|
|
|
if( transcodeFail > 0 )
|
|
|
|
{
|
|
|
|
if( msg.isEmpty() )
|
|
|
|
msg = i18n( "One track was not transcoded",
|
|
|
|
"%n tracks were not transcoded", transcodeFail );
|
|
|
|
else
|
|
|
|
msg += i18n( ", one track was not transcoded",
|
|
|
|
", %n tracks were not transcoded", transcodeFail );
|
|
|
|
|
|
|
|
const ScriptManager* const sm = ScriptManager::instance();
|
|
|
|
if( !sm->transcodeScriptRunning().isEmpty() )
|
|
|
|
msg += i18n( " (no transcode script running)" );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( unplayable.count() + existing.count() > 0 )
|
|
|
|
{
|
|
|
|
TQString longMsg = i18n( "The following tracks were not transferred: ");
|
|
|
|
for( KURL::List::Iterator it = existing.begin();
|
|
|
|
it != existing.end();
|
|
|
|
it++ )
|
|
|
|
{
|
|
|
|
longMsg += "<br>" + (*it).prettyURL();
|
|
|
|
}
|
|
|
|
for( KURL::List::Iterator it = unplayable.begin();
|
|
|
|
it != unplayable.end();
|
|
|
|
it++ )
|
|
|
|
{
|
|
|
|
longMsg += "<br>" + (*it).prettyURL();
|
|
|
|
}
|
|
|
|
Amarok::StatusBar::instance()->shortLongMessage( msg, longMsg, KDE::StatusBar::Sorry );
|
|
|
|
}
|
|
|
|
else if( !msg.isEmpty() )
|
|
|
|
{
|
|
|
|
Amarok::StatusBar::instance()->shortMessage( msg, KDE::StatusBar::Sorry );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_parent->updateButtons();
|
|
|
|
m_parent->queue()->save( Amarok::saveLocation() + "transferlist.xml" );
|
|
|
|
m_transferring = false;
|
|
|
|
|
|
|
|
if( m_deferredDisconnect )
|
|
|
|
{
|
|
|
|
m_deferredDisconnect = false;
|
|
|
|
disconnectDevice( m_runDisconnectHook );
|
|
|
|
}
|
|
|
|
else if( m_scheduledDisconnect )
|
|
|
|
{
|
|
|
|
disconnectDevice( true );
|
|
|
|
}
|
|
|
|
m_scheduledDisconnect = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
MediaDevice::progress() const
|
|
|
|
{
|
|
|
|
return m_parent->m_progress->progress();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::setProgress( const int progress, const int total )
|
|
|
|
{
|
|
|
|
if( total != -1 )
|
|
|
|
m_parent->m_progress->setTotalSteps( total );
|
|
|
|
m_parent->m_progress->setProgress( progress );
|
|
|
|
m_parent->m_progressBox->show();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::fileTransferFinished() //SLOT
|
|
|
|
{
|
|
|
|
m_parent->updateStats();
|
|
|
|
m_parent->m_progressBox->hide();
|
|
|
|
m_parent->m_toolbar->getButton(MediaBrowser::TRANSFER)->setEnabled( isConnected() && m_parent->queue()->childCount() > 0 );
|
|
|
|
m_wait = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
MediaDevice::deleteFromDevice(MediaItem *item, int flags )
|
|
|
|
{
|
|
|
|
MediaItem* fi = item;
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
if ( !(flags & Recursing) )
|
|
|
|
{
|
|
|
|
if( !lockDevice( true ) )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
setCanceled( false );
|
|
|
|
|
|
|
|
m_deleting = true;
|
|
|
|
|
|
|
|
TQPtrList<MediaItem> list;
|
|
|
|
//NOTE we assume that currentItem is the main target
|
|
|
|
int numFiles = m_view->getSelectedLeaves(item, &list, MediaView::OnlySelected | ((flags & OnlyPlayed) ? MediaView::OnlyPlayed : MediaView::None) );
|
|
|
|
|
|
|
|
m_parent->m_stats->setText( i18n( "1 track to be deleted", "%n tracks to be deleted", numFiles ) );
|
|
|
|
if( numFiles > 0 && (flags & DeleteTrack) )
|
|
|
|
{
|
|
|
|
int button = KMessageBox::warningContinueCancel( m_parent,
|
|
|
|
i18n( "<p>You have selected 1 track to be <b>irreversibly</b> deleted.",
|
|
|
|
"<p>You have selected %n tracks to be <b>irreversibly</b> deleted.",
|
|
|
|
numFiles
|
|
|
|
),
|
|
|
|
TQString(),
|
|
|
|
KGuiItem(i18n("&Delete"),"editdelete") );
|
|
|
|
|
|
|
|
if ( button != KMessageBox::Continue )
|
|
|
|
{
|
|
|
|
m_parent->queue()->computeSize();
|
|
|
|
m_parent->updateStats();
|
|
|
|
m_deleting = false;
|
|
|
|
unlockDevice();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!isTransferring())
|
|
|
|
{
|
|
|
|
setProgress( 0, numFiles );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
// don't return if numFiles==0: playlist items might be to delete
|
|
|
|
|
|
|
|
if( !fi )
|
|
|
|
fi = static_cast<MediaItem*>(m_view->firstChild());
|
|
|
|
}
|
|
|
|
|
|
|
|
while( fi )
|
|
|
|
{
|
|
|
|
MediaItem *next = static_cast<MediaItem*>(fi->nextSibling());
|
|
|
|
|
|
|
|
if( isCanceled() )
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !fi->isVisible() )
|
|
|
|
{
|
|
|
|
fi = next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( fi->isSelected() )
|
|
|
|
{
|
|
|
|
int ret = deleteItemFromDevice(fi, flags);
|
|
|
|
if( ret >= 0 && count >= 0 )
|
|
|
|
count += ret;
|
|
|
|
else
|
|
|
|
count = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( fi->childCount() )
|
|
|
|
{
|
|
|
|
int ret = deleteFromDevice( static_cast<MediaItem*>(fi->firstChild()), flags | Recursing );
|
|
|
|
if( ret >= 0 && count >= 0 )
|
|
|
|
count += ret;
|
|
|
|
else
|
|
|
|
count = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_parent->updateStats();
|
|
|
|
|
|
|
|
fi = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!(flags & Recursing))
|
|
|
|
{
|
|
|
|
purgeEmptyItems();
|
|
|
|
synchronizeDevice();
|
|
|
|
m_deleting = false;
|
|
|
|
unlockDevice();
|
|
|
|
|
|
|
|
if(!isTransferring())
|
|
|
|
{
|
|
|
|
TQTimer::singleShot( 1500, m_parent->m_progressBox, TQT_SLOT(hide()) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( m_deferredDisconnect )
|
|
|
|
{
|
|
|
|
m_deferredDisconnect = false;
|
|
|
|
disconnectDevice( m_runDisconnectHook );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_parent->queue()->computeSize();
|
|
|
|
m_parent->updateStats();
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDevice::purgeEmptyItems( MediaItem *root )
|
|
|
|
{
|
|
|
|
MediaItem *it = 0;
|
|
|
|
if( root )
|
|
|
|
{
|
|
|
|
it = static_cast<MediaItem *>(root->firstChild());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
it = static_cast<MediaItem *>(m_view->firstChild());
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaItem *next = 0;
|
|
|
|
for( ; it; it=next )
|
|
|
|
{
|
|
|
|
next = static_cast<MediaItem *>(it->nextSibling());
|
|
|
|
purgeEmptyItems( it );
|
|
|
|
if( it->childCount() == 0 &&
|
|
|
|
(it->type() == MediaItem::ARTIST ||
|
|
|
|
it->type() == MediaItem::ALBUM ||
|
|
|
|
it->type() == MediaItem::PODCASTCHANNEL) )
|
|
|
|
delete it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::save( const TQString &path )
|
|
|
|
{
|
|
|
|
TQFile file( path );
|
|
|
|
|
|
|
|
if( !file.open( IO_WriteOnly ) ) return;
|
|
|
|
|
|
|
|
TQDomDocument newdoc;
|
|
|
|
TQDomElement transferlist = newdoc.createElement( "playlist" );
|
|
|
|
transferlist.setAttribute( "product", "Amarok" );
|
|
|
|
transferlist.setAttribute( "version", APP_VERSION );
|
|
|
|
newdoc.appendChild( transferlist );
|
|
|
|
|
|
|
|
for( const MediaItem *item = static_cast<MediaItem *>( firstChild() );
|
|
|
|
item;
|
|
|
|
item = static_cast<MediaItem *>( item->nextSibling() ) )
|
|
|
|
{
|
|
|
|
TQDomElement i = newdoc.createElement("item");
|
|
|
|
i.setAttribute("url", item->url().url());
|
|
|
|
|
|
|
|
if( item->bundle() )
|
|
|
|
{
|
|
|
|
TQDomElement attr = newdoc.createElement( "Title" );
|
|
|
|
TQDomText t = newdoc.createTextNode( item->bundle()->title() );
|
|
|
|
attr.appendChild( t );
|
|
|
|
i.appendChild( attr );
|
|
|
|
|
|
|
|
attr = newdoc.createElement( "Artist" );
|
|
|
|
t = newdoc.createTextNode( item->bundle()->artist() );
|
|
|
|
attr.appendChild( t );
|
|
|
|
i.appendChild( attr );
|
|
|
|
|
|
|
|
attr = newdoc.createElement( "Album" );
|
|
|
|
t = newdoc.createTextNode( item->bundle()->album() );
|
|
|
|
attr.appendChild( t );
|
|
|
|
i.appendChild( attr );
|
|
|
|
|
|
|
|
attr = newdoc.createElement( "Year" );
|
|
|
|
t = newdoc.createTextNode( TQString::number( item->bundle()->year() ) );
|
|
|
|
attr.appendChild( t );
|
|
|
|
i.appendChild( attr );
|
|
|
|
|
|
|
|
attr = newdoc.createElement( "Comment" );
|
|
|
|
t = newdoc.createTextNode( item->bundle()->comment() );
|
|
|
|
attr.appendChild( t );
|
|
|
|
i.appendChild( attr );
|
|
|
|
|
|
|
|
attr = newdoc.createElement( "Genre" );
|
|
|
|
t = newdoc.createTextNode( item->bundle()->genre() );
|
|
|
|
attr.appendChild( t );
|
|
|
|
i.appendChild( attr );
|
|
|
|
|
|
|
|
attr = newdoc.createElement( "Track" );
|
|
|
|
t = newdoc.createTextNode( TQString::number( item->bundle()->track() ) );
|
|
|
|
attr.appendChild( t );
|
|
|
|
i.appendChild( attr );
|
|
|
|
}
|
|
|
|
|
|
|
|
if(item->type() == MediaItem::PODCASTITEM)
|
|
|
|
{
|
|
|
|
i.setAttribute( "podcast", "1" );
|
|
|
|
}
|
|
|
|
|
|
|
|
if(item->type() == MediaItem::PODCASTITEM
|
|
|
|
&& item->bundle()->podcastBundle())
|
|
|
|
{
|
|
|
|
PodcastEpisodeBundle *peb = item->bundle()->podcastBundle();
|
|
|
|
TQDomElement attr = newdoc.createElement( "PodcastDescription" );
|
|
|
|
TQDomText t = newdoc.createTextNode( peb->description() );
|
|
|
|
attr.appendChild( t );
|
|
|
|
i.appendChild( attr );
|
|
|
|
|
|
|
|
attr = newdoc.createElement( "PodcastAuthor" );
|
|
|
|
t = newdoc.createTextNode( peb->author() );
|
|
|
|
attr.appendChild( t );
|
|
|
|
i.appendChild( attr );
|
|
|
|
|
|
|
|
attr = newdoc.createElement( "PodcastRSS" );
|
|
|
|
t = newdoc.createTextNode( peb->parent().url() );
|
|
|
|
attr.appendChild( t );
|
|
|
|
i.appendChild( attr );
|
|
|
|
|
|
|
|
attr = newdoc.createElement( "PodcastURL" );
|
|
|
|
t = newdoc.createTextNode( peb->url().url() );
|
|
|
|
attr.appendChild( t );
|
|
|
|
i.appendChild( attr );
|
|
|
|
}
|
|
|
|
|
|
|
|
if(item->m_playlistName != TQString())
|
|
|
|
{
|
|
|
|
i.setAttribute( "playlist", item->m_playlistName );
|
|
|
|
}
|
|
|
|
|
|
|
|
if(item->type() == MediaItem::PLAYLIST)
|
|
|
|
{
|
|
|
|
i.setAttribute( "playlistdata", item->data() );
|
|
|
|
if( item->flags() & MediaItem::SmartPlaylist )
|
|
|
|
i.setAttribute( "smartplaylist", "1" );
|
|
|
|
}
|
|
|
|
|
|
|
|
transferlist.appendChild( i );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQTextStream stream( &file );
|
|
|
|
stream.setEncoding( TQTextStream::UnicodeUTF8 );
|
|
|
|
stream << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
|
|
|
stream << newdoc.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::load( const TQString& filename )
|
|
|
|
{
|
|
|
|
TQFile file( filename );
|
|
|
|
if( !file.open( IO_ReadOnly ) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
clearItems();
|
|
|
|
|
|
|
|
TQTextStream stream( &file );
|
|
|
|
stream.setEncoding( TQTextStream::UnicodeUTF8 );
|
|
|
|
|
|
|
|
TQDomDocument d;
|
|
|
|
TQString er;
|
|
|
|
int l, c;
|
|
|
|
if( !d.setContent( stream.read(), &er, &l, &c ) ) { // return error values
|
|
|
|
Amarok::StatusBar::instance()->longMessageThreadSafe( i18n(
|
|
|
|
//TODO add a link to the path to the playlist
|
|
|
|
"The XML in the transferlist was invalid. Please report this as a bug to the Amarok "
|
|
|
|
"developers. Thank you." ), KDE::StatusBar::Error );
|
|
|
|
error() << "[TRANSFERLISTLOADER]: Error loading xml file: " << filename << "(" << er << ")"
|
|
|
|
<< " at line " << l << ", column " << c << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQValueList<TQDomNode> nodes;
|
|
|
|
const TQString ITEM( "item" ); //so we don't construct this TQString all the time
|
|
|
|
for( TQDomNode n = d.namedItem( "playlist" ).firstChild(); !n.isNull(); n = n.nextSibling() )
|
|
|
|
{
|
|
|
|
if( n.nodeName() != ITEM ) continue;
|
|
|
|
|
|
|
|
TQDomElement elem = n.toElement();
|
|
|
|
if( !elem.isNull() )
|
|
|
|
nodes += n;
|
|
|
|
|
|
|
|
if( !elem.hasAttribute( "url" ) )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
KURL url(elem.attribute("url"));
|
|
|
|
|
|
|
|
bool podcast = elem.hasAttribute( "podcast" );
|
|
|
|
PodcastEpisodeBundle peb;
|
|
|
|
if( url.isLocalFile() )
|
|
|
|
peb.setLocalURL( url );
|
|
|
|
MetaBundle *bundle = new MetaBundle( url );
|
|
|
|
for(TQDomNode node = elem.firstChild();
|
|
|
|
!node.isNull();
|
|
|
|
node = node.nextSibling())
|
|
|
|
{
|
|
|
|
if(node.firstChild().isNull())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if(node.nodeName() == "Title" )
|
|
|
|
bundle->setTitle(node.firstChild().toText().nodeValue());
|
|
|
|
else if(node.nodeName() == "Artist" )
|
|
|
|
bundle->setArtist(node.firstChild().toText().nodeValue());
|
|
|
|
else if(node.nodeName() == "Album" )
|
|
|
|
bundle->setAlbum(node.firstChild().toText().nodeValue());
|
|
|
|
else if(node.nodeName() == "Year" )
|
|
|
|
bundle->setYear(node.firstChild().toText().nodeValue().toUInt());
|
|
|
|
else if(node.nodeName() == "Genre" )
|
|
|
|
bundle->setGenre(node.firstChild().toText().nodeValue());
|
|
|
|
else if(node.nodeName() == "Comment" )
|
|
|
|
bundle->setComment(node.firstChild().toText().nodeValue());
|
|
|
|
else if(node.nodeName() == "PodcastDescription" )
|
|
|
|
peb.setDescription( node.firstChild().toText().nodeValue() );
|
|
|
|
else if(node.nodeName() == "PodcastAuthor" )
|
|
|
|
peb.setAuthor( node.firstChild().toText().nodeValue() );
|
|
|
|
else if(node.nodeName() == "PodcastRSS" )
|
|
|
|
peb.setParent( KURL::fromPathOrURL( node.firstChild().toText().nodeValue() ) );
|
|
|
|
else if(node.nodeName() == "PodcastURL" )
|
|
|
|
peb.setURL( KURL::fromPathOrURL( node.firstChild().toText().nodeValue() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( podcast )
|
|
|
|
{
|
|
|
|
bundle->setPodcastBundle( peb );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString playlist = elem.attribute( "playlist" );
|
|
|
|
TQString playlistdata = elem.attribute( "playlistdata" );
|
|
|
|
if( !playlistdata.isEmpty() )
|
|
|
|
{
|
|
|
|
TQString smart = elem.attribute( "smartplaylist" );
|
|
|
|
if( smart.isEmpty() )
|
|
|
|
syncPlaylist( playlist, KURL::fromPathOrURL( playlistdata ), true );
|
|
|
|
else
|
|
|
|
syncPlaylist( playlist, playlistdata, true );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
addURL( url, bundle, playlist );
|
|
|
|
}
|
|
|
|
|
|
|
|
URLsAdded();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaDevice::isPlayable( const MetaBundle &bundle )
|
|
|
|
{
|
|
|
|
if( supportedFiletypes().isEmpty() )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
TQString type = TQString(bundle.url().path().section( ".", -1 )).lower();
|
|
|
|
return supportedFiletypes().contains( type );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaDevice::isPreferredFormat( const MetaBundle &bundle )
|
|
|
|
{
|
|
|
|
if( supportedFiletypes().isEmpty() )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
TQString type = TQString(bundle.url().path().section( ".", -1 )).lower();
|
|
|
|
return ( type == supportedFiletypes().first() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MediaQueue::MediaQueue(MediaBrowser *parent)
|
|
|
|
: KListView( parent ), m_parent( parent )
|
|
|
|
{
|
|
|
|
setFixedHeight( 200 );
|
|
|
|
setSelectionMode( TQListView::Extended );
|
|
|
|
setItemsMovable( true );
|
|
|
|
setDragEnabled( true );
|
|
|
|
setShowSortIndicator( false );
|
|
|
|
setSorting( -1 );
|
|
|
|
setFullWidth( true );
|
|
|
|
setRootIsDecorated( false );
|
|
|
|
setDropVisualizer( true ); //the visualizer (a line marker) is drawn when dragging over tracks
|
|
|
|
setDropHighlighter( true ); //and the highligther (a focus rect) is drawn when dragging over playlists
|
|
|
|
setDropVisualizerWidth( 3 );
|
|
|
|
setAcceptDrops( true );
|
|
|
|
addColumn( i18n( "Transfer Queue" ) );
|
|
|
|
|
|
|
|
itemCountChanged();
|
|
|
|
|
|
|
|
KActionCollection* ac = new KActionCollection( this );
|
|
|
|
KStdAction::selectAll( TQT_TQOBJECT(this), TQT_SLOT( selectAll() ), ac, "MediaQueue" );
|
|
|
|
|
|
|
|
connect( this, TQT_SIGNAL( contextMenuRequested( TQListViewItem*, const TQPoint&, int ) ),
|
|
|
|
TQT_SLOT( slotShowContextMenu( TQListViewItem*, const TQPoint&, int ) ) );
|
|
|
|
connect( this, TQT_SIGNAL( dropped(TQDropEvent*, TQListViewItem*, TQListViewItem*) ),
|
|
|
|
TQT_SLOT( slotDropped(TQDropEvent*, TQListViewItem*, TQListViewItem*) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
MediaQueue::acceptDrag( TQDropEvent *e ) const
|
|
|
|
{
|
|
|
|
TQString data;
|
|
|
|
TQCString subtype;
|
|
|
|
TQTextDrag::decode( e, data, subtype );
|
|
|
|
|
|
|
|
return e->source() == viewport()
|
|
|
|
|| subtype == "amarok-sql"
|
|
|
|
|| KURLDrag::canDecode( e );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::slotDropped( TQDropEvent* e, TQListViewItem* parent, TQListViewItem* after)
|
|
|
|
{
|
|
|
|
if( e->source() != viewport() )
|
|
|
|
{
|
|
|
|
TQString data;
|
|
|
|
TQCString subtype;
|
|
|
|
TQTextDrag::decode( e, data, subtype );
|
|
|
|
KURL::List list;
|
|
|
|
|
|
|
|
if( subtype == "amarok-sql" )
|
|
|
|
{
|
|
|
|
TQString playlist = data.section( "\n", 0, 0 );
|
|
|
|
TQString query = data.section( "\n", 1 );
|
|
|
|
TQStringList values = CollectionDB::instance()->query( query );
|
|
|
|
list = CollectionDB::instance()->URLsFromSqlDrag( values );
|
|
|
|
addURLs( list, playlist );
|
|
|
|
}
|
|
|
|
else if ( KURLDrag::decode( e, list ) )
|
|
|
|
{
|
|
|
|
addURLs( list );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( TQListViewItem *i = currentItem() )
|
|
|
|
{
|
|
|
|
moveItem( i, parent, after );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::dropProxyEvent( TQDropEvent *e )
|
|
|
|
{
|
|
|
|
slotDropped( e, 0, 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaItem*
|
|
|
|
MediaQueue::findPath( TQString path )
|
|
|
|
{
|
|
|
|
for( TQListViewItem *item = firstChild();
|
|
|
|
item;
|
|
|
|
item = item->nextSibling())
|
|
|
|
{
|
|
|
|
if(static_cast<MediaItem *>(item)->url().path() == path)
|
|
|
|
return static_cast<MediaItem *>(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::computeSize() const
|
|
|
|
{
|
|
|
|
m_totalSize = 0;
|
|
|
|
for( TQListViewItem *it = firstChild();
|
|
|
|
it;
|
|
|
|
it = it->nextSibling())
|
|
|
|
{
|
|
|
|
MediaItem *item = static_cast<MediaItem *>(it);
|
|
|
|
|
|
|
|
if( item && item->bundle() &&
|
|
|
|
( !m_parent->currentDevice()
|
|
|
|
|| !m_parent->currentDevice()->isConnected()
|
|
|
|
|| !m_parent->currentDevice()->trackExists(*item->bundle()) ) )
|
|
|
|
m_totalSize += ((item->size()+1023)/1024)*1024;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
KIO::filesize_t
|
|
|
|
MediaQueue::totalSize() const
|
|
|
|
{
|
|
|
|
return m_totalSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::addItemToSize( const MediaItem *item ) const
|
|
|
|
{
|
|
|
|
if( item && item->bundle() &&
|
|
|
|
( !m_parent->currentDevice()
|
|
|
|
|| !m_parent->currentDevice()->isConnected()
|
|
|
|
|| !m_parent->currentDevice()->trackExists(*item->bundle()) ) )
|
|
|
|
m_totalSize += ((item->size()+1023)/1024)*1024;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::subtractItemFromSize( const MediaItem *item, bool unconditionally ) const
|
|
|
|
{
|
|
|
|
if( item && item->bundle() &&
|
|
|
|
( !m_parent->currentDevice()
|
|
|
|
|| !m_parent->currentDevice()->isConnected()
|
|
|
|
|| (unconditionally || !m_parent->currentDevice()->trackExists(*item->bundle())) ) )
|
|
|
|
m_totalSize -= ((item->size()+1023)/1024)*1024;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::removeSelected()
|
|
|
|
{
|
|
|
|
TQPtrList<TQListViewItem> selected = selectedItems();
|
|
|
|
|
|
|
|
for( TQListViewItem *item = selected.first(); item; item = selected.next() )
|
|
|
|
{
|
|
|
|
if( !(static_cast<MediaItem *>(item)->flags() & MediaItem::Transferring) )
|
|
|
|
{
|
|
|
|
subtractItemFromSize( static_cast<MediaItem *>(item) );
|
|
|
|
delete item;
|
|
|
|
if( m_parent->currentDevice() && m_parent->currentDevice()->isTransferring() )
|
|
|
|
{
|
|
|
|
MediaBrowser::instance()->m_progress->setTotalSteps( MediaBrowser::instance()->m_progress->totalSteps() - 1 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaBrowser::instance()->updateStats();
|
|
|
|
MediaBrowser::instance()->updateButtons();
|
|
|
|
itemCountChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::keyPressEvent( TQKeyEvent *e )
|
|
|
|
{
|
|
|
|
if( e->key() == Key_Delete )
|
|
|
|
removeSelected();
|
|
|
|
else
|
|
|
|
KListView::keyPressEvent( e );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::itemCountChanged()
|
|
|
|
{
|
|
|
|
if( childCount() == 0 )
|
|
|
|
hide();
|
|
|
|
else if( !isShown() )
|
|
|
|
show();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::slotShowContextMenu( TQListViewItem* item, const TQPoint& point, int )
|
|
|
|
{
|
|
|
|
if( !childCount() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
KPopupMenu menu( this );
|
|
|
|
|
|
|
|
enum Actions { REMOVE_SELECTED, CLEAR_ALL, START_TRANSFER };
|
|
|
|
|
|
|
|
if( item )
|
|
|
|
menu.insertItem( SmallIconSet( Amarok::icon( "remove_from_playlist" ) ), i18n( "&Remove From Queue" ), REMOVE_SELECTED );
|
|
|
|
|
|
|
|
menu.insertItem( SmallIconSet( Amarok::icon( "playlist_clear" ) ), i18n( "&Clear Queue" ), CLEAR_ALL );
|
|
|
|
menu.insertItem( SmallIconSet( Amarok::icon( "playlist_refresh" ) ), i18n( "&Start Transfer" ), START_TRANSFER );
|
|
|
|
menu.setItemEnabled( START_TRANSFER,
|
|
|
|
MediaBrowser::instance()->currentDevice() &&
|
|
|
|
MediaBrowser::instance()->currentDevice()->isConnected() &&
|
|
|
|
MediaBrowser::instance()->currentDevice()->m_transfer );
|
|
|
|
|
|
|
|
switch( menu.exec( point ) )
|
|
|
|
{
|
|
|
|
case REMOVE_SELECTED:
|
|
|
|
removeSelected();
|
|
|
|
break;
|
|
|
|
case CLEAR_ALL:
|
|
|
|
clearItems();
|
|
|
|
break;
|
|
|
|
case START_TRANSFER:
|
|
|
|
MediaBrowser::instance()->transferClicked();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaQueue::clearItems()
|
|
|
|
{
|
|
|
|
clear();
|
|
|
|
itemCountChanged();
|
|
|
|
if(m_parent)
|
|
|
|
{
|
|
|
|
computeSize();
|
|
|
|
m_parent->updateStats();
|
|
|
|
m_parent->updateButtons();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#include "mediabrowser.moc"
|