/*! * \file mg_selection.h * \brief A general interface to data items, currently only GiantDisc * * \version $Revision: 1.0 $ * \date $Date: 2004-12-07 10:10:35 +0200 (Tue, 07 Dec 2004) $ * \author Wolfgang Rohdewald * \author Responsible author: $Author: wr $ * */ #ifndef _MG_SELECTION_H #define _MG_SELECTION_H #include #include #include #include #include using namespace std; #include "mg_tools.h" #include "mg_valmap.h" #include "mg_item.h" #include "mg_db.h" typedef vector strvector; typedef vector keyvector; /*! * \brief the only interface to the database. * Some member functions are declared const although they can modify the inner state of mgSelection. * But they only modify variables used for caching. With const, we want to express * the logical constness. E.g. the selected items can change without breaking constness: * The selection never defines concrete items but only how to choose them. */ class mgSelection { public: void CopyKeyValues(mgSelection* s); /*! \brief define various ways to play music in random order * \todo Party mode is not implemented, does same as SM_NORMAL */ enum ShuffleMode { SM_NONE, //!< \brief play normal sequence SM_NORMAL, //!< \brief a shuffle with a fair distribution SM_PARTY //!< \brief select the next few songs randomly, continue forever }; //! \brief define various ways to play music in a neverending loop enum LoopMode { LM_NONE, //!< \brief do not loop LM_SINGLE, //!< \brief loop a single item LM_FULL //!< \brief loop the whole item list }; class mgListItems { public: mgListItems() { m_sel=0; } void setOwner(mgSelection* sel); mgListItem* operator[](unsigned int idx); string& id(unsigned int); unsigned int count(unsigned int); bool operator==(const mgListItems&x) const; size_t size() const; int search (const string v) const; unsigned int valindex (const string v) const; unsigned int idindex (const string i) const; void clear(); void push_back(mgListItem* item) { m_items.push_back(item); } vector& items() { return m_items; } //! \brief use only for loading! void sort(bool bycount,mgSortBy SortBy); private: unsigned int index (const string s,bool val,bool second_try=false) const; vector m_items; mgSelection* m_sel; }; /*! \brief the main constructor * \param fall_through if TRUE: If enter() returns a choice * containing only one item, that item is automatically entered. * The analog happens with leave() */ mgSelection ( const bool fall_through = false); /*! \brief a copy constructor. Does a deep copy. * Some of the data base content will only be retrieved by the * new mgSelection as needed, so some data base * overhead is involved */ mgSelection (const mgSelection& s); /*! \brief a copy constructor. Does a deep copy. * Some of the data base content will only be retrieved by the * new mgSelection as needed, so some data base * overhead is involved */ mgSelection (const mgSelection* s); virtual void MakeCollection() =0; //! \brief initializes from a map. void InitFrom(const char *prefix,mgValmap& nv); //! \brief the normal destructor virtual ~mgSelection (); /*! \brief represents all items for the current level. The result * is cached, subsequent accesses to values only incur a * small overhead for building the SQL WHERE command. The items will * be reloaded when the SQL command changes */ mutable mgListItems listitems; /*! \brief returns the name of a key */ mgKeyTypes getKeyType (const unsigned int level) const; mgSortBy getKeySortBy (const unsigned int level) const; //! \brief return the current value of this key mgListItem* getKeyItem (const unsigned int level) const; /*! \brief returns an item value */ string getValue(unsigned int idx) const; /*! \brief returns the current item from the value() list */ string getCurrentValue(); //! \brief the current position unsigned int getPosition ()const; //! \brief go to the current position. If it does not exist, // go to the nearest. unsigned int gotoPosition (); //! \brief the current position in the item list unsigned int getItemPosition () const; //! \brief go to the current item position. If it does not exist, // go to the nearest. unsigned int gotoItemPosition (); /*! \brief enter the the next higher level, go one up in the tree. * If fall_through (see constructor) is set to true, and the * level entered by enter() contains only one item, automatically * goes down further until a level with more than one item is reached. * \param position is the position in the current level that is to be expanded * \return returns false if there is no further level */ bool enter (unsigned int position); /*! \brief enter the next higher level, expanding the current position. * See also enter(unsigned int position) */ bool enter () { return enter (gotoPosition ()); } /*! \brief enter the next higher level, expanding the position holding a certain value * \param value the position holding value will be expanded. */ bool enter (const string value) { return enter (listitems.valindex (value)); } /*! \brief leave the current level, go one up in the tree. * If fall_through (see constructor) is set to true, and the * level entered by leave() contains only one item, automatically * goes up further until a level with more than one item is reached. * \return returns false if there is no further upper level */ bool leave (); /*! \brief leave the current level, go up in the tree until * target level is reached. * If fall_through (see constructor) is set to true, and the * level entered by leave() contains only one item, automatically * goes up further until a level with more than one item is reached. * \return returns false if there is no further upper level */ void leave_all (); unsigned int ordersize() const { return Keys.size(); } /*! \brief the orderlevel is 0 for the top level. After initializing * an mgSelection from file or from another mgSelection, it is 0. * It will only be correct after Activate() has been called. This * is so because setting it correctly needs to access the database. * We do not want to do that before we really need to. */ unsigned int orderlevel() const { return m_level; } //! \brief true if the selection holds no items bool empty(); /*! \brief returns detailed info about all selected items. * The ordering is done only by the keyfield of the current level. * This might have to be changed - suborder by keyfields of detail * levels. This list is cached so several consequent calls mean no * loss of performance. See value(), the same warning applies. * \todo call this more seldom. See getNumItems() */ const vector < mgItem* > &items () const; /*! \brief returns an item from the items() list * \param position the position in the items() list * \return returns NULL if position is out of range */ mgItem* getItem (unsigned int position); /*! \brief returns the current item from the items() list */ mgItem* getCurrentItem () { return getItem (gotoItemPosition()); } /*! \brief toggles the shuffle mode thru all possible values. * When a shuffle modus SM_NORMAL or SM_PARTY is selected, the * order of the items in the item list will be randomly changed. */ ShuffleMode toggleShuffleMode (); //! \brief toggles the loop mode thru all possible values LoopMode toggleLoopMode (); //! \brief returns the current shuffle mode ShuffleMode getShuffleMode () const { return m_shuffle_mode; } //! \brief sets the current shuffle mode void setShuffleMode (const ShuffleMode shuffle_mode); //! \brief returns the current loop mode LoopMode getLoopMode () const { return m_loop_mode; } //! \brief sets the current loop mode void setLoopMode (const LoopMode loop_mode) { m_loop_mode = loop_mode; } /*! \brief adds the whole current item list to a collection * \param Name the name of the collection. If it does not yet exist, * it will be created. */ unsigned int AddToCollection (const string Name); /*! \brief removes the whole current item from a the collection * Remember - this selection can be configured to hold exactly * one list, so this command can be used to clear a selected list. * \param Name the name of the collection */ unsigned int RemoveFromCollection (const string Name); //! \brief delete a collection bool DeleteCollection (const string Name); /*! \brief create a collection only if it does not yet exist. * \return true only if it has been created. false if it already existed. */ bool CreateCollection(const string Name); //! \brief remove all items from the collection void ClearCollection (const string Name); /*! generates an m3u file containing all items. The directory * can be indicated by SetDirectory(). * The file name will be built from the list name, slashes * and spaces converted */ string exportM3U (); /*! \brief go to a position in the current level. If we are at the * most detailled level this also sets the item position since * they are identical. * \param position the wanted position. If it is too big, go to the * last existing position * \return only if no position exists, false will be returned */ void setPosition (unsigned int position); /*! \brief go to the position with value in the current level * \param value the value of the wanted position */ void setPosition (string value); /*! \brief search the first position starting with search in the current level * \param search the search string * \return the new position */ unsigned int searchPosition (string search); /*! \brief go to a position in the item list * \param position the wanted position. If it is too big, go to the * last existing position. If the position is not valid, find the * next valid one. * \return only if no position exists, false will be returned */ void GotoItemPosition (unsigned int position) const; /*! \brief skip some items in the item list * \return false if new position does not exist */ bool skipItems (int step=1) const; //! \brief returns the sum of the durations of all items unsigned long getLength (); /*! \brief returns the sum of the durations of completed items * those are items before the current item position */ unsigned long getCompletedLength () const; /*! returns the name of the current play list. If no play list is active, * the name is built from the name of the key fields. */ string getListname () const; /*! \brief dumps the entire state of this selection into a map, * \param nv the values will be entered into this map */ void DumpState(mgValmap& nv,const char *prefix) const; void ShowState(char *w) const; //! \brief clear the cache, next access will reload from data base void clearCache() const; void refreshValues() const; //! \brief true if values and items need to be reloaded bool cacheIsEmpty() const { return (m_current_values=="" && m_current_tracks==""); } void setKeys(vector& kt); string Name(); bool SameOrder(const mgSelection* other); mgKey* Key(unsigned int idx) const; virtual vector Choices(unsigned int level, unsigned int *current) const = 0; void setOrderByCount(bool groupbycount); bool getOrderByCount() const { return m_orderByCount; } virtual bool NeedKey(unsigned int i) const = 0; virtual mgParts SelParts(bool distinct,bool deepsort) const; virtual bool inCollection(const string Name="") const =0; virtual bool isLanguagelist() const =0; virtual bool isCollectionlist() const =0; /*! \brief sets a default order. Every backend can define any number of * default orders. \param i references the wanted order * \return If i is higher than the highest default order, we return false */ virtual bool InitDefaultOrder(unsigned int i=0) =0; /*! \brief prepare for use: initialize m_level and go to the * correct position. This will execute only once after creation * of the mgSelection, so we can call it too often */ void Activate(); virtual bool keyIsUnique(mgKeyTypes kt) const = 0; bool inItem() const; bool inItems() const; protected: void InitFrom(const mgSelection* s); virtual bool DeduceKeyValue(mgKeyTypes new_kt,const mgSelection *s, vector& items) {return false;} virtual void clean(); virtual void InitSelection (); virtual bool isCollectionOrder() const=0; keyvector Keys; unsigned int m_level; mutable mgDb* m_db; bool m_orderByCount; bool UsedBefore(const mgKeyTypes kt,unsigned int level) const; unsigned int keycount(mgKeyTypes kt) const; virtual mgKeyTypes ktLow() const =0; virtual mgKeyTypes ktHigh() const =0; virtual const char * const ktName(const mgKeyTypes kt) const=0; mgKeyTypes ktValue(const char * name) const; void truncate(unsigned int i); void clear(); void setKey ( const mgKeyTypes kt); private: bool m_active; mutable string m_current_values; mutable string m_current_tracks; //! \brief be careful when accessing this, see mgSelection::items() mutable vector < mgItem* > m_items; bool m_fall_through; unsigned int m_position; mutable unsigned int m_items_position; ShuffleMode m_shuffle_mode; void Shuffle() const; LoopMode m_loop_mode; string ListFilename (); void InitOrder(vector& items); void SetLevel(unsigned int level); void IncLevel(); void DecLevel(); }; #endif