summaryrefslogtreecommitdiff
path: root/mg_selection.c
diff options
context:
space:
mode:
Diffstat (limited to 'mg_selection.c')
-rw-r--r--mg_selection.c974
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);
+}