You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

673 lines
25 KiB

// (c) 2004 Christian Muehlhaeuser <>
// (c) 2005 Martin Aumueller <>
// (c) 2005 Seb Ruiz <>
// (c) 2006 T.R.Shashwath <>
// See COPYING file for licensing information
#include "amarok.h"
#include "amarok_export.h"
#include "browserToolBar.h"
#include "medium.h"
#include "multitabbar.h" //baseclass
#include "plugin/plugin.h" //baseclass
#include "pluginmanager.h"
#include <tqmutex.h>
#include <tqvbox.h> //baseclass
#include <tqdatetime.h>
#include <klistview.h> //baseclass
#include <kurl.h> //stack allocated
#include <kio/global.h> //filesize_t
#include "scrobbler.h" //SubmitItem
#include "metabundle.h"
class MediaBrowser;
class MediaDevice;
class MediaItemTip;
class MediaView;
class SpaceLabel;
class TransferDialog;
class KAction;
class KComboBox;
class KDialogBase;
class KProgress;
class KPushButton;
class KShellProcess;
class TQDragObject;
class TQLabel;
class TQPalette;
class LIBAMAROK_EXPORT MediaItem : public KListViewItem
MediaItem( TQListView* parent );
MediaItem( TQListViewItem* parent );
MediaItem( TQListView* parent, TQListViewItem* after );
MediaItem( TQListViewItem* parent, TQListViewItem* after );
void init();
virtual ~MediaItem();
MediaItem *lastChild() const;
virtual KURL url() const;
const MetaBundle *bundle() const;
void setBundle( MetaBundle *bundle );
enum Flags { Failed=1, BeginTransfer=2, StopTransfer=4, Transferring=8, SmartPlaylist=16 };
void setType( Type type );
void setFailed( bool failed=true );
Type type() const { return m_type; }
MediaItem *findItem(const TQString &key, const MediaItem *after=0) const;
const TQString &data() const { return m_data; }
void setData( const TQString &data ) { m_data = data; }
virtual bool isLeafItem() const; // A leaf node of the tree
virtual bool isFileBacked() const; // Should the file be deleted of the device when removed
virtual TQDateTime playTime() const { return TQDateTime(); }
virtual int played() const { return 0; }
virtual int recentlyPlayed() const { return 0; } // no of times played on device since last sync
virtual void setPlayCount( int ) {}
virtual int rating() const { return 0; } // rating on device, normalized to 100
virtual void setRating( int /*rating*/ ) {}
virtual bool ratingChanged() const { return false; }
virtual void setLastPlayed( uint ) {}
virtual void syncStatsFromPath( const TQString &path );
virtual long size() const;
virtual MediaDevice *device() const { return m_device; }
virtual bool listened() const { return m_listened; }
virtual void setListened( bool listened=true ) { m_listened = listened; }
int compare( TQListViewItem *i, int col, bool ascending ) const;
int flags() const { return m_flags; }
void paintCell( TQPainter *p, const TQColorGroup &cg, int column, int width, int align );
int m_order;
Type m_type;
TQString m_playlistName;
TQString m_data;
MediaDevice *m_device;
int m_flags;
bool m_listened;
static TQPixmap *s_pixUnknown;
static TQPixmap *s_pixRootItem;
static TQPixmap *s_pixFile;
static TQPixmap *s_pixArtist;
static TQPixmap *s_pixAlbum;
static TQPixmap *s_pixPlaylist;
static TQPixmap *s_pixPodcast;
static TQPixmap *s_pixTrack;
static TQPixmap *s_pixInvisible;
static TQPixmap *s_pixStale;
static TQPixmap *s_pixOrphaned;
static TQPixmap *s_pixDirectory;
static TQPixmap *s_pixTransferFailed;
static TQPixmap *s_pixTransferBegin;
static TQPixmap *s_pixTransferEnd;
mutable MetaBundle *m_bundle;
class MediaQueue : public KListView, public DropProxyTarget
MediaQueue(MediaBrowser *parent);
MediaItem *findPath( TQString path );
KIO::filesize_t totalSize() const; // total size of items to transfer in KB
void computeSize() const; // compute total size of items to transfer in KB
void addItemToSize( const MediaItem *item ) const;
void subtractItemFromSize( const MediaItem *item, bool unconditonally=false ) const;
void removeSelected();
void clearItems();
void load( const TQString &path );
void save( const TQString &path );
void syncPlaylist( const TQString &playlistName, const TQString &sql, bool loading=false );
void syncPlaylist( const TQString &playlistName, const KURL &url, bool loading=false );
void addURL( const KURL& url, MetaBundle *bundle=NULL, const TQString &playlistName=TQString() );
void addURL( const KURL& url, MediaItem *item );
void addURLs( const KURL::List urls, const TQString &playlistName=TQString() );
void URLsAdded(); // call after finishing adding single urls
void dropProxyEvent( TQDropEvent *e );
// Reimplemented from KListView
bool acceptDrag( TQDropEvent *e ) const;
TQDragObject *dragObject();
public slots:
void itemCountChanged();
private slots:
void selectAll() {TQListView::selectAll(true); }
void slotShowContextMenu( TQListViewItem* item, const TQPoint& point, int );
void slotDropped (TQDropEvent* e, TQListViewItem* parent, TQListViewItem* after);
void keyPressEvent( TQKeyEvent *e );
MediaBrowser *m_parent;
mutable KIO::filesize_t m_totalSize;
class MediaBrowser : public TQVBox
friend class DeviceConfigureDialog;
friend class MediaDevice;
friend class MediaView;
friend class MediaQueue;
friend class MediumPluginChooser;
friend class MediaItem;
static bool isAvailable();
LIBAMAROK_EXPORT static MediaBrowser *instance() { return s_instance; }
LIBAMAROK_EXPORT static MediaQueue *queue() { return s_instance ? s_instance->m_queue : 0; }
MediaBrowser( const char *name );
virtual ~MediaBrowser();
bool blockQuit() const;
MediaDevice *currentDevice() const;
MediaDevice *deviceFromId( const TQString &id ) const;
TQStringList deviceNames() const;
bool deviceSwitch( const TQString &name );
TQString getInternalPluginName ( const TQString string ) { return m_pluginName[string]; }
TQString getDisplayPluginName ( const TQString string ) { return m_pluginAmarokName[string]; }
const KTrader::OfferList &getPlugins() { return m_plugins; }
void transcodingFinished( const TQString &src, const TQString &dst );
bool isTranscoding() const { return m_waitForTranscode; }
void updateStats();
void updateButtons();
void updateDevices();
// return bundle for url if it is known to MediaBrowser
bool getBundle( const KURL &url, MetaBundle *bundle ) const;
bool isQuitting() const { return m_quitting; }
KURL getProxyUrl( const KURL& daapUrl ) const;
KToolBar* getToolBar() const { return m_toolbar; }
void availabilityChanged( bool isAvailable );
protected slots:
void transferClicked();
private slots:
void slotSetFilterTimeout();
void slotSetFilter();
void slotSetFilter( const TQString &filter );
void slotEditFilter();
void mediumAdded( const Medium *, TQString , bool constructing = false);
void mediumChanged( const Medium *, TQString );
void mediumRemoved( const Medium *, TQString );
void activateDevice( const MediaDevice *device );
void activateDevice( int index, bool skipDummy = true );
void pluginSelected( const Medium *, const TQString );
void showPluginManager();
void cancelClicked();
void connectClicked();
void disconnectClicked();
void customClicked();
void configSelectPlugin( int index );
bool config(); // false if canceled by user
KURL transcode( const KURL &src, const TQString &filetype );
void tagsChanged( const MetaBundle &bundle );
void prepareToQuit();
MediaDevice *loadDevicePlugin( const TQString &deviceName );
void unloadDevicePlugin( MediaDevice *device );
KLineEdit* m_searchEdit;
TQTimer *m_timer;
LIBAMAROK_EXPORT static MediaBrowser *s_instance;
TQValueList<MediaDevice *> m_devices;
TQValueList<MediaDevice *>::iterator m_currentDevice;
TQMap<TQString, TQString> m_pluginName;
TQMap<TQString, TQString> m_pluginAmarokName;
void addDevice( MediaDevice *device );
void removeDevice( MediaDevice *device );
MediaQueue* m_queue;
bool m_waitForTranscode;
KURL m_transcodedUrl;
TQString m_transcodeSrc;
SpaceLabel* m_stats;
TQHBox* m_progressBox;
KProgress* m_progress;
TQVBox* m_views;
KPushButton* m_cancelButton;
//KPushButton* m_playlistButton;
TQVBox* m_configBox;
KComboBox* m_configPluginCombo;
KComboBox* m_deviceCombo;
typedef TQMap<TQString, MediaItem*> ItemMap;
mutable TQMutex m_itemMapMutex;
ItemMap m_itemMap;
KTrader::OfferList m_plugins;
bool m_haveDevices;
bool m_quitting;
class MediaView : public KListView
friend class MediaBrowser;
friend class MediaDevice;
enum Flags
None = 0,
OnlySelected = 1,
OnlyPlayed = 2
MediaView( TQWidget *parent, MediaDevice *device );
virtual ~MediaView();
LIBAMAROK_EXPORT KURL::List nodeBuildDragList( MediaItem* item, int flags=OnlySelected );
int getSelectedLeaves(MediaItem *parent, TQPtrList<MediaItem> *list, int flags=OnlySelected );
LIBAMAROK_EXPORT MediaItem *newDirectory( MediaItem* parent );
bool setFilter( const TQString &filter, MediaItem *parent=NULL );
private slots:
void rmbPressed( TQListViewItem*, const TQPoint&, int );
void renameItem( TQListViewItem *item );
void slotExpand( TQListViewItem* );
void selectAll() { TQListView::selectAll(true); }
void invokeItem( TQListViewItem*, const TQPoint &, int column );
void invokeItem( TQListViewItem* );
void keyPressEvent( TQKeyEvent *e );
// Reimplemented from KListView
void contentsDropEvent( TQDropEvent *e );
void viewportPaintEvent( TQPaintEvent* );
bool acceptDrag( TQDropEvent *e ) const;
TQDragObject *dragObject();
TQWidget *m_parent;
MediaDevice *m_device;
MediaItemTip *m_toolTip;
/* at least the pure virtual functions have to be implemented by a media device,
all items are stored in a hierarchy of MediaItems,
when items are manipulated the MediaItems have to be updated accordingly */
class LIBAMAROK_EXPORT MediaDevice : public TQObject, public Amarok::Plugin
friend class DeviceConfigureDialog;
friend class TransferDialog;
friend class MediaBrowser;
friend class MediaView;
friend class MediaQueue;
enum Flags
None = 0,
OnlyPlayed = 1,
DeleteTrack = 2,
Recursing = 4
virtual void init( MediaBrowser* parent );
virtual ~MediaDevice();
MediaView *view();
* @retrun a KAction that will be plugged into the media device browser toolbar
virtual KAction *customAction() { return 0; }
virtual void rmbPressed( TQListViewItem *item, const TQPoint &point, int ) { (void)item; (void) point; }
* @return list of filetypes playable on this device
* (empty list is interpreted as all types are good)
virtual TQStringList supportedFiletypes() { return TQStringList(); }
* @param bundle describes track that should be checked
* @return true if the device is capable of playing the track referred to by bundle
virtual bool isPlayable( const MetaBundle &bundle );
* @param bundle describes track that should be checked
* @return true if the track is in the preferred (first in list) format of the device
virtual bool isPreferredFormat( const MetaBundle &bundle );
* @return true if the device is connected
virtual bool isConnected() = 0;
* Adds particular tracks to a playlist
* @param playlist parent playlist for tracks to be added to
* @param after insert following this item
* @param items tracks to add to playlist
virtual void addToPlaylist(MediaItem *playlist, MediaItem *after, TQPtrList<MediaItem> items) { Q_UNUSED(playlist); Q_UNUSED(after); Q_UNUSED(items); }
* Create a new playlist
* @param name playlist title
* @param parent parent MediaItem of the new playlist
* @param items tracks to add to the new playlist
* @return the newly created playlist
virtual MediaItem *newPlaylist(const TQString &name, MediaItem *parent, TQPtrList<MediaItem> items) { Q_UNUSED(name); Q_UNUSED(parent); Q_UNUSED(items); return 0; }
* Move items to a directory
* @param directory new parent of dropped items
* @param items tracks to add to the directory
virtual void addToDirectory( MediaItem *directory, TQPtrList<MediaItem> items ) { Q_UNUSED(directory); Q_UNUSED(items); }
* Create a new directory
* @param name directory title
* @param parent parent MediaItem of the new directory
* @param items tracks to add to the new directory
* @return the newly created directory
virtual MediaItem *newDirectory( const TQString &name, MediaItem *parent ) { Q_UNUSED(name); Q_UNUSED(parent); return 0; }
* Notify device of changed tags
* @param item item to be updated
* @param changed bundle containing new tags
* @return the changed MediaItem
virtual MediaItem *tagsChanged( MediaItem *item, const MetaBundle &changed ) { Q_UNUSED(item); Q_UNUSED(changed); return 0; }
* Indicate whether the device has a custom transfer dialog
* @return whether there is a custom dialog
virtual bool hasTransferDialog() { return false; }
* Run the transfer dialog to be used when Transfer is clicked
virtual void runTransferDialog() {}
* Get the transfer dialog, if any
* @return the transfer dialog, if any, else NULL;
virtual TransferDialog *getTransferDialog() { return NULL; }
* Can be used to explicitly indicate whether a device needs manual configuration
* @return whether manual configuration is needed
virtual bool needsManualConfig() { return true; }
virtual void addConfigElements( TQWidget * /*parent*/ ) {}
virtual void removeConfigElements( TQWidget * /*parent*/ ) {}
virtual void applyConfig() {}
virtual void loadConfig();
TQString configString( const TQString &name, const TQString &defValue = TQString() );
void setConfigString( const TQString &name, const TQString &value );
bool configBool( const TQString &name, bool defValue=false );
void setConfigBool( const TQString &name, bool value );
void setRequireMount( const bool b ) { m_requireMount = b; }
bool hasMountPoint() { return m_hasMountPoint; }
void setDeviceType( const TQString &type ) { m_type = type; }
TQString deviceType() { return m_type; }
virtual bool autoConnect() { return false; }
virtual bool asynchronousTransfer() { return false; }
bool isTransferring() { return m_transferring; }
bool isDeleting() { return m_deleting; }
bool isCanceled() { return m_canceled; }
void setCanceled( const bool b ) { m_canceled = b; }
int progress() const;
void setProgress( const int progress, const int total = -1 /* leave total unchanged by default */ );
void hideProgress();
* @return a unique identifier that is constant across sessions
TQString uniqueId() const { return; }
* @return the name for the device that should be presented to the user
TQString name() const { return m_name; }
* @return the device node
TQString deviceNode() const { return m_medium.deviceNode(); }
* @return the device mount point (or empty if non-applicable or unknown)
TQString mountPoint() const { return m_medium.mountPoint(); }
TQString getTransferDir() { return m_transferDir; }
Medium &getMedium() { return m_medium; }
void setSpacesToUnderscores( bool yesno ) { m_spacesToUnderscores = yesno;
setConfigBool( "spacesToUnderscores", yesno); }
bool getSpacesToUnderscores() { return m_spacesToUnderscores; }
void setFirstSort( TQString text ) { m_firstSort = text;
setConfigString( "firstGrouping", text ); }
void setSecondSort( TQString text ) { m_secondSort = text;
setConfigString( "secondGrouping", text ); }
void setThirdSort( TQString text ) { m_thirdSort = text;
setConfigString( "thirdGrouping", text ); }
virtual KURL getProxyUrl( const KURL& /*url*/) { return KURL(); }
virtual void customClicked() { return; }
BundleList bundlesToSync( const TQString &playlistName, const TQString &sql );
BundleList bundlesToSync( const TQString &playlistName, const KURL &url );
void preparePlaylistForSync( const TQString &playlistName, const BundleList &bundles );
bool isOnOtherPlaylist( const TQString &playlistToAvoid, const MetaBundle &bundle );
bool isOnPlaylist( const MediaItem &playlist, const MetaBundle &bundle );
bool isInBundleList( const BundleList &bundles, const MetaBundle &bundle );
bool bundleMatch( const MetaBundle &b1, const MetaBundle &b2 );
public slots:
void abortTransfer();
void transferFiles();
virtual void renameItem( TQListViewItem *item ) {(void)item; }
virtual void expandItem( TQListViewItem *item ) {(void)item; }
bool connectDevice( bool silent=false );
bool disconnectDevice( bool postdisconnecthook=true );
void scheduleDisconnect() { m_scheduledDisconnect = true; }
protected slots:
void fileTransferred( KIO::Job *job );
void fileTransferFinished();
int sysCall(const TQString & command);
int runPreConnectCommand();
int runPostDisconnectCommand();
TQString replaceVariables( const TQString &cmd ); // replace %m with mount point and %d with device node
* Find a particular track
* @param bundle The metabundle of the requested media item
* @return The MediaItem of the item if found, otherwise NULL
* @note This may not be worth implementing for non database driven devices, as it could be slow
virtual MediaItem *trackExists( const MetaBundle& bundle ) = 0;
* Get the capacity and freespace available on the device, in bytes
* @return true if successful
virtual bool getCapacity( KIO::filesize_t *total, KIO::filesize_t *available ) { Q_UNUSED(total); Q_UNUSED(available); return false; }
* Lock device for exclusive access if possible
virtual bool lockDevice( bool tryOnly = false ) = 0;
* Unlock device
virtual void unlockDevice() = 0;
* Connect to device, and populate m_view with MediaItems
* @return true if successful
virtual bool openDevice( bool silent=false ) = 0;
* Wrap up any loose ends and close the device
* @return true if successful
virtual bool closeDevice() = 0;
* Write any pending changes to the device, such as database changes
virtual void synchronizeDevice() = 0;
* Copy a track to the device
* @param bundle The MetaBundle of the item to transfer. Will move the item specified by bundle().url().path()
* @return If successful, the created MediaItem in the media device view, else 0
virtual MediaItem *copyTrackToDevice(const MetaBundle& bundle) = 0;
* Copy track from device to computer
* @param item The MediaItem of the track to transfer.
* @param url The URL to transfer the track to.
* @return The MediaItem transfered.
virtual void copyTrackFromDevice(MediaItem *item);
* Recursively remove MediaItem from the tracklist and the device
* @param item MediaItem to remove
* @param onlyPlayed True if item should be deleted only if it has been played
* @return -1 on failure, number of files deleted otherwise
virtual int deleteItemFromDevice( MediaItem *item, int flags=DeleteTrack ) = 0;
* Abort the currently active track transfer
virtual void cancelTransfer() { /* often checking m_cancel is enough */ }
virtual void updateRootItems();
virtual bool isSpecialItem( MediaItem *item );
int deleteFromDevice( MediaItem *item=0, int flags=DeleteTrack );
void purgeEmptyItems( MediaItem *root=0 );
void syncStatsFromDevice( MediaItem *root=0 );
void syncStatsToDevice( MediaItem *root=0 );
bool kioCopyTrack( const KURL &src, const KURL &dst );
TQString m_name;
bool m_hasMountPoint;
TQString m_preconnectcmd;
TQString m_postdisconnectcmd;
bool m_autoDeletePodcasts;
bool m_syncStats;
bool m_transcode;
bool m_transcodeAlways;
bool m_transcodeRemove;
KShellProcess *sysProc;
MediaBrowser *m_parent;
MediaView *m_view;
Medium m_medium;
TQString m_transferDir;
TQString m_firstSort;
TQString m_secondSort;
TQString m_thirdSort;
bool m_wait;
bool m_waitForDeletion;
bool m_copyFailed;
bool m_requireMount;
bool m_canceled;
bool m_transferring;
bool m_deleting;
bool m_deferredDisconnect;
bool m_scheduledDisconnect;
bool m_runDisconnectHook;
bool m_spacesToUnderscores;
bool m_transfer;
bool m_configure;
bool m_customButton;
TQString m_type;
// root listview items
MediaItem *m_playlistItem;
MediaItem *m_podcastItem;
// items not on the master playlist and not on the podcast playlist are not visible on the ipod
MediaItem *m_invisibleItem;
// items in the database for which the file is missing
MediaItem *m_staleItem;
// files without database entry
MediaItem *m_orphanedItem;
// stow away all items below m_rootItems when device is not current
TQPtrList<TQListViewItem> m_rootItems;