diff options
Diffstat (limited to 'mg_selection.c')
-rw-r--r-- | mg_selection.c | 974 |
1 files changed, 572 insertions, 402 deletions
diff --git a/mg_selection.c b/mg_selection.c index f0016e9..8e30945 100644 --- a/mg_selection.c +++ b/mg_selection.c @@ -13,27 +13,18 @@ #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_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 <interface.h> #include <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 */ @@ -45,17 +36,101 @@ randrange (const unsigned int high) return result; } +bool compvalue (const mgListItem* x, const mgListItem* y) +{ + return x->value()<y->value(); +} -static string -comma (string & s, string n) +bool compid (const mgListItem* x, const mgListItem* y) { - return addsep (s, ",", n); + return x->id()<y->id(); } +bool compidnum (const mgListItem* x, const mgListItem* y) +{ + return atol(x->id().c_str())<atol(y->id().c_str()); +} + +bool compcount (const mgListItem* x, const mgListItem* y) +{ + return x->count()>y->count(); +} + +bool compitem (const mgItem* x, const mgItem* y) +{ + const mgSelection *s = x->getSelection(); + string xval=""; + string yval=""; + int xnum; + int ynum; + for (unsigned int idx=s->orderlevel();idx<s->ordersize();idx++) + { + mgSortBy sb = s->getKeySortBy(idx); + mgListItem *xitem = x->getKeyItem(s->getKeyType (idx)); + mgListItem *yitem = y->getKeyItem(s->getKeyType (idx)); + switch (sb) { + case mgSortNone: + xval=""; + yval=""; + break; + case mgSortById: + xval=xitem->id(); + yval=yitem->id(); + break; + case mgSortByIdNum: + xnum=atol(xitem->id().c_str()); + ynum=atol(yitem->id().c_str()); + if (xnum<ynum) { + xval="0"; + yval="1"; + } else if (xnum>ynum) { + xval="1"; + yval="0"; + } else { + xval="0"; + yval="0"; + } + break; + case mgSortByValue: + xval=xitem->value(); + yval=yitem->value(); + break; + } + if (xval!=yval) break; + } + return xval<yval; +} + +void +mgSelection::mgListItems::sort(bool bycount,mgSortBy SortBy) +{ + if (SortBy==mgSortNone) + { + return; + } + if (bycount) + { + std::sort(m_items.begin(),m_items.end(),compcount); + } + else if (SortBy==mgSortById) + { + std::sort(m_items.begin(),m_items.end(),compid); + } + else if (SortBy==mgSortByIdNum) + { + std::sort(m_items.begin(),m_items.end(),compidnum); + } + else + { + std::sort(m_items.begin(),m_items.end(),compvalue); + } +} void mgSelection::mgListItems::clear() { + for (unsigned int i=0;i<m_items.size();i++) + delete m_items[i]; m_items.clear(); } @@ -65,7 +140,7 @@ mgSelection::mgListItems::operator==(const mgListItems&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]; + result &= *(m_items[i])==*(x.m_items[i]); return result; } @@ -78,13 +153,13 @@ mgSelection::mgListItems::size() const return m_items.size(); } -mgListItem& +mgListItem* mgSelection::mgListItems::operator[](unsigned int idx) { if (!m_sel) mgError("mgListItems: m_sel is 0"); m_sel->refreshValues(); - if (idx>=size()) return zeroitem; + assert(idx<m_items.size()); return m_items[idx]; } @@ -94,6 +169,24 @@ mgSelection::mgListItems::setOwner(mgSelection* sel) m_sel = sel; } +int +mgSelection::mgListItems::search (const string v) const +{ + if (!m_sel) + mgError("mgListItems::index(%s): m_sel is 0",v.c_str()); + unsigned int itemsize = m_items.size(); + const char *cstr = v.c_str(); + unsigned int clen = strlen(cstr); + int result = -1; + for (unsigned int idx = 0 ; idx < itemsize; idx++) + if( strncasecmp( m_items[idx]->value().c_str(), cstr, clen )>=0) + { + result = idx; + break; + } + return result; +} + unsigned int mgSelection::mgListItems::valindex (const string v) const { @@ -103,7 +196,7 @@ mgSelection::mgListItems::valindex (const string v) const unsigned int mgSelection::mgListItems::idindex (const string i) const { - return index(i,true); + return index(i,false); } unsigned int @@ -116,18 +209,25 @@ mgSelection::mgListItems::index (const string s,bool val,bool second_try) const { if (val) { - if (m_items[i].value() == s) + if (m_items[i]->value() == s) return i; } else { - if (m_items[i].id() == s) + if (m_items[i]->id() == s) return i; } } // nochmal mit neuen Werten: if (second_try) { - mgDebug(2,"index: Gibt es nicht:%s",s.c_str()); + mgDebug(5,"index: Gibt es nicht:%s",s.c_str()); + mgDebug(5,"index: wir haben z.B."); + if (size()>0) mgDebug(5,"%s/%s",m_items[0]->value().c_str(),m_items[0]->id().c_str()); + if (size()>1) mgDebug(5,"%s/%s",m_items[1]->value().c_str(),m_items[1]->id().c_str()); + if (size()>2) mgDebug(5,"%s/%s",m_items[2]->value().c_str(),m_items[2]->id().c_str()); + if (size()>3) mgDebug(5,"%s/%s",m_items[3]->value().c_str(),m_items[3]->id().c_str()); + if (size()>4) mgDebug(5,"%s/%s",m_items[4]->value().c_str(),m_items[4]->id().c_str()); + if (size()>5) mgDebug(5,"%s/%s",m_items[5]->value().c_str(),m_items[5]->id().c_str()); return 0; } else @@ -137,6 +237,24 @@ mgSelection::mgListItems::index (const string s,bool val,bool second_try) const } } +bool +mgSelection::inItem() const +{ + return m_level==ordersize()-1; +} + +bool +mgSelection::inItems() const +{ + return m_level==ordersize()-2; +} + +void +mgSelection::setOrderByCount(bool orderbycount) +{ + m_orderByCount = orderbycount; +} + void mgSelection::clearCache() const { @@ -147,28 +265,47 @@ mgSelection::clearCache() const string mgSelection::getCurrentValue() { - return listitems[gotoPosition()].value(); + gotoPosition(); + return getValue(m_position); } -mgListItem& +string +mgSelection::getValue(unsigned int idx) const +{ + if (idx>=listitems.size()) + return ""; + else + return listitems[idx]->value(); +} + +mgListItem* mgSelection::getKeyItem(const unsigned int level) const { - return order.getKeyItem(level); + assert(level<Keys.size()); + return Keys[level]->get(); } mgKeyTypes mgSelection::getKeyType (const unsigned int level) const { - return order.getKeyType(level); + assert(level<Keys.size()); + return Keys[level]->Type(); +} + +mgSortBy +mgSelection::getKeySortBy (const unsigned int level) const +{ + assert(level<Keys.size()); + return Keys[level]->SortBy(); } -mgContentItem * +mgItem * mgSelection::getItem (unsigned int position) { - if (position >= getNumItems ()) + if (position >= items().size()) return 0; - return &(m_items[position]); + return m_items[position]; } @@ -188,17 +325,17 @@ mgSelection::setShuffleMode (mgSelection::ShuffleMode mode) void mgSelection::Shuffle() const { - unsigned int numitems = getNumItems(); + unsigned int numitems = items().size(); if (numitems==0) return; switch (m_shuffle_mode) { case SM_NONE: { - long id = m_items[getItemPosition()].getItemid (); + long id = m_items[getItemPosition()]->getItemid (); m_current_tracks = ""; // force a reload - numitems = getNumItems(); // getNumItems also reloads + numitems = items().size(); // also reloads for (unsigned int i = 0; i < numitems; i++) - if (m_items[i].getItemid () == id) + if (m_items[i]->getItemid () == id) { m_items_position = i; break; @@ -209,7 +346,7 @@ mgSelection::Shuffle() const case SM_NORMAL: { // play all, beginning with current item: - mgContentItem tmp = m_items[getItemPosition()]; + mgItem* tmp = m_items[getItemPosition()]; m_items[getItemPosition()]=m_items[0]; m_items[0]=tmp; m_items_position = 0; @@ -223,7 +360,7 @@ mgSelection::Shuffle() const } } break; /* - * das kapiere ich nicht... (wolfgang) + - Party mode (see iTunes) - initialization - find 15 titles according to the scheme below @@ -251,136 +388,101 @@ mgSelection::LoopMode mgSelection::toggleLoopMode () 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 numitems = getNumItems (); - if (numitems==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(numitems)+","+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()) - numitems + 1; - - // replace the place holder trackid by the correct value: - m_db.exec_sql("UPDATE playlistitem SET trackid="+ltos(m_items[numitems-1].getItemid())+ - " WHERE playlist="+listid+" AND trackid="+trackid); - - // insert all other items: - const char *sql_prefix = "INSERT INTO playlistitem VALUES "; - sql = ""; - for (unsigned int i = 0; i < numitems-1; i++) - { - string item = "(" + listid + "," + ltos (first + i) + "," + - ltos (m_items[i].getItemid ()) + ")"; - 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 numitems; + int result = m_db->AddToCollection(Name,items(),0); + if (result>0) + if (inCollection(Name)) clearCache (); + return result; } 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(KeyMaps.id(keyCollection,Name)); - m_db.exec_sql (sql); - unsigned int removed = m_db.affected_rows (); - if (inCollection(Name)) clearCache (); - return removed; + mgParts p = SelParts(false,false); + unsigned int result = m_db->RemoveFromCollection(Name,items(),&p); + if (result>0) + if (inCollection(Name)) clearCache (); + return result; } 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); + bool result = m_db->DeleteCollection (Name); + if (result) + if (isCollectionlist()) clearCache (); + return result; } void mgSelection::ClearCollection (const string Name) { - if (!m_db.Connected()) return; - string listid = KeyMaps.id(keyCollection,Name); - m_db.exec_sql ("DELETE FROM playlistitem WHERE playlist="+m_db.sql_string(listid)); + m_db->ClearCollection (Name); 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; + bool result = m_db->CreateCollection (Name); + if (result) + if (isCollectionlist()) clearCache (); + return result; } string mgSelection::exportM3U () { - + enter(); // 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 numitems = getNumItems (); + unsigned int numitems = items().size(); for (unsigned i = 0; i < numitems; i++) { - mgContentItem& t = m_items[i]; - fprintf (listfile, "#EXTINF:%d,%s\n", t.getDuration (), - t.getTitle ().c_str ()); - fprintf (listfile, "#MUGGLE:%ld\n", t.getItemid()); - fprintf (listfile, "%s\n", t.getSourceFile (false).c_str ()); + mgItem* t = m_items[i]; + fprintf (listfile, "#EXTINF:%d,%s\n", t->getDuration (), + t->getTitle ().c_str ()); + fprintf (listfile, "#MUGGLE:%ld\n", t->getItemid()); + fprintf (listfile, "%s\n", t->getSourceFile (false).c_str ()); } fclose (listfile); + leave(); return fn; } bool mgSelection::empty() { - if (m_level>= order.size ()-1) - return ( getNumItems () == 0); - else - return ( listitems.size () == 0); + return ( listitems.size () == 0); } void mgSelection::setPosition (unsigned int position) { - if (m_level == order.size()) - mgError("setPosition:m_level==order.size()"); + assert(m_level<ordersize()); m_position = position; } void +mgSelection::setPosition(string value) +{ + setPosition (listitems.valindex (value)); +} + +unsigned int +mgSelection::searchPosition(string search) +{ + int res = listitems.search (search); + if (res>=0) + setPosition (res); + return gotoPosition(); +} + +void mgSelection::GotoItemPosition (unsigned int position) const { m_items_position = position; @@ -390,24 +492,27 @@ mgSelection::GotoItemPosition (unsigned int position) const unsigned int mgSelection::getPosition () const { - if (m_level == order.size()) - mgError("getPosition:m_level==order.size()"); + assert(m_level<ordersize()); return m_position; } unsigned int mgSelection::gotoPosition () { - if (m_level == order.size()) - mgError("gotoPosition:m_level==order.size()"); + assert(m_level<ordersize()); unsigned int itemsize = listitems.size(); if (itemsize==0) m_position = 0; else if (m_position >= itemsize) m_position = itemsize -1; + if (itemsize==0) + Key(m_level)->set (0); + else + Key(m_level)->set (listitems[m_position]); return m_position; } + unsigned int mgSelection::getItemPosition() const { @@ -422,7 +527,7 @@ mgSelection::getItemPosition() const unsigned int mgSelection::gotoItemPosition() { - unsigned int numitems = getNumItems (); + unsigned int numitems = items().size(); if (numitems == 0) { m_items_position = 0; @@ -435,7 +540,7 @@ mgSelection::gotoItemPosition() bool mgSelection::skipItems (int steps) const { - unsigned int numitems = getNumItems(); + unsigned int numitems = items().size(); if (numitems == 0) { m_items_position=0; @@ -462,8 +567,9 @@ bool mgSelection::skipItems (int steps) const m_items_position = new_pos; while (true) { - if (m_items[m_items_position].Valid()) + if (m_items[m_items_position]->Valid()) break; + delete m_items[m_items_position]; m_items.erase(m_items.begin()+m_items_position); if (m_items.size()==0) { @@ -482,9 +588,9 @@ unsigned long mgSelection::getLength () { unsigned long result = 0; - unsigned int numitems = getNumItems (); + unsigned int numitems = items().size(); for (unsigned int i = 0; i < numitems; i++) - result += m_items[i].getDuration (); + result += m_items[i]->getDuration (); return result; } @@ -495,7 +601,7 @@ mgSelection::getCompletedLength () const unsigned long result = 0; items (); // make sure they are loaded for (unsigned int i = 0; i < getItemPosition(); i++) - result += m_items[i].getDuration (); + result += m_items[i]->getDuration (); return result; } @@ -505,16 +611,14 @@ string mgSelection::getListname () const { list<string> st; for (unsigned int i = 0; i < m_level; i++) - st.push_back(order.getKeyItem(i).value()); + st.push_back(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))); + if (ordersize()>0) + result = string(ktName(getKeyType(0))); return result; } @@ -539,23 +643,61 @@ string mgSelection::ListFilename () return res; } -const vector < mgContentItem > & +mgParts +mgSelection::SelParts(bool distinct, bool deepsort) const +{ + assert(m_level<ordersize()); + mgKey *high = Keys[m_level]; + mgListItem* highitem = 0; + if (high->Type()!=keyGdUnique) + { + highitem = high->get(); + high->set(0); + } + mgParts result; + result.orderByCount = m_orderByCount; + for (unsigned int i=0;i<ordersize();i++) + { + if (!deepsort && i>m_level) + break; + if (NeedKey(i)) + result += Keys[i]->Parts(m_db,distinct&&i==m_level); + } + if (highitem) + { + high->set(highitem); + delete highitem; + } + return result; +} + +const vector < mgItem* > & mgSelection::items () const { - if (!m_db.Connected()) return m_items; - if (!m_current_tracks.empty()) return m_items; - m_current_tracks=order.GetContent(m_db,m_level,m_items); - if (m_shuffle_mode!=SM_NONE) - Shuffle(); + if (m_current_tracks.empty()) + { + mgParts p = SelParts(false,true); + m_current_tracks = m_db->LoadItemsInto(p,m_items); + if (m_shuffle_mode==SM_NONE) + { + if (!inCollection("")) + { + for (unsigned int i=0;i<m_items.size();i++) + m_items[i]->setSelection(this); + std::sort(m_items.begin(),m_items.end(),compitem); + } + } + else + Shuffle(); + } return m_items; } void mgSelection::InitSelection() { - m_level = 0; + m_active = false; m_position = 0; m_items_position = 0; - m_itemid = -1; if (the_setup.InitShuffleMode) m_shuffle_mode = SM_NORMAL; else @@ -566,6 +708,8 @@ void mgSelection::InitSelection() { m_loop_mode = LM_NONE; clearCache(); listitems.setOwner(this); + clear(); + m_orderByCount = false; } @@ -585,399 +729,425 @@ mgSelection::mgSelection (const mgSelection* s) InitFrom(s); } -mgSelection::mgSelection (mgValmap& nv) + +void mgSelection::DumpState(mgValmap& nv, const char *prefix) const { - InitFrom(nv); + nv.put(m_fall_through,"%s.FallThrough",prefix); + nv.put(m_orderByCount,"%s.OrderByCount",prefix); + for (unsigned int i=0;i<ordersize();i++) + { + nv.put(int(Key(i)->Type()),"%s.Keys.%d.Type",prefix,i); + if (i<=m_level) + nv.put(getKeyItem(i)->value(), + "%s.Keys.%u.Position",prefix,i); + } } -void -mgSelection::setOrder(mgOrder* o) + +void mgSelection::ShowState(char *w) const { - if (o) - { - order = *o; + mgDebug(1,"ShowState:%s,m_level=%d",w,m_level); + for (unsigned int i=0;i<ordersize();i++) + { + mgDebug(1," %d:Type=%s,val=%s,id=%s", + i,ktName(Key(i)->Type()), + getKeyItem(i)->value().c_str(), + getKeyItem(i)->id().c_str()); } - else - mgWarning("mgSelection::setOrder(0)"); } - void -mgSelection::InitFrom(mgValmap& nv) +mgSelection::InitFrom(const char *prefix,mgValmap& nv) { - extern time_t createtime; - if (m_db.ServerConnected() && !m_db.Connected() - && (time(0)>createtime+10)) + InitSelection(); + clear(); + m_fall_through = true; + setOrderByCount(nv.getbool("%s.OrderByCount",prefix)); + for (unsigned int idx = 0 ; idx < 999 ; idx++) { - 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 items?"))) - { - mgThreadSync *s = mgThreadSync::get_instance(); - if (s) - { - extern char *sync_args[]; - s->Sync(sync_args,(bool)the_setup.DeleteStaleReferences); + unsigned int type = nv.getuint("%s.Keys.%u.Type",prefix,idx); + if (type==0) break; + setKey(mgKeyTypes(type)); + } + vector<mgListItem> items; + for (unsigned int idx = 0 ; idx < ordersize() ; idx++) + { + char b[100]; + sprintf(b,"%s.Keys.%u.Position",prefix,idx); + if (!nv.count(b)) break; + string newval = nv.getstr(b); + items.push_back(mgListItem(newval,KeyMaps.id(Keys[idx]->Type(),newval),0)); + } + if (ordersize() && Keys[ordersize()-1]->Type()!=keyGdUnique) + setKey(keyGdUnique); + InitOrder(items); +} + + +void +mgSelection::CopyKeyValues(mgSelection* s) +{ + if (!s) + mgError("mgSelection::CopyKeyValues(0)"); + if (s==this) + return; + mgItem *o=0; + s->enter(); + if (s->items().size()==1) + o = s->getItem(0)->Clone(); + SetLevel(0); + vector<mgListItem> items; + for (unsigned int idx = 0; idx < ordersize(); idx++) + { + bool found = false; + mgKeyTypes new_kt = getKeyType(idx); + if (o && o->getItemid()>=0) + { + items.push_back(o->getKeyItem(new_kt)); + found = true; + continue; + } + if (s) for (unsigned int i=0;i<s->ordersize();i++) + { + mgKeyTypes old_kt = s->getKeyType(i); + if (old_kt==new_kt && s->getKeyItem(i)) + { + items.push_back(s->getKeyItem(i)); + found = true; + break; } - } - } - free(b); + } + if (found) + continue; + if (!DeduceKeyValue(new_kt,s,items)) + break; } - InitSelection(); - m_fall_through = nv.getbool("FallThrough"); - while (m_level < nv.getuint("Level")) + delete o; + assert(items.size()<=ordersize()); + InitOrder(items); +} + +void +mgSelection::InitOrder(vector<mgListItem>& items) +{ + mgDebug(5,"InitOrder:"); + for (unsigned int idx = 0; idx < items.size(); idx++) + mgDebug(5,"%d:%s/%s",idx,items[idx].value().c_str(),items[idx].id().c_str()); + if (ordersize()==0) + return; + for (unsigned int idx = 0; idx < ordersize(); idx++) + Key(idx)->set(0); + for (unsigned int idx = 0; idx < items.size(); idx++) + Key(idx)->set (&items[idx]); + m_active = false; +} + +void +mgSelection::Activate() +{ + assert(ordersize()); + if (m_level) + assert(m_level<ordersize()); + if (m_active) + return; + m_active = true; + m_level = 0; + for (unsigned int lev = 0; lev < ordersize(); lev++) { - char *idx; - asprintf(&idx,"order.Keys.%u.Position",m_level); - string newval = nv.getstr(idx); - free(idx); - if (!enter (newval)) - if (!select (newval)) break; + if (!getKeyItem(lev)) + break; + m_level = lev; } - assert(m_level<=order.size()); - m_itemid = nv.getlong("ItemId"); - if (m_level==order.size()) - m_items_position = nv.getlong("ItemPosition"); + mgListItem* item = getKeyItem(m_level); + if (item) + setPosition(item->value()); else - setPosition(nv.getstr("Position")); + setPosition(0); + if (inItem()) + DecLevel(); } - mgSelection::~mgSelection () { + m_level=0; + truncate(0); + delete m_db; } void mgSelection::InitFrom(const mgSelection* s) { InitSelection(); + if (!s) + return; + for (unsigned int i = 0; i < s->ordersize();i++) + { + mgKey *k = ktGenerate(s->getKeyType(i)); + k->set(s->getKeyItem(i)); + Keys.push_back(k); + } + m_active = s->m_active; + SetLevel(s->m_level); + if (m_level) + assert(m_level<ordersize()); m_fall_through = s->m_fall_through; - order = s->order; - m_level = s->m_level; + m_orderByCount = s->m_orderByCount; m_position = s->m_position; - m_itemid = s->m_itemid; m_items_position = s->m_items_position; setShuffleMode (s->getShuffleMode ()); setLoopMode (s->getLoopMode ()); } -unsigned int -mgSelection::valcount (string value) -{ - return listitems[listitems.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(); - listitems.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; - mgListItem 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])); - listitems.push_back(n); - } - mysql_free_result (rows); - } - } + assert(m_db); + if (!m_current_values.empty()) + return; + mgParts p = SelParts(true,false); + m_current_values = m_db->LoadValuesInto( + p,getKeyType(m_level),listitems.items(),m_level<ordersize()-2); + if (!inCollection("")) + listitems.sort(m_orderByCount,Keys[m_level]->SortBy()); } -unsigned int -mgSelection::count () const + +void +mgSelection::DecLevel() { - return listitems.size (); + m_level--; + clearCache(); } +void +mgSelection::IncLevel() +{ + m_level++; + clearCache(); +} + +void +mgSelection::SetLevel(unsigned int level) +{ + m_level=level; + clearCache(); +} bool mgSelection::enter (unsigned int position) { - if (order.empty()) - mgWarning("mgSelection::enter(%u): order is empty", position); - if (empty()) - return false; - if (m_level == order.size ()) + assert(!Keys.empty()); + if (inItem()) return false; - setPosition (position); - position = gotoPosition(); // reload adjusted position - if (listitems.size()==0) + if (empty()) + refreshValues(); + if (empty()) return false; - mgListItem item = listitems[position]; + mgDebug(5,"%X:level %d:enter(%d)",this,m_level,position); + if (inCollection()) + { + mgListItem *item=Key(m_level)->get(); + IncLevel(); + Key(m_level)->set(item); + setPosition(0); + gotoPosition(); + return true; + } mgListItems prev; - if (m_fall_through && listitems.size()<10) - prev=listitems; + if (m_level<ordersize()-2 && m_fall_through && listitems.size()<100) + prev=listitems; while (1) { - if (m_level >= order.size () - 1) - return false; - order[m_level++]->set (item); - clearCache(); - m_position = 0; + setPosition(position); + position = gotoPosition(); // reload adjusted position + Key(m_level)->set (listitems[position]); + mgDebug(5,"enter:level=%d,set to %s",m_level,getCurrentValue().c_str()); + IncLevel(); refreshValues(); - if (count()==0) - break; - item=listitems[0]; + position = 0; + if (empty()) + break; if (!m_fall_through) break; - if (m_level==order.size()-1) + if (m_level>=ordersize()-2) break; - if (count () > 1 && !(prev==listitems)) + if (listitems.size () > 1 && !(prev==listitems)) break; } + setPosition(position); + position = gotoPosition(); + mgDebug(5,"enter exits:level=%d,set to %s",m_level,getCurrentValue().c_str()); return true; } - -bool mgSelection::select (unsigned int position) -{ - if (m_level == order.size () - 1) - { - if (getNumItems () <= position) - return false; - order[m_level]->set (listitems[position]); - m_level++; - m_itemid = m_items[position].getItemid (); - - 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_itemid = -1; - clearCache(); - setPosition(prevvalue); - return true; - } + unsigned int position=m_position; + assert(!Keys.empty()); mgListItems prev; - if (m_fall_through && listitems.size()<10) - prev=listitems; + if (m_level>1 && m_fall_through && listitems.size()<100) + prev=listitems; while (1) { - if (m_level < 1) + setPosition(position); + position = gotoPosition(); // reload adjusted position + Key(m_level)->set(0); + if (m_level==0) 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(); + DecLevel(); + refreshValues(); + position = listitems.valindex (getKeyItem(m_level)->value()); if (!m_fall_through) break; - if (count () > 1 && !(prev==listitems)) + if (m_level==0) + break; + if (listitems.size () > 1 && !(prev==listitems)) break; } - setPosition(prevvalue); + setPosition(position); return true; } void mgSelection::leave_all () { - m_level=0; - for (unsigned int i=0;i<order.size();i++) - order[i]->set (zeroitem); - clearCache(); + SetLevel(0); + for (unsigned int i=0;i<ordersize();i++) + Key(i)->set (0); } -void -mgSelection::selectfrom(mgOrder& oldorder,mgContentItem* o) + +void +mgSelection::truncate(unsigned int i) { - leave_all(); - mgListItem selitem; - assert(m_level==0); - for (unsigned int idx = 0; idx < order.size(); 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=KeyMaps.id(new_kt,KeyMaps.value(new_kt,oldorder.getKeyItem(i).id())); - selitem=mgListItem (KeyMaps.value(new_kt,selid),selid); - } - if (selitem.valid()) break; - } - if (!selitem.valid() && o && o->getItemid()>=0) - selitem = o->getKeyItem(new_kt); - if (!selitem.valid()) - break; - if (m_level<order.size()-1) - { - order[m_level++]->set (selitem); - } - else - { - setPosition(selitem.value()); - return; - } - } - if (m_level>0) + while (ordersize()>i) { - m_level--; - selitem = order.getKeyItem(m_level); - order[m_level]->set(zeroitem); - setPosition(selitem.value()); - order[m_level+1]->set(zeroitem); + delete Keys.back(); + Keys.pop_back(); } - assert(m_level<order.size()); } - -string -mgSelection::value(mgKey* k, string idstr) const -{ - return KeyMaps.value(k->Type(),idstr); -} - -string -mgSelection::value(mgKey* k) const +void +mgSelection::setKey (const mgKeyTypes kt) { - return value(k,k->id()); + mgKey *newkey = ktGenerate(kt); + if (newkey) + Keys.push_back(newkey); } - -string -mgSelection::id(mgKey* k, string val) const +void +mgSelection::setKeys(vector<const char *>& kt) { - return KeyMaps.id(k->Type(),val); + clear(); + for (unsigned int i=0;i<kt.size();i++) + { + setKey(ktValue(kt[i])); + } + clean(); } -string -mgSelection::id(mgKey* k) const +void +mgSelection::clear() { - return k->id(); + m_level=0; + clearCache(); + truncate(0); } -bool mgSelection::isLanguagelist() const +void +mgSelection::clean() { - return (order.getKeyType(0) == keyLanguage); + // remove double entries: + keyvector::iterator a; + keyvector::iterator b; + for (a = Keys.begin () ; a != Keys.end (); ++a) + { +cleanagain: + for (b = a+1 ; b != Keys.end(); ++b) + if ((*a)->Type() == (*b)->Type()) + { + delete *b; + Keys.erase(b); + goto cleanagain; + } + } } -bool mgSelection::isCollectionlist () const +string +mgSelection::Name() { - if (order.size()==0) return false; - return (order.getKeyType(0) == keyCollection && m_level == 0); + string result=""; + if (ordersize()>0) + for (unsigned int idx=0;idx<ordersize()-1;idx++) + { + if (!result.empty()) result += ":"; + result += ktName(Keys[idx]->Type()); + } + return result; } bool -mgSelection::inCollection(const string Name) const +mgSelection::SameOrder(const mgSelection* other) { - if (order.size()==0) return false; - bool result = (order.getKeyType(0) == keyCollection && m_level == 1); + bool result = ordersize()==other->ordersize() && m_orderByCount == other->m_orderByCount; if (result) - if (order.getKeyType(1) != keyCollectionItem) - mgError("inCollection: key[1] is not keyCollectionItem"); - if (!Name.empty()) - result &= (order.getKeyItem(0).value() == Name); + for (unsigned int i=0; i<ordersize();i++) + { + result &= Key(i)->Type()==other->Key(i)->Type(); + if (!result) break; + } return result; } - -void mgSelection::DumpState(mgValmap& nv) const +mgKey* +mgSelection::Key(unsigned int idx) const { - nv.put("FallThrough",m_fall_through); - 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("ItemId",m_itemid); - nv.put("Position",listitems[m_position].value()); - if (m_level>=order.size()-1) - nv.put("ItemPosition",getItemPosition()); + assert(idx<ordersize()); + return Keys[idx]; } -map <mgKeyTypes, string> -mgSelection::UsedKeyValues() +bool +mgSelection::UsedBefore(const mgKeyTypes kt,unsigned int level) const { - map <mgKeyTypes, string> result; - for (unsigned int idx = 0 ; idx < m_level ; idx++) - { - result[order.getKeyType(idx)] = order.getKeyItem(idx).value(); - } - if (m_level < order.size()-1) - { - mgKeyTypes ch = order.getKeyType(m_level); - result[ch] = getCurrentValue(); - } - return result; + for (unsigned int lx = 0; lx < level; lx++) + if (getKeyType(lx)==kt) + return true; + return false; } static vector<int> keycounts; unsigned int -mgSelection::keycount(mgKeyTypes kt) +mgSelection::keycount(mgKeyTypes kt) const { if (keycounts.size()==0) { - for (unsigned int ki=int(mgKeyTypesLow);ki<=int(mgKeyTypesHigh);ki++) + for (unsigned int ki=(unsigned int)(ktLow());ki<=(unsigned int)(ktHigh());ki++) { keycounts.push_back(-1); } } - int& count = keycounts[int(kt-mgKeyTypesLow)]; - if (count==-1) + int& kcount = keycounts[int(kt-ktLow())]; + if (kcount==-1) { mgKey* k = ktGenerate(kt); if (k->Enabled(m_db)) - count = m_db.exec_count(k->Parts(m_db,true).sql_count()); + kcount = m_db->exec_count(k->Parts(m_db,true).sql_count()); else - count = 0; + kcount = 0; delete k; } - return count; + return kcount; } +mgKeyTypes +mgSelection::ktValue(const char * name) const +{ + for (int kt=int(ktLow());kt<=int(ktHigh());kt++) + if (!strcmp(name,ktName(mgKeyTypes(kt)))) + return mgKeyTypes(kt); + mgError("ktValue(%s): unknown name",name); + return mgKeyTypes(0); +} |