diff options
Diffstat (limited to 'muggle-plugin/mg_selection.c')
-rw-r--r-- | muggle-plugin/mg_selection.c | 1149 |
1 files changed, 1149 insertions, 0 deletions
diff --git a/muggle-plugin/mg_selection.c b/muggle-plugin/mg_selection.c new file mode 100644 index 0000000..464bddb --- /dev/null +++ b/muggle-plugin/mg_selection.c @@ -0,0 +1,1149 @@ +/*! + * \file mg_selection.c + * \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 $ + * + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <stdio.h> +#include <fts.h> +#include <assert.h> + +#include "i18n.h" +#include "mg_selection.h" +#include "vdr_setup.h" +#include "mg_tools.h" +#include "mg_thread_sync.h" + +#include <mpegfile.h> +#include <flacfile.h> +#include <id3v2tag.h> +#include <fileref.h> + +#if VDRVERSNUM >= 10307 +#include <vdr/interface.h> +#include <vdr/skins.h> +#endif + +//! \brief adds string n to string s, using a comma to separate them +static string comma (string & s, string n); + +/*! \brief returns a random integer within some range + */ +unsigned int +randrange (const unsigned int high) +{ + unsigned int result=0; + result = random () % high; + return result; +} + + +static string +comma (string & s, string n) +{ + return addsep (s, ",", n); +} + + +void +mgSelection::mgSelItems::clear() +{ + m_items.clear(); +} + +bool +mgSelection::mgSelItems::operator==(const mgSelItems&x) const +{ + bool result = m_items.size()==x.m_items.size(); + if (result) + for (unsigned int i=0;i<size();i++) + result &= m_items[i]==x.m_items[i]; + return result; +} + +size_t +mgSelection::mgSelItems::size() const +{ + if (!m_sel) + mgError("mgSelItems: m_sel is 0"); + m_sel->refreshValues(); + return m_items.size(); +} + +mgSelItem& +mgSelection::mgSelItems::operator[](unsigned int idx) +{ + if (!m_sel) + mgError("mgSelItems: m_sel is 0"); + m_sel->refreshValues(); + if (idx>=size()) return zeroitem; + return m_items[idx]; +} + +void +mgSelection::mgSelItems::setOwner(mgSelection* sel) +{ + m_sel = sel; +} + +unsigned int +mgSelection::mgSelItems::valindex (const string v) const +{ + return index(v,true); +} + +unsigned int +mgSelection::mgSelItems::idindex (const string i) const +{ + return index(i,true); +} + +unsigned int +mgSelection::mgSelItems::index (const string s,bool val,bool second_try) const +{ + if (!m_sel) + mgError("mgSelItems::index(%s): m_sel is 0",s.c_str()); + m_sel->refreshValues(); + for (unsigned int i = 0; i < size (); i++) + { + if (val) + { + if (m_items[i].value() == s) + return i; + } + else + { + if (m_items[i].id() == s) + return i; + } + } + // nochmal mit neuen Werten: + if (second_try) { + esyslog("index: Gibt es nicht:%s",s.c_str()); + return 0; + } + else + { + m_sel->clearCache(); + return index(s,val,true); + } +} + +void +mgSelection::clearCache() const +{ + m_current_values = ""; + m_current_tracks = ""; +} + +string +mgSelection::getCurrentValue() +{ + return items[gotoPosition()].value(); +} + +mgSelItem& +mgSelection::getKeyItem(const unsigned int level) const +{ + return order.getKeyItem(level); +} + + +mgKeyTypes +mgSelection::getKeyType (const unsigned int level) const +{ + return order.getKeyType(level); +} + +mgContentItem * +mgSelection::getTrack (unsigned int position) +{ + if (position >= getNumTracks ()) + return 0; + return &(m_tracks[position]); +} + + +mgSelection::ShuffleMode mgSelection::toggleShuffleMode () +{ + setShuffleMode((m_shuffle_mode == SM_PARTY) ? SM_NONE : ShuffleMode (m_shuffle_mode + 1)); + Shuffle(); + return getShuffleMode(); +} + +void +mgSelection::setShuffleMode (mgSelection::ShuffleMode mode) +{ + m_shuffle_mode = mode; +} + +void +mgSelection::Shuffle() const +{ + unsigned int tracksize = getNumTracks(); + if (tracksize==0) return; + switch (m_shuffle_mode) + { + case SM_NONE: + { + long trackid = m_tracks[getTrackPosition()].getTrackid (); + m_current_tracks = ""; // force a reload + tracksize = getNumTracks(); // getNumTracks also reloads + for (unsigned int i = 0; i < tracksize; i++) + if (m_tracks[i].getTrackid () == trackid) + { + setTrackPosition(i); + break; + } + } + break; + case SM_PARTY: + case SM_NORMAL: + { + // play all, beginning with current track: + mgContentItem tmp = m_tracks[getTrackPosition()]; + m_tracks[getTrackPosition()]=m_tracks[0]; + m_tracks[0]=tmp; + setTrackPosition(0); + // randomize all other tracks + for (unsigned int i = 1; i < tracksize; i++) + { + unsigned int j = 1+randrange (tracksize-1); + tmp = m_tracks[i]; + m_tracks[i] = m_tracks[j]; + m_tracks[j] = tmp; + } + } break; +/* + * das kapiere ich nicht... (wolfgang) + - Party mode (see iTunes) + - initialization + - find 15 titles according to the scheme below + - playing + - before entering next title perform track selection + - track selection + - generate a random uid + - if file exists: + - determine maximum playcount of all tracks +- generate a random number n +- if n < playcount / max. playcount +- add the file to the end of the list +*/ + } +} + + +mgSelection::LoopMode mgSelection::toggleLoopMode () +{ + m_loop_mode = (m_loop_mode == LM_FULL) ? LM_NONE : LoopMode (m_loop_mode + 1); + return m_loop_mode; +} + + +unsigned int +mgSelection::AddToCollection (const string Name) +{ + if (!m_db.Connected()) return 0; + CreateCollection(Name); + string listid = m_db.sql_string (m_db.get_col0 + ("SELECT id FROM playlist WHERE title=" + m_db.sql_string (Name))); + unsigned int tracksize = getNumTracks (); + if (tracksize==0) + return 0; + + // this code is rather complicated but works in a multi user + // environment: + + // insert a unique trackid: + string trackid = ltos(m_db.thread_id()+1000000); + m_db.exec_sql("INSERT INTO playlistitem SELECT "+listid+"," + "MAX(tracknumber)+"+ltos(tracksize)+","+trackid+ + " FROM playlistitem WHERE playlist="+listid); + + // find tracknumber of the trackid we just inserted: + string sql = string("SELECT tracknumber FROM playlistitem WHERE " + "playlist=")+listid+" AND trackid="+trackid; + long first = atol(m_db.get_col0(sql).c_str()) - tracksize + 1; + + // replace the place holder trackid by the correct value: + m_db.exec_sql("UPDATE playlistitem SET trackid="+ltos(m_tracks[tracksize-1].getTrackid())+ + " WHERE playlist="+listid+" AND trackid="+trackid); + + // insert all other tracks: + const char *sql_prefix = "INSERT INTO playlistitem VALUES "; + sql = ""; + for (unsigned int i = 0; i < tracksize-1; i++) + { + string item = "(" + listid + "," + ltos (first + i) + "," + + ltos (m_tracks[i].getTrackid ()) + ")"; + comma(sql, item); + if ((i%100)==99) + { + m_db.exec_sql (sql_prefix+sql); + sql = ""; + } + } + if (!sql.empty()) m_db.exec_sql (sql_prefix+sql); + if (inCollection(Name)) clearCache (); + return tracksize; +} + + +unsigned int +mgSelection::RemoveFromCollection (const string Name) +{ + if (!m_db.Connected()) return 0; + mgParts p = order.Parts(m_db,m_level,false); + string sql = p.sql_delete_from_collection(id(keyCollection,Name)); + m_db.exec_sql (sql); + unsigned int removed = m_db.affected_rows (); + if (inCollection(Name)) clearCache (); + return removed; +} + + +bool mgSelection::DeleteCollection (const string Name) +{ + if (!m_db.Connected()) return false; + ClearCollection(Name); + m_db.exec_sql ("DELETE FROM playlist WHERE title=" + m_db.sql_string (Name)); + if (isCollectionlist()) clearCache (); + return (m_db.affected_rows () == 1); +} + + +void mgSelection::ClearCollection (const string Name) +{ + if (!m_db.Connected()) return; + string listid = id(keyCollection,Name); + m_db.exec_sql ("DELETE FROM playlistitem WHERE playlist="+m_db.sql_string(listid)); + if (inCollection(Name)) clearCache (); +} + + +bool mgSelection::CreateCollection(const string Name) +{ + if (!m_db.Connected()) return false; + string name = m_db.sql_string(Name); + if (m_db.exec_count("SELECT count(title) FROM playlist WHERE title = " + name)>0) + return false; + m_db.exec_sql ("INSERT playlist VALUES(" + name + ",'VDR',NULL,NULL,NULL)"); + if (isCollectionlist()) clearCache (); + return true; +} + + +string mgSelection::exportM3U () +{ + +// open a file for writing + string fn = "/tmp/" + ListFilename () + ".m3u"; + FILE * listfile = fopen (fn.c_str (), "w"); + if (!listfile) + return ""; + fprintf (listfile, "#EXTM3U\n"); + unsigned int tracksize = getNumTracks (); + for (unsigned i = 0; i < tracksize; i++) + { + mgContentItem& t = m_tracks[i]; + fprintf (listfile, "#EXTINF:%d,%s\n", t.getDuration (), + t.getTitle ().c_str ()); + fprintf (listfile, "#MUGGLE:%ld\n", t.getTrackid()); + fprintf (listfile, "%s\n", t.getSourceFile (false).c_str ()); + } + fclose (listfile); + return fn; +} + +bool +mgSelection::empty() +{ + if (m_level>= order.size ()-1) + return ( getNumTracks () == 0); + else + return ( items.size () == 0); +} + +void +mgSelection::setPosition (unsigned int position) +{ + if (m_level == order.size()) + setTrackPosition(position); + else + m_position = position; +} + +void +mgSelection::setTrackPosition (unsigned int position) const +{ + m_tracks_position = position; +} + +unsigned int +mgSelection::getPosition () const +{ + if (m_level == order.size()) + return getTrackPosition(); + else + return m_position; +} + +unsigned int +mgSelection::gotoPosition () +{ + if (m_level == order.size ()) + return gotoTrackPosition(); + else + { + unsigned int itemsize = items.size(); + if (itemsize==0) + m_position = 0; + else if (m_position >= itemsize) + m_position = itemsize -1; + return m_position; + } +} + +unsigned int +mgSelection::getTrackPosition() const +{ + if (m_tracks_position>=m_tracks.size()) + if (m_tracks.size()==0) + m_tracks_position=0; + else + m_tracks_position = m_tracks.size()-1; + return m_tracks_position; +} + +unsigned int +mgSelection::gotoTrackPosition() +{ + unsigned int tracksize = getNumTracks (); + if (tracksize == 0) + setTrackPosition(0); + else if (m_tracks_position >= tracksize) + setTrackPosition(tracksize -1); + return m_tracks_position; +} + +bool mgSelection::skipTracks (int steps) +{ + unsigned int tracksize = getNumTracks(); + if (tracksize == 0) + return false; + if (m_loop_mode == LM_SINGLE) + return true; + unsigned int old_pos = m_tracks_position; + unsigned int new_pos; + if (old_pos + steps < 0) + { + if (m_loop_mode == LM_NONE) + return false; + new_pos = tracksize - 1; + } + else + new_pos = old_pos + steps; + if (new_pos >= tracksize) + { + if (m_loop_mode == LM_NONE) + return false; + new_pos = 0; + } + setTrackPosition (new_pos); + return (new_pos == gotoTrackPosition()); +} + + +unsigned long +mgSelection::getLength () +{ + unsigned long result = 0; + unsigned int tracksize = getNumTracks (); + for (unsigned int i = 0; i < tracksize; i++) + result += m_tracks[i].getDuration (); + return result; +} + + +unsigned long +mgSelection::getCompletedLength () const +{ + unsigned long result = 0; + tracks (); // make sure they are loaded + for (unsigned int i = 0; i < getTrackPosition(); i++) + result += m_tracks[i].getDuration (); + return result; +} + + + +string mgSelection::getListname () const +{ + list<string> st; + for (unsigned int i = 0; i < m_level; i++) + st.push_back(order.getKeyItem(i).value()); + st.unique(); + string result=""; + for (list < string >::iterator it = st.begin (); it != st.end (); ++it) + { + addsep (result, ":", *it); + } + if (result.empty ()) + if (order.size()>0) + result = string(ktName(order.getKeyType(0))); + return result; +} + +string mgSelection::ListFilename () +{ + string res = getListname (); + // convert char set ? + string::iterator it; + for (it=res.begin();it!=res.end();it++) + { + char& c = *it; + switch (c) + { + case '\'': + case '/': + case '\\': + case ' ': + case ')': + case '(': c = '_';break; + } + } + return res; +} + +const vector < mgContentItem > & +mgSelection::tracks () const +{ + if (!m_db.Connected()) return m_tracks; + if (!m_current_tracks.empty()) return m_tracks; + mgParts p = order.Parts(m_db,m_level); + p.fields.clear(); + p.fields.push_back("tracks.id"); + p.fields.push_back("tracks.title"); + p.fields.push_back("tracks.mp3file"); + p.fields.push_back("tracks.artist"); + p.fields.push_back("album.title"); + p.fields.push_back("tracks.genre1"); + p.fields.push_back("tracks.genre2"); + p.fields.push_back("tracks.bitrate"); + p.fields.push_back("tracks.year"); + p.fields.push_back("tracks.rating"); + p.fields.push_back("tracks.length"); + p.fields.push_back("tracks.samplerate"); + p.fields.push_back("tracks.channels"); + p.fields.push_back("tracks.lang"); + p.tables.push_back("tracks"); + p.tables.push_back("album"); + for (unsigned int i = m_level; i<order.size(); i++) + p += order.Key(i)->Parts(m_db,true); + m_current_tracks = p.sql_select(false); + m_tracks.clear (); + MYSQL_RES *rows = m_db.exec_sql (m_current_tracks); + if (rows) + { + MYSQL_ROW row; + while ((row = mysql_fetch_row (rows)) != 0) + m_tracks.push_back (mgContentItem (this,row)); + mysql_free_result (rows); + } + if (m_shuffle_mode!=SM_NONE) + Shuffle(); + return m_tracks; +} + + +void mgSelection::InitSelection() { + m_Directory="."; + m_level = 0; + m_position = 0; + m_tracks_position = 0; + m_trackid = -1; + if (the_setup.InitShuffleMode) + m_shuffle_mode = SM_NORMAL; + else + m_shuffle_mode = SM_NONE; + if (the_setup.InitLoopMode) + m_loop_mode = LM_FULL; + else + m_loop_mode = LM_NONE; + clearCache(); + items.setOwner(this); +} + + +mgSelection::mgSelection (const bool fall_through) +{ + InitSelection (); + m_fall_through = fall_through; +} + +mgSelection::mgSelection (const mgSelection &s) +{ + InitFrom(&s); +} + +mgSelection::mgSelection (const mgSelection* s) +{ + InitFrom(s); +} + +mgSelection::mgSelection (mgValmap& nv) +{ + InitFrom(nv); +} + +void +mgSelection::setOrder(mgOrder* o) +{ + if (o) + { + order = *o; + } + else + mgWarning("mgSelection::setOrder(0)"); +} + + +void +mgSelection::InitFrom(mgValmap& nv) +{ + extern time_t createtime; + if (m_db.ServerConnected() && !m_db.Connected() + && (time(0)>createtime+10)) + { + char *b; + asprintf(&b,tr("Create database %s?"),the_setup.DbName); + if (Interface->Confirm(b)) + { + char *argv[2]; + argv[0]="."; + argv[1]=0; + m_db.Create(); + if (Interface->Confirm(tr("Import tracks?"))) + { + mgThreadSync *s = mgThreadSync::get_instance(); + if (s) + { + extern char *sync_args[]; + s->Sync(sync_args,the_setup.DeleteStaleReferences); + } + } + } + free(b); + } + InitSelection(); + m_fall_through = nv.getbool("FallThrough"); + m_Directory = nv.getstr("Directory"); + while (m_level < nv.getuint("Level")) + { + char *idx; + asprintf(&idx,"order.Keys.%u.Position",m_level); + string newval = nv.getstr(idx); + free(idx); + if (!enter (newval)) + if (!select (newval)) break; + } + m_trackid = nv.getlong("TrackId"); + setPosition(nv.getstr("Position")); + if (m_level>=order.size()-1) + setTrackPosition(nv.getlong("TrackPosition")); +} + + +mgSelection::~mgSelection () +{ +} + +void mgSelection::InitFrom(const mgSelection* s) +{ + InitSelection(); + m_fall_through = s->m_fall_through; + m_Directory = s->m_Directory; + map_values = s->map_values; + map_ids = s->map_ids; + order = s->order; + m_level = s->m_level; + m_position = s->m_position; + m_trackid = s->m_trackid; + m_tracks_position = s->m_tracks_position; + setShuffleMode (s->getShuffleMode ()); + setLoopMode (s->getLoopMode ()); +} + +unsigned int +mgSelection::ordersize () +{ + return order.size (); +} + +unsigned int +mgSelection::valcount (string value) +{ + return items[items.valindex(value)].count(); +} + +void +mgSelection::refreshValues () const +{ + assert(this); + if (!m_db.Connected()) + return; + if (m_current_values.empty()) + { + mgParts p = order.Parts(m_db,m_level); + m_current_values = p.sql_select(); + items.clear (); + MYSQL_RES *rows = m_db.exec_sql (m_current_values); + if (rows) + { + unsigned int num_fields = mysql_num_fields(rows); + MYSQL_ROW row; + while ((row = mysql_fetch_row (rows)) != 0) + { + if (!row[0]) continue; + string r0 = row[0]; + if (!strcmp(row[0],"NULL")) // there is a genre NULL! + continue; + mgSelItem n; + if (num_fields==3) + { + if (!row[1]) continue; + n.set(row[0],row[1],atol(row[2])); + } + else + n.set(value(order.Key(m_level),r0),r0,atol(row[1])); + items.push_back(n); + } + mysql_free_result (rows); + } + } +} + +unsigned int +mgSelection::count () const +{ + return items.size (); +} + + +bool mgSelection::enter (unsigned int position) +{ + if (order.empty()) + esyslog("mgSelection::enter(%u): order is empty", position); + if (empty()) + return false; + setPosition (position); + position = gotoPosition(); // reload adjusted position + if (items.size()==0) + return false; + mgSelItem item = items[position]; + mgSelItems prev; + if (m_fall_through && items.size()<10) + prev=items; + while (1) + { + if (m_level >= order.size () - 1) + return false; + order[m_level++]->set (item); + clearCache(); + if (m_level >= order.size()) + mgError("mgSelection::enter(%u): level greater than order.size() %u", + m_level,order.size()); + m_position = 0; + refreshValues(); + if (count()==0) + break; + item=items[0]; + if (!m_fall_through) + break; + if (m_level==order.size()-1) + break; + if (count () > 1 && !(prev==items)) + break; + } + return true; +} + + +bool mgSelection::select (unsigned int position) +{ + if (m_level == order.size () - 1) + { + if (getNumTracks () <= position) + return false; + order[m_level]->set (items[position]); + m_level++; + m_trackid = m_tracks[position].getTrackid (); + + clearCache(); + return true; + } + else + return enter (position); +} + +bool +mgSelection::leave () +{ + string prevvalue; + if (order.empty()) + { + mgWarning("mgSelection::leave(): order is empty"); + return false; + } + if (m_level == order.size ()) + { + m_level--; + prevvalue=order.getKeyItem(m_level).value(); + order[m_level]->set(zeroitem); + m_trackid = -1; + clearCache(); + setPosition(prevvalue); + return true; + } + mgSelItems prev; + if (m_fall_through && items.size()<10) + prev=items; + while (1) + { + if (m_level < 1) + return false; + if (m_level<order.size()) order[m_level]->set (zeroitem); + m_level--; + prevvalue=order.getKeyItem(m_level).value(); + if (m_level<order.size()) order[m_level]->set (zeroitem); + clearCache(); + if (!m_fall_through) + break; + if (count () > 1 && !(prev==items)) + break; + } + setPosition(prevvalue); + return true; +} + +void +mgSelection::leave_all () +{ + m_level=0; + for (unsigned int i=0;i<ordersize();i++) + order[i]->set (zeroitem); + clearCache(); +} + +void +mgSelection::selectfrom(mgOrder& oldorder,mgContentItem* o) +{ + leave_all(); + mgSelItem selitem; + assert(m_level==0); + for (unsigned int idx = 0; idx < ordersize(); idx++) + { + selitem = zeroitem; + mgKeyTypes new_kt = getKeyType(idx); + for (unsigned int i=0;i<oldorder.size();i++) + { + mgKeyTypes old_kt = oldorder.getKeyType(i); + if (old_kt==new_kt) + selitem = oldorder.getKeyItem(i); + else if (old_kt>new_kt + && iskeyGenre(old_kt) + && iskeyGenre(new_kt)) + { + string selid=id(new_kt,value(new_kt,oldorder.getKeyItem(i).id())); + selitem=mgSelItem (value(new_kt,selid),selid); + } + if (selitem.valid()) break; + } + if (!selitem.valid() && o && o->getTrackid()>=0) + selitem = o->getKeyItem(new_kt); + if (!selitem.valid()) + break; + if (m_level<ordersize()-1) + { + order[m_level++]->set (selitem); + } + else + { + setPosition(selitem.value()); + return; + } + } + if (m_level>0) + { + m_level--; + selitem = order.getKeyItem(m_level); + order[m_level]->set(zeroitem); + setPosition(selitem.value()); + order[m_level+1]->set(zeroitem); + } + assert(m_level<ordersize()); +} + +string +mgSelection::value(mgKeyTypes kt, string idstr) const +{ + if (loadvalues (kt)) + { + map<string,string>& valmap = map_values[kt]; + map<string,string>::iterator it; + it = valmap.find(idstr); + if (it!=valmap.end()) + { + string r = it->second; + if (!r.empty()) + return r; + } + map_ids[kt].clear(); + loadvalues(kt); + it = valmap.find(idstr); + if (it!=valmap.end()) + return valmap[idstr]; + } + return idstr; +} + +string +mgSelection::value(mgKey* k, string idstr) const +{ + return value(k->Type(),idstr); +} + +string +mgSelection::value(mgKey* k) const +{ + return value(k,k->id()); +} + +string +mgSelection::id(mgKeyTypes kt, string val) const +{ + if (loadvalues (kt)) + { + map<string,string>& idmap = map_ids[kt]; + string v = idmap[val]; + if (kt==keyGenre1) v=v.substr(0,1); + if (kt==keyGenre2) v=v.substr(0,2); + if (kt==keyGenre3) v=v.substr(0,3); + return v; + } + else + return val; +} + +string +mgSelection::id(mgKey* k, string val) const +{ + return id(k->Type(),val); +} + +string +mgSelection::id(mgKey* k) const +{ + return k->id(); +} + +bool +mgSelection::UsedBefore(mgOrder *o,const mgKeyTypes kt,unsigned int level) const +{ + if (level>=o->size()) + level = o->size() -1; + for (unsigned int lx = 0; lx < level; lx++) + if (o->getKeyType(lx)==kt) + return true; + return false; +} + +bool mgSelection::isLanguagelist() const +{ + return (order.getKeyType(0) == keyLanguage); +} + +bool mgSelection::isCollectionlist () const +{ + if (order.size()==0) return false; + return (order.getKeyType(0) == keyCollection && m_level == 0); +} + +bool +mgSelection::inCollection(const string Name) const +{ + if (order.size()==0) return false; + bool result = (order.getKeyType(0) == keyCollection && m_level == 1); + if (result) + if (order.getKeyType(1) != keyCollectionItem) + mgError("inCollection: key[1] is not keyCollectionItem"); + if (!Name.empty()) + result &= (order.getKeyItem(0).value() == Name); + return result; +} + + +void mgSelection::DumpState(mgValmap& nv) const +{ + nv.put("FallThrough",m_fall_through); + nv.put("Directory",m_Directory); + nv.put("Level",int(m_level)); + for (unsigned int i=0;i<order.size();i++) + { + if (i<m_level) { + char *n; + asprintf(&n,"order.Keys.%u.Position",i); + nv.put(n,getKeyItem(i).value()); + free(n); + } + } + nv.put("TrackId",m_trackid); + nv.put("Position",items[m_position].value()); + if (m_level>=order.size()-1) + nv.put("TrackPosition",getTrackPosition()); +} + +map <mgKeyTypes, string> +mgSelection::UsedKeyValues() +{ + map <mgKeyTypes, string> result; + for (unsigned int idx = 0 ; idx < level() ; idx++) + { + result[order.getKeyType(idx)] = order.getKeyItem(idx).value(); + } + if (level() < order.size()-1) + { + mgKeyTypes ch = order.getKeyType(level()); + result[ch] = getCurrentValue(); + } + return result; +} + +bool +mgSelection::loadvalues (mgKeyTypes kt) const +{ + if (map_ids.count(kt)>0) + return true; + map<string,string>& idmap = map_ids[kt]; + mgKey* k = ktGenerate(kt); + if (k->map_idfield().empty()) + { + delete k; + return false; + } + map<string,string>& valmap = map_values[kt]; + char *b; + asprintf(&b,"select %s,%s from %s;",k->map_idfield().c_str(),k->map_valuefield().c_str(),k->map_valuetable().c_str()); + MYSQL_RES *rows = m_db.exec_sql (string(b)); + free(b); + if (rows) + { + MYSQL_ROW row; + while ((row = mysql_fetch_row (rows)) != 0) + { + if (row[0] && row[1]) + { + valmap[row[0]] = row[1]; + idmap[row[1]] = row[0]; + } + } + mysql_free_result (rows); + } + delete k; + return true; +} + +static vector<int> keycounts; + +unsigned int +mgSelection::keycount(mgKeyTypes kt) +{ + if (keycounts.size()==0) + { + for (unsigned int ki=int(mgKeyTypesLow);ki<=int(mgKeyTypesHigh);ki++) + { + keycounts.push_back(-1); + } + } + int& count = keycounts[int(kt-mgKeyTypesLow)]; + if (count==-1) + { + mgKey* k = ktGenerate(kt); + if (k->Enabled(m_db)) + count = m_db.exec_count(k->Parts(m_db,true).sql_count()); + else + count = 0; + delete k; + } + return count; +} + + +vector <const char *> +mgSelection::choices(mgOrder *o,unsigned int level, unsigned int *current) +{ + vector<const char*> result; + if (level>o->size()) + { + *current = 0; + return result; + } + for (unsigned int ki=int(mgKeyTypesLow);ki<=int(mgKeyTypesHigh);ki++) + { + mgKeyTypes kt = mgKeyTypes(ki); + if (kt==o->getKeyType(level)) + { + *current = result.size(); + result.push_back(ktName(kt)); + continue; + } + if (UsedBefore(o,kt,level)) + continue; + if (kt==keyDecade && UsedBefore(o,keyYear,level)) + continue; + if (kt==keyGenre1) + { + if (UsedBefore(o,keyGenre2,level)) continue; + if (UsedBefore(o,keyGenre3,level)) continue; + if (UsedBefore(o,keyGenres,level)) continue; + } + if (kt==keyGenre2) + { + if (UsedBefore(o,keyGenre3,level)) continue; + if (UsedBefore(o,keyGenres,level)) continue; + } + if (kt==keyGenre3) + { + if (UsedBefore(o,keyGenres,level)) continue; + } + if (kt==keyFolder1) + { + if (UsedBefore(o,keyFolder2,level)) continue; + if (UsedBefore(o,keyFolder3,level)) continue; + if (UsedBefore(o,keyFolder4,level)) continue; + } + if (kt==keyFolder2) + { + if (UsedBefore(o,keyFolder3,level)) continue; + if (UsedBefore(o,keyFolder4,level)) continue; + } + if (kt==keyFolder3) + { + if (UsedBefore(o,keyFolder4,level)) continue; + } + if (kt==keyCollection || kt==keyCollectionItem) + result.push_back(ktName(kt)); + else if (keycount(kt)>1) + result.push_back(ktName(kt)); + } + return result; +} + |