summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b>2005-02-07 09:17:20 +0000
committerLarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b>2005-02-07 09:17:20 +0000
commit14f5032e141da131f3d1f72979cf415d6105d819 (patch)
tree2977f746c00056a1ad1420b99b98dc46920ff365
parent19b5fbe6f03d263ab8e427022156ed7d2bfcaea1 (diff)
downloadvdr-plugin-muggle-14f5032e141da131f3d1f72979cf415d6105d819.tar.gz
vdr-plugin-muggle-14f5032e141da131f3d1f72979cf415d6105d819.tar.bz2
Merged most recent stuff from osd_extensions trunk for next release (0.1.2)
git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@447 e10066b5-e1e2-0310-b819-94efdf66514b
-rw-r--r--Makefile28
-rw-r--r--i18n.c51
-rw-r--r--mg_actions.c469
-rw-r--r--mg_actions.h40
-rw-r--r--mg_db.c698
-rw-r--r--mg_db.h89
-rw-r--r--mg_order.c492
-rw-r--r--mg_order.h41
-rw-r--r--mg_tools.c3
-rw-r--r--muggle.c2
-rwxr-xr-xmugglei.c168
-rwxr-xr-xscripts/languages.txt1
-rw-r--r--vdr_decoder.c20
-rw-r--r--vdr_decoder_flac.c9
-rw-r--r--vdr_decoder_mp3.c4
-rw-r--r--vdr_decoder_ogg.c3
-rw-r--r--vdr_menu.c373
-rw-r--r--vdr_menu.h53
-rw-r--r--vdr_player.c22
-rw-r--r--vdr_setup.c51
-rw-r--r--vdr_setup.h1
-rw-r--r--vdr_sound.c7
-rw-r--r--vdr_stream.c2
23 files changed, 1657 insertions, 970 deletions
diff --git a/Makefile b/Makefile
index 3e962b6..1dd8773 100644
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,15 @@
#
PLUGIN = muggle
+#if you want ogg / flac support, remove the trailing x in those two
+#definitions:
+define have_vorbisfile
+1
+endef
+define have_flac
+1
+endef
+
### The version number of this plugin (taken from the main source file):
VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g')
@@ -16,7 +25,7 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri
### The C++ compiler and options:
CXX ?= g++-3.3
-CXXFLAGS ?= -fPIC -O2 -Wall -Woverloaded-virtual -Wno-deprecated -g
+CXXFLAGS ?= -fPIC -O0 -Wall -Woverloaded-virtual -Wno-deprecated -g
### The directory environment:
@@ -44,7 +53,7 @@ PACKAGE = vdr-$(ARCHIVE)
INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include \
-I/usr/include/mysql/ -I/usr/include/taglib
-DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' # -DHAVE_VORBISFILE -DHAVE_FLAC
+DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
MIFLAGS += -I/usr/include/taglib -lmysqlclient
@@ -52,11 +61,22 @@ MIFLAGS += -I/usr/include/taglib -lmysqlclient
OBJS = $(PLUGIN).o i18n.o mg_valmap.o mg_order.o mg_db.o mg_actions.o vdr_menu.o mg_tools.o \
vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o \
- vdr_setup.o vdr_decoder_ogg.o vdr_decoder_flac.o
+ vdr_setup.o
-LIBS = -lmad -lmysqlclient # -lvorbisfile -lvorbis -lFLAC++
+LIBS = -lmad -lmysqlclient
MILIBS = -lmysqlclient -ltag
+ifdef have_vorbisfile
+DEFINES += -DHAVE_VORBISFILE
+OBJS += vdr_decoder_ogg.o
+LIBS += -lvorbisfile -lvorbis
+endif
+ifdef have_flac
+DEFINES += -DHAVE_FLAC
+OBJS += vdr_decoder_flac.o
+LIBS += -lFLAC++
+endif
+
### Targets:
all: libvdr-$(PLUGIN).so mugglei
diff --git a/i18n.c b/i18n.c
index 13b7fea..6ba1065 100644
--- a/i18n.c
+++ b/i18n.c
@@ -13,6 +13,40 @@ const tI18nPhrase Phrases[] =
{
{
+ "Key %d",
+ "Schlüsselfeld %d",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Key %d", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ {
+ "Create",
+ "Neu",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Nouveau", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ {
"Search",
"Suchen",
"", // TODO
@@ -98,6 +132,23 @@ const tI18nPhrase Phrases[] =
"", // TODO
},
{
+ "Create order",
+ "Sortierung neu anlegen",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Créer un ordre nouveaux", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ {
"Create collection",
"Sammlung neu anlegen",
"", // TODO
diff --git a/mg_actions.c b/mg_actions.c
index 48b06f3..42337d9 100644
--- a/mg_actions.c
+++ b/mg_actions.c
@@ -36,33 +36,48 @@ IsEntry(mgActions i)
class mgOsdItem : public mgAction, public cOsdItem
{
public:
- eOSState ProcessKey(eKeys key);
- protected:
- virtual eOSState Process(eKeys key) { return osUnknown; }
+ eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); }
};
+
eOSState
-mgOsdItem::ProcessKey(eKeys key)
+mgAction::ProcessKey(eKeys key)
{
+ if (key!=kNone)
+ mgDebug(1,"mgAction::ProcessKey(%d)",key);
eOSState result = Process(key);
if (result != osUnknown)
IgnoreNextEvent = true;
return result;
}
+class mgNone: public mgOsdItem
+{ public:
+ void Notify() {};
+ bool Enabled(mgActions on) { return false; }
+ eOSState Process(eKeys key) { return osUnknown; }
+};
+
//! \brief used for normal data base items
class mgEntry : public mgOsdItem
{
public:
void Notify();
bool Enabled(mgActions on) { return IsEntry(on);}
- const char *ButtonName() { return ""; }
const char *MenuName (const unsigned int idx,const string value);
- virtual eOSState Process(eKeys key);
+ eOSState Process(eKeys key);
void Execute();
eOSState Back();
};
+class mgKeyItem : public mgAction, public cMenuEditStraItem
+{
+ public:
+ mgKeyItem(const char *Name, int *Value, int NumStrings, const char *const *Strings) : cMenuEditStraItem(Name, Value, NumStrings, Strings) {}
+ eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); }
+ eOSState Process(eKeys key);
+};
+
class mgDoCollEntry : public mgEntry
{
public:
@@ -100,7 +115,6 @@ mgDoCollEntry::Process(eKeys key)
eOSState result = osContinue;
switch (key)
{
- case kBlue:
case kBack:
break;
case kOk:
@@ -202,9 +216,60 @@ class mgCommand : public mgOsdItem
public:
bool Enabled(mgActions on);
virtual eOSState Process(eKeys key);
+};
+
+class mgActOrder : public mgOsdItem
+{
+ public:
+ const char* MenuName(const unsigned int idx,const string value);
+ virtual eOSState Process(eKeys key);
void Execute();
};
+const char*
+mgActOrder::MenuName(const unsigned int idx,const string value)
+{
+ return strdup(value.c_str());
+}
+
+eOSState
+mgActOrder::Process(eKeys key)
+{
+ mgMenu *n = osd ()->newmenu;
+ osd ()->newmenu = NULL;
+ eOSState result = osContinue;
+ switch (key)
+ {
+ case kBack:
+ break;
+ case kOk:
+ Execute ();
+ break;
+ default:
+ osd ()->newmenu = n; // wrong key: stay in submenu
+ result = osUnknown;
+ break;
+ }
+ return result;
+}
+
+void
+mgActOrder::Execute()
+{
+ mgSelection *s = osd()->selection();
+ mgOrder oldorder = s->getOrder();
+ mgContentItem o;
+ s->select();
+ if (s->getNumTracks()==1)
+ o = s->getTrack(0);
+ osd()->UseNormalSelection(); // Default for all orders
+ osd()->setOrder(s,osd()->Current());
+ mgSelection *newsel = osd()->selection();
+ newsel->selectfrom(oldorder,&o);
+ osd()->newposition = newsel->getPosition();
+ osd()->SaveState();
+}
+
bool
mgCommand::Enabled(mgActions on)
{
@@ -321,7 +386,6 @@ mgCommand::Process(eKeys key)
else
parent->TreeYellowAction = Type();
break;
- case kBlue:
case kBack:
break;
case kOk:
@@ -335,16 +399,11 @@ mgCommand::Process(eKeys key)
return result;
}
-void
-mgCommand::Execute()
-{
-}
class mgExternal : public mgCommand
{
public:
const char *ButtonName();
- const char *MenuName (const unsigned int idx,const string value);
void Execute();
bool Enabled(mgActions on) { return true; }
private:
@@ -385,12 +444,6 @@ mgExternal::ButtonName()
return "";
}
-const char*
-mgExternal::MenuName(const unsigned int idx,const string value)
-{
- return strdup(ButtonName());
-}
-
cCommand *
mgExternal::Command()
{
@@ -426,7 +479,17 @@ mgExternal::Execute()
if (!m3u_file.empty ())
{
/*char *result = (char *)*/
- command->Execute (m3u_file.c_str ());
+ string quoted = "'" + m3u_file + "'";
+ char prev[1000];
+ if (!getcwd(prev,1000))
+ mgError("current path too long");
+ if (chdir(the_setup.ToplevelDir))
+ mgError("cannnot change to directory %s",
+ the_setup.ToplevelDir);
+ command->Execute (quoted.c_str ());
+ chdir(prev);
+ selection()->clearCache();
+ osd()->forcerefresh = true; // the ext cmd could change the database
/* What to do? Recode cMenuText (not much)?
if (result)
{
@@ -475,17 +538,91 @@ mgChooseOrder::Process(eKeys key)
void mgChooseOrder::Execute()
{
osd ()->newmenu = new mgMenuOrders;
+ osd ()->newposition = osd()->getCurrentOrder();
+
+}
+
+class mgEditOrder : public mgCommand
+{
+ public:
+ bool Enabled(mgActions on) { return true; }
+ eOSState Process(eKeys key);
+ void Execute () { osd ()->newmenu = new mgMenuOrder; }
+ const char *ButtonName() { return tr("Edit"); }
+};
+
+eOSState
+mgEditOrder::Process(eKeys key)
+{
+ if (key == kOk)
+ {
+ Execute();
+ return osContinue;
+ }
+ else
+ return mgCommand::Process(key);
+}
+
+class mgCreateOrder : public mgCommand
+{
+ public:
+ bool Enabled(mgActions on) { return true; }
+ void Execute ();
+ const char *ButtonName() { return tr("Create"); }
+};
+void
+mgCreateOrder::Execute()
+{
+ osd()->AddOrder();
+ osd()->SaveState();
+ osd()->forcerefresh = true;
}
+class mgDeleteOrder : public mgCommand
+{
+ public:
+ bool Enabled(mgActions on) { return true; }
+ void Execute ();
+ const char *ButtonName() { return tr("Delete"); }
+};
+
+void
+mgDeleteOrder::Execute()
+{
+ osd()->DeleteOrder();
+ osd()->SaveState();
+ osd()->forcerefresh = true;
+ osd()->newposition = osd()->Current();
+}
+
+//! \brief show the normal selection list
+class mgShowList: public mgOsdItem
+{
+ public:
+ bool Enabled(mgActions) { return true; }
+ const char *ButtonName () { return tr("List"); }
+ void Execute() { osd()->newmenu=NULL; }
+};
+
+
+//! \brief show the command submenu
+class mgShowCommands: public mgOsdItem
+{
+ public:
+ bool Enabled(mgActions on) { return true; }
+ const char *ButtonName () { return tr("Commands"); }
+ void Execute() { osd()->newmenu = new mgSubmenu; }
+};
+
+
//! \brief toggles between the normal and the collection selection
class mgToggleSelection:public mgCommand
{
public:
- bool Enabled(mgActions on = mgActions(0)) { return true; }
+ bool Enabled(mgActions on) { return true; }
void Execute ();
const char *ButtonName ();
- const char *MenuName (const unsigned int idx,const string value);
};
const char *
@@ -497,17 +634,6 @@ mgToggleSelection::ButtonName ()
return tr ("Collections");
}
-
-const char *
-mgToggleSelection::MenuName (const unsigned int idx,const string value)
-{
- if (osd ()->UsingCollection)
- return strdup(tr ("Search"));
- else
- return strdup(tr ("Collections"));
-}
-
-
void
mgToggleSelection::Execute ()
{
@@ -526,7 +652,7 @@ mgToggleSelection::Execute ()
class mgSetDefaultCollection:public mgCommand
{
public:
- bool Enabled(mgActions on = mgActions(0));
+ bool Enabled(mgActions on);
void Execute ();
const char *ButtonName ()
{
@@ -567,11 +693,7 @@ class mgSetButton : public mgCommand
{
public:
bool Enabled(mgActions on) { return true; }
- const char *ButtonName()
- {
- return tr("Set");
- }
- const char *MenuName(const unsigned int idx,const string value) { return strdup(""); }
+ const char *ButtonName() { return tr("Set"); }
};
@@ -579,19 +701,9 @@ class mgSetButton : public mgCommand
class mgInstantPlay : public mgCommand {
public:
void Execute ();
- const char *ButtonName ()
- {
- return tr ("Instant play");
- }
- const char *MenuName (const unsigned int idx,const string value);
+ const char *ButtonName () { return tr ("Instant play"); }
};
-const char *
-mgInstantPlay::MenuName (const unsigned int idx,const string value)
-{
- return strdup(tr("Instant play"));
-}
-
void
mgInstantPlay::Execute()
{
@@ -636,7 +748,8 @@ mgAddAllToCollection::ExecuteMove()
char *b;
asprintf(&b,tr("'%s' to collection"),selection()->getCurrentValue().c_str());
osd ()->newmenu = new mgTreeAddToCollSelector(string(b));
- osd ()->newposition = osd()->collselection()->getPosition(0);
+ osd ()->collselection()->leave_all();
+ osd ()->newposition = osd()->collselection()->getPosition();
free(b);
}
@@ -699,7 +812,7 @@ mgAddAllToDefaultCollection::ExecuteSelection (mgSelection *s)
class mgAddThisToCollection:public mgAddAllToCollection
{
public:
- bool Enabled(mgActions on = mgActions(0));
+ bool Enabled(mgActions on);
void Execute ();
const char *ButtonName ();
const char *MenuName (const unsigned int idx,const string value);
@@ -738,7 +851,7 @@ mgAddThisToCollection::MenuName (const unsigned int idx,const string value)
class mgAddThisToDefaultCollection:public mgAddAllToDefaultCollection
{
public:
- bool Enabled(mgActions on = mgActions(0));
+ bool Enabled(mgActions on);
void Execute ();
const char *ButtonName ();
const char *MenuName (const unsigned int idx,const string value);
@@ -778,60 +891,6 @@ mgAddThisToDefaultCollection::MenuName (const unsigned int idx,const string valu
return b;
}
-class mgOrderNew : public mgCommand
-{
- protected:
- virtual void NewOrder() = 0;
- public:
- bool Enabled(mgActions on = mgActions(0)) { return true; }
- void Execute();
-};
-
-void
-mgOrderNew::Execute()
-{
- mgSelection *s = osd()->selection();
- map<mgKeyTypes,string>* oldkeys = s->UsedKeyValues();
- mgContentItem o;
- s->select();
- if (s->getNumTracks()==1)
- {
- o = s->getTrack(0);
- }
- osd()->UseNormalSelection(); // Default for all orders
- NewOrder();
- mgSelection *newsel = osd()->selection();
- newsel->leave(0);
- for (unsigned int idx = 0; idx < newsel->ordersize(); idx++)
- {
- string selval = "";
- mgKeyTypes new_kt = newsel->getKeyType(idx);
- if (oldkeys->count(new_kt)>0)
- selval=(*oldkeys)[new_kt];
- else if (o.getId()>=0)
- selval=o.getKeyValue(new_kt);
- if (!selval.empty())
- {
- if (idx<newsel->ordersize()-1)
- newsel->select(selval);
- else
- newsel->setPosition(selval);
- continue;
- }
- break;
- }
- delete oldkeys;
- osd()->newposition = newsel->getPosition(newsel->level());
- newsel->clearCache();
-}
-
-class mgOrderCollItem : public mgOrderNew
-{
- const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Collection -> Item")); }
- void NewOrder() { osd ()->UseCollectionSelection (); }
-};
-
-
//! \brief remove selected items from default collection
class mgRemoveAllFromCollection:public mgCommand
{
@@ -864,7 +923,7 @@ mgRemoveAllFromCollection::MenuName (const unsigned int idx,const string value)
class mgClearCollection : public mgCommand
{
public:
- bool Enabled(mgActions on = mgActions(0));
+ bool Enabled(mgActions on);
void Execute ();
const char *ButtonName ()
{
@@ -922,50 +981,45 @@ mgRemoveThisFromCollection::MenuName (const unsigned int idx,const string value)
return strdup(tr("Remove from a collection"));
}
-/*! \brief create collection directly in the collection list
- */
-class mgCreateCollection: public mgAction, public cMenuEditStrItem
+class mgEditAction : public mgAction
+{
+ public:
+ mgEditAction() : mgAction() { memset(m_value,0,30); }
+ protected:
+ char m_value[30];
+};
+
+class mgCreate : public mgEditAction, public cMenuEditStrItem
{
public:
- mgCreateCollection();
+ mgCreate(const char* mn);
+ virtual bool Enabled(mgActions on)=0;
void Notify();
- bool Enabled(mgActions on = mgActions(0));
- eOSState ProcessKey(eKeys key);
- void Execute ();
- const char *MenuName (const unsigned int idx=0,const string value="");
- private:
+ eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); }
+ eOSState Process(eKeys key);
+ protected:
bool Editing();
- char value[30];
};
+mgCreate::mgCreate(const char *mn) : mgEditAction(), cMenuEditStrItem(mn,m_value,30,tr(FileNameChars))
+{
+}
bool
-mgCreateCollection::Editing()
+mgCreate::Editing()
{
return (strchr(cOsdItem::Text(),'[') && strchr(cOsdItem::Text(),']'));
}
-
void
-mgCreateCollection::Notify()
+mgCreate::Notify()
{
if (!Editing())
m->SetHelpKeys();
}
-const char*
-mgCreateCollection::MenuName(const unsigned int idx,const string value)
-{
- return strdup(tr ("Create collection"));
-}
-
-mgCreateCollection::mgCreateCollection() : mgAction(), cMenuEditStrItem(MenuName(),value,30,tr(FileNameChars))
-{
- value[0]=0;
-}
-
eOSState
-mgCreateCollection::ProcessKey(eKeys key)
+mgCreate::Process(eKeys key)
{
if (key == kOk)
if (Editing())
@@ -978,16 +1032,29 @@ mgCreateCollection::ProcessKey(eKeys key)
return osUnknown;
}
-bool
-mgCreateCollection::Enabled(mgActions on)
+class mgCreateCollection : public mgCreate
+{
+ public:
+ mgCreateCollection();
+ bool Enabled(mgActions on);
+ void Execute ();
+ const char *MenuName (const unsigned int idx=0,const string value="");
+};
+
+mgCreateCollection::mgCreateCollection() : mgCreate(MenuName())
{
- return selection()->isCollectionlist();
+}
+
+const char*
+mgCreateCollection::MenuName(const unsigned int idx,const string value)
+{
+ return strdup(tr ("Create collection"));
}
void
mgCreateCollection::Execute ()
{
- string name = trim(value);
+ string name = trim(m_value);
if (name.empty()) return;
bool created = selection ()->CreateCollection (name);
if (created)
@@ -1006,12 +1073,19 @@ mgCreateCollection::Execute ()
osd()->Message1 ("Collection '%s' NOT created", name);
}
+bool
+mgCreateCollection::Enabled(mgActions on)
+{
+ return selection()->isCollectionlist();
+}
+
+
//! \brief delete collection
class mgDeleteCollection:public mgCommand
{
public:
void Execute ();
- bool Enabled(mgActions on = mgActions(0));
+ bool Enabled(mgActions on);
const char *ButtonName ()
{
return tr ("Delete");
@@ -1079,69 +1153,11 @@ mgExportTracklist::Execute ()
osd()->Message1 ("written to %s", m3u_file);
}
-class mgOrderArtistAlbumTitle : public mgOrderNew
-{
- const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Artist -> Album -> Track")); }
- void NewOrder()
- {
- selection ()->setKey (0, keyArtist);
- selection ()->setKey (1, keyAlbum);
- selection ()->setKey (2, keyTrack);
- }
-};
-
-
-class mgOrderAlbumTitle : public mgOrderNew
-{
- const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Album -> Track")); }
- void NewOrder()
- {
- selection ()->setKey (0, keyAlbum);
- selection ()->setKey (1, keyTrack);
- }
-};
-
-
-class mgOrderGenreYearTitle : public mgOrderNew
-{
- const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Genre 1 -> Year -> Track")); }
- void NewOrder()
- {
- selection ()->setKey (0, keyGenre1);
- selection ()->setKey (1, keyYear);
- selection ()->setKey (2, keyTrack);
- }
-};
-
-
-class mgOrderGenreArtistAlbumTitle : public mgOrderNew
-{
- const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Genre 1 -> Artist ->Album -> Track")); }
- void NewOrder()
- {
- selection ()->setKey (0, keyGenre1);
- selection ()->setKey (1, keyArtist);
- selection ()->setKey (2, keyAlbum);
- selection ()->setKey (3, keyTrack);
- }
-};
-
-
-class mgOrderArtistTitle : public mgOrderNew
-{
- const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Artist -> Track")); }
- void NewOrder()
- {
- selection ()->setKey (0, keyArtist);
- selection ()->setKey (1, keyTrack);
- }
-};
-
-
mgActions
mgAction::Type()
{
const type_info& t = typeid(*this);
+ if (t == typeid(mgNone)) return actNone;
if (t == typeid(mgChooseOrder)) return actChooseOrder;
if (t == typeid(mgToggleSelection)) return actToggleSelection;
if (t == typeid(mgClearCollection)) return actClearCollection;
@@ -1159,13 +1175,13 @@ mgAction::Type()
if (t == typeid(mgRemoveThisFromCollection)) return actRemoveThisFromCollection;
if (t == typeid(mgEntry)) return actEntry;
if (t == typeid(mgSetButton)) return actSetButton;
- if (t == typeid(mgOrderCollItem)) return ActOrderCollItem;
- if (t == typeid(mgOrderArtistAlbumTitle)) return ActOrderArtistAlbumTitle;
- if (t == typeid(mgOrderArtistTitle)) return ActOrderArtistTitle;
- if (t == typeid(mgOrderAlbumTitle)) return ActOrderAlbumTitle;
- if (t == typeid(mgOrderGenreYearTitle)) return ActOrderGenreYearTitle;
- if (t == typeid(mgOrderGenreArtistAlbumTitle)) return ActOrderArtistAlbumTitle;
+ if (t == typeid(mgShowList)) return actShowList;
+ if (t == typeid(mgShowCommands)) return actShowCommands;
if (t == typeid(mgSetDefaultCollection)) return actSetDefaultCollection;
+ if (t == typeid(mgActOrder)) return actOrder;
+ if (t == typeid(mgCreateOrder)) return actCreateOrder;
+ if (t == typeid(mgDeleteOrder)) return actDeleteOrder;
+ if (t == typeid(mgEditOrder)) return actEditOrder;
if (t == typeid(mgExternal0)) return actExternal0;
if (t == typeid(mgExternal1)) return actExternal1;
if (t == typeid(mgExternal2)) return actExternal2;
@@ -1186,16 +1202,22 @@ mgAction::Type()
if (t == typeid(mgExternal17)) return actExternal17;
if (t == typeid(mgExternal18)) return actExternal18;
if (t == typeid(mgExternal19)) return actExternal19;
- mgError("Unknown mgAction %s",t.name());
return mgActions(0);
}
mgAction*
+actGenerateKeyItem(const char *Name, int *Value, int NumStrings, const char * const * Strings)
+{
+ return new mgKeyItem(Name,Value,NumStrings,Strings);
+}
+
+mgAction*
actGenerate(const mgActions action)
{
mgAction * result = NULL;
switch (action)
{
+ case actNone: result = new mgNone;break;
case actChooseOrder: result = new mgChooseOrder;break;
case actToggleSelection: result = new mgToggleSelection;break;
case actClearCollection: result = new mgClearCollection;break;
@@ -1213,13 +1235,15 @@ actGenerate(const mgActions action)
case actRemoveThisFromCollection: result = new mgRemoveThisFromCollection;break;
case actEntry: result = new mgEntry;break;
case actSetButton: result = new mgSetButton;break;
- case ActOrderCollItem: result = new mgOrderCollItem;break;
- case ActOrderArtistAlbumTitle: result = new mgOrderArtistAlbumTitle;break;
- case ActOrderArtistTitle: result = new mgOrderArtistTitle;break;
- case ActOrderAlbumTitle: result = new mgOrderAlbumTitle;break;
- case ActOrderGenreYearTitle: result = new mgOrderGenreYearTitle;break;
- case ActOrderGenreArtistAlbumTitle: result = new mgOrderGenreArtistAlbumTitle;break;
+ case actShowList: result = new mgShowList;break;
+ case actShowCommands: result = new mgShowCommands;break;
+ case actUnused5: break;
case actSetDefaultCollection: result = new mgSetDefaultCollection;break;
+ case actOrder: result = new mgActOrder;break;
+ case actUnused6: break;
+ case actCreateOrder: result = new mgCreateOrder;break;
+ case actDeleteOrder: result = new mgDeleteOrder;break;
+ case actEditOrder: result = new mgEditOrder;break;
case actExternal0: result = new mgExternal0;break;
case actExternal1: result = new mgExternal1;break;
case actExternal2: result = new mgExternal2;break;
@@ -1258,3 +1282,28 @@ mgAction::collselection()
return osd()->collselection();
}
+eOSState
+mgKeyItem::Process(eKeys key)
+{
+ mgMenuOrder *menu = dynamic_cast<mgMenuOrder*>(m);
+ if (key==kOk)
+ {
+ if (menu->ChangeOrder(key))
+ return osContinue;
+ else
+ {
+ menu->SaveOrder();
+ osd ()->newmenu = NULL;
+ return osContinue;
+ }
+ } else if (key==kBack)
+ {
+ osd ()->newmenu = NULL;
+ return osContinue;
+ }
+ if (key==kUp || key==kDown)
+ if (menu->ChangeOrder(key))
+ return osContinue;
+ return cMenuEditStraItem::ProcessKey(key);
+}
+
diff --git a/mg_actions.h b/mg_actions.h
index 3712191..3994b10 100644
--- a/mg_actions.h
+++ b/mg_actions.h
@@ -27,10 +27,11 @@ class mgMainMenu;
/*! \brief defines all actions which can appear in command submenus.
* Since these values are saved in muggle.state, new actions should
- * always be appended. The order does not matter. Value 0 means undefined.
+ * always be appended. The order does not matter. actNone should be 0.
*/
enum mgActions {
- actChooseOrder=1, //!< show a menu with all possible orders
+ actNone,
+ actChooseOrder, //!< show a menu with all possible orders
actToggleSelection, //!< toggle between search and collection view
actClearCollection, //!< clear a collection,
actCreateCollection,
@@ -45,15 +46,17 @@ enum mgActions {
actRemoveThisFromCollection, //!< remove selected item from default collection
actEntry, //!< used for normal data base items
actSetButton, //!< connect a button with an action
- ActOrderCollItem, //!< order by collections
- ActOrderArtistAlbumTitle, //!< order by Artist/Album/Title
- ActOrderArtistTitle, //!< order by Artist/Title
- ActOrderAlbumTitle, //!< order by Album/Title
- ActOrderGenreYearTitle, //!< order by Genre1/Year/Title
- ActOrderGenreArtistAlbumTitle, //!< order by Genre1/Artist/Album/Title
+ actShowList,
+ actShowCommands,
+ actCreateOrder,
+ actDeleteOrder,
+ actUnused5, //!< order by Genre1/Artist/Album/Title
actAddAllToDefaultCollection,
actAddThisToDefaultCollection,
actSetDefaultCollection,
+ actOrder,
+ actUnused6,
+ actEditOrder,
actExternal0 = 1000, //!< used for external commands, the number is the entry number in the .conf file starting with line 0
actExternal1, //!< used for external commands, the number is the entry number in the .conf file
actExternal2, //!< used for external commands, the number is the entry number in the .conf file
@@ -88,7 +91,7 @@ class mgAction
virtual bool Enabled(mgActions on = mgActions(0));
//! \brief the action to be executed
- virtual void Execute () = 0;
+ virtual void Execute () {}
//! \brief handles the kBack key
virtual eOSState Back();
@@ -98,14 +101,17 @@ class mgAction
*/
virtual const char *ButtonName ()
{
- return NULL;
+ return "";
}
/*! \brief the name for a menu entry. If empty, no button will be able
* to execute this. The returned C string must be freeable at any time.
* \param value a string that can be used for building the menu name.
*/
- virtual const char *MenuName (const unsigned int idx=0,const string value="") = 0;
+ virtual const char *MenuName (const unsigned int idx=0,const string value="")
+ {
+ return strdup(ButtonName());
+ }
//! \brief default constructor
mgAction ();
@@ -149,12 +155,24 @@ class mgAction
mgSelection* playselection ();
virtual void Notify();
+ eOSState ProcessKey(eKeys key);
+ virtual eOSState Process(eKeys key) { return osUnknown; }
private:
mgMainMenu *m_osd;
};
+class mgActionWithIntValue: public mgAction
+{
+ public:
+ mgActionWithIntValue() { intval=0; }
+ void setValue(int i) { intval = i; }
+ protected:
+ int intval;
+};
+
//! \brief generate an mgAction for action
mgAction* actGenerate(const mgActions action);
+mgAction* actGenerateKeyItem(const char *Name, int *Value, int NumStrings, const char * const * Strings);
#endif
diff --git a/mg_db.c b/mg_db.c
index 7e4817f..dfc0843 100644
--- a/mg_db.c
+++ b/mg_db.c
@@ -13,6 +13,9 @@
#include "vdr_setup.h"
#include "mg_tools.h"
+bool needGenre2;
+bool needGenre2_set;
+
//! \brief adds string n to string s, using a comma to separate them
static string comma (string & s, string n);
@@ -36,11 +39,21 @@ comma (string & s, string n)
static string zerostring;
+bool
+mgSelection::mgSelStrings::operator==(const mgSelStrings&x) const
+{
+ bool result = strings.size()==x.strings.size();
+ if (result)
+ for (unsigned int i=0;i<size();i++)
+ result &= strings[i]==x.strings[i];
+ return result;
+}
+
size_t
-mgSelection::mgSelStrings::size()
+mgSelection::mgSelStrings::size() const
{
if (!m_sel)
- mgError("mgSelStrings: m_sel is NULL");
+ mgError("mgSelStrings: m_sel is 0");
m_sel->refreshValues();
return strings.size();
}
@@ -49,7 +62,7 @@ string&
mgSelection::mgSelStrings::operator[](unsigned int idx)
{
if (!m_sel)
- mgError("mgSelStrings: m_sel is NULL");
+ mgError("mgSelStrings: m_sel is 0");
m_sel->refreshValues();
if (idx>=strings.size()) return zerostring;
return strings[idx];
@@ -77,9 +90,16 @@ mgSelection::getCurrentValue()
string
mgSelection::getKeyValue(const unsigned int level) const
{
- return order.getKeyValue(level);
+ return order.getKeyValue(level);
}
+unsigned int
+mgSelection::getKeyIndex(const unsigned int level) const
+{
+ return valindex(getKeyValue(level));
+}
+
+
mgKeyTypes
mgSelection::getKeyType (const unsigned int level) const
{
@@ -98,26 +118,14 @@ mgSelection::exec_sql(string query) const
*/
string mgSelection::get_col0 (string query) const
{
- MYSQL_RES * sql_result = exec_sql (query);
- if (!sql_result)
- return "NULL";
- MYSQL_ROW row = mysql_fetch_row (sql_result);
- string result;
- if (row == NULL)
- result = "NULL";
- else if (row[0] == NULL)
- result = "NULL";
- else
- result = row[0];
- mysql_free_result (sql_result);
- return result;
+ return ::get_col0(m_db, query);
}
unsigned long
mgSelection::exec_count (string query) const
{
- return atol (get_col0 (query).c_str ());
+ return ::exec_count(m_db, query);
}
@@ -133,13 +141,29 @@ mgSelection::sql_string (const string s) const
}
string
+mgContentItem::getKeyId(mgKeyTypes kt)
+{
+ if (m_id<0)
+ return "";
+ switch (kt) {
+ case keyGenres:
+ case keyGenre1:
+ case keyGenre2:
+ case keyGenre3: return m_genre1_id;
+ default: return getKeyValue(kt);
+ }
+}
+
+string
mgContentItem::getKeyValue(mgKeyTypes kt)
{
if (m_id<0)
return "";
switch (kt) {
- case keyGenre1: return getGenre1();
- case keyGenre2: return getGenre2();
+ case keyGenres:
+ case keyGenre1:
+ case keyGenre2:
+ case keyGenre3: return getGenre();
case keyArtist: return getArtist();
case keyAlbum: return getAlbum();
case keyYear: return string(ltos(getYear()));
@@ -154,26 +178,23 @@ mgContentItem *
mgSelection::getTrack (unsigned int position)
{
if (position >= getNumTracks ())
- return NULL;
+ return 0;
return &(m_tracks[position]);
}
string mgContentItem::getGenre () const
{
- return m_genre1;
-}
-
-
-string mgContentItem::getGenre1 () const
-{
- return m_genre1;
-}
-
-
-string mgContentItem::getGenre2 () const
-{
- return m_genre2;
+ string result="";
+ if (m_genre1!="NULL")
+ result = m_genre1;
+ if (m_genre2!="NULL")
+ {
+ if (!result.empty())
+ result += "/";
+ result += m_genre2;
+ }
+ return result;
}
@@ -350,7 +371,7 @@ void mgSelection::ClearCollection (const string Name)
{
if (!m_db) return;
string listid = id(keyCollection,Name);
- exec_sql ("DELETE FROM playlistitem WHERE playlist="+listid);
+ exec_sql ("DELETE FROM playlistitem WHERE playlist="+sql_string(listid));
if (inCollection(Name)) clearCache ();
}
@@ -371,18 +392,19 @@ string mgSelection::exportM3U ()
{
// open a file for writing
- string fn = m_Directory + '/' + ListFilename () + ".m3u";
+ string fn = "/tmp/" + ListFilename () + ".m3u";
FILE * listfile = fopen (fn.c_str (), "w");
if (!listfile)
return "";
- fprintf (listfile, "#EXTM3U");
+ 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, "%s", t.getSourceFile ().c_str ());
+ fprintf (listfile, "#MUGGLE:%d\n", t.getId());
+ fprintf (listfile, "%s\n", t.getSourceFile (false).c_str ());
}
fclose (listfile);
return fn;
@@ -400,45 +422,40 @@ mgSelection::empty()
void
mgSelection::setPosition (unsigned int position)
{
- if (m_level < order.size ())
- m_position[m_level] = position;
- if (m_level >= order.size ()-1)
- setTrackPosition(position);
+ if (m_level == order.size())
+ setTrackPosition(position);
+ else
+ m_position = position;
}
-
void
mgSelection::setTrackPosition (unsigned int position)
{
- m_tracks_position = position;
+ m_tracks_position = position;
}
-
unsigned int
-mgSelection::getPosition (unsigned int level) const
+mgSelection::getPosition () const
{
- if (level == order.size ())
- return getTrackPosition();
+ if (m_level == order.size())
+ return getTrackPosition();
else
- return m_position[m_level];
+ return m_position;
}
unsigned int
-mgSelection::gotoPosition (unsigned int level)
+mgSelection::gotoPosition ()
{
- if (level>order.size())
- mgError("mgSelection::gotoPosition: level %u > order.size %u",
- level,order.size());
- if (level == order.size ())
+ if (m_level == order.size ())
return gotoTrackPosition();
else
{
unsigned int valsize = values.size();
if (valsize==0)
- m_position[m_level] = 0;
- else if (m_position[m_level] >= valsize)
- m_position[m_level] = valsize -1;
- return m_position[m_level];
+ m_position = 0;
+ else if (m_position >= valsize)
+ m_position = valsize -1;
+ return m_position;
}
}
@@ -517,13 +534,18 @@ mgSelection::getCompletedLength () const
string mgSelection::getListname () const
{
- string
- result = "";
+ list<string> st;
for (unsigned int i = 0; i < m_level; i++)
- addsep (result, ":", getKeyValue(i));
+ st.push_back(order.getKeyValue(i));
+ 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.Key(0)->Type ()));
+ result = string(ktName(order.getKeyType(0)));
return result;
}
@@ -531,6 +553,20 @@ 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;
}
@@ -557,23 +593,26 @@ mgSelection::tracks () const
p.tables.push_back("tracks");
p.tables.push_back("album");
for (unsigned int i = m_level; i<order.size(); i++)
- p.orders += order.Key(i)->Parts(true).orders;
+ p += order.Key(i)->Parts(true);
m_current_tracks = p.sql_select(false);
m_tracks.clear ();
MYSQL_RES *rows = exec_sql (m_current_tracks);
if (rows)
{
MYSQL_ROW row;
- while ((row = mysql_fetch_row (rows)) != NULL)
- {
+ while ((row = mysql_fetch_row (rows)) != 0)
m_tracks.push_back (mgContentItem (this,row));
- }
mysql_free_result (rows);
}
return m_tracks;
}
+mgContentItem::mgContentItem ()
+{
+ m_id = -1;
+}
+
mgContentItem::mgContentItem (const mgContentItem* c)
{
m_id = c->m_id;
@@ -581,6 +620,8 @@ mgContentItem::mgContentItem (const mgContentItem* c)
m_mp3file = c->m_mp3file;
m_artist = c->m_artist;
m_albumtitle = c->m_albumtitle;
+ m_genre1_id = c->m_genre1_id;
+ m_genre2_id = c->m_genre2_id;
m_genre1 = c->m_genre1;
m_genre2 = c->m_genre2;
m_bitrate = c->m_bitrate;
@@ -591,6 +632,55 @@ mgContentItem::mgContentItem (const mgContentItem* c)
m_channels = c->m_channels;
}
+#if VDRVERSNUM >= 10318
+static char *readline(FILE *f)
+{
+ static char buffer[MAXPARSEBUFFER];
+ if (fgets(buffer, sizeof(buffer), f) > 0) {
+ int l = strlen(buffer) - 1;
+ if (l >= 0 && buffer[l] == '\n')
+ buffer[l] = 0;
+ return buffer;
+ }
+ return 0;
+}
+#endif
+
+static const char *FINDCMD = "cd '%s' 2>/dev/null && find -follow -name '%s' -print 2>/dev/null";
+
+static string
+GdFindFile( string tld, string mp3file )
+{
+ string result = "";
+ char *cmd = 0;
+ asprintf( &cmd, FINDCMD, tld.c_str(), mp3file.c_str() );
+ FILE *p = popen( cmd, "r" );
+ if (p)
+ {
+ char *s;
+ if( (s = readline(p) ) != 0)
+ result = tld + s;
+ pclose(p);
+ }
+
+ free( cmd );
+
+ return result;
+}
+
+string
+mgContentItem::getSourceFile(bool AbsolutePath) const
+{
+ const string& tld = the_setup.ToplevelDir;
+ string result="";
+ if (AbsolutePath) result = tld;
+ if (the_setup.GdCompatibility)
+ result += GdFindFile(tld,m_mp3file);
+ else
+ result += m_mp3file;
+ return result;
+}
+
mgContentItem::mgContentItem (const mgSelection* sel,const MYSQL_ROW row)
{
m_id = atol (row[0]);
@@ -605,17 +695,23 @@ mgContentItem::mgContentItem (const mgSelection* sel,const MYSQL_ROW row)
if (row[3])
m_artist = row[3];
else
- m_artist = row[3];
+ m_artist = "NULL";
if (row[4])
m_albumtitle = row[4];
else
m_albumtitle = "NULL";
if (row[5])
- m_genre1 = sel->value(keyGenre1,row[5]);
+ {
+ m_genre1_id = row[5];
+ m_genre1 = sel->value(keyGenres,row[5]);
+ }
else
m_genre1 = "NULL";
if (row[6])
- m_genre2 = sel->value(keyGenre2,row[5]);
+ {
+ m_genre2_id = row[6];
+ m_genre2 = sel->value(keyGenres,row[6]);
+ }
else
m_genre2 = "NULL";
if (row[7])
@@ -648,31 +744,24 @@ void mgSelection::InitSelection() {
m_Directory=".";
InitDatabase();
m_level = 0;
- m_position.reserve (20);
+ m_position = 0;
m_tracks_position = 0;
m_trackid = -1;
m_shuffle_mode = SM_NONE;
m_loop_mode = LM_NONE;
clearCache();
values.setOwner(this);
+ if (!needGenre2_set)
+ {
+ needGenre2_set=true;
+ needGenre2=exec_count("SELECT COUNT(DISTINCT genre2) from tracks")>1;
+ }
}
-mgSelection::mgSelection()
-{
- setDB(0);
- m_Host = "";
- m_User = "";
- m_Password = "";
- InitSelection ();
- m_fall_through = false;
-}
-mgSelection::mgSelection (const string Host, const string User, const string Password, const bool fall_through)
+mgSelection::mgSelection (const bool fall_through)
{
setDB(0);
- m_Host = Host;
- m_User = User;
- m_Password = Password;
InitSelection ();
m_fall_through = fall_through;
}
@@ -700,36 +789,35 @@ mgSelection::setDB(MYSQL *db)
}
void
+mgSelection::setOrder(mgOrder* o)
+{
+ if (o)
+ {
+ order = *o;
+ order.setDB(m_db);
+ }
+ else
+ mgWarning("mgSelection::setOrder(0)");
+}
+
+void
mgSelection::InitFrom(mgValmap& nv)
{
setDB(0);
- m_Host = nv.getstr("Host");
- m_User = nv.getstr("User");
- m_Password = nv.getstr("Password");
InitSelection();
m_fall_through = nv.getbool("FallThrough");
m_Directory = nv.getstr("Directory");
- for (unsigned int i = 0; i < 99 ; i++)
- {
- char *idx;
- asprintf(&idx,"Keys.%u.Choice",i);
- unsigned int v = nv.getuint(idx);
- free(idx);
- if (v==0) break;
- setKey (i,mgKeyTypes(v) );
- }
while (m_level < nv.getuint("Level"))
{
char *idx;
- asprintf(&idx,"Keys.%u.Position",m_level);
- unsigned int newpos = nv.getuint(idx);
+ asprintf(&idx,"order.Keys.%u.Position",m_level);
+ string newval = nv.getstr(idx);
free(idx);
- if (!enter (newpos))
- if (!select (newpos)) break;
+ if (!enter (newval))
+ if (!select (newval)) break;
}
m_trackid = nv.getlong("TrackId");
- // TODO do we really need Position AND TrackPosition in muggle.state?
- setPosition(nv.getlong("Position"));
+ setPosition(nv.getstr("Position"));
if (m_level>=order.size()-1)
setTrackPosition(nv.getlong("TrackPosition"));
setShuffleMode(ShuffleMode(nv.getuint("ShuffleMode")));
@@ -740,18 +828,12 @@ mgSelection::InitFrom(mgValmap& nv)
mgSelection::~mgSelection ()
{
if (m_db)
- {
- mgDebug(3,"%X: closing m_db %X",this,m_db);
mysql_close (m_db);
- }
}
void mgSelection::InitFrom(const mgSelection* s)
{
setDB(0);
- m_Host = s->m_Host;
- m_User = s->m_User;
- m_Password = s->m_Password;
InitSelection();
m_fall_through = s->m_fall_through;
m_Directory = s->m_Directory;
@@ -759,9 +841,7 @@ void mgSelection::InitFrom(const mgSelection* s)
map_ids = s->map_ids;
order = s->order;
m_level = s->m_level;
- m_position.reserve (s->m_position.capacity());
- for (unsigned int i = 0; i < s->m_position.capacity(); i++)
- m_position[i] = s->m_position[i];
+ m_position = s->m_position;
m_trackid = s->m_trackid;
m_tracks_position = s->m_tracks_position;
setShuffleMode (s->getShuffleMode ());
@@ -770,7 +850,7 @@ void mgSelection::InitFrom(const mgSelection* s)
const mgSelection& mgSelection::operator=(const mgSelection &s)
{
- if ((&m_Host)==&(s.m_Host)) { // prevent s = s
+ if (this==&s) { // prevent s = s
return *this;
}
InitFrom(&s);
@@ -785,7 +865,7 @@ mgSelection::ordersize ()
}
unsigned int
-mgSelection::valindex (const string val,const bool second_try)
+mgSelection::valindex (const string val,const bool second_try) const
{
for (unsigned int i = 0; i < values.size (); i++)
{
@@ -794,8 +874,9 @@ mgSelection::valindex (const string val,const bool second_try)
}
// nochmal mit neuen Werten:
clearCache();
+ refreshValues();
if (second_try) {
- mgWarning("valindex: Gibt es nicht:%s",val.c_str());
+ esyslog("valindex: Gibt es nicht:%s",val.c_str());
return 0;
}
else
@@ -803,13 +884,32 @@ mgSelection::valindex (const string val,const bool second_try)
}
+unsigned int
+mgSelection::idindex (const string id,const bool second_try) const
+{
+ for (unsigned int i = 0; i < m_ids.size (); i++)
+ if (m_ids[i] == id)
+ return i;
+ // nochmal mit neuen Werten:
+ clearCache();
+ refreshValues();
+ if (second_try) {
+ mgWarning("idindex: Gibt es nicht:%s",id.c_str());
+ return 0;
+ }
+ else
+ return idindex(id,true);
+}
+
+
void
mgSelection::refreshValues () const
{
+ assert(this);
if (!m_db) return;
- mgOrder o1 = order;
if (m_current_values.empty())
{
+ mgOrder o1 = order;
mgParts p = order.Parts(m_level);
m_current_values = p.sql_select();
values.strings.clear ();
@@ -819,16 +919,16 @@ mgSelection::refreshValues () const
{
unsigned int num_fields = mysql_num_fields(rows);
MYSQL_ROW row;
- while ((row = mysql_fetch_row (rows)) != NULL)
+ while ((row = mysql_fetch_row (rows)) != 0)
{
- string r0 = "NULL";
- if (row[0])
- r0 = row[0];
+ if (!row[0]) continue;
+ string r0 = row[0];
+ if (r0=="NULL") // there is a genre NULL!
+ continue;
if (num_fields==2)
{
- string r1 = "NULL";
- if (row[1])
- r1 = row[1];
+ if (!row[1]) continue;
+ string r1 = row[1];
values.strings.push_back (r0);
m_ids.push_back (r1);
}
@@ -848,66 +948,57 @@ mgSelection::count () const
{
return values.size ();
}
-
void
mgSelection::InitDatabase ()
{
if (m_db)
{
- mgDebug(3,"%X: InitDatabase closes %X",this,m_db);
mysql_close (m_db);
setDB(0);
}
- if (m_Host == "") return;
+ if (the_setup.DbHost == "") return;
setDB(mysql_init (0));
- mgDebug(3,"%X: InitDatabase opens %X",this, m_db);
if (!m_db)
return;
- if (mysql_real_connect (m_db, m_Host.c_str (), m_User.c_str (), m_Password.c_str (),
- "GiantDisc", 0, NULL, 0) == NULL) {
- mgWarning("Failed to connect to host '%s' as User '%s', Password '%s': Error: %s",
- m_Host.c_str(),m_User.c_str(),m_Password.c_str(),mysql_error(m_db));
- mysql_close (m_db);
- setDB(0);
- return;
- }
- return;
-}
-
-
-void
-mgSelection::setKey (const unsigned int level, const mgKeyTypes kt)
-{
- mgKey *newkey = ktGenerate(kt,m_db);
- if (level == 0 && kt == keyCollection)
+ bool success;
+ if (the_setup.DbSocket != NULL)
{
- order.clear ();
- order += newkey;
- order += ktGenerate(keyCollectionItem,m_db);
- return;
+ mgDebug(1,"Using socket %s for connecting to Database %s as user %s.",
+ the_setup.DbSocket,
+ the_setup.DbName,
+ the_setup.DbUser);
+ mgDebug(3,"DbPassword is: '%s'",the_setup.DbPass);
+ success = (mysql_real_connect( m_db,
+ "",
+ the_setup.DbUser,
+ the_setup.DbPass,
+ the_setup.DbName,
+ 0,
+ the_setup.DbSocket, 0 ) != 0 );
}
- if (level == order.size ())
+ else
{
- order += newkey;
+ mgDebug(1,"Using TCP-%s for connecting to Database %s as user %s.",
+ the_setup.DbHost,
+ the_setup.DbName,
+ the_setup.DbUser);
+ mgDebug(3,"DbPassword is: '%s'",the_setup.DbPass);
+ success = ( mysql_real_connect( m_db,
+ the_setup.DbHost,
+ the_setup.DbUser,
+ the_setup.DbPass,
+ the_setup.DbName,
+ the_setup.DbPort,
+ 0, 0 ) != 0 );
}
- else
+ if (!success)
{
- if (level >= order.size())
- mgError("mgSelection::setKey(%u,%s): level greater than order.size() %u",
- level,ktName(kt),order.size());
- delete order[level];
- order[level] = newkey;
+ mgWarning("Failed to connect to host '%s' as User '%s', Password '%s': Error: %s",
+ the_setup.DbHost,the_setup.DbUser,the_setup.DbPass,mysql_error(m_db));
+ mysql_close (m_db);
+ setDB(0);
}
-
- order.clean();
-
-// clear values for this and following levels (needed for copy constructor)
- for (unsigned int i = level; i < order.size (); i++)
- order[i]->set ("",EMPTY);
-
- if (m_level > level)
- m_level = level;
- if (m_level == level) setPosition(0);
+ return;
}
@@ -921,9 +1012,11 @@ bool mgSelection::enter (unsigned int position)
position = gotoPosition(); // reload adjusted position
string value = values[position];
string id = m_ids[position];
+ mgSelStrings prev;
+ if (m_fall_through && values.size()<10)
+ prev=values;
while (1)
{
- mgDebug(3,"enter(level=%u,pos=%u, id=%s)",m_level,position,id.c_str());
if (m_level >= order.size () - 1)
return false;
order[m_level++]->set (value,id);
@@ -931,18 +1024,18 @@ bool mgSelection::enter (unsigned int position)
if (m_level >= order.size())
mgError("mgSelection::enter(%u): level greater than order.size() %u",
m_level,order.size());
- if (m_position.capacity () == m_position.size ())
- m_position.reserve (m_position.capacity () + 10);
- m_position[m_level] = 0;
+ m_position = 0;
+ refreshValues();
+ if (count()==0)
+ break;
+ value = values[0];
+ id = m_ids[0];
if (!m_fall_through)
break;
- if (count () > 1)
+ if (m_level==order.size()-1)
+ break;
+ if (count () > 1 && !(prev==values))
break;
- if (count () == 1)
- {
- value = values[0];
- id = m_ids[0];
- }
}
return true;
}
@@ -950,7 +1043,6 @@ bool mgSelection::enter (unsigned int position)
bool mgSelection::select (unsigned int position)
{
- mgDebug(3,"select(%u) on Level %d",position,m_level);
if (m_level == order.size () - 1)
{
if (getNumTracks () <= position)
@@ -966,9 +1058,10 @@ bool mgSelection::select (unsigned int position)
return enter (position);
}
-
-bool mgSelection::leave ()
+bool
+mgSelection::leave ()
{
+ string prevvalue;
if (order.empty())
{
mgWarning("mgSelection::leave(): order is empty");
@@ -977,29 +1070,103 @@ bool mgSelection::leave ()
if (m_level == order.size ())
{
m_level--;
+ prevvalue=order.getKeyValue(m_level);
+ order[m_level]->set("",EMPTY);
m_trackid = -1;
clearCache();
+ setPosition(prevvalue);
return true;
}
+ mgSelStrings prev;
+ if (m_fall_through && values.size()<10)
+ prev=values;
while (1)
{
if (m_level < 1)
return false;
- order[m_level]->set ("",EMPTY);
- order[--m_level]->set ("",EMPTY);
+ if (m_level<order.size()) order[m_level]->set ("",EMPTY);
+ m_level--;
+ prevvalue=order.getKeyValue(m_level);
+ if (m_level<order.size()) order[m_level]->set ("",EMPTY);
clearCache();
if (!m_fall_through)
break;
- if (count () > 1)
+ if (count () > 1 && !(prev==values))
break;
}
+ setPosition(prevvalue);
return true;
}
+void
+mgSelection::leave_all ()
+{
+ m_level=0;
+ for (unsigned int i=0;i<ordersize();i++)
+ order[i]->set ("",EMPTY);
+ clearCache();
+}
+
+void
+mgSelection::selectfrom(mgOrder& oldorder,mgContentItem* o)
+{
+ leave_all();
+ string selval;
+ string selid;
+ assert(m_level==0);
+ for (unsigned int idx = 0; idx < ordersize(); idx++)
+ {
+ selval = EMPTY;
+ selid = EMPTY;
+ 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)
+ {
+ selval = oldorder.getKeyValue(i);
+ selid = oldorder.getKeyId(i);
+ }
+ else if (old_kt>new_kt
+ && iskeyGenre(old_kt)
+ && iskeyGenre(new_kt))
+ {
+ selid = id(new_kt,value(new_kt,oldorder.getKeyId(i)));
+ selval= value(new_kt,selid);
+ }
+ if (selid!=EMPTY) break;
+ }
+ if (selid==EMPTY && o && o->getId()>=0)
+ {
+ selval = o->getKeyValue(new_kt);
+ selid = o->getKeyId(new_kt);
+ }
+ if (selid==EMPTY)
+ break;
+ if (m_level<ordersize()-1)
+ {
+ order[m_level++]->set (selval, selid);
+ }
+ else
+ {
+ setPosition(selval);
+ return;
+ }
+ }
+ if (m_level>0)
+ {
+ m_level--;
+ selval = order.getKeyValue(m_level);
+ order[m_level]->set("",EMPTY);
+ setPosition(selval);
+ order[m_level+1]->set("",EMPTY);
+ }
+ assert(m_level<ordersize());
+}
+
string
mgSelection::value(mgKeyTypes kt, string id) const
{
- if (kt==keyGenre2) kt = keyGenre1;
if (loadvalues (kt))
{
map<string,string>& valmap = map_values[kt];
@@ -1035,11 +1202,14 @@ mgSelection::value(mgKey* k) const
string
mgSelection::id(mgKeyTypes kt, string val) const
{
- if (kt==keyGenre2) kt = keyGenre1;
if (loadvalues (kt))
{
map<string,string>& idmap = map_ids[kt];
- return idmap[val];
+ 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;
@@ -1058,12 +1228,12 @@ mgSelection::id(mgKey* k) const
}
bool
-mgSelection::UsedBefore(const mgKeyTypes kt,unsigned int level) const
+mgSelection::UsedBefore(mgOrder *o,const mgKeyTypes kt,unsigned int level) const
{
- if (level>=order.size())
- level = order.size() -1;
+ if (level>=o->size())
+ level = o->size() -1;
for (unsigned int lx = 0; lx < level; lx++)
- if (order.Key(lx)->Type()==kt)
+ if (o->getKeyType(lx)==kt)
return true;
return false;
}
@@ -1072,16 +1242,16 @@ mgSelection::UsedBefore(const mgKeyTypes kt,unsigned int level) const
bool mgSelection::isCollectionlist () const
{
if (order.size()==0) return false;
- return (order.Key(0)->Type() == keyCollection && m_level == 0);
+ return (order.getKeyType(0) == keyCollection && m_level == 0);
}
bool
mgSelection::inCollection(const string Name) const
{
if (order.size()==0) return false;
- bool result = (order.Key(0)->Type() == keyCollection && m_level == 1);
+ bool result = (order.getKeyType(0) == keyCollection && m_level == 1);
if (result)
- if (order.Key(1)->Type() != keyCollectionItem)
+ if (order.getKeyType(1) != keyCollectionItem)
mgError("inCollection: key[1] is not keyCollectionItem");
if (!Name.empty())
result &= (order.getKeyValue(0) == Name);
@@ -1089,52 +1259,8 @@ mgSelection::inCollection(const string Name) const
}
-#if 0
-void
-keychoice(mgOrder& o,const unsigned int level)
-{
- if (level > o.size ())
- return;
- std::cout<<"possible choices:";
- for (mgKeyTypes kt = mgKeyTypes(1); kt <= mgKeyTypesHigh; kt = mgKeyTypes(int(kt)+1))
- {
- if (level !=0 && kt == keyCollection)
- continue;
- if (level !=1 && kt == keyCollectionItem)
- continue;
- if (level == 1 && o[0]->Type() != keyCollection && kt == keyCollectionItem)
- continue;
- if (level == 1 && o[0]->Type() == keyCollection && kt != keyCollectionItem)
- continue;
- if (level >1 && o[0]->Type() == keyCollection)
- break;
- if (kt == keyDecade && UsedBefore(o,keyYear,level))
- continue;
- if (o[0]->Type() == keyCollection)
- {
- std::cout<<" "<<ktName(kt);
- }
- else if (!UsedBefore(o,kt,level))
- {
- if (keycounts[kt]==-1)
- {
- mgOrder oc(db);
- oc += ktGenerate(kt,db);
- keycounts[kt]=atol(get_col0(oc.Parts(0).sql_count()).c_str());
- }
- if (keycounts[kt]>1)
- std::cout<<" "<<ktName(kt)<<"("<<keycounts[kt]<<")";
- }
- }
- std::cout<<endl;
-}
-#endif
-
void mgSelection::DumpState(mgValmap& nv) const
{
- nv.put("Host",m_Host);
- nv.put("User",m_User);
- nv.put("Password",m_Password);
nv.put("FallThrough",m_fall_through);
nv.put("ShuffleMode",int(m_shuffle_mode));
nv.put("LoopMode",int(m_loop_mode));
@@ -1142,36 +1268,31 @@ void mgSelection::DumpState(mgValmap& nv) const
nv.put("Level",int(m_level));
for (unsigned int i=0;i<order.size();i++)
{
- char *n;
- asprintf(&n,"Keys.%d.Choice",i);
- nv.put(n,int(order.Key(i)->Type()));
- free(n);
if (i<m_level) {
- asprintf(&n,"Keys.%d.Position",i);
- nv.put(n,m_position[i]);
+ char *n;
+ asprintf(&n,"order.Keys.%u.Position",i);
+ nv.put(n,getKeyValue(i));
free(n);
}
}
nv.put("TrackId",m_trackid);
- if (m_level == order.size ())
- nv.put("Position",getTrackPosition());
- else
- nv.put("Position",m_position[m_level]);
- nv.put("TrackPosition",getTrackPosition());
+ nv.put("Position",values[m_position]);
+ if (m_level>=order.size()-1)
+ nv.put("TrackPosition",getTrackPosition());
}
-map <mgKeyTypes, string> *
+map <mgKeyTypes, string>
mgSelection::UsedKeyValues()
{
- map <mgKeyTypes, string> *result = new map<mgKeyTypes, string>;
+ map <mgKeyTypes, string> result;
for (unsigned int idx = 0 ; idx < level() ; idx++)
{
- (*result)[order.Key(idx)->Type()] = order.getKeyValue(idx);
+ result[order.getKeyType(idx)] = order.getKeyValue(idx);
}
if (level() < order.size()-1)
{
mgKeyTypes ch = order.getKeyType(level());
- (*result)[ch] = getCurrentValue();
+ result[ch] = getCurrentValue();
}
return result;
}
@@ -1179,18 +1300,15 @@ mgSelection::UsedKeyValues()
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,m_db);
if (k->map_idfield().empty())
{
delete k;
return false;
}
- map<string,string>& idmap = map_ids[kt];
- if (!idmap.empty())
- {
- delete k;
- return true;
- }
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());
@@ -1199,14 +1317,82 @@ mgSelection::loadvalues (mgKeyTypes kt) const
if (rows)
{
MYSQL_ROW row;
- while ((row = mysql_fetch_row (rows)) != NULL)
+ 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)
+{
+ assert(strlen(m_db->host));
+ 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,m_db);
+ count = exec_count(k->Parts(true).sql_count());
+ 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 && UsedBefore(o,keyGenres,level))
+ continue;
+ if (kt==keyGenre2 && UsedBefore(o,keyGenres,level))
+ continue;
+ if (kt==keyGenre3 && UsedBefore(o,keyGenres,level))
+ continue;
+ if (kt==keyGenre1 && UsedBefore(o,keyGenre3,level))
+ continue;
+ if (kt==keyGenre2 && UsedBefore(o,keyGenre3,level))
+ continue;
+ if (kt==keyGenre1 && UsedBefore(o,keyGenre2,level))
+ continue;
+ if (kt==keyCollection || kt==keyCollectionItem)
+ result.push_back(ktName(kt));
+ else if (keycount(kt)>1)
+ result.push_back(ktName(kt));
+ }
+ return result;
+}
diff --git a/mg_db.h b/mg_db.h
index 2ba02df..5ef26e2 100644
--- a/mg_db.h
+++ b/mg_db.h
@@ -37,11 +37,10 @@ class mgSelection;
class mgContentItem
{
public:
- mgContentItem ()
- {
- }
+ mgContentItem ();
string getKeyValue(mgKeyTypes kt);
+ string getKeyId(mgKeyTypes kt);
//! \brief copy constructor
mgContentItem(const mgContentItem* c);
@@ -61,10 +60,7 @@ class mgContentItem
}
//! \brief returns filename
- string getSourceFile () const
- {
- return m_mp3file;
- }
+ string getSourceFile (bool AbsolutePath=true) const;
//! \brief returns artist
string getArtist () const
@@ -75,13 +71,7 @@ class mgContentItem
//! \brief returns the name of the album
string getAlbum () const;
-//! \brief returns the name of genre 1
- string getGenre1 () const;
-
-//! \brief returns the name of genre 2
- string getGenre2 () const;
-
-//! \brief returns the name of genre 1
+//! \brief returns the name of genre
string getGenre () const;
//! \brief returns the bitrate
@@ -111,6 +101,8 @@ class mgContentItem
string m_mp3file;
string m_artist;
string m_albumtitle;
+ string m_genre1_id;
+ string m_genre2_id;
string m_genre1;
string m_genre2;
string m_bitrate;
@@ -140,20 +132,18 @@ class mgSelection
void setOwner(mgSelection* sel);
public:
string& operator[](unsigned int idx);
- size_t size();
+ bool operator==(const mgSelStrings&x) const;
+ size_t size() const;
};
-
public:
+//! \brief defines an order to be used
+ void setOrder(mgOrder *o);
+
+ mgOrder& getOrder() { return order; }
+
/*! \brief define various ways to play music in random order
* \todo Party mode is not implemented, does same as SM_NORMAL
*/
-/*! \brief defines a field to be used as key for selection
- *
- * \param level 0 is the top level
- * \param kt type of the key field. For possible values see mg_order.h
- */
- void setKey (const unsigned int level, const mgKeyTypes kt);
-
enum ShuffleMode
{
SM_NONE, //!< \brief play normal sequence
@@ -172,20 +162,12 @@ class mgSelection
//! \brief escapes special characters
string sql_string(const string s) const;
-//! \brief the default constructor. Does not start a DB connection.
- mgSelection();
-
/*! \brief the main constructor
- * \param Host where the data base lives. If not localhost, TCP/IP is used.
- * \param User if empty, the current user is used.
- * \param Password no comment
* \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 string Host, const string User =
- "", const string Password = "", const bool fall_through =
- false);
+ 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
@@ -226,13 +208,14 @@ class mgSelection
//! \brief return the current value of this key
string getKeyValue (const unsigned int level) const;
+ unsigned int getKeyIndex(const unsigned int level) const;
/*! \brief returns the current item from the value() list
*/
string getCurrentValue();
//! \brief returns a map (new allocated) for all used key fields and their values
- map<mgKeyTypes,string> * UsedKeyValues();
+ map<mgKeyTypes,string> UsedKeyValues();
//! \brief the number of key fields used for the query
unsigned int ordersize ();
@@ -240,18 +223,12 @@ class mgSelection
//! \brief the number of music items currently selected
unsigned int count () const;
-//! \brief the current position in the current level
- unsigned int gotoPosition ()
- {
- return gotoPosition (m_level);
- }
-
//! \brief the current position
- unsigned int getPosition (unsigned int level)const;
+ unsigned int getPosition ()const;
//! \brief go to the current position. If it does not exist,
// go to the nearest.
- unsigned int gotoPosition (unsigned int level);
+ unsigned int gotoPosition ();
//! \brief the current position in the tracks list
@@ -307,6 +284,13 @@ class mgSelection
return select (valindex(value));
}
+ bool selectid (const string id)
+ {
+ return select(idindex(id));
+ }
+
+ void selectfrom(mgOrder& oldorder,mgContentItem* o);
+
/*! \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
@@ -322,12 +306,7 @@ class mgSelection
* 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 (const unsigned int target_level)
- {
- while (m_level>target_level)
- if (!leave()) return false;
- return true;
- }
+ void leave_all ();
//! \brief the current level in the tree
unsigned int level () const
@@ -529,6 +508,8 @@ class mgSelection
string id(mgKeyTypes kt, string val) const;
string id(mgKey* k, string val) const;
string id(mgKey* k) const;
+ unsigned int keycount(mgKeyTypes kt);
+ vector <const char *> choices(mgOrder *o,unsigned int level, unsigned int *current);
private:
mutable map <mgKeyTypes, map<string,string> > map_values;
@@ -541,20 +522,17 @@ class mgSelection
//! \brief initializes maps for id/value mapping in both direction
bool loadvalues (mgKeyTypes kt) const;
bool m_fall_through;
- vector < unsigned int >m_position;
+ unsigned int m_position;
mutable unsigned int m_tracks_position;
ShuffleMode m_shuffle_mode;
LoopMode m_loop_mode;
MYSQL *m_db;
void setDB(MYSQL *db);
- string m_Host;
- string m_User;
- string m_Password;
unsigned int m_level;
long m_trackid;
mgOrder order;
- bool UsedBefore (const mgKeyTypes kt, unsigned int level) const;
+ bool UsedBefore (mgOrder *o,const mgKeyTypes kt, unsigned int level) const;
void InitSelection ();
void InitDatabase ();
/*! \brief returns the SQL command for getting all values.
@@ -565,7 +543,8 @@ class mgSelection
* entries and the wrong tracks might be played.
*/
string sql_values ();
- unsigned int valindex (const string val,const bool second_try=false);
+ unsigned int valindex (const string val,const bool second_try=false) const;
+ unsigned int idindex (const string val,const bool second_try=false) const;
string ListFilename ();
string m_Directory;
void loadgenres ();
@@ -579,9 +558,9 @@ class mgSelection
* returning only one row.
* \param query the SQL query to be executed
*/
- unsigned long mgSelection::exec_count (string query) const;
-
+ unsigned long exec_count (string query) const;
+
};
diff --git a/mg_order.c b/mg_order.c
index ead9436..46d4d57 100644
--- a/mg_order.c
+++ b/mg_order.c
@@ -2,6 +2,12 @@
#include "mg_tools.h"
#include "i18n.h"
+
+bool iskeyGenre(mgKeyTypes kt)
+{
+ return kt>=keyGenre1 && kt <= keyGenres;
+}
+
class mgRefParts : public mgParts {
public:
mgRefParts(const mgReference& r);
@@ -27,16 +33,6 @@ sql_string (MYSQL *db, const string s)
return result;
}
-//! \brief adds n1=n2 to string s, using AND to separate several such items
-static string
-undequal (string & s, string n1, string op, string n2)
-{
- if (n1.compare (n2) || op != "=")
- return addsep (s, " AND ", n1 + op + n2);
- else
- return s;
-}
-
/*! \brief if the SQL command works on only 1 table, remove all table
* qualifiers. Example: SELECT tracks.title FROM tracks becomes SELECT title
* FROM tracks
@@ -83,28 +79,98 @@ exec_sql (MYSQL *db,string query)
return mysql_store_result (db);
}
+string
+get_col0(MYSQL *db, string query)
+{
+ MYSQL_RES * sql_result = exec_sql (db, query);
+ if (!sql_result)
+ return "NULL";
+ MYSQL_ROW row = mysql_fetch_row (sql_result);
+ string result;
+ if (row == NULL)
+ result = "NULL";
+ else if (row[0] == NULL)
+ result = "NULL";
+ else
+ result = row[0];
+ mysql_free_result (sql_result);
+ return result;
+}
+
+int
+exec_count(MYSQL *db, string query)
+{
+ return atol (get_col0 (db, query).c_str ());
+}
+
+
+string&
+addsep (string & s, string sep, string n)
+{
+ if (!n.empty ())
+ {
+ if (!s.empty ())
+ s.append (sep);
+ s.append (n);
+ }
+ return s;
+}
+
+
+static string
+sql_list (string prefix,strlist v,string sep=",",string postfix="")
+{
+ string result = "";
+ for (list < string >::iterator it = v.begin (); it != v.end (); ++it)
+ {
+ addsep (result, sep, *it);
+ }
+ if (!result.empty())
+ {
+ result.insert(0," "+prefix+" ");
+ result += postfix;
+ }
+ return result;
+}
+
+//! \brief converts long to string
+string
+itos (int i)
+{
+ stringstream s;
+ s << i;
+ return s.str ();
+}
+
+//! \brief convert long to string
+string
+ltos (long l)
+{
+ stringstream s;
+ s << l;
+ return s.str ();
+}
class mgKeyNormal : public mgKey {
public:
mgKeyNormal(const mgKeyNormal& k);
- mgKeyNormal(const mgKeyTypes kt, string table, string field);
+ mgKeyNormal(const mgKeyTypes kt, string table, string field);
virtual mgParts Parts(bool orderby=false) const;
string value() const;
string id() const;
void set(string value,string id);
mgKeyTypes Type() const { return m_kt; }
- virtual string expr() const { return field(); }
- virtual string field() const { return m_table + "." + m_field; }
+ virtual string expr() const { return m_table + "." + m_field; }
virtual string table() const { return m_table; }
protected:
- virtual string orderfield() const { return expr(); }
+ string IdClause(string what,string::size_type start=0,string::size_type len=string::npos) const;
void AddIdClause(mgParts &result,string what) const;
+ string m_id;
private:
mgKeyTypes m_kt;
string m_field;
string m_table;
string m_value;
- string m_id;
};
class mgKeyTrack : public mgKeyNormal {
@@ -113,24 +179,96 @@ class mgKeyTrack : public mgKeyNormal {
mgParts Parts(bool orderby=false) const;
};
-class mgKeyGenre1 : public mgKeyNormal {
+class mgKeyGenres : public mgKeyNormal {
public:
- mgKeyGenre1() : mgKeyNormal(keyGenre1,"tracks","genre1") {};
+ mgKeyGenres() : mgKeyNormal(keyGenres,"tracks","genre1") {};
+ mgKeyGenres(mgKeyTypes kt) : mgKeyNormal(kt,"tracks","genre1") {};
mgParts Parts(bool orderby=false) const;
string map_idfield() const { return "id"; }
string map_valuefield() const { return "genre"; }
string map_valuetable() const { return "genre"; }
+ string GenreClauses(bool orderby) const;
+ virtual unsigned int genrelevel() const { return 4; }
};
-class mgKeyGenre2 : public mgKeyNormal {
+class mgKeyGenre1 : public mgKeyGenres
+{
public:
- mgKeyGenre2() : mgKeyNormal(keyGenre2,"tracks","genre2") {};
- mgParts Parts(bool orderby=false) const;
- string map_idfield() const { return "id"; }
- string map_valuefield() const { return "genre"; }
- string map_valuetable() const { return "genre"; }
+ mgKeyGenre1() : mgKeyGenres(keyGenre1) {}
+ unsigned int genrelevel() const { return 1; }
+};
+
+class mgKeyGenre2 : public mgKeyGenres
+{
+ public:
+ mgKeyGenre2() : mgKeyGenres(keyGenre2) {}
+ unsigned int genrelevel() const { return 2; }
+};
+
+class mgKeyGenre3 : public mgKeyGenres
+{
+ public:
+ mgKeyGenre3() : mgKeyGenres(keyGenre3) {}
+ unsigned int genrelevel() const { return 3; }
};
+string
+mgKeyGenres::GenreClauses(bool orderby) const
+{
+ strlist g1;
+ strlist g2;
+
+ if (orderby)
+ if (genrelevel()==4)
+ {
+ g1.push_back("tracks.genre1=genre.id");
+ g2.push_back("tracks.genre2=genre.id");
+ }
+ else
+ {
+ g1.push_back("substring(tracks.genre1,1,"+ltos(genrelevel())+")=genre.id");
+ g2.push_back("substring(tracks.genre2,1,"+ltos(genrelevel())+")=genre.id");
+ }
+
+ if (id() != EMPTY)
+ {
+ unsigned int len=genrelevel();
+ if (len==4) len=0;
+ g1.push_back(IdClause("tracks.genre1",0,genrelevel()));
+ g2.push_back(IdClause("tracks.genre2",0,genrelevel()));
+ }
+
+ extern bool needGenre2;
+ if (needGenre2)
+ {
+ string o1=sql_list("(",g1," AND ",")");
+ if (o1.empty())
+ return "";
+ string o2=sql_list("(",g2," AND ",")");
+ return string("(") + o1 + " OR " + o2 + string(")");
+ }
+ else
+ return sql_list("",g1," AND ");
+}
+
+
+mgParts
+mgKeyGenres::Parts(bool orderby) const
+{
+ mgParts result;
+ result.clauses.push_back(GenreClauses(orderby));
+ result.tables.push_back("tracks");
+ if (orderby)
+ {
+ result.fields.push_back("genre.genre");
+ result.fields.push_back("genre.id");
+ result.tables.push_back("genre");
+ result.orders.push_back("genre.genre");
+ }
+ return result;
+}
+
+
class mgKeyLanguage : public mgKeyNormal {
public:
mgKeyLanguage() : mgKeyNormal(keyLanguage,"tracks","lang") {};
@@ -182,6 +320,11 @@ mgKey::~mgKey()
{
}
+void
+mgKey::setdb(MYSQL *db)
+{
+ m_db = db;
+}
mgKeyNormal::mgKeyNormal(const mgKeyNormal& k)
{
@@ -226,34 +369,35 @@ mgKeyNormal::Parts(bool orderby) const
if (orderby)
{
result.fields.push_back(expr());
- result.orders.push_back(orderfield());
+ result.orders.push_back(expr());
}
return result;
}
-void
-mgKeyNormal::AddIdClause(mgParts &result,string what) const
+string
+mgKeyNormal::IdClause(string what,string::size_type start,string::size_type len) const
{
assert(strlen(m_db->host));
- if (id() != EMPTY)
+ if (len==0)
+ len=string::npos;
+ if (id() == "'NULL'")
+ return what + " is NULL";
+ else if (len==string::npos)
+ return what + "=" + sql_string(m_db,id());
+ else
{
- string op;
- string xid;
- if (id() == "'NULL'")
- {
- op = "is";
- xid = "NULL";
- }
- else
- {
- op = "=";
- xid = sql_string(m_db,id());
- }
- string clause = "";
- result.clauses.push_back(undequal(clause,what,op,xid));
+ return "substring("+what + ","+ltos(start+1)+","+ltos(len)+")="
+ + sql_string(m_db,id().substr(start,len));
}
}
+void
+mgKeyNormal::AddIdClause(mgParts &result,string what) const
+{
+ if (id() != EMPTY)
+ result.clauses.push_back(IdClause(what));
+}
+
mgParts
mgKeyTrack::Parts(bool orderby) const
{
@@ -271,38 +415,6 @@ mgKeyTrack::Parts(bool orderby) const
}
mgParts
-mgKeyGenre1::Parts(bool orderby) const
-{
- mgParts result;
- AddIdClause(result,"tracks.genre1");
- result.tables.push_back("tracks");
- if (orderby)
- {
- result.fields.push_back("genre.genre");
- result.fields.push_back("tracks.genre1");
- result.tables.push_back("genre");
- result.orders.push_back("genre.genre");
- }
- return result;
-}
-
-mgParts
-mgKeyGenre2::Parts(bool orderby) const
-{
- mgParts result;
- AddIdClause(result,"tracks.genre2");
- result.tables.push_back("tracks");
- if (orderby)
- {
- result.fields.push_back("genre.genre");
- result.fields.push_back("tracks.genre2");
- result.tables.push_back("genre");
- result.orders.push_back("genre.genre");
- }
- return result;
-}
-
-mgParts
mgKeyLanguage::Parts(bool orderby) const
{
mgParts result;
@@ -366,54 +478,6 @@ mgParts::operator+=(mgParts a)
return *this;
}
-
-string&
-addsep (string & s, string sep, string n)
-{
- if (!n.empty ())
- {
- if (!s.empty ())
- s.append (sep);
- s.append (n);
- }
- return s;
-}
-
-
-static string
-sql_list (string prefix,list < string > v,string sep=",",string postfix="")
-{
- string result = "";
- for (list < string >::iterator it = v.begin (); it != v.end (); it++)
- {
- addsep (result, sep, *it);
- }
- if (!result.empty())
- {
- result.insert(0," "+prefix+" ");
- result += postfix;
- }
- return result;
-}
-
-//! \brief converts long to string
-string
-itos (int i)
-{
- stringstream s;
- s << i;
- return s.str ();
-}
-
-//! \brief convert long to string
-string
-ltos (long l)
-{
- stringstream s;
- s << l;
- return s.str ();
-}
-
mgRefParts::mgRefParts(const mgReference& r)
{
tables.push_back(r.t1());
@@ -428,7 +492,7 @@ mgParts::Prepare()
tables.unique();
strlist::reverse_iterator it;
string prevtable = "";
- for (it = tables.rbegin(); it != tables.rend(); it++)
+ for (it = tables.rbegin(); it != tables.rend(); ++it)
{
if (!prevtable.empty())
*this += ref.Connect(prevtable,*it);
@@ -475,7 +539,7 @@ mgParts::sql_count()
bool
mgParts::UsesTracks()
{
- for (list < string >::iterator it = tables.begin (); it != tables.end (); it++)
+ for (list < string >::iterator it = tables.begin (); it != tables.end (); ++it)
if (*it == "tracks") return true;
return false;
}
@@ -525,6 +589,10 @@ mgReference::mgReference(string t1,string f1,string t2,string f2)
mgOrder::mgOrder()
{
+ setDB(0);
+ setKey (0,keyArtist);
+ setKey (1,keyAlbum);
+ setKey (2,keyTrack);
}
mgKey*
@@ -540,10 +608,22 @@ mgOrder::operator[](unsigned int idx)
return Keys[idx];
}
+bool
+operator==(const mgOrder& a, const mgOrder &b)
+{
+ bool result = a.size()==b.size();
+ if (result)
+ for (unsigned int i=0; i<a.size();i++)
+ {
+ result &= a.Key(i)->Type()==b.Key(i)->Type();
+ if (!result) break;
+ }
+ return result;
+}
+
const mgOrder&
mgOrder::operator=(const mgOrder& from)
{
- Name = from.Name;
Keys.clear();
for (unsigned int i = 0; i < from.size();i++)
{
@@ -551,7 +631,7 @@ mgOrder::operator=(const mgOrder& from)
k->set(from.getKeyValue(i),from.getKeyId(i));
Keys.push_back(k);
}
- setDB(from.m_db);
+ if (from.m_db) setDB(from.m_db);
return *this;
}
@@ -562,6 +642,77 @@ mgOrder::operator+=(mgKey* k) {
return *this;
}
+string
+mgOrder::Name()
+{
+ string result="";
+ for (unsigned int idx=0;idx<size();idx++)
+ {
+ if (!result.empty()) result += ":";
+ result += ktName(Keys[idx]->Type());
+ }
+ return result;
+}
+
+void
+mgOrder::setKey (const unsigned int level, const mgKeyTypes kt)
+{
+ mgKey *newkey = ktGenerate(kt,m_db);
+ if (level == 0 && kt == keyCollection)
+ {
+ clear ();
+ Keys.push_back(newkey);
+ Keys.push_back(ktGenerate(keyCollectionItem,m_db));
+ return;
+ }
+ if (level == size ())
+ {
+ Keys.push_back(newkey);
+ }
+ else
+ {
+ if (level >= Keys.size())
+ mgError("mgOrder::setKey(%u,%s): level greater than size() %u",
+ level,ktName(kt),Keys.size());
+ delete Keys[level];
+ Keys[level] = newkey;
+ }
+
+// clear values for this and following levels (needed for copy constructor)
+ for (unsigned int i = level; i < Keys.size (); i++)
+ Keys[i]->set ("",EMPTY);
+}
+
+mgOrder::mgOrder(mgValmap& nv,char *prefix)
+{
+ setDB(0);
+ for (unsigned int i = 0; i < 999 ; i++)
+ {
+ char *idx;
+ asprintf(&idx,"%s.Keys.%u.Type",prefix,i);
+ unsigned int v = nv.getuint(idx);
+ free(idx);
+ if (v==0) break;
+ setKey (i,mgKeyTypes(v) );
+ }
+}
+
+mgOrder::mgOrder(vector<mgKeyTypes> kt)
+{
+ setDB(0);
+ setKeys(kt);
+}
+
+void
+mgOrder::setKeys(vector<mgKeyTypes> kt)
+{
+ clear();
+ for (unsigned int i=0;i<kt.size();i++)
+ setKey(size(),kt[i]);
+ clean();
+}
+
+
mgKeyTypes
mgOrder::getKeyType(unsigned int idx) const
{
@@ -588,7 +739,7 @@ mgOrder::setDB(MYSQL *db)
{
m_db = db;
keyvector::iterator i;
- for (i = Keys.begin () ; i != Keys.end (); i++)
+ for (i = Keys.begin () ; i != Keys.end (); ++i)
{
(*i)->setdb(db);
}
@@ -598,7 +749,7 @@ mgKey*
mgOrder::find(const mgKeyTypes kt)
{
keyvector::iterator i;
- for (i = Keys.begin () ; i != Keys.end (); i++)
+ for (i = Keys.begin () ; i != Keys.end (); ++i)
{
if ((*i)->Type() == kt)
return *i;
@@ -625,25 +776,47 @@ mgOrder::clean()
bool album_found = false;
bool tracknb_found = false;
bool title_found = false;
- for (i = Keys.begin () ; i != Keys.end (); i++)
+ bool is_unique = false;
+ for (i = Keys.begin () ; i != Keys.end (); ++i)
{
mgKeyNormal* k = dynamic_cast<mgKeyNormal*>(*i);
album_found |= (k->Type()==keyAlbum);
tracknb_found |= (k->Type()==keyTrack);
title_found |= (k->Type()==keyTitle);
- if (tracknb_found || (album_found && title_found))
+ is_unique = tracknb_found || (album_found && title_found);
+ if (is_unique)
{
- for (j = i+1 ; j !=Keys.end(); j++)
+ for (j = i+1 ; j !=Keys.end(); ++j)
delete *j;
Keys.erase(i+1,Keys.end ());
break;
}
- for (j = i+1 ; j != Keys.end(); j++)
- if (*i == *j) {
+ if (k->Type()==keyYear)
+ {
+ for (j = i+1 ; j != Keys.end(); ++j)
+ if ((*j)->Type() == keyDecade)
+ {
+ delete *j;
+ Keys.erase(j);
+ break;
+ }
+ }
+cleanagain:
+ for (j = i+1 ; j != Keys.end(); ++j)
+ if ((*i)->Type() == (*j)->Type())
+ {
delete *j;
Keys.erase(j);
+ goto cleanagain;
}
}
+ if (!is_unique)
+ {
+ if (!album_found)
+ Keys.push_back(ktGenerate(keyAlbum,m_db));
+ if (!title_found)
+ Keys.push_back(ktGenerate(keyTitle,m_db));
+ }
}
@@ -657,18 +830,37 @@ mgOrder::Parts(unsigned int level,bool orderby) const
if (i==Keys.size()) break;
mgKeyNormal *k = dynamic_cast<mgKeyNormal*>(Keys[i]);
k->setdb(m_db);
+ mgKeyTypes kt = k->Type();
+ if (iskeyGenre(kt))
+ {
+ for (unsigned int j=i+1;j<=level;j++)
+ {
+ if (j>=Keys.size())
+ break;
+ mgKeyNormal *kn = dynamic_cast<mgKeyNormal*>(Keys[j]);
+ if (kn)
+ {
+ mgKeyTypes knt = kn->Type();
+ if (iskeyGenre(knt)
+ && knt>kt && kn->id()!=EMPTY)
+ goto next;
+ }
+ }
+ }
result += k->Parts(orderby && (i==level));
+next:
+ continue;
}
return result;
}
+//! \brief right now thread locking should not be needed here
mgReferences::mgReferences()
{
push_back(mgReference ("tracks","id","playlistitem","trackid"));
push_back(mgReference ("playlist","id","playlistitem","playlist"));
push_back(mgReference ("tracks","sourceid","album","cddbid"));
- push_back(mgReference ("tracks","genre1","genre","id"));
- push_back(mgReference ("tracks","genre2","genre","id"));
+ push_back(mgReference ("tracks","lang","language","id"));
}
bool
@@ -691,6 +883,8 @@ mgParts
mgReferences::ConnectToTracks(string table) const
{
mgParts result;
+ if (table=="tracks")
+ return result;
result += FindConnectionBetween(table,"tracks");
if (result.empty())
{
@@ -711,6 +905,8 @@ mgReferences::Connect(string table1, string table2) const
mgParts result;
// same table?
if (table1 == table2) return result;
+ if (table1=="genre") return ConnectToTracks(table2);
+ if (table2=="genre") return ConnectToTracks(table1);
// do not connect aliases. See sql_delete_from_collection
if (table1.find(" as ")!=string::npos) return result;
if (table2.find(" as ")!=string::npos) return result;
@@ -734,8 +930,10 @@ ktGenerate(const mgKeyTypes kt,MYSQL* db)
mgKey* result = 0;
switch (kt)
{
+ case keyGenres: result = new mgKeyGenres;break;
case keyGenre1: result = new mgKeyGenre1;break;
case keyGenre2: result = new mgKeyGenre2;break;
+ case keyGenre3: result = new mgKeyGenre3;break;
case keyArtist: result = new mgKeyNormal(kt,"tracks","artist");break;
case keyTitle: result = new mgKeyNormal(kt,"tracks","title");break;
case keyTrack: result = new mgKeyTrack;break;
@@ -757,8 +955,10 @@ ktName(const mgKeyTypes kt)
const char * result = "";
switch (kt)
{
- case keyGenre1: result = "Genre";break;
- case keyGenre2: result = "Genre 2";break;
+ case keyGenres: result = "Genre";break;
+ case keyGenre1: result = "Genre1";break;
+ case keyGenre2: result = "Genre2";break;
+ case keyGenre3: result = "Genre3";break;
case keyArtist: result = "Artist";break;
case keyTitle: result = "Title";break;
case keyTrack: result = "Track";break;
@@ -773,3 +973,23 @@ ktName(const mgKeyTypes kt)
return tr(result);
}
+mgKeyTypes
+ktValue(const char * name)
+{
+ for (int kt=int(mgKeyTypesLow);kt<int(mgKeyTypesHigh);kt++)
+ if (!strcmp(name,ktName(mgKeyTypes(kt))))
+ return mgKeyTypes(kt);
+ mgError("ktValue(%s): unknown name",name);
+ return mgKeyTypes(0);
+}
+
+
+vector<const char*>
+ktNames()
+{
+ static vector<const char*> result;
+ for (unsigned int i = int(mgKeyTypesLow); i <= int(mgKeyTypesHigh); i++)
+ result.push_back(ktName(mgKeyTypes(i)));
+ return result;
+}
+
diff --git a/mg_order.h b/mg_order.h
index d46f244..71907da 100644
--- a/mg_order.h
+++ b/mg_order.h
@@ -12,6 +12,8 @@
#include <sstream>
#include <ostream>
+#include "mg_valmap.h"
+
using namespace std;
typedef list<string> strlist;
@@ -24,20 +26,26 @@ static const string EMPTY = "XNICHTGESETZTX";
string& addsep (string & s, string sep, string n);
enum mgKeyTypes {
- keyGenre1 = 1,
+ keyGenre1=1, // the genre types must have exactly this order!
keyGenre2,
+ keyGenre3,
+ keyGenres,
+ keyDecade,
+ keyYear,
keyArtist,
+ keyAlbum,
keyTitle,
keyTrack,
- keyDecade,
- keyCollection,
- keyCollectionItem,
- keyAlbum,
keyLanguage,
keyRating,
- keyYear,
+ keyCollection,
+ keyCollectionItem,
};
-const mgKeyTypes mgKeyTypesHigh = keyYear;
+const mgKeyTypes mgKeyTypesLow = keyGenre1;
+const mgKeyTypes mgKeyTypesHigh = keyCollectionItem;
+const unsigned int mgKeyTypesNr = keyCollectionItem;
+
+bool iskeyGenre(mgKeyTypes kt);
class mgParts;
@@ -79,7 +87,7 @@ class mgKey {
virtual string map_idfield() const { return ""; }
virtual string map_valuefield() const { return ""; }
virtual string map_valuetable() const { return ""; }
- void setdb(MYSQL *db) { m_db = db; }
+ void setdb(MYSQL *db);
protected:
MYSQL *m_db;
};
@@ -88,9 +96,10 @@ class mgKey {
mgKey*
ktGenerate(const mgKeyTypes kt,MYSQL *db);
-const char * const
-ktName(const mgKeyTypes kt);
-
+const char * const ktName(const mgKeyTypes kt);
+mgKeyTypes ktValue(const char * name);
+vector < const char*> ktNames();
+
typedef vector<mgKey*> keyvector;
class mgParts {
@@ -117,6 +126,8 @@ string
sql_string (MYSQL *db, const string s);
MYSQL_RES * exec_sql (MYSQL *db,string query);
+string get_col0 (MYSQL *db,string query);
+int exec_count (MYSQL *db,string query);
//! \brief converts long to string
string itos (int i);
@@ -130,9 +141,10 @@ const unsigned int MaxKeys = 20;
class mgOrder {
public:
mgOrder();
+ mgOrder(mgValmap& nv, char *prefix);
+ mgOrder(vector<mgKeyTypes> kt);
void setDB(MYSQL *db);
mgParts Parts(unsigned int level,bool orderby=true) const;
- string Name;
const mgOrder& operator=(const mgOrder& from);
mgOrder& operator+=(mgKey* k);
mgKey*& operator[](unsigned int idx);
@@ -146,9 +158,14 @@ public:
mgKeyTypes getKeyType(unsigned int idx) const;
string getKeyValue(unsigned int idx) const;
string getKeyId(unsigned int idx) const;
+ void setKeys(vector<mgKeyTypes> kt);
+ string Name();
private:
MYSQL *m_db;
keyvector Keys;
+ void setKey (const unsigned int level, const mgKeyTypes kt);
};
+bool operator==(const mgOrder& a,const mgOrder&b); //! \brief compares only the order, not the current key values
+
#endif // _MG_SQL_H
diff --git a/mg_tools.c b/mg_tools.c
index 3a484c5..3b07ba5 100644
--- a/mg_tools.c
+++ b/mg_tools.c
@@ -84,7 +84,8 @@ mgWarning (const char *fmt, ...)
isyslog ("Warning: %s\n", buffer);
#endif
}
-
+ extern void showmessage(const char*);
+ showmessage(buffer);
va_end (ap);
}
diff --git a/muggle.c b/muggle.c
index c4e1831..179909e 100644
--- a/muggle.c
+++ b/muggle.c
@@ -21,7 +21,7 @@
#include <getopt.h>
#include <config.h>
-static const char *VERSION = "0.1.1";
+static const char *VERSION = "0.1.2";
static const char *DESCRIPTION = "Media juggle plugin for VDR";
static const char *MAINMENUENTRY = "Muggle";
diff --git a/mugglei.c b/mugglei.c
index 2e780df..19a0b3d 100755
--- a/mugglei.c
+++ b/mugglei.c
@@ -23,6 +23,9 @@
#include <stdlib.h>
#include <tag.h>
+#include <mpegfile.h>
+#include <flacfile.h>
+#include <id3v2tag.h>
#include <fileref.h>
#include "mg_tools.h"
@@ -35,6 +38,10 @@ bool import_assorted, delete_mode;
#define MAX_QUERY_BUFLEN 2048
static char querybuf[MAX_QUERY_BUFLEN];
+void showmessage(const char *msg)
+{
+}
+
MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...)
{
va_list ap;
@@ -45,10 +52,6 @@ MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...)
{
mgError( "SQL error in MUGGLE:\n%s\n", querybuf );
}
-
-#ifdef VERBOSE
- std::cout << querybuf << std::endl;
-#endif
MYSQL_RES *result = mysql_store_result(db);
@@ -67,10 +70,6 @@ void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...)
mgError( "SQL error in MUGGLE:\n%s\n", querybuf );
}
-#ifdef VERBOSE
- std::cout << querybuf << std::endl;
-#endif
-
va_end(ap);
}
@@ -142,23 +141,14 @@ time_t get_db_modification_time( long uid )
TagLib::String escape_string( MYSQL *db, TagLib::String s )
{
- TagLib::String r;
+ char *buf = strdup( s.toCString() );
+ char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 );
- if( !s.isNull() && !s.isEmpty() )
- {
- char *buf = strdup( s.toCString() );
- char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 );
+ mysql_real_escape_string( db, escbuf, s.toCString(), s.size() );
+ TagLib::String r = TagLib::String( escbuf );
- mysql_real_escape_string( db, escbuf, s.toCString(), s.size() );
- r = TagLib::String( escbuf );
-
- free( escbuf );
- free( buf);
- }
- else
- {
- r = s;
- }
+ free( escbuf );
+ free( buf);
return r;
}
@@ -204,88 +194,48 @@ TagLib::String find_genre_id( TagLib::String genre )
// read tags from the mp3 file and store them into the corresponding database entry
void update_db( long uid, std::string filename )
{
- TagLib::String title, album, artist, genre, cddbid;
+ TagLib::String title, album, artist, genre, cddbid, language;
uint trackno, year;
// ID3_Tag filetag( filename.c_str() );
TagLib::FileRef f( filename.c_str() );
- if( !f.isNull() )
+ if( !f.isNull() && f.tag() )
{
-#ifdef VERBOSE
- std::cout << "Evaluating " << filename << std::endl;
-#endif
+ // std::cout << "Evaluating " << filename << std::endl;
TagLib::Tag *tag = f.tag();
- if( tag )
- {
- // obtain tag information
- title = tag->title();
- album = tag->album();
- year = tag->year();
- artist = tag->artist();
- trackno = tag->track();
- genre = tag->genre();
-
-#ifdef VERBOSE
- std::cout << "-- TAG --" << std::endl;
- std::cout << "title - '" << tag->title() << "'" << std::endl;
- std::cout << "artist - '" << tag->artist() << "'" << std::endl;
- std::cout << "album - '" << tag->album() << "'" << std::endl;
- std::cout << "year - '" << tag->year() << "'" << std::endl;
- std::cout << "comment - '" << tag->comment() << "'" << std::endl;
- std::cout << "track - '" << tag->track() << "'" << std::endl;
- std::cout << "genre - '" << tag->genre() << "'" << std::endl;
-#endif
- }
- else
- {
-#ifdef VERBOSE
- std::cerr << "No id3 tag found." << std::endl;
-#endif
-
- // use basename
- TagLib::String file( filename.c_str() );
-
- int pos = title.size();
- while( pos > 0 && title[pos] != '\\' )
- {
- pos --;
- }
-
- title = file.substr( pos );
-
- // will be associated to an Unassigned album for Unknown
- album = "";
- artist = "Unknown";
-
- year = 0;
- trackno = 0;
- }
-
- if( title.isNull() || title.isEmpty() )
- {
-#ifdef VERBOSE
- std::cout << "No title tag found." << std::endl;
-#endif
- // use basename
- TagLib::String file( filename.c_str() );
-
- int pos = title.size();
- while( pos > 0 && title[pos] != '\\' )
- {
- pos --;
- }
-
- title = file.substr( pos );
- }
- if( artist.isNull() || artist.isEmpty() )
- {
- artist = "Unknown";
-#ifdef VERBOSE
- std::cout << "No artist tag found." << std::endl;
-#endif
- }
+ // obtain tag information
+ title = tag->title();
+ album = tag->album();
+ year = tag->year();
+ artist = tag->artist();
+ trackno = tag->track();
+ genre = tag->genre();
+ language = "";
+ TagLib::ID3v2::Tag * id3v2tag=0;
+ if (filename.substr(filename.size()-5)==".flac")
+ {
+ TagLib::FLAC::File f(filename.c_str());
+ id3v2tag = f.ID3v2Tag();
+ if (id3v2tag)
+ {
+ TagLib::ID3v2::FrameList l = id3v2tag->frameListMap()["TLAN"];
+ if (!l.isEmpty())
+ language = l.front()->toString();
+ }
+ }
+ else if (filename.substr(filename.size()-4)==".mp3")
+ {
+ TagLib::MPEG::File f(filename.c_str());
+ id3v2tag = f.ID3v2Tag();
+ if (id3v2tag)
+ {
+ TagLib::ID3v2::FrameList l = id3v2tag->frameListMap()["TLAN"];
+ if (!l.isEmpty())
+ language = l.front()->toString();
+ }
+ }
TagLib::String gid = find_genre_id( genre );
@@ -304,7 +254,7 @@ void update_db( long uid, std::string filename )
// finally update the database
// obtain associated album or create
- if( album.isNull() || album.isEmpty() )
+ if( album == "" )
{ // no album found, create default album for artist
MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"Unassigned\" AND artist=\"%s\"", artist.toCString() );
MYSQL_ROW row = mysql_fetch_row( result );
@@ -385,20 +335,32 @@ void update_db( long uid, std::string filename )
mgSqlWriteQuery( db, "UPDATE tracks SET artist=\"%s\", title=\"%s\", year=\"%d\","
"sourceid=\"%s\", mp3file=\"%s\", length=%d, bitrate=\"%d\","
- "samplerate=%d, channels=%d, genre1=\"%s\" WHERE id=%d",
+ "samplerate=%d, channels=%d, genre1=\"%s\", lang=\"%s\" WHERE id=%d",
artist.toCString(), title.toCString(), year,
cddbid.toCString(), filename.c_str(), len, bitrate,
- sample, channels, gid.toCString(), uid );
+ sample, channels, gid.toCString(), uid, language.toCString() );
}
else
{ // the entry does not exist, create it
mgSqlWriteQuery( db,
"INSERT INTO tracks "
- "(artist, title, year,sourceid,tracknb,mp3file,length,bitrate,samplerate,channels,genre1,genre2) VALUES"
- "(\"%s\", \"%s\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d, \"%s\",\"\")",
+ "(artist, title, year,sourceid,tracknb,mp3file,length,bitrate,samplerate,channels,genre1,genre2,lang) VALUES"
+ "(\"%s\", \"%s\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d, \"%s\",\"\",\"%s\")",
artist.toCString(), title.toCString(), year, cddbid.toCString(),
- trackno, filename.c_str(), len, bitrate, sample, channels, gid.toCString() );
+ trackno, filename.c_str(), len, bitrate, sample, channels, gid.toCString(),
+ language.toCString());
+#ifdef VERBOSE
+ std::cout << "-- TAG --" << std::endl;
+ std::cout << "title - '" << tag->title() << "'" << std::endl;
+ std::cout << "artist - '" << tag->artist() << "'" << std::endl;
+ std::cout << "album - '" << tag->album() << "'" << std::endl;
+ std::cout << "year - '" << tag->year() << "'" << std::endl;
+ std::cout << "comment - '" << tag->comment() << "'" << std::endl;
+ std::cout << "track - '" << tag->track() << "'" << std::endl;
+ std::cout << "genre - '" << tag->genre() << "'" << std::endl;
+ std::cout << "language- '" << language << "'" << std::endl;
+#endif
}
}
}
diff --git a/scripts/languages.txt b/scripts/languages.txt
index 68dfdec..a24d25b 100755
--- a/scripts/languages.txt
+++ b/scripts/languages.txt
@@ -13,6 +13,7 @@ en English
eo Esperanto
es Spanish
fi Finnish
+fon Fon
fr French
hi Hindi
hu Hungarian
diff --git a/vdr_decoder.c b/vdr_decoder.c
index d5e8004..93dc540 100644
--- a/vdr_decoder.c
+++ b/vdr_decoder.c
@@ -25,6 +25,8 @@
#include "vdr_decoder.h"
#include "vdr_decoder_mp3.h"
+extern void showmessage(const char *);
+
#ifdef HAVE_VORBISFILE
#include "vdr_decoder_ogg.h"
#endif
@@ -60,13 +62,21 @@ mgMediaType mgDecoders::getMediaType (std::string s)
{
if (!strcmp (p, ".ogg"))
{
+#ifdef HAVE_VORBISFILE
mt = MT_OGG;
+#else
+ mgWarning("Support for vorbis not compiled in, define HAVE_VORBISFILE in Makefile");
+#endif
}
else
{
if (!strcmp (p, ".flac"))
{
+#ifdef HAVE_FLAC
mt = MT_FLAC;
+#else
+ mgWarning("Support for flac not compiled in, define HAVE_FLAC in Makefile");
+#endif
}
}
}
@@ -79,11 +89,19 @@ mgDecoders::findDecoder (mgContentItem * item)
{
mgDecoder *decoder = 0;
- std::string filename = the_setup.getFilename( item->getSourceFile () );
+ std::string filename = item->getSourceFile ();
struct stat st;
if (stat (filename.c_str (), &st))
{
+ char *b=0;
+ int nsize = filename.size();
+ if (nsize<30)
+ asprintf(&b,tr("%s not readable"),filename.c_str());
+ else
+ asprintf(&b,tr("%s..%s not readable"),filename.substr(0,20).c_str(),filename.substr(nsize-20).c_str());;
+ showmessage(b);
+ free(b);
esyslog ("ERROR: cannot stat %s. Meaning not found, not a valid file, or no access rights", filename.c_str ());
return 0;
}
diff --git a/vdr_decoder_flac.c b/vdr_decoder_flac.c
index c640205..f18ceab 100644
--- a/vdr_decoder_flac.c
+++ b/vdr_decoder_flac.c
@@ -34,8 +34,7 @@ mgFlacDecoder::mgFlacDecoder( mgContentItem *item )
{
mgLog lg( "mgFlacDecoder::mgFlacDecoder" );
- m_filename = the_setup.getFilename( item->getSourceFile () );
- // m_filename = item->getSourceFile();
+ m_filename = item->getSourceFile();
m_pcm = 0;
m_reservoir = 0;
@@ -100,10 +99,10 @@ bool mgFlacDecoder::clean()
if( m_reservoir )
{
- delete m_reservoir[0];
- delete m_reservoir[1];
+ delete[] m_reservoir[0];
+ delete[] m_reservoir[1];
}
- delete m_reservoir;
+ delete[] m_reservoir;
// why false? true?
return true;
diff --git a/vdr_decoder_mp3.c b/vdr_decoder_mp3.c
index 754ab39..2ad6bc3 100644
--- a/vdr_decoder_mp3.c
+++ b/vdr_decoder_mp3.c
@@ -60,8 +60,7 @@ mgMP3Decoder::mgMP3Decoder (mgContentItem * item, bool preinit):mgDecoder
m_stream = 0;
m_isStream = false;
- m_filename = the_setup.getFilename( item->getSourceFile () );
- // m_filename = item->getSourceFile ();
+ m_filename = item->getSourceFile ();
if (preinit)
{
@@ -102,6 +101,7 @@ mgMP3Decoder::init ()
m_playtime = mad_timer_zero;
m_skiptime = mad_timer_zero;
m_framenum = m_framemax = 0;
+ m_frameinfo = 0;
m_mute = m_errcount = 0;
}
diff --git a/vdr_decoder_ogg.c b/vdr_decoder_ogg.c
index 24d253d..96c44cf 100644
--- a/vdr_decoder_ogg.c
+++ b/vdr_decoder_ogg.c
@@ -239,8 +239,7 @@ mgOggFile::stream (short *buffer, int samples)
mgOggDecoder::mgOggDecoder (mgContentItem * item):mgDecoder (item)
{
- // m_filename = item->getSourceFile ();
- m_filename = the_setup.getFilename( item->getSourceFile () );
+ m_filename = item->getSourceFile ();
m_file = new mgOggFile (m_filename);
m_pcm = 0;
init ();
diff --git a/vdr_menu.c b/vdr_menu.c
index e3db994..e7e107b 100644
--- a/vdr_menu.c
+++ b/vdr_menu.c
@@ -45,7 +45,8 @@ mgStatus::OsdCurrentItem(const char* Text)
mgAction * a = dynamic_cast<mgAction *>(i);
if (!a)
mgError("mgStatus::OsdCurrentItem expected an mgAction*");
- a->TryNotify();
+ if (a)
+ a->TryNotify();
}
void Play(mgSelection *sel,const bool select) {
@@ -82,6 +83,27 @@ mgMainMenu::PlayInstant(const bool select)
}
void
+mgMainMenu::setOrder(mgSelection *sel,unsigned int idx)
+{
+ mgOrder* o = getOrder(idx);
+ if (o->size()>0)
+ {
+ m_current_order = idx;
+ sel->setOrder(o);
+ }
+ else
+ mgWarning("mgMainMenu::setOrder: orders[%u] is empty",idx);
+}
+
+mgOrder* mgMainMenu::getOrder(unsigned int idx)
+{
+ if (idx>=orders.size())
+ mgError("mgMainMenu::getOrder(%u): orders.size() is %d",
+ idx,orders.size());
+ return orders[idx];
+}
+
+void
mgMainMenu::CollectionChanged(string name)
{
delete moveselection;
@@ -151,7 +173,7 @@ mgMenu::GenerateAction(const mgActions action,mgActions on)
return result;
}
-void
+eOSState
mgMenu::ExecuteAction(const mgActions action,mgActions on)
{
mgAction *a = GenerateAction (action,on);
@@ -159,7 +181,9 @@ mgMenu::ExecuteAction(const mgActions action,mgActions on)
{
a->Execute ();
delete a;
+ return osContinue;
}
+ return osUnknown;
}
@@ -178,19 +202,42 @@ PlayerControl ()
mgMenu::mgMenu ()
{
m_osd = NULL;
- TreeRedAction = mgActions(0);
- TreeGreenAction = mgActions(0);
- TreeYellowAction = mgActions(0);
- CollRedAction = mgActions(0);
- CollGreenAction = mgActions(0);
- CollYellowAction = mgActions(0);
+ m_parent_index=-1;
+ TreeRedAction = actNone;
+ TreeGreenAction = actNone;
+ TreeYellowAction = actNone;
+ TreeBlueAction = actNone;
+ CollRedAction = actNone;
+ CollGreenAction = actNone;
+ CollYellowAction = actNone;
+ CollBlueAction = actNone;
}
// ----------------------- mgMainMenu ----------------------
-void mgMainMenu::SaveState()
+void
+mgMainMenu::DumpOrders(mgValmap& nv)
+{
+ map<string,mgOrder*>::iterator it;
+ for (unsigned int idx=0;idx<orders.size();idx++)
+ {
+ mgOrder *o = orders[idx];
+ if (!o)
+ mgError("DumpOrders:order[%u] is 0",idx);
+ char *n;
+ for (unsigned int i=0;i<o->size();i++)
+ {
+ asprintf(&n,"order%u.Keys.%d.Type",idx,i);
+ nv.put(n,int(o->Key(i)->Type()));
+ free(n);
+ }
+ }
+}
+
+void
+mgMainMenu::SaveState()
{
char *b;
asprintf(&b,"%s/muggle.state",cPlugin::ConfigDirectory ("muggle"));
@@ -206,6 +253,8 @@ void mgMainMenu::SaveState()
nmain.put("CollRedAction",int(Menus.front()->CollRedAction));
nmain.put("CollGreenAction",int(Menus.front()->CollGreenAction));
nmain.put("CollYellowAction",int(Menus.front()->CollYellowAction));
+ nmain.put("CurrentOrder",m_current_order);
+ DumpOrders(nmain);
mgValmap nsel("tree");
m_treesel.DumpState(nsel);
mgValmap ncol("collection");
@@ -216,7 +265,7 @@ void mgMainMenu::SaveState()
fclose(f);
}
-mgMainMenu::mgMainMenu ():cOsdMenu ("")
+mgMainMenu::mgMainMenu ():cOsdMenu ("",25)
{
m_Status = new mgStatus(this);
m_message = NULL;
@@ -230,11 +279,7 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("")
mgValmap nmain("MainMenu");
// define defaults for values missing in state file:
- nsel.put("Keys.0.Choice",keyArtist);
- nsel.put("Keys.1.Choice",keyAlbum);
- nsel.put("Keys.2.Choice",keyTitle);
- ncol.put("Keys.0.Choice",keyCollection);
- ncol.put("Keys.1.Choice",keyCollectionItem);
+ nsel.put("FallThrough",true);
nmain.put("DefaultCollection",play_collection);
nmain.put("UsingCollection","false");
nmain.put("TreeRedAction",int(actAddThisToCollection));
@@ -257,21 +302,31 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("")
}
// get values from mgValmaps
+ LoadOrders(nmain);
default_collection = nmain.getstr("DefaultCollection");
UsingCollection = nmain.getbool("UsingCollection");
InitMapFromSetup(nsel);
- m_treesel.InitFrom (nsel);
InitMapFromSetup(ncol);
+ m_treesel.setOrder(getOrder(m_current_order));
+ m_treesel.InitFrom (nsel);
+ m_treesel.CreateCollection(default_collection);
+ if (default_collection!=play_collection)
+ m_treesel.CreateCollection(play_collection);
+ vector<mgKeyTypes> kt;
+ kt.push_back(keyCollection);
+ mgOrder o;
+ o.setKeys(kt);
+ m_collectionsel.setOrder(&o);
m_collectionsel.InitFrom (ncol);
+ m_playsel.setOrder(&o);
m_playsel.InitFrom(ncol);
// initialize
- m_collectionsel.CreateCollection(default_collection);
- m_collectionsel.CreateCollection(play_collection);
- m_playsel.setKey(0,keyCollection);
- m_playsel.setKey(1,keyCollectionItem);
- m_playsel.leave(0);
- m_playsel.enter(play_collection);
+ if (m_playsel.level()!=1)
+ {
+ m_playsel.leave_all();
+ m_playsel.enter(play_collection);
+ }
UseNormalSelection ();
unsigned int posi = selection()->gotoPosition();
LoadExternalCommands(); // before AddMenu()
@@ -290,6 +345,57 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("")
}
void
+mgMainMenu::AddOrder()
+{
+ orders.push_back(new mgOrder);
+}
+
+void
+mgMainMenu::DeleteOrder()
+{
+ orders.erase(orders.begin()+Current());
+}
+
+void
+mgMainMenu::LoadOrders(mgValmap& nv)
+{
+ for (unsigned int idx=0;idx<10;idx++)
+ {
+ char b[10];
+ sprintf(b,"order%u",idx);
+ mgOrder *o = new mgOrder(nv,b);
+ if (o->size()==0)
+ {
+ delete o;
+ continue;
+ }
+ orders.push_back(o);
+ }
+ m_current_order = nv.getuint("CurrentOrder");
+ if (m_current_order >= orders.size())
+ m_current_order=0;
+ if (orders.size()>0) return;
+
+ nv.put("order1.Keys.0.Type",keyArtist);
+ nv.put("order1.Keys.1.Type",keyAlbum);
+ nv.put("order1.Keys.2.Type",keyTrack);
+
+ nv.put("order2.Keys.0.Type",keyAlbum);
+ nv.put("order2.Keys.1.Type",keyTrack);
+
+ nv.put("order3.Keys.0.Type",keyGenres);
+ nv.put("order3.Keys.1.Type",keyArtist);
+ nv.put("order3.Keys.2.Type",keyAlbum);
+ nv.put("order3.Keys.3.Type",keyTrack);
+
+ nv.put("order4.Keys.0.Type",keyArtist);
+ nv.put("order4.Keys.1.Type",keyTrack);
+
+ nv.put("CurrentOrder",0);
+ LoadOrders(nv);
+}
+
+void
mgMainMenu::LoadExternalCommands()
{
// Read commands for collections in etc. /video/muggle/playlist_commands.conf
@@ -326,11 +432,7 @@ void
mgMainMenu::InitMapFromSetup (mgValmap& nv)
{
// values from setup override saved values
- nv["Host"] = the_setup.DbHost;
- nv["User"] = the_setup.DbUser;
- nv["Password"] = the_setup.DbPass;
nv["Directory"] = cPlugin::ConfigDirectory ("muggle");
- nv["ToplevelDir"] = the_setup.ToplevelDir;
}
void
@@ -353,13 +455,29 @@ mgMenu::AddAction (const mgActions action, mgActions on,const bool hotkey)
void
mgMenu::AddExternalAction(const mgActions action, const char *title)
{
- mgAction *a = GenerateAction(action,mgActions(0));
+ mgAction *a = GenerateAction(action,actNone);
if (!a) return;
a->SetText(osd()->hk(title));
osd()->AddItem(a);
}
void
+mgMainMenu::AddOrderActions(mgMenu* m)
+{
+ map<string,mgOrder*>::iterator it;
+ for (unsigned int idx=0;idx<orders.size();idx++)
+ {
+ mgOrder *o = orders[idx];
+ if (!o)
+ mgError("AddOrderAction:orders[%u] is 0",idx);
+ mgAction *a = m->GenerateAction(actOrder,actNone);
+ assert(a);
+ a->SetText(hk(o->Name().c_str()));
+ AddItem(a);
+ }
+}
+
+void
mgMenu::AddSelectionItems (mgSelection *sel,mgActions act)
{
for (unsigned int i = 0; i < sel->values.size (); i++)
@@ -371,7 +489,7 @@ mgMenu::AddSelectionItems (mgSelection *sel,mgActions act)
}
if (osd()->ShowingCollections ())
{
- mgAction *a = GenerateAction(actCreateCollection,mgActions(0));
+ mgAction *a = GenerateAction(actCreateCollection,actNone);
if (a)
{
a->SetText(a->MenuName(),false);
@@ -381,20 +499,6 @@ mgMenu::AddSelectionItems (mgSelection *sel,mgActions act)
}
-const char *
-mgMenu::BlueName (mgActions on)
-{
- // do not use typeid because we want to catch descendants too
- if (dynamic_cast<mgTreeCollSelector*>(this))
- return tr ("List");
- else if (osd()->Current()<0)
- return tr("Commands");
- else if (dynamic_cast<mgTree*>(this))
- return tr("Commands");
- else
- return tr ("List");
-}
-
const char*
mgMenu::HKey(const mgActions act,mgActions on)
{
@@ -411,23 +515,25 @@ mgMenu::HKey(const mgActions act,mgActions on)
void
mgMenu::SetHelpKeys(mgActions on)
{
- mgActions r,g,y;
+ mgActions r,g,y,b;
if (osd()->UsingCollection)
{
r = CollRedAction;
g = CollGreenAction;
y = CollYellowAction;
+ b = CollBlueAction;
}
else
{
r = TreeRedAction;
g = TreeGreenAction;
y = TreeYellowAction;
+ b = TreeBlueAction;
}
osd()->SetHelpKeys(HKey(r,on),
HKey(g,on),
HKey(y,on),
- BlueName(on));
+ HKey(b,on));
}
@@ -463,8 +569,7 @@ mgSubmenu::BuildOsd ()
snprintf(b,99,tr("Commands:%s"),trim(osd()->selection()->getCurrentValue()).c_str());
mgActions on = osd()->CurrentType();
InitOsd (b);
- mgMenu *p = osd ()->Parent ();
- if (!p)
+ if (!osd ()->Parent ())
return;
AddAction(actInstantPlay,on);
AddAction(actAddThisToCollection,on);
@@ -502,7 +607,7 @@ mgSubmenu::BuildOsd ()
mgActions
mgMainMenu::CurrentType()
{
- mgActions result = mgActions(0);
+ mgActions result = actNone;
cOsdItem* c = Get(Current());
if (c)
{
@@ -518,37 +623,36 @@ eOSState
mgMenu::ExecuteButton(eKeys key)
{
mgActions on = osd()->CurrentType();
- switch (key)
- {
- case kRed:
- if (osd()->UsingCollection)
- ExecuteAction (CollRedAction,on);
- else
- ExecuteAction (TreeRedAction,on);
- return osContinue;
- case kGreen:
- if (osd()->UsingCollection)
- ExecuteAction (CollGreenAction,on);
- else
- ExecuteAction (TreeGreenAction,on);
- return osContinue;
- case kYellow:
- if (osd()->UsingCollection)
- ExecuteAction (CollYellowAction,on);
- else
- ExecuteAction (TreeYellowAction,on);
- return osContinue;
- case kBlue:
- osd()->newmenu = new mgSubmenu;
- return osContinue;
- default:
- break;
- }
- return osUnknown;
+ mgActions action = actNone;
+ if (osd()->UsingCollection)
+ switch (key)
+ {
+ case kRed: action = CollRedAction; break;
+ case kGreen: action = CollGreenAction; break;
+ case kYellow: action = CollYellowAction; break;
+ case kBlue: action = CollBlueAction; break;
+ default: break;
+ }
+ else
+ switch (key)
+ {
+ case kRed: action = TreeRedAction; break;
+ case kGreen: action = TreeGreenAction; break;
+ case kYellow: action = TreeYellowAction; break;
+ case kBlue: action = TreeBlueAction; break;
+ default: break;
+ }
+ return ExecuteAction(action,on);
+}
+
+mgTree::mgTree()
+{
+ TreeBlueAction = actShowCommands;
+ CollBlueAction = actShowCommands;
}
eOSState
-mgTree::Process (eKeys key)
+mgMenu::Process (eKeys key)
{
return ExecuteButton(key);
}
@@ -695,6 +799,7 @@ void
mgMainMenu::CloseMenu()
{
mgMenu* m = Menus.back();
+ if (newposition==-1) newposition = m->getParentIndex();
Menus.pop_back ();
delete m;
}
@@ -704,24 +809,32 @@ mgMainMenu::showMessage()
{
if (m_message)
{
+ showmessage(m_message);
+ free(m_message);
+ m_message = NULL;
+ }
+}
+
+void
+showmessage(const char * msg)
+{
#if VDRVERSNUM >= 10307
- Skins.Message (mtInfo, m_message,2);
+ Skins.Message (mtInfo, msg,2);
Skins.Flush ();
#else
- Interface->Status (m_message);
+ Interface->Status (msg);
Interface->Flush ();
#endif
- free(m_message);
- m_message = NULL;
- }
}
-
void
mgMainMenu::AddMenu (mgMenu * m,unsigned int position)
{
Menus.push_back (m);
m->setosd (this);
+ m->setParentIndex(Current());
+ if (Get(Current()))
+ m->setParentName(Get(Current())->Text());
m->Display (position);
}
@@ -732,25 +845,94 @@ mgMenu::setosd(mgMainMenu *osd)
m_prevUsingCollection = osd->UsingCollection;
}
-eOSState
-mgSubmenu::Process (eKeys key)
+mgSubmenu::mgSubmenu()
{
- return osUnknown;
+ TreeBlueAction = actShowList;
+ CollBlueAction = actShowList;
}
void
mgMenuOrders::BuildOsd ()
{
- InitOsd (tr ("Select an order"));
- AddAction(ActOrderCollItem);
- AddAction(ActOrderArtistAlbumTitle);
- AddAction(ActOrderArtistTitle);
- AddAction(ActOrderAlbumTitle);
- AddAction(ActOrderGenreYearTitle);
- AddAction(ActOrderGenreArtistAlbumTitle);
+ TreeRedAction = actEditOrder;
+ TreeGreenAction = actCreateOrder;
+ TreeYellowAction = actDeleteOrder;
+ InitOsd (tr ("Select an order"));
+ osd()->AddOrderActions(this);
+}
+
+mgMenuOrder::mgMenuOrder()
+{
+ m_order=0;
}
+mgMenuOrder::~mgMenuOrder()
+{
+ if (m_order)
+ delete m_order;
+}
+
+void
+mgMenuOrder::BuildOsd ()
+{
+ if (!m_order)
+ {
+ m_order = new mgOrder;
+ *m_order = *(osd()->getOrder(getParentIndex()));
+ }
+ InitOsd (m_order->Name().c_str());
+ m_keytypes.clear();
+ m_keytypes.reserve(mgKeyTypesNr+1);
+ m_keynames.clear();
+ m_keynames.reserve(50);
+ for (unsigned int i=0;i<m_order->size();i++)
+ {
+ unsigned int kt;
+ m_keynames.push_back(selection()->choices(m_order,i,&kt));
+ m_keytypes.push_back(kt);
+ char buf[20];
+ sprintf(buf,tr("Key %d"),i+1);
+ mgAction *a = actGenerateKeyItem(buf,(int*)&m_keytypes[i],m_keynames[i].size(),&m_keynames[i][0]);
+ a->SetMenu(this);
+ osd()->AddItem(a);
+ }
+}
+
+bool
+mgMenuOrder::ChangeOrder(eKeys key)
+{
+ vector <mgKeyTypes> newtypes;
+ newtypes.clear();
+ for (unsigned int i=0; i<m_keytypes.size();i++)
+ newtypes.push_back(ktValue(m_keynames[i][m_keytypes[i]]));
+ mgOrder n = mgOrder(newtypes);
+ bool result = !(n == *m_order);
+ *m_order = n;
+ if (result)
+ {
+ osd()->forcerefresh = true;
+ int np = osd()->Current();
+ if (key==kUp && np) np--;
+ if (key==kDown) np++;
+ osd()->newposition = np;
+ }
+ return result;
+}
+
+void
+mgMenuOrder::SaveOrder()
+{
+ *(osd()->getOrder(getParentIndex())) = *m_order;
+ osd()->SaveState();
+}
+
+
+mgTreeCollSelector::mgTreeCollSelector()
+{
+ TreeBlueAction = actShowList;
+ CollBlueAction = actShowList;
+}
mgTreeCollSelector::~mgTreeCollSelector()
{
@@ -763,10 +945,10 @@ mgTreeCollSelector::BuildOsd ()
osd()->UsingCollection = true;
mgSelection *coll = osd()->collselection();
InitOsd (m_title.c_str());
- coll->leave(0);
+ coll->leave_all();
coll->setPosition(osd()->default_collection);
AddSelectionItems (coll,coll_action());
- osd()->newposition = coll->gotoPosition(0);
+ osd()->newposition = coll->gotoPosition();
cOsdItem *c = osd()->Get(osd()->newposition);
mgAction *a = dynamic_cast<mgAction *>(c);
a->IgnoreNextEvent = true;
@@ -787,13 +969,12 @@ mgMainMenu::DisplayGoto (unsigned int select)
{
if (select >= 0)
{
+ if ((int)select>=Count())
+ select = Count() -1;
SetCurrent (Get (select));
RefreshCurrent ();
}
Display ();
-#if VDRVERSNUM >= 10307
- DisplayMenu()->SetTabs(25);
-#endif
}
diff --git a/vdr_menu.h b/vdr_menu.h
index faadb87..ddd9787 100644
--- a/vdr_menu.h
+++ b/vdr_menu.h
@@ -31,6 +31,8 @@ using namespace std;
//! \param select if true, play only what the current position selects
void Play(mgSelection *sel,const bool select=false);
+void showmessage(const char *msg);
+
class cCommands;
class mgSelection;
@@ -68,7 +70,18 @@ class mgMainMenu:public cOsdMenu
char *m_message;
void showMessage();
void LoadExternalCommands();
+ vector<mgOrder*> orders;
+ unsigned int m_current_order;
+ void DumpOrders(mgValmap& nv);
+ void LoadOrders(mgValmap& nv);
public:
+ void AddOrder();
+ void DeleteOrder();
+ void AddOrderActions(mgMenu *m);
+ unsigned int getCurrentOrder() { return m_current_order; }
+ mgOrder* getOrder(unsigned int idx);
+ void setOrder(mgSelection *sel, unsigned int idx);
+
mgSelection *moveselection;
mgActions CurrentType();
@@ -243,7 +256,8 @@ class mgMenu
// creation.
void AddSelectionItems (mgSelection *sel,mgActions act = actEntry);
//! \brief the name of the blue button depends of where we are
- const char *mgMenu::BlueName (mgActions on);
+ int m_parent_index;
+ string m_parent_name;
public:
/*! sets the correct help keys.
* \todo without data from mysql, no key is shown,
@@ -254,11 +268,16 @@ class mgMenu
mgAction* GenerateAction(const mgActions action,mgActions on);
//! \brief executes the wanted action
- void ExecuteAction (const mgActions action, const mgActions on);
+ eOSState ExecuteAction (const mgActions action, const mgActions on);
//! \brief sets the pointer to the owning mgMainMenu
void setosd (mgMainMenu* osd);
+ void setParentIndex(int idx) { m_parent_index = idx; }
+ int getParentIndex() { return m_parent_index; }
+ void setParentName(string name) { m_parent_name = name; }
+ string getParentName() { return m_parent_name; }
+
//! \brief the pointer to the owning mgMainMenu
mgMainMenu* osd ()
{
@@ -299,10 +318,7 @@ class mgMenu
* value for a new display. If NULL is returned, the caller will display
* the previous menu.
*/
- virtual eOSState Process (eKeys Key)
- {
- return osUnknown;
- }
+ virtual eOSState Process (eKeys Key);
//! \brief the ID of the action defined by the red button.
mgActions TreeRedAction;
@@ -315,13 +331,17 @@ class mgMenu
//! \brief the action defined by the yellow button.
mgActions TreeYellowAction;
mgActions CollYellowAction;
+
+//! \brief the action defined by the blue button.
+ mgActions TreeBlueAction;
+ mgActions CollBlueAction;
};
//! \brief an mgMenu class for navigating through the data base
class mgTree:public mgMenu
{
public:
- eOSState Process (eKeys Key);
+ mgTree();
protected:
void BuildOsd ();
};
@@ -330,7 +350,7 @@ class mgTree:public mgMenu
class mgSubmenu:public mgMenu
{
public:
- eOSState Process (eKeys Key);
+ mgSubmenu::mgSubmenu();
protected:
void BuildOsd ();
};
@@ -342,10 +362,27 @@ class mgMenuOrders:public mgMenu
void BuildOsd ();
};
+class mgMenuOrder : public mgMenu
+{
+ public:
+ mgMenuOrder();
+ ~mgMenuOrder();
+ bool ChangeOrder(eKeys key);
+ void SaveOrder();
+ protected:
+ void BuildOsd ();
+ private:
+ void AddKeyActions(mgMenu *m,mgOrder *o);
+ mgOrder * m_order;
+ vector<int> m_keytypes;
+ vector < vector <const char*> > m_keynames;
+};
+
//! \brief an mgMenu class for selecting a collection
class mgTreeCollSelector:public mgMenu
{
public:
+ mgTreeCollSelector();
~mgTreeCollSelector();
protected:
void BuildOsd ();
diff --git a/vdr_player.c b/vdr_player.c
index e4dd7ef..cea9e3b 100644
--- a/vdr_player.c
+++ b/vdr_player.c
@@ -323,10 +323,10 @@ mgPCMPlayer::SetPlayMode (ePlayMode mode)
{
m_playmode_mutex.Lock ();
if (mode != m_playmode)
- {
+ {
m_playmode = mode;
m_playmode_cond.Broadcast ();
- }
+ }
m_playmode_mutex.Unlock ();
}
@@ -336,10 +336,10 @@ mgPCMPlayer::WaitPlayMode (ePlayMode mode, bool inv)
{
// must be called with m_playmode_mutex LOCKED !!!
- while( m_active
- && ((!inv && mode != m_playmode) || (inv && mode == m_playmode)) )
+ while (m_active
+ && ((!inv && mode != m_playmode) || (inv && mode == m_playmode)))
{
- m_playmode_cond.Wait (m_playmode_mutex);
+ m_playmode_cond.Wait (m_playmode_mutex);
}
}
@@ -412,18 +412,20 @@ mgPCMPlayer::Action (void)
if (m_playing)
{
- if ((m_decoder = mgDecoders::findDecoder (m_playing))
- && m_decoder->start ())
+ std::string filename = m_playing->getSourceFile ();
+ if ((m_decoder = mgDecoders::findDecoder (m_playing))
+ && m_decoder->start ())
{
levelgood = true;
haslevel = false;
- scale.Init ();
level.Init ();
m_state = msDecode;
break;
}
+ else
+ mgWarning("found no decoder for %s",filename.c_str());
}
m_state = msEof;
}
@@ -620,8 +622,8 @@ mgPCMPlayer::Action (void)
{
if (m_ringbuffer->Available () == 0)
{
- // m_active = false;
- SetPlayMode (pmStopped);
+ m_active = false;
+ SetPlayMode (pmStopped);
}
}
break;
diff --git a/vdr_setup.c b/vdr_setup.c
index 2239191..0334fea 100644
--- a/vdr_setup.c
+++ b/vdr_setup.c
@@ -24,8 +24,6 @@
mgSetup the_setup;
-std::string GdFindFile( std::string mp3file, std::string ToplevelDir );
-char *readline(FILE *f);
// --- mgMenuSetup -----------------------------------------------------------
@@ -92,52 +90,3 @@ mgSetup::mgSetup ()
Only48kHz = 0;
ToplevelDir = "/mnt/music/";
}
-
-std::string
-mgSetup::getFilename( std::string basename )
-{
- if( GdCompatibility )
- {
- return GdFindFile( basename, std::string( ToplevelDir ) );
- }
- else
- {
- return std::string( ToplevelDir ) + basename;
- }
-}
-
-#define FINDCMD "cd '%s' && find -follow -name '%s' 2> /dev/null"
-
-char *readline(FILE *f)
-{
- static char buffer[MAXPARSEBUFFER];
- if (fgets(buffer, sizeof(buffer), f) > 0) {
- int l = strlen(buffer) - 1;
- if (l >= 0 && buffer[l] == '\n')
- buffer[l] = 0;
- return buffer;
- }
- return NULL;
-}
-
-std::string GdFindFile( std::string mp3file, std::string tld )
-{
- std::string fullname = "";
- char *cmd = NULL;
- asprintf( &cmd, FINDCMD, tld.c_str(), mp3file.c_str() );
- FILE *p = popen( cmd, "r" );
- if (p)
- {
- char *s;
- while( (s = readline(p) ) != NULL)
- {
- // printf( "Found: %s", s );
- fullname = std::string( tld ) + std::string( s );
- }
- pclose(p);
- }
-
- free( cmd );
-
- return fullname;
-}
diff --git a/vdr_setup.h b/vdr_setup.h
index b820857..f17aa2a 100644
--- a/vdr_setup.h
+++ b/vdr_setup.h
@@ -58,7 +58,6 @@ class mgSetup
public:
mgSetup (void);
- std::string getFilename( std::string basename );
};
extern mgSetup the_setup;
diff --git a/vdr_sound.c b/vdr_sound.c
index 7fc8361..7c4304f 100644
--- a/vdr_sound.c
+++ b/vdr_sound.c
@@ -343,7 +343,7 @@ cNormalize::cNormalize (void)
cNormalize::~cNormalize ()
{
#ifdef USE_FAST_LIMITER
- delete table;
+ delete[] table;
#endif
}
@@ -550,15 +550,14 @@ class cScale
inline unsigned long Prng (unsigned long state);
inline signed long LinearDither (mad_fixed_t sample, struct dither *dither);
public:
- void Init (void);
+ cScale();
void Stats (void);
unsigned int ScaleBlock (unsigned char *data, unsigned int size,
unsigned int &nsamples, const mad_fixed_t * &left,
const mad_fixed_t * &right, eAudioMode mode);
};
-void
-cScale::Init (void)
+cScale::cScale()
{
#ifdef DEBUG
clipped_samples = 0;
diff --git a/vdr_stream.c b/vdr_stream.c
index 4f79224..872ac4b 100644
--- a/vdr_stream.c
+++ b/vdr_stream.c
@@ -139,7 +139,7 @@ mgStream::close (void)
else
{
#endif
- delete m_buffer;
+ delete[] m_buffer;
m_buffer = 0;
#ifdef USE_MMAP
}