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.
amarok/amarok/src/playlistbrowseritem.h

650 lines
23 KiB

/***************************************************************************
* copyright : (c) 2004 Pierpaolo Di Panfilo *
* (c) 2005-2006 Seb Ruiz <me@sebruiz.net> *
* (c) 2006 Bart Cerneels <bart.cerneels@gmail.com> *
* (c) 2006 Adam Pigg <adam@piggz.co.uk> *
* (c) 2006 Bonne Eggleston <b.eggleston@gmail.com> *
* See COPYING file for licensing information *
***************************************************************************/
#ifndef PLAYLISTBROWSERITEM_H
#define PLAYLISTBROWSERITEM_H
#include "dynamicmode.h"
#include "podcastbundle.h"
#include "podcastsettings.h"
#include <kdialogbase.h> // StreamEditor baseclass
#include <kio/job.h>
#include <klineedit.h>
#include <klistview.h>
#include <kurl.h>
#include <tqdom.h>
#include <tqfile.h>
#include <tqhttp.h>
#include <tqptrlist.h>
#include <tqtimer.h> // Podcast loading animation
#include <tqurl.h>
class MetaBundle;
class PlaylistTrackItem;
class TrackItemInfo;
namespace KIO { class Job; class TransferJob; class CopyJob; } //podcast downloads
/**
* RTTI VALUES
* 1000 - PlaylistCategory
* 1001 - PlaylistEntry
* 1002 - PlaylistTrackItem
* 1003 - StreamEntry
* 1004 - SmartPlaylist
* 1005 - DynamicEntry (Dynamic)
* 1006 - PodcastChannel
* 1007 - PodcastEpisode
*/
/* A base class to be able to use polymorphism and avoid tons of casts */
class PlaylistBrowserEntry : public TQObject, public KListViewItem
{
Q_OBJECT
TQ_OBJECT
public:
PlaylistBrowserEntry( TQListViewItem *parent, TQListViewItem *after )
: KListViewItem( parent, after) { m_kept = true; }
PlaylistBrowserEntry( TQListView *parent, TQListViewItem *after )
: KListViewItem( parent, after) { m_kept = true; }
PlaylistBrowserEntry( TQListViewItem *parent, TQListViewItem *after, const TQString &name )
: KListViewItem( parent, after, name) { m_kept = true; }
virtual TQDomElement xml() const { return TQDomElement(); }
TQListViewItem* parent() const { return KListViewItem::parent(); }
bool isKept() const { return m_kept; } // if kept == true, then it will be saved
void setKept( bool k ); // to the cache files. If false, non-renameable
virtual void updateInfo();
virtual void setDynamic( bool ) {};
public slots:
virtual void slotDoubleClicked();
virtual void slotRenameItem();
virtual void slotPostRenameItem( const TQString newName );
virtual void showContextMenu( const TQPoint & ) {};
protected:
virtual int compare( TQListViewItem*, int, bool ) const; //reimplemented
/** Interval of the download pixmap animation, in milliseconds */
static const int ANIMATION_INTERVAL = 250;
private:
bool m_kept;
};
class DynamicEntry : public PlaylistBrowserEntry, public DynamicMode
{
Q_OBJECT
TQ_OBJECT
public:
DynamicEntry( TQListViewItem *parent, TQListViewItem *after, const TQString &title );
DynamicEntry( TQListViewItem *parent, TQListViewItem *after, const TQDomElement &xmlDefinition );
~DynamicEntry() { };
virtual TQString text( int column ) const;
virtual TQDomElement xml() const;
static const int RTTI = 1005;
int rtti() const { return RTTI; }
public slots:
virtual void slotDoubleClicked();
virtual void showContextMenu( const TQPoint & );
};
class PlaylistCategory : public PlaylistBrowserEntry
{
Q_OBJECT
TQ_OBJECT
public:
PlaylistCategory( TQListView *parent, TQListViewItem *after, const TQString &, bool isFolder=false );
PlaylistCategory( PlaylistCategory *parent, TQListViewItem *after, const TQString &, bool isFolder=true );
PlaylistCategory( TQListView *parent, TQListViewItem *after, const TQDomElement &xmlDefinition, bool isFolder=false);
PlaylistCategory( PlaylistCategory *parent, TQListViewItem *after, const TQDomElement &xmlDefinition );
PlaylistCategory( PlaylistCategory *parent, TQListViewItem *after, const TQString &t, const int id );
~PlaylistCategory() { };
const TQString &title() const { return m_title; }
bool isFolder() { return m_folder; }
void paintCell( TQPainter*, const TQColorGroup&, int, int, int );
void setId( const int id ) { m_id = id; }
const int id() const { return m_id; }
virtual TQDomElement xml() const;
int rtti() const { return RTTI; }
static const int RTTI = 1000; //category item
public slots:
virtual void slotDoubleClicked();
virtual void slotRenameItem();
virtual void showContextMenu( const TQPoint & );
protected:
void okRename( int col );
private:
void setXml( const TQDomElement &xml );
TQString m_title;
int m_id;
bool m_folder;
};
class PlaylistEntry : public PlaylistBrowserEntry
{
Q_OBJECT
TQ_OBJECT
friend class PlaylistTrackItem;
friend class TrackItemInfo;
friend class PlaylistCategory;
public:
PlaylistEntry( TQListViewItem *parent, TQListViewItem *after, const KURL &, int tracks=0, int length=0 );
PlaylistEntry( TQListViewItem *parent, TQListViewItem *after, const TQDomElement &xmlDefinition );
~PlaylistEntry();
void sortChildItems ( int /*column*/, bool /*ascending*/ ) { /* Don't sort its tqchildren */ }; //reimplemented
void load();
const KURL &url() const { return m_url; }
void setUrl( const TQString &u ) { m_url.setPath( u ); }
int trackCount() const { return m_trackCount; }
int length() const { return m_length; }
bool isDynamic() const { return m_dynamic; }
bool isLoaded() const { return m_loaded; }
void setDynamic( bool );
int compare( TQListViewItem* i, int col ) const; //reimpl.
KURL::List tracksURL(); //returns the list of tracks url
void insertTracks( TQListViewItem *after, KURL::List list );
void insertTracks( TQListViewItem *after, TQValueList<MetaBundle> bundles );
// isLast is used to avoid saving the playlist to disk every time a track is removed
// when removing a list of tracks from the playlist
void removeTrack( TQListViewItem *item, bool isLast = true );
//returns a list of track information
TQPtrList<TrackItemInfo> trackList() const { return m_trackList; }
TQPtrList<TrackItemInfo> droppedTracks() const { return tmp_droppedTracks; }
void setOpen( bool );
void setup();
void paintCell( TQPainter*, const TQColorGroup&, int, int, int );
virtual TQDomElement xml() const;
virtual void updateInfo();
//rtti is used to distinguish different kinds of list view items
int rtti() const { return RTTI; }
static const int RTTI = 1001; //playlist item
public slots:
virtual void slotDoubleClicked();
virtual void slotPostRenameItem( const TQString newName );
virtual void showContextMenu( const TQPoint & );
signals:
void startingLoading();
void loaded();
private slots:
void slotAnimation();
private:
void customEvent( TQCustomEvent* e );
void startAnimation();
void stopAnimation();
KURL m_url; //playlist url
int m_length; //total length in seconds
int m_trackCount; //track counter
TQPtrList<TrackItemInfo> m_trackList; //tracks in playlist
TQPtrList<TrackItemInfo> tmp_droppedTracks; //tracks dropped to the playlist while it wasn't been loaded
bool m_loading;
bool m_loaded; //playlist loaded
bool m_dynamic; //the playlist is scheduled for dynamic mode rotation
TQPixmap *m_loading1, *m_loading2; //icons for loading animation
TQTimer m_animationTimer;
uint m_iconCounter;
PlaylistTrackItem *m_lastTrack;
};
class PlaylistTrackItem : public PlaylistBrowserEntry
{
Q_OBJECT
TQ_OBJECT
friend class TrackItemInfo;
public:
PlaylistTrackItem( TQListViewItem *parent, TQListViewItem *after, TrackItemInfo *info );
const KURL &url();
TrackItemInfo *trackInfo() const { return m_trackInfo; }
int rtti() const { return RTTI; }
static const int RTTI = 1002; //track item
public slots:
virtual void slotDoubleClicked();
virtual void slotRenameItem() { /* Do nothing */ };
virtual void showContextMenu( const TQPoint & );
private:
TrackItemInfo *m_trackInfo;
};
/// Stored in the database
class PodcastEpisode : public PlaylistBrowserEntry
{
Q_OBJECT
TQ_OBJECT
public:
PodcastEpisode( TQListViewItem *parent, TQListViewItem *after, const TQDomElement &xml,
const int feedType, const bool &isNew=false );
PodcastEpisode( TQListViewItem *parent, TQListViewItem *after, PodcastEpisodeBundle &bundle );
void downloadMedia();
void setOnDisk( bool d = true );
TQListViewItem *itemChannel() { return m_parent; }
const bool isNew() const { return m_bundle.isNew(); }
void setNew( const bool &n = true );
void setListened( const bool &n = true ) { setNew( !n ); }
// for convenience
const int dBId() const { return m_bundle.dBId(); }
const KURL url() const { return m_bundle.url(); }
const TQString title() const { return m_bundle.title(); }
const TQString author() const { return m_bundle.author(); }
const TQString date() const { return m_bundle.date(); }
const TQString type() const { return m_bundle.type(); }
const TQString description() const { return m_bundle.description(); }
const TQString guid() const { return m_bundle.guid(); }
const int duration() const { return m_bundle.duration(); }
const KURL &localUrl() const { return m_bundle.localUrl(); }
void setLocalUrlBase( const TQString &s );
void setLocalUrl( const KURL &localUrl );
void setup();
void paintCell( TQPainter*, const TQColorGroup&, int, int, int );
virtual void updateInfo();
void addToMediaDevice();
int rtti() const { return RTTI; }
static const int RTTI = 1007; //PodcastEpisode
static void createLocalDir( const KURL &localDir );
signals:
void downloadFinished();
void downloadAborted();
public slots:
const bool isOnDisk();
virtual void slotDoubleClicked();
virtual void slotRenameItem() { /* Do nothing */ };
virtual void showContextMenu( const TQPoint & );
private slots:
void abortDownload();
void downloadResult( KIO::Job * transferJob );
void slotAnimation();
void redirected( KIO::Job * job,const KURL & redirectedUrl );
private:
enum FeedType{ RSS=0, ATOM=1 };
virtual int compare( TQListViewItem*, int, bool ) const; //reimplemented
void associateWithLocalFile();
void startAnimation();
void stopAnimation();
void updatePixmap();
TQListViewItem *m_parent; //podcast channel it belongs to
PodcastEpisodeBundle m_bundle;
KURL m_localUrl;
bool m_fetching;
TQTimer m_animationTimer;
uint m_iconCounter;
KIO::StoredTransferJob* m_podcastEpisodeJob;
TQString m_filename;
bool m_downloaded; //marked as downloaded in cached xml
bool m_onDisk;
};
/// Stored in the database
class PodcastChannel : public PlaylistBrowserEntry
{
Q_OBJECT
TQ_OBJECT
public:
PodcastChannel( TQListViewItem *parent, TQListViewItem *after, const KURL &url,
const TQDomNode &channelSettings );
PodcastChannel( TQListViewItem *parent, TQListViewItem *after, const KURL &url );
PodcastChannel( TQListViewItem *parent, TQListViewItem *after, const KURL &url,
const TQDomNode &channelSettings, const TQDomDocument &xml );
PodcastChannel( TQListViewItem *parent, TQListViewItem *after, const PodcastChannelBundle &pcb );
enum MediaFetch{ STREAM=0, AUTOMATIC=1 };
void setNew( const bool n = true );
bool hasNew() const { return m_new; }
// iterate over all tqchildren and explicitly check if there are any episodes which have not been listened
// to. Mark the channel as new/listened after doing this.
void checkAndSetNew();
void setListened( const bool n = true ); // over rides each child so it has been listened
void setOpen( bool open ); // if !m_polished, load the tqchildren. Lazy loading to improve start times
void load();
const bool isPolished() const { return m_polished; }
void configure();
void fetch();
void rescan();
const KURL &url() const { return m_url; }
const KURL link() const { return m_bundle.link(); }
const TQString title() const { return m_bundle.title(); }
const TQString description() const { return m_bundle.description(); }
const TQString copyright() const { return m_bundle.copyright(); }
const bool autoscan() const { return m_bundle.autoscan(); }
const bool autotransfer() const { return m_bundle.autotransfer(); }
const int fetchType() const { return m_bundle.fetchType(); }
const bool hasPurge() const { return m_bundle.hasPurge(); }
const int purgeCount() const { return m_bundle.purgeCount(); }
const TQString &saveLocation() const { return m_bundle.saveLocation(); }
PodcastSettings *getSettings() const
{
return new PodcastSettings( title(), saveLocation(),
autoscan(), fetchType(), autotransfer(),
hasPurge(), purgeCount() );
}
void setParent( PlaylistCategory *newParent );
void setSettings( PodcastSettings *settings );
void setXml( const TQDomNode &xml, const int feedType );
virtual void updateInfo();
int rtti() const { return RTTI; }
static const int RTTI = 1006; //podcastchannel
public slots:
virtual void slotDoubleClicked();
virtual void slotRenameItem() { /* Do nothing */ };
virtual void showContextMenu( const TQPoint & );
private slots:
void abortFetch();
void downloadChildQueue();
void fetchResult( KIO::Job* job );
void slotAnimation();
private:
enum FeedType{ RSS=0, ATOM=1 };
static const int EPISODE_LIMIT = 10; //Maximum number of episodes initially shown
bool containsItem( TQDomElement xml );
void downloadChildren();
const bool episodeExists( const TQDomNode &xml, const int feedType );
void purge();
void restorePurged();
void removeChildren();
void setDOMSettings( const TQDomNode &channelSettings );
void startAnimation();
void stopAnimation();
PodcastChannelBundle m_bundle;
/// loading all of the podcast episodes during startup can be very inefficient.
/// When the user expands the podcast for the first time, we load up the episodes.
bool m_polished;
KURL m_url; //remote xml url
bool m_fetching;
bool m_updating;
TQTimer m_animationTimer;
uint m_iconCounter;
bool m_new;
bool m_hasProblem;
KIO::TransferJob *m_podcastJob;
PlaylistCategory *m_parent; // category it belongs to
TQString m_podcastCurrentUrl;
TQPtrList<PodcastEpisode> m_podcastDownloadQueue;
bool m_settingsValid;
};
class StreamEntry : public PlaylistBrowserEntry
{
Q_OBJECT
TQ_OBJECT
public:
StreamEntry( TQListViewItem *parent, TQListViewItem *after, const KURL &, const TQString &t );
StreamEntry( TQListViewItem *parent, TQListViewItem *after, const TQDomElement &xmlDefinition );
~StreamEntry() { };
void setURL ( KURL u ) { m_url = u; }
void setTitle( TQString t ) { m_title = t; }
void setup();
void paintCell( TQPainter*, const TQColorGroup&, int, int, int );
const KURL &url() const { return m_url; }
const TQString &title() const { return m_title; }
virtual TQDomElement xml() const;
virtual void updateInfo();
int rtti() const { return RTTI; }
static const int RTTI = 1003; //stream item
public slots:
virtual void slotDoubleClicked();
virtual void showContextMenu( const TQPoint & );
protected:
TQString m_title;
KURL m_url;
};
class LastFmEntry : public StreamEntry
{
Q_OBJECT
TQ_OBJECT
public:
LastFmEntry( TQListViewItem *parent, TQListViewItem *after, const KURL &u, const TQString &t )
: StreamEntry( parent, after, u, t ) { }
LastFmEntry( TQListViewItem *parent, TQListViewItem *after, const TQDomElement &xmlDefinition )
: StreamEntry( parent, after, xmlDefinition ) { }
virtual TQDomElement xml() const;
public slots:
virtual void slotRenameItem() { /* Do nothing */ }
public:
int rtti() const { return RTTI; }
static const int RTTI = 1008; //lastfm item
};
class StreamEditor : public KDialogBase
{
public:
StreamEditor( TQWidget *parent, const TQString &title, const TQString &url, bool readonly = false );
KURL url() const { return KURL( m_urlLineEdit->text() ); }
TQString name() const { return m_nameLineEdit->text().replace( "\n", " " ); }
private:
KLineEdit *m_urlLineEdit;
KLineEdit *m_nameLineEdit;
};
class SmartPlaylist : public PlaylistBrowserEntry
{
Q_OBJECT
TQ_OBJECT
public:
SmartPlaylist( TQListViewItem *parent, TQListViewItem *after, const TQString &name, const TQString &query );
SmartPlaylist( TQListViewItem *parent, TQListViewItem *after, const TQString &name,
const TQString &urls, const TQString &tags );
SmartPlaylist( TQListViewItem *parent, TQListViewItem *after, const TQDomElement &xmlDefinition );
bool isDynamic() const { return m_dynamic; }
bool isEditable() const { return !m_xml.isNull(); }
bool isTimeOrdered(); //returns yes if the ordering is based on a time attribute
TQString query();
TQString title() const { return m_title; }
virtual TQDomElement xml() const { return m_xml; }
int length();
void setDynamic( bool );
void setXml( const TQDomElement &xml );
int rtti() const { return RTTI; }
static const int RTTI = 1004; //smart playlist item
public slots:
virtual void slotDoubleClicked();
virtual void slotPostRenameItem( const TQString newName );
virtual void showContextMenu( const TQPoint & );
private:
// for xml playlists, this member is computed on demand
TQString m_sqlForTags;
TQString m_title;
TQDomElement m_xml;
TQListViewItem *m_after;
bool m_dynamic;
// Build the query for a given xml object. If \p for expand is true,
// insert (*ExpandString*) as placeholders for tqchildrens' filters
static TQString xmlToQuery( const TQDomElement &xml, bool forExpand = false );
};
//this class is used to store information of a playlist track
class TrackItemInfo
{
public:
TrackItemInfo( const MetaBundle &mb );
~TrackItemInfo() {}
const KURL &url() const { return m_url; }
const TQString &album() const { return m_album; }
const TQString &artist() const { return m_artist; }
const TQString &title() const { return m_title; }
const int length() const { return m_length; }
private:
KURL m_url;
TQString m_artist;
TQString m_album;
TQString m_title;
int m_length;
};
/*!
@brief Implement a shoutcast playlist category
On open, download the shoutcast genre XML file.
Process the file and add each genre as a ShoutcastGenre
style PlaylistCategory
*/
class ShoutcastBrowser : public PlaylistCategory
{
Q_OBJECT
TQ_OBJECT
public:
ShoutcastBrowser( PlaylistCategory* parent );
void setOpen( bool open );
public slots:
virtual void slotDoubleClicked();
private slots:
void doneGenreDownload( KIO::Job *job, const KURL &from, const KURL &to, bool directory, bool renamed );
void jobFinished( KIO::Job *job );
void slotAnimation();
private:
bool m_downloading;
KIO::CopyJob *m_cj;
TQPixmap *m_loading1, *m_loading2; //icons for loading animation
TQTimer m_animationTimer;
};
/*!
@brief Implement a shoutcast genre category
On open, download the shoutcast station list XML file.
Process the file and add each station as a StreamEntry
*/
class ShoutcastGenre : public PlaylistCategory
{
Q_OBJECT
TQ_OBJECT
public:
ShoutcastGenre( ShoutcastBrowser *browser, TQListViewItem *after, TQString genre );
void setOpen( bool open );
void appendAlternateGenre( TQString alternateGenre ) { m_alternateGenres << alternateGenre; }
public slots:
virtual void slotDoubleClicked();
private slots:
void doneListDownload( KIO::Job *job, const KURL &from, const KURL &to, bool directory, bool renamed );
void jobFinished( KIO::Job *job );
void slotAnimation();
private:
void startGenreDownload( TQString genre, TQString tmppath );
bool m_downloading;
TQString m_genre;
TQPixmap *m_loading1, *m_loading2; //icons for loading animation
TQTimer m_animationTimer;
TQStringList m_alternateGenres;
TQStringList m_stations;
int m_totalJobs;
int m_completedJobs;
};
#endif