/*************************************************************************** * copyright : (c) 2004 Pierpaolo Di Panfilo * * (c) 2005-2006 Seb Ruiz * * (c) 2006 Bart Cerneels * * (c) 2006 Adam Pigg * * (c) 2006 Bonne Eggleston * * See COPYING file for licensing information * ***************************************************************************/ #ifndef PLAYLISTBROWSERITEM_H #define PLAYLISTBROWSERITEM_H #include "dynamicmode.h" #include "podcastbundle.h" #include "podcastsettings.h" #include // StreamEditor baseclass #include #include #include #include #include #include #include #include #include // Podcast loading animation #include 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 children */ }; //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 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 trackList() const { return m_trackList; } TQPtrList 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 m_trackList; //tracks in playlist TQPtrList 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 children 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 children. 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 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 childrens' 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