summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b>2005-04-19 18:01:19 +0000
committerLarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b>2005-04-19 18:01:19 +0000
commite636aa59d86868039f39b0e39e944871cae5b9db (patch)
treef44f9f196ce7de52a2dacdff3588ec99e5d03e40
parent143d3397960c698935869cae65db8d8937e4d22a (diff)
downloadvdr-plugin-muggle-e636aa59d86868039f39b0e39e944871cae5b9db.tar.gz
vdr-plugin-muggle-e636aa59d86868039f39b0e39e944871cae5b9db.tar.bz2
Merged changes from 0.1.6-wr
git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@637 e10066b5-e1e2-0310-b819-94efdf66514b
-rw-r--r--HISTORY15
-rw-r--r--Makefile14
-rw-r--r--i18n.c31
-rw-r--r--mg_content.c171
-rw-r--r--mg_content.h38
-rw-r--r--mg_incremental_search.c78
-rw-r--r--mg_incremental_search.h36
-rw-r--r--mg_mysql.c38
-rw-r--r--mg_mysql.h2
-rw-r--r--mg_order.c284
-rw-r--r--mg_order.h74
-rw-r--r--mg_selection.c528
-rw-r--r--mg_selection.h140
-rw-r--r--mg_setup.c34
-rw-r--r--mg_setup.h9
-rw-r--r--mg_sync.c40
-rw-r--r--mg_sync.h8
-rw-r--r--mg_thread_sync.c9
-rw-r--r--mg_tools.c37
-rw-r--r--mg_tools.h31
-rw-r--r--muggle.c30
-rw-r--r--muggle.doxygen2
-rwxr-xr-xmugglei.c23
-rw-r--r--vdr_actions.c167
-rw-r--r--vdr_actions.h4
-rw-r--r--vdr_decoder.c15
-rw-r--r--vdr_menu.c310
-rw-r--r--vdr_menu.h80
-rw-r--r--vdr_player.c43
-rw-r--r--vdr_player.h1
-rw-r--r--vdr_setup.c22
-rw-r--r--vdr_setup.h2
32 files changed, 1344 insertions, 972 deletions
diff --git a/HISTORY b/HISTORY
index 83071c1..a104534 100644
--- a/HISTORY
+++ b/HISTORY
@@ -184,3 +184,18 @@ XXXXXXXXXX: Version 0.0.8-ALPHA
- import now runs as a separate thread and no longer blocks user
input and VDR is no longer killed by the watchdog during import
+2005-04-19: Version 0.1.7-BETA
+- Removed option -g. muggle now automatically does the right thing:
+ GD compatibility mode is used when importing files [0-9][0-9]/filename
+ relative to the toplevel directory where filename contains no /
+- External command find is no longer needed
+- Better error handling for unreadable files when playing
+- Changed several includes from <vdr/X.h> to <X.h>
+- Elected a new pope
+- Makefile respects preset values for directories
+- Debug messages now go into /var/log/debug. Up to now it was stderr.
+- Playlist view: after pressing arrow up, playing looped backwards.
+ Now it always goes forward.
+- Play mode: in endless mode (all tracks), trying to go back on the
+ first track restarted the first track. Now it goes to the last track.
+- Added incremental search \ No newline at end of file
diff --git a/Makefile b/Makefile
index f1a691a..b3d5d6f 100644
--- a/Makefile
+++ b/Makefile
@@ -29,11 +29,10 @@ CXXFLAGS ?= -fPIC -O0 -Wall -Woverloaded-virtual -Wno-deprecated -g
### The directory environment:
-DVBDIR = ../../../../DVB
-VDRDIR = ../../../
-# /usr/local/src/VDR
-LIBDIR = ../../lib
-TMPDIR = /tmp
+DVBDIR ?= ../../../../DVB
+VDRDIR ?= ../../../
+LIBDIR ?= ../../lib
+TMPDIR ?= /tmp
### Allow user defined options to overwrite defaults:
@@ -57,9 +56,10 @@ DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DMYSQLCLIENTVERSION='
### The object files (add further files here):
-OBJS = $(PLUGIN).o i18n.o mg_valmap.o mg_mysql.o mg_sync.o mg_thread_sync.o mg_order.o mg_content.o mg_selection.o vdr_actions.o vdr_menu.o mg_tools.o \
+OBJS = $(PLUGIN).o i18n.o mg_valmap.o mg_mysql.o mg_sync.o mg_thread_sync.o mg_order.o \
+ mg_content.o mg_selection.o vdr_actions.o vdr_menu.o mg_tools.o \
vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o \
- vdr_setup.o mg_setup.o
+ vdr_setup.o mg_setup.o mg_incremental_search.o
LIBS = -lmad $(shell taglib-config --libs)
MILIBS = $(shell taglib-config --libs)
diff --git a/i18n.c b/i18n.c
index 891bdfd..ff8eff7 100644
--- a/i18n.c
+++ b/i18n.c
@@ -319,7 +319,7 @@ const tI18nPhrase Phrases[] =
"", // TODO
},
{
- "Export track list",
+ "Export item list",
"Stückliste exportieren",
"", // TODO
"", // TODO
@@ -999,6 +999,23 @@ const tI18nPhrase Phrases[] =
"", // TODO
},
{
+ "Genre",
+ "Genre",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Genre", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ {
"Genre 2",
"Genre 2",
"", // TODO
@@ -1220,13 +1237,13 @@ const tI18nPhrase Phrases[] =
"", // TODO
},
{
- "Imported %d tracks...",
- "%d Tracks importiert...",
+ "Imported %d items...",
+ "%d Titel importiert...",
"", // TODO
"", // TODO
"", // TODO
"", // TODO
- "%d tracks importés...", // TODO
+ "%d titres importés...", // TODO
"", // TODO
"", // TODO
"", // TODO
@@ -1237,13 +1254,13 @@ const tI18nPhrase Phrases[] =
"", // TODO
},
{
- "Import done:imported %d tracks",
- "Import fertig:%d Tracks importiert",
+ "Import done:imported %d items",
+ "Import fertig:%d Titel importiert",
"", // TODO
"", // TODO
"", // TODO
"", // TODO
- "Import finis:%d tracks importés...", // TODO
+ "Import finis:%d titres importés...", // TODO
"", // TODO
"", // TODO
"", // TODO
diff --git a/mg_content.c b/mg_content.c
index 74b9fce..5ab6874 100644
--- a/mg_content.c
+++ b/mg_content.c
@@ -9,14 +9,64 @@
*
*/
-#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include "i18n.h"
+#include <tools.h>
#include "mg_selection.h"
#include "mg_setup.h"
#include "mg_tools.h"
-mgSelItem*
+mgListItem zeroitem;
+
+mgListItem::mgListItem()
+{
+ m_valid=false;
+ m_count=0;
+}
+
+mgListItem::mgListItem(string v,string i,unsigned int c)
+{
+ set(v,i,c);
+}
+
+void
+mgListItem::set(string v,string i,unsigned int c)
+{
+ m_valid=true;
+ m_value=v;
+ m_id=i;
+ m_count=c;
+}
+
+void
+mgListItem::operator=(const mgListItem& from)
+{
+ m_valid=from.m_valid;
+ m_value=from.m_value;
+ m_id=from.m_id;
+ m_count=from.m_count;
+}
+
+void
+mgListItem::operator=(const mgListItem* from)
+{
+ m_valid=from->m_valid;
+ m_value=from->m_value;
+ m_id=from->m_id;
+ m_count=from->m_count;
+}
+
+bool
+mgListItem::operator==(const mgListItem& other) const
+{
+ return m_value == other.m_value
+ && m_id == other.m_id;
+}
+
+mgListItem*
mgContentItem::getKeyItem(mgKeyTypes kt)
{
string val;
@@ -49,10 +99,10 @@ mgContentItem::getKeyItem(mgKeyTypes kt)
free(fbuf);
break;
}
- default: return new mgSelItem;
+ default: return new mgListItem;
}
}
- return new mgSelItem(val,id);
+ return new mgListItem(val,id);
}
@@ -125,11 +175,15 @@ int mgContentItem::getChannels () const
mgContentItem::mgContentItem ()
{
+ m_valid = true;
+ m_validated = false;
m_trackid = -1;
}
mgContentItem::mgContentItem (const mgContentItem* c)
{
+ m_valid = true;
+ m_validated = false;
m_trackid = c->m_trackid;
m_title = c->m_title;
m_mp3file = c->m_mp3file;
@@ -149,55 +203,88 @@ mgContentItem::mgContentItem (const mgContentItem* c)
m_channels = c->m_channels;
}
-static char *mg_readline(FILE *f)
-{
- static char buffer[10000];
- 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;
-}
-
-static const char *FINDCMD = "cd '%s' 2>/dev/null && find -follow -name '%s' -print 2>/dev/null";
-
-static string
-GdFindFile( const char* tld, string mp3file )
+bool
+mgContentItem::Valid() const
{
- string result = "";
- char *cmd = 0;
- asprintf( &cmd, FINDCMD, tld, mp3file.c_str() );
- FILE *p = popen( cmd, "r" );
- if (p)
+ if (!m_validated)
{
- char *s;
- if( (s = mg_readline(p) ) != 0)
- result = string(s);
- pclose(p);
+ getSourceFile(); // sets m_valid as a side effect
+ m_validated=true;
}
+ return m_valid;
+}
- free( cmd );
+static bool music_dir_exists[100];
+static bool music_dirs_scanned=false;
- return result;
+bool
+mgContentItem::readable(string filename) const
+{
+ return !access(filename.c_str(),R_OK);
}
string
mgContentItem::getSourceFile(bool AbsolutePath) const
{
- const char* tld = the_setup.ToplevelDir;
- string result="";
- if (AbsolutePath) result = tld;
- if (the_setup.GdCompatibility)
- result += GdFindFile(tld,m_mp3file);
- else
- result += m_mp3file;
+ string tld = the_setup.ToplevelDir;
+ string result = m_mp3file;
+ if (m_validated && !m_valid)
+ return m_mp3file;
+ if (!readable(tld+result))
+ {
+ result.clear();
+ if (!music_dirs_scanned)
+ {
+ for (unsigned int i =0 ; i < 100 ; i++)
+ {
+ struct stat stbuf;
+ char *dir;
+ asprintf(&dir,"%s%02d",tld.c_str(),i);
+ music_dir_exists[i]=!stat(dir,&stbuf);
+ free(dir);
+ }
+ music_dirs_scanned=true;
+ }
+ for (unsigned int i =0 ; i < 100 ; i++)
+ {
+ if (!music_dir_exists[i])
+ continue;
+ char *file;
+ asprintf(&file,"%02d/%s",i,m_mp3file.c_str());
+ if (readable(tld+file))
+ {
+ m_mp3file = file;
+ result = m_mp3file;
+ }
+ free(file);
+ if (!result.empty())
+ break;
+ }
+ }
+ if (result.empty())
+ {
+ char *b=0;
+ int nsize = m_mp3file.size();
+ if (nsize<30)
+ asprintf(&b,tr("%s not readable"),m_mp3file.c_str());
+ else
+ asprintf(&b,tr("%s..%s not readable"),m_mp3file.substr(0,20).c_str(),m_mp3file.substr(nsize-20).c_str());;
+ extern void showmessage(const char*,int duration=0);
+ showmessage(b);
+ free(b);
+ esyslog ("ERROR: cannot stat %s. Meaning not found, not a valid file, or no access rights", m_mp3file.c_str ());
+ m_valid = false;
+ return m_mp3file;
+ }
+ if (AbsolutePath)
+ result = tld + result;
return result;
}
-mgContentItem::mgContentItem (const mgSelection* sel,const MYSQL_ROW row)
+mgContentItem::mgContentItem (const MYSQL_ROW row)
{
+ m_valid = true;
+ m_validated = false;
m_trackid = atol (row[0]);
if (row[1])
m_title = row[1];
@@ -218,14 +305,14 @@ mgContentItem::mgContentItem (const mgSelection* sel,const MYSQL_ROW row)
if (row[5])
{
m_genre1_id = row[5];
- m_genre1 = sel->value(keyGenres,row[5]);
+ m_genre1 = KeyMaps.value(keyGenres,row[5]);
}
else
m_genre1 = "NULL";
if (row[6])
{
m_genre2_id = row[6];
- m_genre2 = sel->value(keyGenres,row[6]);
+ m_genre2 = KeyMaps.value(keyGenres,row[6]);
}
else
m_genre2 = "NULL";
@@ -256,7 +343,7 @@ mgContentItem::mgContentItem (const mgSelection* sel,const MYSQL_ROW row)
if (row[13])
{
m_language_id = row[13];
- m_language = sel->value(keyLanguage,row[13]);
+ m_language = KeyMaps.value(keyLanguage,row[13]);
}
else
m_language_id = "NULL";
diff --git a/mg_content.h b/mg_content.h
index ee97745..653299c 100644
--- a/mg_content.h
+++ b/mg_content.h
@@ -12,37 +12,56 @@
#ifndef _MG_CONTENT_H
#define _MG_CONTENT_H
#include <stdlib.h>
-#include <mysql/mysql.h>
#include <string>
#include <list>
#include <vector>
#include <map>
+#include <mysql/mysql.h>
using namespace std;
#include "mg_tools.h"
#include "mg_valmap.h"
-#include "mg_order.h"
typedef vector<string> strvector;
class mgSelection;
+class mgListItem
+{
+ public:
+ mgListItem();
+ mgListItem(string v,string i,unsigned int c=0);
+ void set(string v,string i,unsigned int c=0);
+ void operator=(const mgListItem& from);
+ void operator=(const mgListItem* from);
+ bool operator==(const mgListItem& other) const;
+ string value() const { return m_value; }
+ string id() const { return m_id; }
+ unsigned int count() const { return m_count; }
+ bool valid() const { return m_valid; }
+ private:
+ bool m_valid;
+ string m_value;
+ string m_id;
+ unsigned int m_count;
+};
+
//! \brief represents a content item like an mp3 file.
class mgContentItem
{
public:
mgContentItem ();
- mgSelItem* getKeyItem(mgKeyTypes kt);
+ mgListItem* getKeyItem(mgKeyTypes kt);
//! \brief copy constructor
mgContentItem(const mgContentItem* c);
//! \brief construct an item from an SQL row
- mgContentItem (const mgSelection* sel, const MYSQL_ROW row);
+ mgContentItem (const MYSQL_ROW row);
//! \brief returns track id
- long getTrackid () const
+ long getItemid () const
{
return m_trackid;
}
@@ -91,11 +110,16 @@ class mgContentItem
//! \brief returns # of channels
int getChannels () const;
+
+ bool Valid() const;
private:
+ mutable bool m_valid;
+ mutable bool m_validated;
long m_trackid;
string m_title;
- string m_mp3file;
+ mutable string m_mp3file;
+ string m_realfile;
string m_artist;
string m_albumtitle;
string m_genre1_id;
@@ -110,6 +134,8 @@ class mgContentItem
int m_duration;
int m_samplerate;
int m_channels;
+ bool readable(string filename) const;
};
+extern mgListItem zeroitem;
#endif
diff --git a/mg_incremental_search.c b/mg_incremental_search.c
new file mode 100644
index 0000000..b268d6a
--- /dev/null
+++ b/mg_incremental_search.c
@@ -0,0 +1,78 @@
+/*! \file mg_incremental_search.c
+ * \ingroup muggle
+ * \brief A class that encapsulates incremental search
+ *
+ * \version $Revision: $
+ * \date $Date: $
+ * \author Lars von Wedel
+ * \author file owner: $Author: $
+ *
+ */
+
+#include "mg_incremental_search.h"
+#include <iostream>
+
+using namespace std;
+
+static char* keys[] = { " 0",
+ ".-_1",
+ "abc2",
+ "def3",
+ "ghi4",
+ "jkl5",
+ "mno6",
+ "pqrs7",
+ "tuv8",
+ "wxyz9" };
+
+mgIncrementalSearch::mgIncrementalSearch()
+ : m_position(-1), m_repeats(0), m_last_key(100), m_last_keypress(0.0)
+{ }
+
+string mgIncrementalSearch::KeyStroke( unsigned key )
+{
+ struct timeval now ;
+ gettimeofday( &now, NULL );
+
+ double current_t = now.tv_sec + (double)now.tv_usec / 1000000.0;
+ double delta_t = current_t - m_last_keypress;
+ m_last_keypress = current_t;
+
+ const double IS_TIMEOUT = 1.0; // 1 second
+ if( delta_t > IS_TIMEOUT || key != m_last_key )
+ {
+ m_position ++;
+
+ char tmp[2];
+ tmp[0] = (keys[key])[0];
+ tmp[1] = '\0';
+
+ m_buffer += string( tmp );
+
+ m_repeats = 0;
+ m_last_key = key;
+ }
+ else
+ {
+ // within timeout and have the same key
+ // position remains
+ m_repeats ++;
+
+ if( (unsigned) m_repeats >= strlen( keys[m_position] ) )
+ {
+ // wrap around to first char
+ m_repeats = 0;
+ }
+ m_buffer[m_position] = (keys[key])[m_repeats];
+ }
+ return m_buffer;
+}
+
+string mgIncrementalSearch::Backspace()
+{
+ if( !m_buffer.empty() )
+ {
+ m_buffer.erase( m_buffer.size()-1, 1 );
+ }
+ return m_buffer;
+}
diff --git a/mg_incremental_search.h b/mg_incremental_search.h
new file mode 100644
index 0000000..e125577
--- /dev/null
+++ b/mg_incremental_search.h
@@ -0,0 +1,36 @@
+/*! \file mg_incremental_search.h
+ * \ingroup muggle
+ * \brief A class that encapsulates incremental search
+ *
+ * \version $Revision: $
+ * \date $Date: $
+ * \author Lars von Wedel
+ * \author file owner: $Author: $
+ *
+ */
+
+/* makes sure we don't use the same declarations twice */
+#ifndef _MUGGLE_INCSEARCH_H
+#define _MUGGLE_INCSEARCH_H
+
+#include <string>
+#include <sys/time.h>
+
+class mgIncrementalSearch
+{
+ public:
+ mgIncrementalSearch();
+
+ std::string KeyStroke( unsigned key );
+
+ std::string Backspace();
+
+ private:
+ std::string m_buffer;
+ int m_position;
+ unsigned m_repeats, m_last_key;
+
+ double m_last_keypress;
+};
+
+#endif
diff --git a/mg_mysql.c b/mg_mysql.c
index 411629c..c26f724 100644
--- a/mg_mysql.c
+++ b/mg_mysql.c
@@ -22,7 +22,6 @@
bool needGenre2;
static bool needGenre2_set;
-bool NoHost();
bool UsingEmbedded();
class mysqlhandle_t {
@@ -401,19 +400,27 @@ void mgmySql::Create()
createtime=time(0);
// create database and tables
mgDebug(1,"Dropping and recreating database %s",the_setup.DbName);
- if (sql_query("DROP DATABASE IF EXISTS GiantDisc;"))
+ char buffer[500];
+ sprintf(buffer,"DROP DATABASE IF EXISTS %s",the_setup.DbName);
+ if (strlen(buffer)>400)
+ mgError("name of database too long: %s",the_setup.DbName);
+ if (sql_query(buffer))
{
- mgWarning("Cannot drop existing database:%s",mysql_error (m_db));
+ mgWarning("Cannot drop %s:%s",the_setup.DbName,
+ mysql_error (m_db));
return;
}
- if (sql_query("CREATE DATABASE GiantDisc;"))
+ sprintf(buffer,"CREATE DATABASE %s",the_setup.DbName);
+ if (sql_query(buffer))
{
- mgWarning("Cannot create database:%s",mysql_error (m_db));
+ mgWarning("Cannot create %s:%s",the_setup.DbName,mysql_error (m_db));
return;
}
if (!UsingEmbedded())
- sql_query("grant all privileges on GiantDisc.* to vdr@localhost;");
+ sprintf(buffer,"grant all privileges on %s.* to vdr@localhost",
+ the_setup.DbName);
+ sql_query(buffer);
// ignore error. If we can create the data base, we can do everything
// with it anyway.
@@ -426,7 +433,8 @@ void mgmySql::Create()
if (sql_query (db_cmds[i]))
{
mgWarning("%20s: %s",db_cmds[i],mysql_error (m_db));
- sql_query("DROP DATABASE IF EXISTS GiantDisc;"); // clean up
+ sprintf(buffer,"DROP DATABASE IF EXISTS %s",the_setup.DbName);
+ sql_query(buffer); // clean up
return;
}
}
@@ -486,12 +494,6 @@ mgmySql::Connected () const
return m_database_found;
}
-bool
-NoHost()
-{
- return (!the_setup.DbHost
- || strlen(the_setup.DbHost)==0);
-}
bool
UsingEmbedded()
@@ -499,7 +501,7 @@ UsingEmbedded()
#ifdef HAVE_ONLY_SERVER
return false;
#else
- return NoHost();
+ return the_setup.NoHost();
#endif
}
@@ -519,7 +521,7 @@ mgmySql::Connect ()
}
else
{
- if (NoHost() || !strcmp(the_setup.DbHost,"localhost"))
+ if (the_setup.NoHost() || !strcmp(the_setup.DbHost,"localhost"))
mgDebug(1,"Using socket %s for connecting to local system as user %s.",
the_setup.DbSocket, the_setup.DbUser);
else
@@ -539,9 +541,11 @@ mgmySql::Connect ()
{
m_database_found = mysql_select_db(m_db,the_setup.DbName)==0;
{
- if (!Connected())
+ if (Connected())
+ mgDebug(1,"Selected database %s",the_setup.DbName);
+ else
if (!createtime)
- mgWarning(mysql_error(m_db));
+ mgWarning("%s:%s",the_setup.DbName,mysql_error(m_db));
}
}
if (!needGenre2_set && Connected())
diff --git a/mg_mysql.h b/mg_mysql.h
index 3c3fde1..9ade129 100644
--- a/mg_mysql.h
+++ b/mg_mysql.h
@@ -61,7 +61,6 @@ class mgmySql
bool ServerConnected() const;
bool Connected() const;
bool HasFolderFields() const { return m_hasfolderfields;}
- void Connect();
//! \brief create database and tables
void Create();
void FillTables();
@@ -71,6 +70,7 @@ class mgmySql
bool m_database_found;
bool m_hasfolderfields;
bool sql_query(const char *query);
+ void Connect();
};
#endif
diff --git a/mg_order.c b/mg_order.c
index 525469c..4c44f28 100644
--- a/mg_order.c
+++ b/mg_order.c
@@ -4,7 +4,10 @@
#include <stdio.h>
#include <assert.h>
-mgSelItem zeroitem;
+static map <mgKeyTypes, map<string,string> > map_values;
+static map <mgKeyTypes, map<string,string> > map_ids;
+
+mgKeyMaps KeyMaps;
bool iskeyGenre(mgKeyTypes kt)
{
@@ -100,50 +103,6 @@ ltos (long l)
return s.str ();
}
-mgSelItem::mgSelItem()
-{
- m_valid=false;
- m_count=0;
-}
-
-mgSelItem::mgSelItem(string v,string i,unsigned int c)
-{
- set(v,i,c);
-}
-
-void
-mgSelItem::set(string v,string i,unsigned int c)
-{
- m_valid=true;
- m_value=v;
- m_id=i;
- m_count=c;
-}
-
-void
-mgSelItem::operator=(const mgSelItem& from)
-{
- m_valid=from.m_valid;
- m_value=from.m_value;
- m_id=from.m_id;
- m_count=from.m_count;
-}
-
-void
-mgSelItem::operator=(const mgSelItem* from)
-{
- m_valid=from->m_valid;
- m_value=from->m_value;
- m_id=from->m_id;
- m_count=from->m_count;
-}
-
-bool
-mgSelItem::operator==(const mgSelItem& other) const
-{
- return m_value == other.m_value
- && m_id == other.m_id;
-}
class mgKeyNormal : public mgKey {
public:
@@ -153,15 +112,15 @@ class mgKeyNormal : public mgKey {
string value() const;
string id() const;
bool valid() const;
- void set(mgSelItem& item);
- mgSelItem& get();
+ void set(mgListItem& item);
+ mgListItem& get();
mgKeyTypes Type() const { return m_kt; }
virtual string expr() const { return m_table + "." + m_field; }
virtual string table() const { return m_table; }
protected:
string IdClause(mgmySql &db,string what,string::size_type start=0,string::size_type len=string::npos) const;
void AddIdClause(mgmySql &db,mgParts &result,string what) const;
- mgSelItem m_item;
+ mgListItem m_item;
string m_field;
private:
mgKeyTypes m_kt;
@@ -253,10 +212,10 @@ class mgKeyGenres : public mgKeyNormal {
mgKeyGenres() : mgKeyNormal(keyGenres,"tracks","genre1") {};
mgKeyGenres(mgKeyTypes kt) : mgKeyNormal(kt,"tracks","genre1") {};
mgParts Parts(mgmySql &db,bool orderby=false) const;
+ protected:
string map_idfield() const { return "id"; }
string map_valuefield() const { return "genre"; }
- string map_valuetable() const { return "genre"; }
- protected:
+ string map_table() const { return "genre"; }
virtual unsigned int genrelevel() const { return 4; }
private:
string GenreClauses(mgmySql &db,bool orderby) const;
@@ -344,18 +303,20 @@ class mgKeyLanguage : public mgKeyNormal {
public:
mgKeyLanguage() : mgKeyNormal(keyLanguage,"tracks","lang") {};
mgParts Parts(mgmySql &db,bool orderby=false) const;
+ protected:
string map_idfield() const { return "id"; }
string map_valuefield() const { return "language"; }
- string map_valuetable() const { return "language"; }
+ string map_table() const { return "language"; }
};
class mgKeyCollection: public mgKeyNormal {
public:
mgKeyCollection() : mgKeyNormal(keyCollection,"playlist","id") {};
mgParts Parts(mgmySql &db,bool orderby=false) const;
+ protected:
string map_idfield() const { return "id"; }
string map_valuefield() const { return "title"; }
- string map_valuetable() const { return "playlist"; }
+ string map_table() const { return "playlist"; }
};
class mgKeyCollectionItem : public mgKeyNormal {
public:
@@ -404,12 +365,12 @@ mgKeyNormal::mgKeyNormal(const mgKeyTypes kt, string table, string field)
}
void
-mgKeyNormal::set(mgSelItem& item)
+mgKeyNormal::set(mgListItem& item)
{
m_item=item;
}
-mgSelItem&
+mgListItem&
mgKeyNormal::get()
{
return m_item;
@@ -802,7 +763,7 @@ mgOrder::getKeyType(unsigned int idx) const
return Keys[idx]->Type();
}
-mgSelItem&
+mgListItem&
mgOrder::getKeyItem(unsigned int idx) const
{
assert(idx<Keys.size());
@@ -931,6 +892,43 @@ next:
return result;
}
+string
+mgOrder::GetContent(mgmySql &db,unsigned int level,vector < mgContentItem > &content) const
+{
+ mgParts p = Parts(db,level);
+ p.fields.clear();
+ p.fields.push_back("tracks.id");
+ p.fields.push_back("tracks.title");
+ p.fields.push_back("tracks.mp3file");
+ p.fields.push_back("tracks.artist");
+ p.fields.push_back("album.title");
+ p.fields.push_back("tracks.genre1");
+ p.fields.push_back("tracks.genre2");
+ p.fields.push_back("tracks.bitrate");
+ p.fields.push_back("tracks.year");
+ p.fields.push_back("tracks.rating");
+ p.fields.push_back("tracks.length");
+ p.fields.push_back("tracks.samplerate");
+ p.fields.push_back("tracks.channels");
+ p.fields.push_back("tracks.lang");
+ p.tables.push_back("tracks");
+ p.tables.push_back("album");
+ for (unsigned int i = level; i<size(); i++)
+ p += Key(i)->Parts(db,true);
+ string result = p.sql_select(false);
+ content.clear ();
+ MYSQL_RES *rows = db.exec_sql (result);
+ if (rows)
+ {
+ MYSQL_ROW row;
+ while ((row = mysql_fetch_row (rows)) != 0)
+ content.push_back (mgContentItem (row));
+ mysql_free_result (rows);
+ }
+ return result;
+}
+
+
//! \brief right now thread locking should not be needed here
mgReferences::mgReferences()
{
@@ -1078,13 +1076,179 @@ ktValue(const char * name)
return mgKeyTypes(0);
}
+static vector<int> keycounts;
+
+unsigned int
+mgOrder::keycount(mgKeyTypes kt) const
+{
+ 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);
+ struct mgmySql db;
+ if (k->Enabled(db))
+ count = db.exec_count(k->Parts(db,true).sql_count());
+ else
+ count = 0;
+ delete k;
+ }
+ return count;
+}
+
+
+bool
+mgOrder::UsedBefore(const mgKeyTypes kt,unsigned int level) const
+{
+ if (level>=size())
+ level = size() -1;
+ for (unsigned int lx = 0; lx < level; lx++)
+ if (getKeyType(lx)==kt)
+ return true;
+ return false;
+}
-vector<const char*>
-ktNames()
+vector <const char *>
+mgOrder::Choices(unsigned int level, unsigned int *current) const
{
- static vector<const char*> result;
- for (unsigned int i = int(mgKeyTypesLow); i <= int(mgKeyTypesHigh); i++)
- result.push_back(ktName(mgKeyTypes(i)));
+ vector<const char*> result;
+ if (level>size())
+ {
+ *current = 0;
+ return result;
+ }
+ for (unsigned int ki=int(mgKeyTypesLow);ki<=int(mgKeyTypesHigh);ki++)
+ {
+ mgKeyTypes kt = mgKeyTypes(ki);
+ if (kt==getKeyType(level))
+ {
+ *current = result.size();
+ result.push_back(ktName(kt));
+ continue;
+ }
+ if (UsedBefore(kt,level))
+ continue;
+ if (kt==keyDecade && UsedBefore(keyYear,level))
+ continue;
+ if (kt==keyGenre1)
+ {
+ if (UsedBefore(keyGenre2,level)) continue;
+ if (UsedBefore(keyGenre3,level)) continue;
+ if (UsedBefore(keyGenres,level)) continue;
+ }
+ if (kt==keyGenre2)
+ {
+ if (UsedBefore(keyGenre3,level)) continue;
+ if (UsedBefore(keyGenres,level)) continue;
+ }
+ if (kt==keyGenre3)
+ {
+ if (UsedBefore(keyGenres,level)) continue;
+ }
+ if (kt==keyFolder1)
+ {
+ if (UsedBefore(keyFolder2,level)) continue;
+ if (UsedBefore(keyFolder3,level)) continue;
+ if (UsedBefore(keyFolder4,level)) continue;
+ }
+ if (kt==keyFolder2)
+ {
+ if (UsedBefore(keyFolder3,level)) continue;
+ if (UsedBefore(keyFolder4,level)) continue;
+ }
+ if (kt==keyFolder3)
+ {
+ if (UsedBefore(keyFolder4,level)) continue;
+ }
+ if (kt==keyCollection || kt==keyCollectionItem)
+ result.push_back(ktName(kt));
+ else if (keycount(kt)>1)
+ result.push_back(ktName(kt));
+ }
return result;
}
+bool
+mgKey::LoadMap() const
+{
+ map<string,string>& idmap = map_ids[Type()];
+ if (map_idfield().empty())
+ {
+ return false;
+ }
+ mgmySql db;
+ map<string,string>& valmap = map_values[Type()];
+ char *b;
+ asprintf(&b,"select %s,%s from %s;",map_idfield().c_str(),map_valuefield().c_str(),map_table().c_str());
+ MYSQL_RES *rows = db.exec_sql (string(b));
+ free(b);
+ if (rows)
+ {
+ MYSQL_ROW row;
+ while ((row = mysql_fetch_row (rows)) != 0)
+ {
+ if (row[0] && row[1])
+ {
+ valmap[row[0]] = row[1];
+ idmap[row[1]] = row[0];
+ }
+ }
+ mysql_free_result (rows);
+ }
+ return true;
+}
+
+bool
+mgKeyMaps::loadvalues (mgKeyTypes kt) const
+{
+ if (map_ids.count(kt)>0)
+ return true;
+ mgKey* k = ktGenerate(kt);
+ bool result = k->LoadMap();
+ delete k;
+ return result;
+}
+
+string
+mgKeyMaps::value(mgKeyTypes kt, string idstr) const
+{
+ if (loadvalues (kt))
+ {
+ map<string,string>& valmap = map_values[kt];
+ map<string,string>::iterator it;
+ it = valmap.find(idstr);
+ if (it!=valmap.end())
+ {
+ string r = it->second;
+ if (!r.empty())
+ return r;
+ }
+ map_ids[kt].clear();
+ loadvalues(kt);
+ it = valmap.find(idstr);
+ if (it!=valmap.end())
+ return valmap[idstr];
+ }
+ return idstr;
+}
+
+string
+mgKeyMaps::id(mgKeyTypes kt, string valstr) const
+{
+ if (loadvalues (kt))
+ {
+ map<string,string>& idmap = map_ids[kt];
+ string v = idmap[valstr];
+ 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;
+ }
+ return valstr;
+}
diff --git a/mg_order.h b/mg_order.h
index 8bbaaa5..41d110b 100644
--- a/mg_order.h
+++ b/mg_order.h
@@ -5,9 +5,13 @@
#include <string>
#include <list>
#include <vector>
+#include <map>
#include <sstream>
+using namespace std;
#include "mg_valmap.h"
#include "mg_mysql.h"
+#include "mg_content.h"
+#include "mg_tools.h"
using namespace std;
@@ -18,34 +22,6 @@ strlist& operator+=(strlist&a, strlist b);
//! \brief adds string n to string s, using string sep to separate them
string& addsep (string & s, string sep, string n);
-enum mgKeyTypes {
- keyGenre1=1, // the genre types must have exactly this order!
- keyGenre2,
- keyGenre3,
- keyGenres,
- keyDecade,
- keyYear,
- keyArtist,
- keyAlbum,
- keyTitle,
- keyTrack,
- keyLanguage,
- keyRating,
- keyFolder1,
- keyFolder2,
- keyFolder3,
- keyFolder4,
- keyCreated,
- keyModified,
- keyArtistABC,
- keyTitleABC,
- keyCollection,
- keyCollectionItem,
-};
-const mgKeyTypes mgKeyTypesLow = keyGenre1;
-const mgKeyTypes mgKeyTypesHigh = keyCollectionItem;
-const unsigned int mgKeyTypesNr = keyCollectionItem;
-
bool iskeyGenre(mgKeyTypes kt);
class mgParts;
@@ -75,26 +51,16 @@ private:
mgParts ConnectToTracks(string table) const;
};
-class mgSelItem
-{
+class mgKeyMaps {
public:
- mgSelItem();
- mgSelItem(string v,string i,unsigned int c=0);
- void set(string v,string i,unsigned int c=0);
- void operator=(const mgSelItem& from);
- void operator=(const mgSelItem* from);
- bool operator==(const mgSelItem& other) const;
- string value() const { return m_value; }
- string id() const { return m_id; }
- unsigned int count() const { return m_count; }
- bool valid() const { return m_valid; }
+ string value(mgKeyTypes kt, string idstr) const;
+ string id(mgKeyTypes kt, string valstr) const;
private:
- bool m_valid;
- string m_value;
- string m_id;
- unsigned int m_count;
+ bool loadvalues (mgKeyTypes kt) const;
};
+extern mgKeyMaps KeyMaps;
+
class mgKey {
public:
virtual ~mgKey() {};
@@ -103,13 +69,15 @@ class mgKey {
virtual bool valid() const = 0;
virtual string value () const = 0;
//!\brief translate field into user friendly string
- virtual void set(mgSelItem& item) = 0;
- virtual mgSelItem& get() = 0;
+ virtual void set(mgListItem& item) = 0;
+ virtual mgListItem& get() = 0;
virtual mgKeyTypes Type() const = 0;
+ virtual bool Enabled(mgmySql &db) { return true; }
+ virtual bool LoadMap() const;
+ protected:
virtual string map_idfield() const { return ""; }
virtual string map_valuefield() const { return ""; }
- virtual string map_valuetable() const { return ""; }
- virtual bool Enabled(mgmySql &db) { return true; }
+ virtual string map_table() const { return ""; }
};
@@ -118,7 +86,6 @@ ktGenerate(const mgKeyTypes kt);
const char * const ktName(const mgKeyTypes kt);
mgKeyTypes ktValue(const char * name);
-vector < const char*> ktNames();
typedef vector<mgKey*> keyvector;
@@ -172,21 +139,24 @@ public:
void clear();
mgKey* Key(unsigned int idx) const;
mgKeyTypes getKeyType(unsigned int idx) const;
- mgSelItem& getKeyItem(unsigned int idx) const;
+ mgListItem& getKeyItem(unsigned int idx) const;
void setKeys(vector<mgKeyTypes> kt);
string Name();
void setOrderByCount(bool orderbycount) { m_orderByCount = orderbycount;}
bool getOrderByCount() { return m_orderByCount; }
+ string GetContent(mgmySql &db,unsigned int level,vector < mgContentItem > &content) const;
+ vector <const char*> Choices(unsigned int level, unsigned int *current) const;
private:
bool m_orderByCount;
bool isCollectionOrder() const;
keyvector Keys;
void setKey ( const mgKeyTypes kt);
void clean();
+ unsigned int keycount(mgKeyTypes kt) const;
+ bool UsedBefore(const mgKeyTypes kt,unsigned int level) const;
};
bool operator==(const mgOrder& a,const mgOrder&b); //! \brief compares only the order, not the current key values
-extern mgSelItem zeroitem;
-#endif // _MG_SQL_H
+#endif
diff --git a/mg_selection.c b/mg_selection.c
index 464bddb..f0016e9 100644
--- a/mg_selection.c
+++ b/mg_selection.c
@@ -28,8 +28,8 @@
#include <fileref.h>
#if VDRVERSNUM >= 10307
-#include <vdr/interface.h>
-#include <vdr/skins.h>
+#include <interface.h>
+#include <skins.h>
#endif
//! \brief adds string n to string s, using a comma to separate them
@@ -54,13 +54,13 @@ comma (string & s, string n)
void
-mgSelection::mgSelItems::clear()
+mgSelection::mgListItems::clear()
{
m_items.clear();
}
bool
-mgSelection::mgSelItems::operator==(const mgSelItems&x) const
+mgSelection::mgListItems::operator==(const mgListItems&x) const
{
bool result = m_items.size()==x.m_items.size();
if (result)
@@ -70,47 +70,47 @@ mgSelection::mgSelItems::operator==(const mgSelItems&x) const
}
size_t
-mgSelection::mgSelItems::size() const
+mgSelection::mgListItems::size() const
{
if (!m_sel)
- mgError("mgSelItems: m_sel is 0");
+ mgError("mgListItems: m_sel is 0");
m_sel->refreshValues();
return m_items.size();
}
-mgSelItem&
-mgSelection::mgSelItems::operator[](unsigned int idx)
+mgListItem&
+mgSelection::mgListItems::operator[](unsigned int idx)
{
if (!m_sel)
- mgError("mgSelItems: m_sel is 0");
+ mgError("mgListItems: m_sel is 0");
m_sel->refreshValues();
if (idx>=size()) return zeroitem;
return m_items[idx];
}
void
-mgSelection::mgSelItems::setOwner(mgSelection* sel)
+mgSelection::mgListItems::setOwner(mgSelection* sel)
{
m_sel = sel;
}
unsigned int
-mgSelection::mgSelItems::valindex (const string v) const
+mgSelection::mgListItems::valindex (const string v) const
{
return index(v,true);
}
unsigned int
-mgSelection::mgSelItems::idindex (const string i) const
+mgSelection::mgListItems::idindex (const string i) const
{
return index(i,true);
}
unsigned int
-mgSelection::mgSelItems::index (const string s,bool val,bool second_try) const
+mgSelection::mgListItems::index (const string s,bool val,bool second_try) const
{
if (!m_sel)
- mgError("mgSelItems::index(%s): m_sel is 0",s.c_str());
+ mgError("mgListItems::index(%s): m_sel is 0",s.c_str());
m_sel->refreshValues();
for (unsigned int i = 0; i < size (); i++)
{
@@ -127,7 +127,7 @@ mgSelection::mgSelItems::index (const string s,bool val,bool second_try) const
}
// nochmal mit neuen Werten:
if (second_try) {
- esyslog("index: Gibt es nicht:%s",s.c_str());
+ mgDebug(2,"index: Gibt es nicht:%s",s.c_str());
return 0;
}
else
@@ -147,10 +147,10 @@ mgSelection::clearCache() const
string
mgSelection::getCurrentValue()
{
- return items[gotoPosition()].value();
+ return listitems[gotoPosition()].value();
}
-mgSelItem&
+mgListItem&
mgSelection::getKeyItem(const unsigned int level) const
{
return order.getKeyItem(level);
@@ -164,11 +164,11 @@ mgSelection::getKeyType (const unsigned int level) const
}
mgContentItem *
-mgSelection::getTrack (unsigned int position)
+mgSelection::getItem (unsigned int position)
{
- if (position >= getNumTracks ())
+ if (position >= getNumItems ())
return 0;
- return &(m_tracks[position]);
+ return &(m_items[position]);
}
@@ -188,19 +188,19 @@ mgSelection::setShuffleMode (mgSelection::ShuffleMode mode)
void
mgSelection::Shuffle() const
{
- unsigned int tracksize = getNumTracks();
- if (tracksize==0) return;
+ unsigned int numitems = getNumItems();
+ if (numitems==0) return;
switch (m_shuffle_mode)
{
case SM_NONE:
{
- long trackid = m_tracks[getTrackPosition()].getTrackid ();
+ long id = m_items[getItemPosition()].getItemid ();
m_current_tracks = ""; // force a reload
- tracksize = getNumTracks(); // getNumTracks also reloads
- for (unsigned int i = 0; i < tracksize; i++)
- if (m_tracks[i].getTrackid () == trackid)
+ numitems = getNumItems(); // getNumItems also reloads
+ for (unsigned int i = 0; i < numitems; i++)
+ if (m_items[i].getItemid () == id)
{
- setTrackPosition(i);
+ m_items_position = i;
break;
}
}
@@ -208,18 +208,18 @@ mgSelection::Shuffle() const
case SM_PARTY:
case SM_NORMAL:
{
- // play all, beginning with current track:
- mgContentItem tmp = m_tracks[getTrackPosition()];
- m_tracks[getTrackPosition()]=m_tracks[0];
- m_tracks[0]=tmp;
- setTrackPosition(0);
- // randomize all other tracks
- for (unsigned int i = 1; i < tracksize; i++)
+ // play all, beginning with current item:
+ mgContentItem tmp = m_items[getItemPosition()];
+ m_items[getItemPosition()]=m_items[0];
+ m_items[0]=tmp;
+ m_items_position = 0;
+ // randomize all other items
+ for (unsigned int i = 1; i < numitems; i++)
{
- unsigned int j = 1+randrange (tracksize-1);
- tmp = m_tracks[i];
- m_tracks[i] = m_tracks[j];
- m_tracks[j] = tmp;
+ unsigned int j = 1+randrange (numitems-1);
+ tmp = m_items[i];
+ m_items[i] = m_items[j];
+ m_items[j] = tmp;
}
} break;
/*
@@ -228,11 +228,11 @@ mgSelection::Shuffle() const
- initialization
- find 15 titles according to the scheme below
- playing
- - before entering next title perform track selection
- - track selection
+ - before entering next title perform item selection
+ - item selection
- generate a random uid
- if file exists:
- - determine maximum playcount of all tracks
+ - determine maximum playcount of all items
- generate a random number n
- if n < playcount / max. playcount
- add the file to the end of the list
@@ -255,8 +255,8 @@ mgSelection::AddToCollection (const string Name)
CreateCollection(Name);
string listid = m_db.sql_string (m_db.get_col0
("SELECT id FROM playlist WHERE title=" + m_db.sql_string (Name)));
- unsigned int tracksize = getNumTracks ();
- if (tracksize==0)
+ unsigned int numitems = getNumItems ();
+ if (numitems==0)
return 0;
// this code is rather complicated but works in a multi user
@@ -265,25 +265,25 @@ mgSelection::AddToCollection (const string Name)
// insert a unique trackid:
string trackid = ltos(m_db.thread_id()+1000000);
m_db.exec_sql("INSERT INTO playlistitem SELECT "+listid+","
- "MAX(tracknumber)+"+ltos(tracksize)+","+trackid+
+ "MAX(tracknumber)+"+ltos(numitems)+","+trackid+
" FROM playlistitem WHERE playlist="+listid);
// find tracknumber of the trackid we just inserted:
string sql = string("SELECT tracknumber FROM playlistitem WHERE "
"playlist=")+listid+" AND trackid="+trackid;
- long first = atol(m_db.get_col0(sql).c_str()) - tracksize + 1;
+ long first = atol(m_db.get_col0(sql).c_str()) - numitems + 1;
// replace the place holder trackid by the correct value:
- m_db.exec_sql("UPDATE playlistitem SET trackid="+ltos(m_tracks[tracksize-1].getTrackid())+
+ m_db.exec_sql("UPDATE playlistitem SET trackid="+ltos(m_items[numitems-1].getItemid())+
" WHERE playlist="+listid+" AND trackid="+trackid);
- // insert all other tracks:
+ // insert all other items:
const char *sql_prefix = "INSERT INTO playlistitem VALUES ";
sql = "";
- for (unsigned int i = 0; i < tracksize-1; i++)
+ for (unsigned int i = 0; i < numitems-1; i++)
{
string item = "(" + listid + "," + ltos (first + i) + "," +
- ltos (m_tracks[i].getTrackid ()) + ")";
+ ltos (m_items[i].getItemid ()) + ")";
comma(sql, item);
if ((i%100)==99)
{
@@ -293,7 +293,7 @@ mgSelection::AddToCollection (const string Name)
}
if (!sql.empty()) m_db.exec_sql (sql_prefix+sql);
if (inCollection(Name)) clearCache ();
- return tracksize;
+ return numitems;
}
@@ -302,7 +302,7 @@ mgSelection::RemoveFromCollection (const string Name)
{
if (!m_db.Connected()) return 0;
mgParts p = order.Parts(m_db,m_level,false);
- string sql = p.sql_delete_from_collection(id(keyCollection,Name));
+ string sql = p.sql_delete_from_collection(KeyMaps.id(keyCollection,Name));
m_db.exec_sql (sql);
unsigned int removed = m_db.affected_rows ();
if (inCollection(Name)) clearCache ();
@@ -323,7 +323,7 @@ bool mgSelection::DeleteCollection (const string Name)
void mgSelection::ClearCollection (const string Name)
{
if (!m_db.Connected()) return;
- string listid = id(keyCollection,Name);
+ string listid = KeyMaps.id(keyCollection,Name);
m_db.exec_sql ("DELETE FROM playlistitem WHERE playlist="+m_db.sql_string(listid));
if (inCollection(Name)) clearCache ();
}
@@ -350,13 +350,13 @@ string mgSelection::exportM3U ()
if (!listfile)
return "";
fprintf (listfile, "#EXTM3U\n");
- unsigned int tracksize = getNumTracks ();
- for (unsigned i = 0; i < tracksize; i++)
+ unsigned int numitems = getNumItems ();
+ for (unsigned i = 0; i < numitems; i++)
{
- mgContentItem& t = m_tracks[i];
+ mgContentItem& t = m_items[i];
fprintf (listfile, "#EXTINF:%d,%s\n", t.getDuration (),
t.getTitle ().c_str ());
- fprintf (listfile, "#MUGGLE:%ld\n", t.getTrackid());
+ fprintf (listfile, "#MUGGLE:%ld\n", t.getItemid());
fprintf (listfile, "%s\n", t.getSourceFile (false).c_str ());
}
fclose (listfile);
@@ -367,108 +367,124 @@ bool
mgSelection::empty()
{
if (m_level>= order.size ()-1)
- return ( getNumTracks () == 0);
+ return ( getNumItems () == 0);
else
- return ( items.size () == 0);
+ return ( listitems.size () == 0);
}
void
mgSelection::setPosition (unsigned int position)
{
if (m_level == order.size())
- setTrackPosition(position);
- else
- m_position = position;
+ mgError("setPosition:m_level==order.size()");
+ m_position = position;
}
void
-mgSelection::setTrackPosition (unsigned int position) const
+mgSelection::GotoItemPosition (unsigned int position) const
{
- m_tracks_position = position;
+ m_items_position = position;
+ skipItems(0);
}
unsigned int
mgSelection::getPosition () const
{
if (m_level == order.size())
- return getTrackPosition();
- else
- return m_position;
+ mgError("getPosition:m_level==order.size()");
+ return m_position;
}
unsigned int
mgSelection::gotoPosition ()
{
- if (m_level == order.size ())
- return gotoTrackPosition();
- else
- {
- unsigned int itemsize = items.size();
- if (itemsize==0)
- m_position = 0;
- else if (m_position >= itemsize)
- m_position = itemsize -1;
- return m_position;
- }
+ if (m_level == order.size())
+ mgError("gotoPosition:m_level==order.size()");
+ unsigned int itemsize = listitems.size();
+ if (itemsize==0)
+ m_position = 0;
+ else if (m_position >= itemsize)
+ m_position = itemsize -1;
+ return m_position;
}
unsigned int
-mgSelection::getTrackPosition() const
+mgSelection::getItemPosition() const
{
- if (m_tracks_position>=m_tracks.size())
- if (m_tracks.size()==0)
- m_tracks_position=0;
+ if (m_items_position>=m_items.size())
+ if (m_items.size()==0)
+ m_items_position=0;
else
- m_tracks_position = m_tracks.size()-1;
- return m_tracks_position;
+ m_items_position = m_items.size()-1;
+ return m_items_position;
}
unsigned int
-mgSelection::gotoTrackPosition()
+mgSelection::gotoItemPosition()
{
- unsigned int tracksize = getNumTracks ();
- if (tracksize == 0)
- setTrackPosition(0);
- else if (m_tracks_position >= tracksize)
- setTrackPosition(tracksize -1);
- return m_tracks_position;
+ unsigned int numitems = getNumItems ();
+ if (numitems == 0)
+ {
+ m_items_position = 0;
+ return 0;
+ }
+ if (m_items_position >= numitems)
+ m_items_position = numitems -1;
+ return m_items_position;
}
-bool mgSelection::skipTracks (int steps)
+bool mgSelection::skipItems (int steps) const
{
- unsigned int tracksize = getNumTracks();
- if (tracksize == 0)
+ unsigned int numitems = getNumItems();
+ if (numitems == 0)
+ {
+ m_items_position=0;
return false;
- if (m_loop_mode == LM_SINGLE)
- return true;
- unsigned int old_pos = m_tracks_position;
+ }
+ int old_pos = m_items_position;
unsigned int new_pos;
+ if (m_loop_mode == LM_SINGLE)
+ steps = 0;
if (old_pos + steps < 0)
{
if (m_loop_mode == LM_NONE)
return false;
- new_pos = tracksize - 1;
+ new_pos = numitems - 1;
}
else
new_pos = old_pos + steps;
- if (new_pos >= tracksize)
+ if (new_pos >= numitems)
{
if (m_loop_mode == LM_NONE)
return false;
new_pos = 0;
}
- setTrackPosition (new_pos);
- return (new_pos == gotoTrackPosition());
+ m_items_position = new_pos;
+ while (true)
+ {
+ if (m_items[m_items_position].Valid())
+ break;
+ m_items.erase(m_items.begin()+m_items_position);
+ if (m_items.size()==0)
+ {
+ m_items_position = 0;
+ return false;
+ }
+ if (steps<0 && m_items.size()>0 && m_items_position>0)
+ m_items_position--;
+ if (m_items_position==m_items.size())
+ m_items_position--;
+ }
+ return true;
}
-
unsigned long
mgSelection::getLength ()
{
unsigned long result = 0;
- unsigned int tracksize = getNumTracks ();
- for (unsigned int i = 0; i < tracksize; i++)
- result += m_tracks[i].getDuration ();
+ unsigned int numitems = getNumItems ();
+ for (unsigned int i = 0; i < numitems; i++)
+ result += m_items[i].getDuration ();
return result;
}
@@ -477,9 +493,9 @@ unsigned long
mgSelection::getCompletedLength () const
{
unsigned long result = 0;
- tracks (); // make sure they are loaded
- for (unsigned int i = 0; i < getTrackPosition(); i++)
- result += m_tracks[i].getDuration ();
+ items (); // make sure they are loaded
+ for (unsigned int i = 0; i < getItemPosition(); i++)
+ result += m_items[i].getDuration ();
return result;
}
@@ -524,52 +540,22 @@ string mgSelection::ListFilename ()
}
const vector < mgContentItem > &
-mgSelection::tracks () const
-{
- if (!m_db.Connected()) return m_tracks;
- if (!m_current_tracks.empty()) return m_tracks;
- mgParts p = order.Parts(m_db,m_level);
- p.fields.clear();
- p.fields.push_back("tracks.id");
- p.fields.push_back("tracks.title");
- p.fields.push_back("tracks.mp3file");
- p.fields.push_back("tracks.artist");
- p.fields.push_back("album.title");
- p.fields.push_back("tracks.genre1");
- p.fields.push_back("tracks.genre2");
- p.fields.push_back("tracks.bitrate");
- p.fields.push_back("tracks.year");
- p.fields.push_back("tracks.rating");
- p.fields.push_back("tracks.length");
- p.fields.push_back("tracks.samplerate");
- p.fields.push_back("tracks.channels");
- p.fields.push_back("tracks.lang");
- p.tables.push_back("tracks");
- p.tables.push_back("album");
- for (unsigned int i = m_level; i<order.size(); i++)
- p += order.Key(i)->Parts(m_db,true);
- m_current_tracks = p.sql_select(false);
- m_tracks.clear ();
- MYSQL_RES *rows = m_db.exec_sql (m_current_tracks);
- if (rows)
- {
- MYSQL_ROW row;
- while ((row = mysql_fetch_row (rows)) != 0)
- m_tracks.push_back (mgContentItem (this,row));
- mysql_free_result (rows);
- }
+mgSelection::items () const
+{
+ if (!m_db.Connected()) return m_items;
+ if (!m_current_tracks.empty()) return m_items;
+ m_current_tracks=order.GetContent(m_db,m_level,m_items);
if (m_shuffle_mode!=SM_NONE)
Shuffle();
- return m_tracks;
+ return m_items;
}
void mgSelection::InitSelection() {
- m_Directory=".";
m_level = 0;
m_position = 0;
- m_tracks_position = 0;
- m_trackid = -1;
+ m_items_position = 0;
+ m_itemid = -1;
if (the_setup.InitShuffleMode)
m_shuffle_mode = SM_NORMAL;
else
@@ -579,7 +565,7 @@ void mgSelection::InitSelection() {
else
m_loop_mode = LM_NONE;
clearCache();
- items.setOwner(this);
+ listitems.setOwner(this);
}
@@ -631,13 +617,13 @@ mgSelection::InitFrom(mgValmap& nv)
argv[0]=".";
argv[1]=0;
m_db.Create();
- if (Interface->Confirm(tr("Import tracks?")))
+ if (Interface->Confirm(tr("Import items?")))
{
mgThreadSync *s = mgThreadSync::get_instance();
if (s)
{
extern char *sync_args[];
- s->Sync(sync_args,the_setup.DeleteStaleReferences);
+ s->Sync(sync_args,(bool)the_setup.DeleteStaleReferences);
}
}
}
@@ -645,7 +631,6 @@ mgSelection::InitFrom(mgValmap& nv)
}
InitSelection();
m_fall_through = nv.getbool("FallThrough");
- m_Directory = nv.getstr("Directory");
while (m_level < nv.getuint("Level"))
{
char *idx;
@@ -655,10 +640,12 @@ mgSelection::InitFrom(mgValmap& nv)
if (!enter (newval))
if (!select (newval)) break;
}
- m_trackid = nv.getlong("TrackId");
- setPosition(nv.getstr("Position"));
- if (m_level>=order.size()-1)
- setTrackPosition(nv.getlong("TrackPosition"));
+ assert(m_level<=order.size());
+ m_itemid = nv.getlong("ItemId");
+ if (m_level==order.size())
+ m_items_position = nv.getlong("ItemPosition");
+ else
+ setPosition(nv.getstr("Position"));
}
@@ -670,28 +657,19 @@ void mgSelection::InitFrom(const mgSelection* s)
{
InitSelection();
m_fall_through = s->m_fall_through;
- m_Directory = s->m_Directory;
- map_values = s->map_values;
- map_ids = s->map_ids;
order = s->order;
m_level = s->m_level;
m_position = s->m_position;
- m_trackid = s->m_trackid;
- m_tracks_position = s->m_tracks_position;
+ m_itemid = s->m_itemid;
+ m_items_position = s->m_items_position;
setShuffleMode (s->getShuffleMode ());
setLoopMode (s->getLoopMode ());
}
unsigned int
-mgSelection::ordersize ()
-{
- return order.size ();
-}
-
-unsigned int
mgSelection::valcount (string value)
{
- return items[items.valindex(value)].count();
+ return listitems[listitems.valindex(value)].count();
}
void
@@ -704,7 +682,7 @@ mgSelection::refreshValues () const
{
mgParts p = order.Parts(m_db,m_level);
m_current_values = p.sql_select();
- items.clear ();
+ listitems.clear ();
MYSQL_RES *rows = m_db.exec_sql (m_current_values);
if (rows)
{
@@ -716,7 +694,7 @@ mgSelection::refreshValues () const
string r0 = row[0];
if (!strcmp(row[0],"NULL")) // there is a genre NULL!
continue;
- mgSelItem n;
+ mgListItem n;
if (num_fields==3)
{
if (!row[1]) continue;
@@ -724,7 +702,7 @@ mgSelection::refreshValues () const
}
else
n.set(value(order.Key(m_level),r0),r0,atol(row[1]));
- items.push_back(n);
+ listitems.push_back(n);
}
mysql_free_result (rows);
}
@@ -734,43 +712,42 @@ mgSelection::refreshValues () const
unsigned int
mgSelection::count () const
{
- return items.size ();
+ return listitems.size ();
}
bool mgSelection::enter (unsigned int position)
{
if (order.empty())
- esyslog("mgSelection::enter(%u): order is empty", position);
+ mgWarning("mgSelection::enter(%u): order is empty", position);
if (empty())
return false;
+ if (m_level == order.size ())
+ return false;
setPosition (position);
position = gotoPosition(); // reload adjusted position
- if (items.size()==0)
+ if (listitems.size()==0)
return false;
- mgSelItem item = items[position];
- mgSelItems prev;
- if (m_fall_through && items.size()<10)
- prev=items;
+ mgListItem item = listitems[position];
+ mgListItems prev;
+ if (m_fall_through && listitems.size()<10)
+ prev=listitems;
while (1)
{
if (m_level >= order.size () - 1)
return false;
order[m_level++]->set (item);
clearCache();
- if (m_level >= order.size())
- mgError("mgSelection::enter(%u): level greater than order.size() %u",
- m_level,order.size());
m_position = 0;
refreshValues();
if (count()==0)
break;
- item=items[0];
+ item=listitems[0];
if (!m_fall_through)
break;
if (m_level==order.size()-1)
break;
- if (count () > 1 && !(prev==items))
+ if (count () > 1 && !(prev==listitems))
break;
}
return true;
@@ -781,11 +758,11 @@ bool mgSelection::select (unsigned int position)
{
if (m_level == order.size () - 1)
{
- if (getNumTracks () <= position)
+ if (getNumItems () <= position)
return false;
- order[m_level]->set (items[position]);
+ order[m_level]->set (listitems[position]);
m_level++;
- m_trackid = m_tracks[position].getTrackid ();
+ m_itemid = m_items[position].getItemid ();
clearCache();
return true;
@@ -808,14 +785,14 @@ mgSelection::leave ()
m_level--;
prevvalue=order.getKeyItem(m_level).value();
order[m_level]->set(zeroitem);
- m_trackid = -1;
+ m_itemid = -1;
clearCache();
setPosition(prevvalue);
return true;
}
- mgSelItems prev;
- if (m_fall_through && items.size()<10)
- prev=items;
+ mgListItems prev;
+ if (m_fall_through && listitems.size()<10)
+ prev=listitems;
while (1)
{
if (m_level < 1)
@@ -827,7 +804,7 @@ mgSelection::leave ()
clearCache();
if (!m_fall_through)
break;
- if (count () > 1 && !(prev==items))
+ if (count () > 1 && !(prev==listitems))
break;
}
setPosition(prevvalue);
@@ -838,7 +815,7 @@ void
mgSelection::leave_all ()
{
m_level=0;
- for (unsigned int i=0;i<ordersize();i++)
+ for (unsigned int i=0;i<order.size();i++)
order[i]->set (zeroitem);
clearCache();
}
@@ -847,9 +824,9 @@ void
mgSelection::selectfrom(mgOrder& oldorder,mgContentItem* o)
{
leave_all();
- mgSelItem selitem;
+ mgListItem selitem;
assert(m_level==0);
- for (unsigned int idx = 0; idx < ordersize(); idx++)
+ for (unsigned int idx = 0; idx < order.size(); idx++)
{
selitem = zeroitem;
mgKeyTypes new_kt = getKeyType(idx);
@@ -862,16 +839,16 @@ mgSelection::selectfrom(mgOrder& oldorder,mgContentItem* o)
&& iskeyGenre(old_kt)
&& iskeyGenre(new_kt))
{
- string selid=id(new_kt,value(new_kt,oldorder.getKeyItem(i).id()));
- selitem=mgSelItem (value(new_kt,selid),selid);
+ string selid=KeyMaps.id(new_kt,KeyMaps.value(new_kt,oldorder.getKeyItem(i).id()));
+ selitem=mgListItem (KeyMaps.value(new_kt,selid),selid);
}
if (selitem.valid()) break;
}
- if (!selitem.valid() && o && o->getTrackid()>=0)
+ if (!selitem.valid() && o && o->getItemid()>=0)
selitem = o->getKeyItem(new_kt);
if (!selitem.valid())
break;
- if (m_level<ordersize()-1)
+ if (m_level<order.size()-1)
{
order[m_level++]->set (selitem);
}
@@ -889,36 +866,14 @@ mgSelection::selectfrom(mgOrder& oldorder,mgContentItem* o)
setPosition(selitem.value());
order[m_level+1]->set(zeroitem);
}
- assert(m_level<ordersize());
+ assert(m_level<order.size());
}
-string
-mgSelection::value(mgKeyTypes kt, string idstr) const
-{
- if (loadvalues (kt))
- {
- map<string,string>& valmap = map_values[kt];
- map<string,string>::iterator it;
- it = valmap.find(idstr);
- if (it!=valmap.end())
- {
- string r = it->second;
- if (!r.empty())
- return r;
- }
- map_ids[kt].clear();
- loadvalues(kt);
- it = valmap.find(idstr);
- if (it!=valmap.end())
- return valmap[idstr];
- }
- return idstr;
-}
string
mgSelection::value(mgKey* k, string idstr) const
{
- return value(k->Type(),idstr);
+ return KeyMaps.value(k->Type(),idstr);
}
string
@@ -927,26 +882,11 @@ mgSelection::value(mgKey* k) const
return value(k,k->id());
}
-string
-mgSelection::id(mgKeyTypes kt, string val) const
-{
- if (loadvalues (kt))
- {
- map<string,string>& idmap = map_ids[kt];
- string v = idmap[val];
- if (kt==keyGenre1) v=v.substr(0,1);
- if (kt==keyGenre2) v=v.substr(0,2);
- if (kt==keyGenre3) v=v.substr(0,3);
- return v;
- }
- else
- return val;
-}
string
mgSelection::id(mgKey* k, string val) const
{
- return id(k->Type(),val);
+ return KeyMaps.id(k->Type(),val);
}
string
@@ -955,17 +895,6 @@ mgSelection::id(mgKey* k) const
return k->id();
}
-bool
-mgSelection::UsedBefore(mgOrder *o,const mgKeyTypes kt,unsigned int level) const
-{
- if (level>=o->size())
- level = o->size() -1;
- for (unsigned int lx = 0; lx < level; lx++)
- if (o->getKeyType(lx)==kt)
- return true;
- return false;
-}
-
bool mgSelection::isLanguagelist() const
{
return (order.getKeyType(0) == keyLanguage);
@@ -994,7 +923,6 @@ mgSelection::inCollection(const string Name) const
void mgSelection::DumpState(mgValmap& nv) const
{
nv.put("FallThrough",m_fall_through);
- nv.put("Directory",m_Directory);
nv.put("Level",int(m_level));
for (unsigned int i=0;i<order.size();i++)
{
@@ -1005,62 +933,28 @@ void mgSelection::DumpState(mgValmap& nv) const
free(n);
}
}
- nv.put("TrackId",m_trackid);
- nv.put("Position",items[m_position].value());
+ nv.put("ItemId",m_itemid);
+ nv.put("Position",listitems[m_position].value());
if (m_level>=order.size()-1)
- nv.put("TrackPosition",getTrackPosition());
+ nv.put("ItemPosition",getItemPosition());
}
map <mgKeyTypes, string>
mgSelection::UsedKeyValues()
{
map <mgKeyTypes, string> result;
- for (unsigned int idx = 0 ; idx < level() ; idx++)
+ for (unsigned int idx = 0 ; idx < m_level ; idx++)
{
result[order.getKeyType(idx)] = order.getKeyItem(idx).value();
}
- if (level() < order.size()-1)
+ if (m_level < order.size()-1)
{
- mgKeyTypes ch = order.getKeyType(level());
+ mgKeyTypes ch = order.getKeyType(m_level);
result[ch] = getCurrentValue();
}
return result;
}
-bool
-mgSelection::loadvalues (mgKeyTypes kt) const
-{
- if (map_ids.count(kt)>0)
- return true;
- map<string,string>& idmap = map_ids[kt];
- mgKey* k = ktGenerate(kt);
- if (k->map_idfield().empty())
- {
- delete k;
- return false;
- }
- map<string,string>& valmap = map_values[kt];
- char *b;
- asprintf(&b,"select %s,%s from %s;",k->map_idfield().c_str(),k->map_valuefield().c_str(),k->map_valuetable().c_str());
- MYSQL_RES *rows = m_db.exec_sql (string(b));
- free(b);
- if (rows)
- {
- MYSQL_ROW row;
- while ((row = mysql_fetch_row (rows)) != 0)
- {
- if (row[0] && row[1])
- {
- valmap[row[0]] = row[1];
- idmap[row[1]] = row[0];
- }
- }
- mysql_free_result (rows);
- }
- delete k;
- return true;
-}
-
static vector<int> keycounts;
unsigned int
@@ -1087,63 +981,3 @@ mgSelection::keycount(mgKeyTypes kt)
}
-vector <const char *>
-mgSelection::choices(mgOrder *o,unsigned int level, unsigned int *current)
-{
- vector<const char*> result;
- if (level>o->size())
- {
- *current = 0;
- return result;
- }
- for (unsigned int ki=int(mgKeyTypesLow);ki<=int(mgKeyTypesHigh);ki++)
- {
- mgKeyTypes kt = mgKeyTypes(ki);
- if (kt==o->getKeyType(level))
- {
- *current = result.size();
- result.push_back(ktName(kt));
- continue;
- }
- if (UsedBefore(o,kt,level))
- continue;
- if (kt==keyDecade && UsedBefore(o,keyYear,level))
- continue;
- if (kt==keyGenre1)
- {
- if (UsedBefore(o,keyGenre2,level)) continue;
- if (UsedBefore(o,keyGenre3,level)) continue;
- if (UsedBefore(o,keyGenres,level)) continue;
- }
- if (kt==keyGenre2)
- {
- if (UsedBefore(o,keyGenre3,level)) continue;
- if (UsedBefore(o,keyGenres,level)) continue;
- }
- if (kt==keyGenre3)
- {
- if (UsedBefore(o,keyGenres,level)) continue;
- }
- if (kt==keyFolder1)
- {
- if (UsedBefore(o,keyFolder2,level)) continue;
- if (UsedBefore(o,keyFolder3,level)) continue;
- if (UsedBefore(o,keyFolder4,level)) continue;
- }
- if (kt==keyFolder2)
- {
- if (UsedBefore(o,keyFolder3,level)) continue;
- if (UsedBefore(o,keyFolder4,level)) continue;
- }
- if (kt==keyFolder3)
- {
- if (UsedBefore(o,keyFolder4,level)) continue;
- }
- if (kt==keyCollection || kt==keyCollectionItem)
- result.push_back(ktName(kt));
- else if (keycount(kt)>1)
- result.push_back(ktName(kt));
- }
- return result;
-}
-
diff --git a/mg_selection.h b/mg_selection.h
index f9fa455..60018f6 100644
--- a/mg_selection.h
+++ b/mg_selection.h
@@ -29,31 +29,32 @@ typedef vector<string> strvector;
* \brief the only interface to the database.
* Some member functions are declared const although they can modify the inner state of mgSelection.
* But they only modify variables used for caching. With const, we want to express
- * the logical constness. E.g. the selected tracks can change without breaking constness:
- * The selection never defines concrete tracks but only how to choose them.
+ * the logical constness. E.g. the selected items can change without breaking constness:
+ * The selection never defines concrete items but only how to choose them.
*/
class mgSelection
{
- class mgSelItems
+ public:
+ class mgListItems
{
public:
- mgSelItems() { m_sel=0; }
+ mgListItems() { m_sel=0; }
void setOwner(mgSelection* sel);
- mgSelItem& operator[](unsigned int idx);
+ mgListItem& operator[](unsigned int idx);
string& id(unsigned int);
unsigned int count(unsigned int);
- bool operator==(const mgSelItems&x) const;
+ bool operator==(const mgListItems&x) const;
size_t size() const;
unsigned int valindex (const string v) const;
unsigned int idindex (const string i) const;
void clear();
- void push_back(mgSelItem& item) { m_items.push_back(item); }
+ void push_back(mgListItem& item) { m_items.push_back(item); }
private:
unsigned int index (const string s,bool val,bool second_try=false) const;
- vector<mgSelItem> m_items;
+ vector<mgListItem> m_items;
mgSelection* m_sel;
};
- public:
+
//! \brief defines an order to be used
void setOrder(mgOrder *o);
@@ -73,8 +74,8 @@ class mgSelection
enum LoopMode
{
LM_NONE, //!< \brief do not loop
- LM_SINGLE, //!< \brief loop a single track
- LM_FULL //!< \brief loop the whole track list
+ LM_SINGLE, //!< \brief loop a single item
+ LM_FULL //!< \brief loop the whole item list
};
/*! \brief the main constructor
@@ -109,14 +110,14 @@ class mgSelection
* small overhead for building the SQL WHERE command. The items will
* be reloaded when the SQL command changes
*/
- mutable mgSelItems items;
+ mutable mgListItems listitems;
/*! \brief returns the name of a key
*/
mgKeyTypes getKeyType (const unsigned int level) const;
//! \brief return the current value of this key
- mgSelItem& getKeyItem (const unsigned int level) const;
+ mgListItem& getKeyItem (const unsigned int level) const;
/*! \brief returns the current item from the value() list
*/
@@ -126,7 +127,7 @@ class mgSelection
map<mgKeyTypes,string> UsedKeyValues();
//! \brief the number of key fields used for the query
- unsigned int ordersize ();
+ unsigned int ordersize () { return order.size(); }
//! \brief the number of music items currently selected
unsigned int count () const;
@@ -139,12 +140,12 @@ class mgSelection
unsigned int gotoPosition ();
-//! \brief the current position in the tracks list
- unsigned int getTrackPosition () const;
+//! \brief the current position in the item list
+ unsigned int getItemPosition () const;
- //! \brief go to the current track position. If it does not exist,
+ //! \brief go to the current item position. If it does not exist,
// go to the nearest.
- unsigned int gotoTrackPosition ();
+ unsigned int gotoItemPosition ();
/*! \brief enter the the next higher level, go one up in the tree.
* If fall_through (see constructor) is set to true, and the
@@ -181,7 +182,7 @@ class mgSelection
*/
bool enter (const string value)
{
- return enter (items.valindex (value));
+ return enter (listitems.valindex (value));
}
/*! \brief like enter but if we are at the leaf level simply select
@@ -189,12 +190,12 @@ class mgSelection
*/
bool select (const string value)
{
- return select (items.valindex(value));
+ return select (listitems.valindex(value));
}
bool selectid (const string i)
{
- return select(items.idindex(i));
+ return select(listitems.idindex(i));
}
void selectfrom(mgOrder& oldorder,mgContentItem* o);
@@ -216,7 +217,7 @@ class mgSelection
*/
void leave_all ();
-//! \brief the current level in the tree
+//! \brief the current level in the tree. This is at most order.size().
unsigned int level () const
{
return m_level;
@@ -225,31 +226,31 @@ class mgSelection
//! \brief true if the selection holds no items
bool empty();
-/*! \brief returns detailed info about all selected tracks.
+/*! \brief returns detailed info about all selected items.
* The ordering is done only by the keyfield of the current level.
* This might have to be changed - suborder by keyfields of detail
* levels. This list is cached so several consequent calls mean no
* loss of performance. See value(), the same warning applies.
- * \todo call this more seldom. See getNumTracks()
+ * \todo call this more seldom. See getNumItems()
*/
- const vector < mgContentItem > &tracks () const;
+ const vector < mgContentItem > &items () const;
-/*! \brief returns an item from the tracks() list
- * \param position the position in the tracks() list
+/*! \brief returns an item from the items() list
+ * \param position the position in the items() list
* \return returns NULL if position is out of range
*/
- mgContentItem* getTrack (unsigned int position);
+ mgContentItem* getItem (unsigned int position);
-/*! \brief returns the current item from the tracks() list
+/*! \brief returns the current item from the items() list
*/
- mgContentItem* getCurrentTrack ()
+ mgContentItem* getCurrentItem ()
{
- return getTrack (gotoTrackPosition());
+ return getItem (gotoItemPosition());
}
/*! \brief toggles the shuffle mode thru all possible values.
* When a shuffle modus SM_NORMAL or SM_PARTY is selected, the
- * order of the tracks in the track list will be randomly changed.
+ * order of the items in the item list will be randomly changed.
*/
ShuffleMode toggleShuffleMode ();
@@ -277,13 +278,13 @@ class mgSelection
m_loop_mode = loop_mode;
}
-/*! \brief adds the whole current track list to a collection
+/*! \brief adds the whole current item list to a collection
* \param Name the name of the collection. If it does not yet exist,
* it will be created.
*/
unsigned int AddToCollection (const string Name);
-/*! \brief removes the whole current track from a the collection
+/*! \brief removes the whole current item from a the collection
* Remember - this selection can be configured to hold exactly
* one list, so this command can be used to clear a selected list.
* \param Name the name of the collection
@@ -299,7 +300,7 @@ class mgSelection
//! \brief remove all items from the collection
void ClearCollection (const string Name);
-/*! generates an m3u file containing all tracks. The directory
+/*! generates an m3u file containing all items. The directory
* can be indicated by SetDirectory().
* The file name will be built from the list name, slashes
* and spaces converted
@@ -308,7 +309,7 @@ class mgSelection
/*! \brief go to a position in the current level. If we are at the
- * most detailled level this also sets the track position since
+ * most detailled level this also sets the item position since
* they are identical.
* \param position the wanted position. If it is too big, go to the
* last existing position
@@ -321,59 +322,38 @@ class mgSelection
*/
void setPosition (const string value)
{
- setPosition (items.valindex (value));
+ setPosition (listitems.valindex (value));
}
-/*! \brief go to a position in the track list
+/*! \brief go to a position in the item list
* \param position the wanted position. If it is too big, go to the
- * last existing position
+ * last existing position. If the position is not valid, find the
+ * next valid one.
* \return only if no position exists, false will be returned
*/
- void setTrackPosition (unsigned int position) const;
+ void GotoItemPosition (unsigned int position) const;
-/*! \brief skip some tracks in the track list
+/*! \brief skip some items in the item list
* \return false if new position does not exist
*/
- bool skipTracks (int step=1);
-
-/*! \brief skip forward by 1 in the track list
- * \return false if new position does not exist
- */
- bool skipFwd ()
- {
- return skipTracks (+1);
- }
+ bool skipItems (int step=1) const;
-/*! \brief skip back by 1 in the track list
- * \return false if new position does not exist
- */
- bool skipBack ()
- {
- return skipTracks (-1);
- }
-
-//! \brief returns the sum of the durations of all tracks
+//! \brief returns the sum of the durations of all items
unsigned long getLength ();
-/*! \brief returns the sum of the durations of completed tracks
- * those are tracks before the current track position
+/*! \brief returns the sum of the durations of completed items
+ * those are items before the current item position
*/
unsigned long getCompletedLength () const;
-/*! returns the number of tracks in the track list
- * \todo should not call tracks () which loads all track info.
- * instead, only count the tracks. If the size differs from
- * m_tracks.size(), invalidate m_tracks
+/*! returns the number of items in the item list
+ * \todo should not call items () which loads all item info.
+ * instead, only count the items. If the size differs from
+ * m_items.size(), invalidate m_items
*/
- unsigned int getNumTracks () const
- {
- return tracks ().size ();
- }
-
-//! sets the directory for the storage of m3u file
- void SetDirectory (const string directory)
+ unsigned int getNumItems () const
{
- m_Directory = directory;
+ return items ().size ();
}
/*! returns the name of the current play list. If no play list is active,
@@ -407,7 +387,7 @@ class mgSelection
void refreshValues() const;
- //! \brief true if values and tracks need to be reloaded
+ //! \brief true if values and items need to be reloaded
bool cacheIsEmpty() const
{
return (m_current_values=="" && m_current_tracks=="");
@@ -419,7 +399,6 @@ class mgSelection
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);
unsigned int valcount (string val);
bool Connected() { return m_db.Connected(); }
@@ -428,29 +407,28 @@ class mgSelection
mutable map <mgKeyTypes, map<string,string> > map_ids;
mutable string m_current_values;
mutable string m_current_tracks;
-//! \brief be careful when accessing this, see mgSelection::tracks()
- mutable vector < mgContentItem > m_tracks;
+//! \brief be careful when accessing this, see mgSelection::items()
+ mutable vector < mgContentItem > m_items;
//! \brief initializes maps for id/value mapping in both direction
bool loadvalues (mgKeyTypes kt) const;
bool m_fall_through;
unsigned int m_position;
- mutable unsigned int m_tracks_position;
+ mutable unsigned int m_items_position;
ShuffleMode m_shuffle_mode;
void Shuffle() const;
LoopMode m_loop_mode;
mutable mgmySql m_db;
unsigned int m_level;
- long m_trackid;
+ long m_itemid;
mgOrder order;
- bool UsedBefore (mgOrder *o,const mgKeyTypes kt, unsigned int level) const;
void InitSelection ();
/*! \brief returns the SQL command for getting all values.
* For the leaf level, all values are returned. For upper
* levels, every distinct value is returned only once.
* This must be so for the leaf level because otherwise
* the value() entries do not correspond to the track()
- * entries and the wrong tracks might be played.
+ * entries and the wrong items might be played.
*/
string sql_values ();
string ListFilename ();
diff --git a/mg_setup.c b/mg_setup.c
index c649536..a1ffcd4 100644
--- a/mg_setup.c
+++ b/mg_setup.c
@@ -1,5 +1,5 @@
/*!
- * \file vdr_setup.c
+ * \file mg_setup.c
* \brief A setup class for a VDR media plugin (muggle)
*
* \version $Revision: 1.3 $
@@ -7,7 +7,7 @@
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
* \author Responsible author: $Author: wr61 $
*
- * $Id: vdr_setup.c 399 2005-01-24 14:45:30Z wr61 $
+ * $Id: mg_setup.c 399 2005-01-24 14:45:30Z wr61 $
*
* Partially adapted from
* MP3/MPlayer plugin to VDR (C++)
@@ -16,6 +16,7 @@
#include "mg_setup.h"
+#include <string>
mgSetup the_setup;
@@ -30,8 +31,31 @@ mgSetup::mgSetup ()
TargetLevel = DEFAULT_TARGET_LEVEL;
LimiterLevel = DEFAULT_LIMITER_LEVEL;
Only48kHz = 0;
- ToplevelDir = "/mnt/music/";
- DbHost = "localhost";
- DbName = "GiantDisc";
+
+ DbHost = 0;
+ DbSocket = 0;
+ DbPort = 0;
+ DbName = strdup ("GiantDisc");
+ DbUser = 0;
+ DbPass = 0;
+ ToplevelDir = strdup("/mnt/music/");
+
DeleteStaleReferences = false;
}
+
+mgSetup::~mgSetup ()
+{
+ free(DbHost);
+ free(DbSocket);
+ free(DbName);
+ free(DbUser);
+ free(DbPass);
+ free(ToplevelDir);
+}
+
+bool
+mgSetup::NoHost() const
+{
+ return !DbHost || strlen(DbHost)==0;
+}
+
diff --git a/mg_setup.h b/mg_setup.h
index 7423d4d..f5fd95d 100644
--- a/mg_setup.h
+++ b/mg_setup.h
@@ -30,12 +30,14 @@
class mgSetup
{
public:
+ mgSetup (void);
+ ~mgSetup (void);
+ bool NoHost() const;
int InitLoopMode;
int InitShuffleMode;
int AudioMode;
int DisplayMode;
int BackgrMode;
- int MenuMode;
int TargetLevel;
int LimiterLevel;
int Only48kHz;
@@ -46,15 +48,10 @@ class mgSetup
char *DbUser;
char *DbPass;
int DbPort;
- bool GdCompatibility;
char *ToplevelDir;
- char PathPrefix[MAX_STRING_LEN];
-
int DeleteStaleReferences;
- public:
- mgSetup (void);
};
diff --git a/mg_sync.c b/mg_sync.c
index 677c3f8..1e4645a 100644
--- a/mg_sync.c
+++ b/mg_sync.c
@@ -29,13 +29,13 @@
#include "mg_setup.h"
char *
-mgSync::sql_Cstring(TagLib::String s,char *buf)
+mgDbGd::sql_Cstring(TagLib::String s,char *buf)
{
return m_db.sql_Cstring(s.toCString(),buf);
}
char *
-mgSync::lower(char *s)
+mgDbGd::lower(char *s)
{
char *p=s;
while (*p)
@@ -48,7 +48,7 @@ mgSync::lower(char *s)
}
TagLib::String
-mgSync::getlanguage(const char *filename)
+mgDbGd::getlanguage(const char *filename)
{
TagLib::String result = "";
TagLib::ID3v2::Tag * id3v2tag=0;
@@ -78,7 +78,7 @@ mgSync::getlanguage(const char *filename)
}
char *
-mgSync::getAlbum(const char *c_album,const char *c_artist,const char *c_directory)
+mgDbGd::getAlbum(const char *c_album,const char *c_artist,const char *c_directory)
{
char * result;
char *b;
@@ -134,8 +134,11 @@ mgSync::getAlbum(const char *c_album,const char *c_artist,const char *c_director
return result;
}
-mgSync::mgSync()
+mgDbGd::mgDbGd(bool separate_thread)
{
+ m_separate_thread = separate_thread;
+ if (separate_thread)
+ mysql_thread_init();
m_genre_rows=0;
if (!m_db.Connected())
return;
@@ -150,13 +153,15 @@ mgSync::mgSync()
srandom( tv.tv_usec );
}
-mgSync::~mgSync()
+mgDbGd::~mgDbGd()
{
- if (m_genre_rows) mysql_free_result(m_genre_rows);
+ if (m_genre_rows) mysql_free_result(m_genre_rows);
+ if (m_separate_thread)
+ mysql_thread_end();
}
void
-mgSync::UpdateTrack(long trackid)
+mgDbGd::UpdateTrack(long trackid)
{
char sql[7000];
char *c_cddbid=getAlbum(c_album,c_artist,c_directory);
@@ -171,7 +176,7 @@ mgSync::UpdateTrack(long trackid)
}
void
-mgSync::AddTrack()
+mgDbGd::AddTrack()
{
char sql[7000];
char *c_cddbid=getAlbum(c_album,c_artist,c_directory);
@@ -188,7 +193,7 @@ mgSync::AddTrack()
}
bool
-mgSync::GetFileInfo(const char *filename)
+mgDbGd::GetFileInfo(const char *filename)
{
TagLib::FileRef f( filename) ;
if (f.isNull())
@@ -235,17 +240,20 @@ mgSync::GetFileInfo(const char *filename)
}
void
-mgSync::SyncFile(const char *filename)
+mgDbGd::SyncFile(const char *filename)
{
if (!strncmp(filename,"./",2)) // strip leading ./
filename += 2;
- if (strlen(filename)>255)
+ const char *cfilename=filename;
+ if (isdigit(filename[0]) && isdigit(filename[1]) && filename[2]=='/' && !strchr(filename+3,'/'))
+ cfilename=cfilename+3;
+ if (strlen(cfilename)>255)
{
mgWarning("Length of file exceeds database field capacity: %s", filename);
return;
}
mgDebug(3,"Importing %s",filename);
- sql_Cstring(filename,c_mp3file);
+ sql_Cstring(cfilename,c_mp3file);
char sql[600];
sprintf(sql,"SELECT id from tracks WHERE mp3file=%s",c_mp3file);
string s = m_db.get_col0(sql);
@@ -262,7 +270,7 @@ mgSync::SyncFile(const char *filename)
}
void
-mgSync::Sync(char * const * path_argv, bool delete_missing)
+mgDbGd::Sync(char * const * path_argv, bool delete_missing)
{
extern void showimportcount(unsigned int,bool final=false);
if (!m_db.Connected())
@@ -299,7 +307,7 @@ mgSync::Sync(char * const * path_argv, bool delete_missing)
}
void
-mgSync::Create()
+mgDbGd::Create()
{
- m_db.Create();
+ m_db.Create();
}
diff --git a/mg_sync.h b/mg_sync.h
index 6374d59..bcc32db 100644
--- a/mg_sync.h
+++ b/mg_sync.h
@@ -18,12 +18,11 @@
#include "mg_mysql.h"
-class mgSync
+class mgDbGd
{
public:
- mgSync();
- ~mgSync();
-
+ mgDbGd(bool SeparateThread=false);
+ ~mgDbGd();
//! \brief drop and create the data base GiantDisc
void Create();
@@ -37,6 +36,7 @@ class mgSync
void Sync(char * const * path_argv, bool delete_missing = false);
private:
+ bool m_separate_thread;
mgmySql m_db;
char *sql_Cstring(TagLib::String s,char *buf=0);
char *lower(char *s);
diff --git a/mg_thread_sync.c b/mg_thread_sync.c
index 85b55a6..710eb1c 100644
--- a/mg_thread_sync.c
+++ b/mg_thread_sync.c
@@ -1,6 +1,4 @@
-#include <mysql/mysql.h>
-
#include "mg_thread_sync.h"
#include "mg_sync.h"
@@ -49,15 +47,10 @@ bool mgThreadSync::Sync(char * const * path_argv, bool delete_missing )
void
mgThreadSync::Action()
{
- mysql_thread_init();
-
if( m_path )
{
- mgSync s;
+ mgDbGd s(true);
s.Sync( m_path, m_delete );
}
-
- mysql_thread_end();
}
-
diff --git a/mg_tools.c b/mg_tools.c
index ce09ab6..d2dc1ef 100644
--- a/mg_tools.c
+++ b/mg_tools.c
@@ -8,6 +8,8 @@
* \author file owner: $Author$
*/
+#include <tools.h>
+
#include "mg_tools.h"
/*extern "C"
@@ -29,6 +31,7 @@ void
mgSetDebugLevel (int new_level)
{
DEBUG_LEVEL = new_level;
+ SysLogLevel = new_level;
}
@@ -42,16 +45,7 @@ mgDebug (int level, const char *fmt, ...)
va_start (ap, fmt);
vsnprintf (buffer, MAX_BUFLEN - 1, fmt, ap);
- if (STANDALONE)
- {
- fprintf (stderr, "dbg %d: %s\n", level, buffer);
- }
- else
- {
-#if !STANDALONE
- isyslog ("%s\n", buffer);
-#endif
- }
+ dsyslog ("%s\n", buffer);
}
va_end (ap);
}
@@ -74,16 +68,7 @@ mgWarning (const char *fmt, ...)
va_start (ap, fmt);
vsnprintf (buffer, MAX_BUFLEN - 1, fmt, ap);
- if (STANDALONE)
- {
- fprintf (stderr, "warning: %s\n", buffer);
- }
- else
- {
-#if !STANDALONE
- isyslog ("Warning: %s\n", buffer);
-#endif
- }
+ isyslog ("Warning: %s\n", buffer);
extern void showmessage(const char*,int duration=0);
showmessage(buffer);
va_end (ap);
@@ -98,17 +83,7 @@ mgError (const char *fmt, ...)
va_start (ap, fmt);
vsnprintf (buffer, MAX_BUFLEN - 1, fmt, ap);
- if (STANDALONE)
- {
- fprintf (stderr, "Error: %s\n", buffer);
- exit (1);
- }
- else
- {
-#if !STANDALONE
- isyslog ("Error in Muggle: %s\n", buffer);
-#endif
- }
+ isyslog ("Error in Muggle: %s\n", buffer);
va_end (ap);
}
diff --git a/mg_tools.h b/mg_tools.h
index 65ba262..b36ac3f 100644
--- a/mg_tools.h
+++ b/mg_tools.h
@@ -15,9 +15,6 @@
#include <iostream>
#include <string>
-#include <mysql.h>
-
-#define STANDALONE 1 // what's this?
/*!
* \brief Logging utilities
@@ -86,4 +83,32 @@ std::string trim(std::string const& source, char const* delims = " \t\r\n");
char *SeparateFolders(const char *filename, char * folders[],unsigned int fcount);
+enum mgKeyTypes {
+ keyGenre1=1, // the genre types must have exactly this order!
+ keyGenre2,
+ keyGenre3,
+ keyGenres,
+ keyDecade,
+ keyYear,
+ keyArtist,
+ keyAlbum,
+ keyTitle,
+ keyTrack,
+ keyLanguage,
+ keyRating,
+ keyFolder1,
+ keyFolder2,
+ keyFolder3,
+ keyFolder4,
+ keyCreated,
+ keyModified,
+ keyArtistABC,
+ keyTitleABC,
+ keyCollection,
+ keyCollectionItem,
+};
+const mgKeyTypes mgKeyTypesLow = keyGenre1;
+const mgKeyTypes mgKeyTypesHigh = keyCollectionItem;
+const unsigned int mgKeyTypesNr = keyCollectionItem;
+
#endif /* _MUGGLE_TOOLS_H */
diff --git a/muggle.c b/muggle.c
index a249c27..5d0c84f 100644
--- a/muggle.c
+++ b/muggle.c
@@ -20,7 +20,7 @@
#include <getopt.h>
#include <config.h>
-static const char *VERSION = "0.1.6";
+static const char *VERSION = "0.1.7";
static const char *DESCRIPTION = "Media juggle plugin for VDR";
static const char *MAINMENUENTRY = "Muggle";
@@ -47,15 +47,6 @@ mgMuggle::MainMenuEntry (void)
mgMuggle::mgMuggle (void)
{
-// defaults for database arguments
- the_setup.DbHost = 0;
- the_setup.DbSocket = 0;
- the_setup.DbPort = 0;
- the_setup.DbName = strdup ("GiantDisc");
- the_setup.DbUser = 0;
- the_setup.DbPass = 0;
- the_setup.GdCompatibility = false;
- the_setup.ToplevelDir = strdup ("/mnt/music/");
#ifndef HAVE_ONLY_SERVER
char *buf;
asprintf(&buf,"%s/.muggle",getenv("HOME"));
@@ -68,11 +59,6 @@ mgMuggle::mgMuggle (void)
void
mgMuggle::Stop (void)
{
- free(the_setup.DbHost);
- free(the_setup.DbName);
- free(the_setup.DbUser);
- free(the_setup.DbPass);
- free(the_setup.ToplevelDir);
}
@@ -87,7 +73,7 @@ mgMuggle::CommandLineHelp (void)
" -h HHHH, --host=HHHH specify database host (default is mysql embedded)\n"
#endif
" -s SSSS --socket=PATH specify database socket\n"
- " -n NNNN, --name=NNNN specify database name (overridden by -g)\n"
+ " -n NNNN, --name=NNNN specify database name (default is GiantDisc)\n"
" -p PPPP, --port=PPPP specify port of database server (default is )\n"
" -u UUUU, --user=UUUU specify database user (default is )\n"
" -w WWWW, --password=WWWW specify database password (default is empty)\n"
@@ -95,7 +81,6 @@ mgMuggle::CommandLineHelp (void)
#ifndef HAVE_ONLY_SERVER
" -d DIRN, --datadir=DIRN specify directory for embedded sql data (default is $HOME/.muggle)\n"
#endif
- " -g, --giantdisc enable full Giantdisc compatibility mode\n"
" -v, --verbose specify debug level. The higher the more. Default is 1\n"
"\n"
"if the specified host is localhost, sockets will be used if possible.\n"
@@ -130,7 +115,6 @@ bool mgMuggle::ProcessArgs (int argc, char *argv[])
{"datadir", required_argument, NULL, 'd'},
#endif
{"toplevel", required_argument, NULL, 't'},
- {"giantdisc", no_argument, NULL, 'g'},
{NULL}
};
@@ -139,9 +123,9 @@ bool mgMuggle::ProcessArgs (int argc, char *argv[])
option_index = 0;
while ((c =
#ifndef HAVE_ONLY_SERVER
- getopt_long (argc, argv, "gh:s:n:p:t:u:w:d:v:", long_options,
+ getopt_long (argc, argv, "h:s:n:p:t:u:w:d:v:", long_options,
#else
- getopt_long (argc, argv, "gh:s:n:p:t:u:w:v:", long_options,
+ getopt_long (argc, argv, "h:s:n:p:t:u:w:v:", long_options,
#endif
&option_index)) != -1)
{
@@ -203,12 +187,6 @@ bool mgMuggle::ProcessArgs (int argc, char *argv[])
}
}
break;
- case 'g':
- {
- the_setup.DbName = strcpyrealloc (the_setup.DbName, "GiantDisc");
- the_setup.GdCompatibility = true;
- }
- break;
default:
return false;
}
diff --git a/muggle.doxygen b/muggle.doxygen
index 45daa09..98099e5 100644
--- a/muggle.doxygen
+++ b/muggle.doxygen
@@ -23,7 +23,7 @@ PROJECT_NAME = Muggle media plugin
# This could be handy for archiving the generated documentation or
# if some version control system is used.
-PROJECT_NUMBER = 0.1.6
+PROJECT_NUMBER = 0.1.7
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
diff --git a/mugglei.c b/mugglei.c
index 76cf7a4..a0f9a68 100755
--- a/mugglei.c
+++ b/mugglei.c
@@ -14,15 +14,8 @@
#include <stdio.h>
#include <sys/stat.h>
#include <sys/time.h>
-#include <mysql/mysql.h>
#include <getopt.h>
-/*extern "C"
-{*/
- #include <stdarg.h>
- #include <stdio.h>
-/*}
-*/
-#include <stdlib.h>
+#include <stdarg.h>
#include <tag.h>
#include <mpegfile.h>
@@ -126,27 +119,27 @@ int main( int argc, char *argv[] )
} break;
case 'h':
{
- the_setup.DbHost = optarg;
+ the_setup.DbHost = strdup(optarg);
} break;
case 'n':
{
- the_setup.DbName = optarg;
+ the_setup.DbName = strdup(optarg);
} break;
case 'u':
{
- the_setup.DbUser = optarg;
+ the_setup.DbUser = strdup(optarg);
} break;
case 'p':
{
- the_setup.DbPass = optarg;
+ the_setup.DbPass = strdup(optarg);
} break;
case 's':
{
- the_setup.DbSocket = optarg;
+ the_setup.DbSocket = strdup(optarg);
} break;
case 't':
{
- the_setup.ToplevelDir = optarg;
+ the_setup.ToplevelDir = strdup(optarg);
} break;
case 'z':
{
@@ -168,7 +161,7 @@ int main( int argc, char *argv[] )
#endif
}
}
- mgSync *sync = new mgSync; // because we want to delete it before database_end
+ mgDbGd *sync = new mgDbGd; // because we want to delete it before database_end
if (create_mode)
sync->Create();
if (optind<argc)
diff --git a/vdr_actions.c b/vdr_actions.c
index 6f58da7..0615a32 100644
--- a/vdr_actions.c
+++ b/vdr_actions.c
@@ -2,7 +2,8 @@
* \file vdr_actions.c
* \brief Implements all actions for browsing media libraries within VDR
*
- * \version $Revision: 1.27 $ * \date $Date: 2004-12-25 16:52:35 +0100 (Sat, 25 Dec 2004) $
+ * \version $Revision: 1.27 $
+ * \date $Date: 2004-12-25 16:52:35 +0100 (Sat, 25 Dec 2004) $
* \author Wolfgang Rohdewald
* \author Responsible author: $Author: wr61 $
*
@@ -15,6 +16,7 @@
#include <typeinfo>
#include <string>
#include <vector>
+#include <assert.h>
#include <menuitems.h>
#include <tools.h>
@@ -24,7 +26,7 @@
#include "vdr_actions.h"
#include "vdr_menu.h"
#include "i18n.h"
-#include <vdr/interface.h>
+#include <interface.h>
#define DEBUG
#include "mg_tools.h"
@@ -74,7 +76,7 @@ class mgEntry : public mgOsdItem
public:
void Notify();
bool Enabled(mgActions on) { return IsEntry(on);}
- const char *MenuName (const unsigned int idx,const mgSelItem& item);
+ const char *MenuName (const unsigned int idx,const mgListItem& item);
eOSState Process(eKeys key);
void Execute();
eOSState Back();
@@ -100,8 +102,6 @@ class mgDoCollEntry : public mgEntry
{
public:
virtual eOSState Process(eKeys key);
- protected:
- string getTarget();
};
class mgAddCollEntry : public mgDoCollEntry
@@ -146,23 +146,11 @@ mgDoCollEntry::Process(eKeys key)
return result;
}
-string
-mgDoCollEntry::getTarget()
-{
- string result = cOsdItem::Text();
- if (result[0]==' ')
- result.erase(0,5);
- else
- result.erase(0,3);
- string::size_type lparen = result.find(" [");
- result.erase(lparen,string::npos);
- return result;
-}
void
mgAddCollEntry::Execute()
{
- string target = getTarget();
+ string target = selection()->getCurrentValue();
osd()->default_collection = target;
if (target == osd()->play_collection)
if (!PlayerControl())
@@ -176,7 +164,7 @@ mgAddCollEntry::Execute()
void
mgRemoveCollEntry::Execute()
{
- string target = getTarget();
+ string target = selection()->getCurrentValue();
int removed = osd()->moveselection->RemoveFromCollection (target);
osd()->Message1 ("Removed %s entries",ltos(removed));
osd()->CollectionChanged(target);
@@ -243,13 +231,13 @@ class mgCommand : public mgOsdItem
class mgActOrder : public mgOsdItem
{
public:
- const char* MenuName(const unsigned int idx,const mgSelItem& item);
+ const char* MenuName(const unsigned int idx,const mgListItem& item);
virtual eOSState Process(eKeys key);
void Execute();
};
const char*
-mgActOrder::MenuName(const unsigned int idx,const mgSelItem& item)
+mgActOrder::MenuName(const unsigned int idx,const mgListItem& item)
{
return strdup(item.value().c_str());
}
@@ -282,8 +270,8 @@ mgActOrder::Execute()
mgOrder oldorder = s->getOrder();
mgContentItem o;
s->select();
- if (s->getNumTracks()==1)
- o = s->getTrack(0);
+ if (s->getNumItems()==1)
+ o = s->getItem(0);
osd()->UseNormalSelection(); // Default for all orders
osd()->setOrder(s,osd()->Current());
mgSelection *newsel = osd()->selection();
@@ -328,28 +316,39 @@ mgEntry::Notify()
const char *
-mgEntry::MenuName(const unsigned int idx,const mgSelItem& item)
+mgEntry::MenuName(const unsigned int idx,const mgListItem& item)
{
- char *result;
char ct[20];
- ct[0]=0;
unsigned int selcount = item.count();
if (selection()->level()<selection()->ordersize()-1 || selcount>1)
- sprintf(ct," [%u]",selcount);
- // when changing this, also change mgDoCollEntry::getTarget()
+ {
+ char numct[20];
+ sprintf(numct,"%u",selcount);
+ memset(ct,' ',19);
+ if (strlen(numct)<4)
+ ct[6-strlen(numct)*2]=0;
+ else
+ ct[0]=0;
+ strcat(ct,numct);
+ strcat(ct," ");
+ assert(strlen(ct)<20);
+ }
+ else
+ ct[0]=0;
+ char *result;
if (selection()->isCollectionlist())
{
if (item.value() == osd()->default_collection)
- asprintf(&result,"-> %s%s",item.value().c_str(),ct);
+ asprintf(&result,"-> %s%s",ct,item.value().c_str());
else
- asprintf(&result," %s%s",item.value().c_str(),ct);
+ asprintf(&result," %s%s",ct,item.value().c_str());
}
else if (selection()->inCollection())
- asprintf(&result,"%4d %s%s",idx,item.value().c_str(),ct);
+ asprintf(&result,"%4d %s",idx,item.value().c_str());
else if (selection()->isLanguagelist())
- asprintf(&result,"%s%s",dgettext("iso_639",item.value().c_str()),ct);
+ asprintf(&result,"%s%s",ct,dgettext("iso_639",item.value().c_str()));
else
- asprintf(&result,"%s%s",item.value().c_str(),ct);
+ asprintf(&result,"%s%s",ct,item.value().c_str());
return result;
}
@@ -369,15 +368,50 @@ mgEntry::Execute()
eOSState
mgEntry::Process(eKeys key)
{
- switch (key) {
+ eOSState result = osUnknown;
+
+ mgTree *menu = dynamic_cast<mgTree*>(m); // und 0 abfangen
+ switch (key)
+ {
case kOk:
- Execute();
- return osContinue;
+ {
+ if (menu)
+ menu->TerminateIncrementalSearch( true );
+ Execute();
+
+ result = osContinue;
+ } break;
+ case k0...k9:
+ {
+ if (menu)
+ {
+ menu->UpdateIncrementalSearch( key );
+ result = osContinue;
+ }
+ } break;
case kBack:
- return Back();
+ {
+ if( menu && menu->UpdateIncrementalSearch( key ) )
+ { // search is continued
+ result = osContinue;
+ }
+ else
+ { // search is not active at all
+ result = Back();
+ }
+ } break;
default:
- return osUnknown;
+ {
+ if( key != kNone )
+ {
+ if (menu)
+ menu->TerminateIncrementalSearch( true );
+ }
+ result = osUnknown;
+ }
}
+
+ return result;
}
@@ -541,7 +575,7 @@ class mgChooseOrder : public mgCommand
virtual eOSState Process(eKeys key);
void Execute ();
const char *ButtonName() { return tr("Order"); }
- const char *MenuName(const unsigned int idx,const mgSelItem& item)
+ const char *MenuName(const unsigned int idx,const mgListItem& item)
{ return strdup(tr("Select an order")); }
};
@@ -726,10 +760,10 @@ class mgSetDefaultCollection:public mgCommand
{
return tr ("Default");
}
- const char *MenuName (const unsigned int idx,const mgSelItem& item);
+ const char *MenuName (const unsigned int idx,const mgListItem& item);
};
-const char * mgSetDefaultCollection::MenuName(const unsigned int idx,const mgSelItem& item)
+const char * mgSetDefaultCollection::MenuName(const unsigned int idx,const mgListItem& item)
{
char *b;
asprintf (&b, tr("Set default to collection '%s'"),
@@ -788,13 +822,13 @@ class mgAddAllToCollection:public mgCommand {
{
return tr ("Add");
}
- const char *MenuName (const unsigned int idx,const mgSelItem& item);
+ const char *MenuName (const unsigned int idx,const mgListItem& item);
protected:
void ExecuteMove();
};
const char *
-mgAddAllToCollection::MenuName (const unsigned int idx,const mgSelItem& item)
+mgAddAllToCollection::MenuName (const unsigned int idx,const mgListItem& item)
{
return strdup(tr("Add all to a collection"));
}
@@ -812,7 +846,7 @@ void
mgAddAllToCollection::ExecuteMove()
{
if (osd() ->Menus.size()>1)
- osd ()->CloseMenu(); // TODO Gebastel...
+ osd ()->CloseMenu(); // TODO Gebastel...
char *b;
asprintf(&b,tr("'%s' to collection"),selection()->getCurrentValue().c_str());
osd ()->newmenu = new mgTreeAddToCollSelector(string(b));
@@ -833,11 +867,11 @@ class mgAddAllToDefaultCollection:public mgCommand {
{
return tr ("Add");
}
- const char *MenuName (const unsigned int idx,const mgSelItem& item);
+ const char *MenuName (const unsigned int idx,const mgListItem& item);
};
const char *
-mgAddAllToDefaultCollection::MenuName (const unsigned int idx,const mgSelItem& item)
+mgAddAllToDefaultCollection::MenuName (const unsigned int idx,const mgListItem& item)
{
char *b;
asprintf (&b, tr ("Add all to '%s'"),
@@ -883,7 +917,7 @@ class mgAddThisToCollection:public mgAddAllToCollection
bool Enabled(mgActions on);
void Execute ();
const char *ButtonName ();
- const char *MenuName (const unsigned int idx,const mgSelItem& item);
+ const char *MenuName (const unsigned int idx,const mgListItem& item);
};
@@ -910,7 +944,7 @@ mgAddThisToCollection::Enabled(mgActions on)
}
const char *
-mgAddThisToCollection::MenuName (const unsigned int idx,const mgSelItem& item)
+mgAddThisToCollection::MenuName (const unsigned int idx,const mgListItem& item)
{
return strdup(tr("Add to a collection"));
}
@@ -922,7 +956,7 @@ class mgAddThisToDefaultCollection:public mgAddAllToDefaultCollection
bool Enabled(mgActions on);
void Execute ();
const char *ButtonName ();
- const char *MenuName (const unsigned int idx,const mgSelItem& item);
+ const char *MenuName (const unsigned int idx,const mgListItem& item);
};
@@ -952,7 +986,7 @@ mgAddThisToDefaultCollection::Enabled(mgActions on)
}
const char *
-mgAddThisToDefaultCollection::MenuName (const unsigned int idx,const mgSelItem& item)
+mgAddThisToDefaultCollection::MenuName (const unsigned int idx,const mgListItem& item)
{
char *b;
asprintf (&b, tr ("Add to '%s'"), osd ()->default_collection.c_str ());
@@ -968,7 +1002,7 @@ class mgRemoveAllFromCollection:public mgCommand
{
return tr ("Remove");
}
- const char *MenuName (const unsigned int idx,const mgSelItem& item);
+ const char *MenuName (const unsigned int idx,const mgListItem& item);
};
void
@@ -983,7 +1017,7 @@ mgRemoveAllFromCollection::Execute ()
}
const char *
-mgRemoveAllFromCollection::MenuName (const unsigned int idx,const mgSelItem& item)
+mgRemoveAllFromCollection::MenuName (const unsigned int idx,const mgListItem& item)
{
return strdup(tr("Remove all from a collection"));
}
@@ -997,11 +1031,11 @@ class mgClearCollection : public mgCommand
{
return tr ("Clear");
}
- const char *MenuName (const unsigned int idx,const mgSelItem& item);
+ const char *MenuName (const unsigned int idx,const mgListItem& item);
};
const char *
-mgClearCollection::MenuName (const unsigned int idx,const mgSelItem& item)
+mgClearCollection::MenuName (const unsigned int idx,const mgListItem& item)
{
return strdup(tr("Clear the collection"));
}
@@ -1032,7 +1066,7 @@ class mgRemoveThisFromCollection:public mgRemoveAllFromCollection
{
return tr ("Remove");
}
- const char *MenuName (const unsigned int idx,const mgSelItem& item);
+ const char *MenuName (const unsigned int idx,const mgListItem& item);
};
@@ -1048,7 +1082,7 @@ mgRemoveThisFromCollection::Execute ()
const char *
-mgRemoveThisFromCollection::MenuName (const unsigned int idx,const mgSelItem& item)
+mgRemoveThisFromCollection::MenuName (const unsigned int idx,const mgListItem& item)
{
return strdup(tr("Remove from a collection"));
}
@@ -1110,7 +1144,7 @@ class mgCreateCollection : public mgCreate
mgCreateCollection();
bool Enabled(mgActions on);
void Execute ();
- const char *MenuName (const unsigned int idx=0,const mgSelItem& item=zeroitem);
+ const char *MenuName (const unsigned int idx=0,const mgListItem& item=zeroitem);
};
mgCreateCollection::mgCreateCollection() : mgCreate(MenuName())
@@ -1118,7 +1152,7 @@ mgCreateCollection::mgCreateCollection() : mgCreate(MenuName())
}
const char*
-mgCreateCollection::MenuName(const unsigned int idx,const mgSelItem& item)
+mgCreateCollection::MenuName(const unsigned int idx,const mgListItem& item)
{
return strdup(tr ("Create collection"));
}
@@ -1136,7 +1170,6 @@ mgCreateCollection::Execute ()
selection ()->clearCache();
if (selection()->isCollectionlist())
{
-// selection ()->setPosition(selection()->id(keyCollection,name));
selection ()->setPosition(name);
}
osd()->forcerefresh = true;
@@ -1162,7 +1195,7 @@ class mgDeleteCollection:public mgCommand
{
return tr ("Delete");
}
- const char *MenuName (const unsigned int idx,const mgSelItem& item);
+ const char *MenuName (const unsigned int idx,const mgListItem& item);
};
bool
@@ -1178,7 +1211,7 @@ mgDeleteCollection::Enabled(mgActions on)
return result;
}
-const char* mgDeleteCollection::MenuName(const unsigned int idx,const mgSelItem& item)
+const char* mgDeleteCollection::MenuName(const unsigned int idx,const mgListItem& item)
{
return strdup(tr("Delete the collection"));
}
@@ -1201,8 +1234,8 @@ mgDeleteCollection::Execute ()
}
-//! \brief export track list for all selected items
-class mgExportTracklist:public mgCommand
+//! \brief export item list for all selected items
+class mgExportItemlist:public mgCommand
{
public:
void Execute ();
@@ -1210,14 +1243,14 @@ class mgExportTracklist:public mgCommand
{
return tr ("Export");
}
- const char *MenuName (const unsigned int idx,const mgSelItem& item)
+ const char *MenuName (const unsigned int idx,const mgListItem& item)
{
return strdup(tr ("Export track list"));
}
};
void
-mgExportTracklist::Execute ()
+mgExportItemlist::Execute ()
{
selection ()->select ();
string m3u_file = selection ()->exportM3U ();
@@ -1239,7 +1272,7 @@ mgAction::Type()
if (t == typeid(mgAddAllToDefaultCollection)) return actAddAllToDefaultCollection;
if (t == typeid(mgRemoveAllFromCollection)) return actRemoveAllFromCollection;
if (t == typeid(mgDeleteCollection)) return actDeleteCollection;
- if (t == typeid(mgExportTracklist)) return actExportTracklist;
+ if (t == typeid(mgExportItemlist)) return actExportItemlist;
if (t == typeid(mgAddCollEntry)) return actAddCollEntry;
if (t == typeid(mgRemoveCollEntry)) return actRemoveCollEntry;
if (t == typeid(mgAddThisToCollection)) return actAddThisToCollection;
@@ -1306,7 +1339,7 @@ actGenerate(const mgActions action)
case actAddAllToDefaultCollection: result = new mgAddAllToDefaultCollection;break;
case actRemoveAllFromCollection:result = new mgRemoveAllFromCollection;break;
case actDeleteCollection: result = new mgDeleteCollection;break;
- case actExportTracklist: result = new mgExportTracklist;break;
+ case actExportItemlist: result = new mgExportItemlist;break;
case actAddCollEntry: result = new mgAddCollEntry;break;
case actRemoveCollEntry: result = new mgRemoveCollEntry;break;
case actAddThisToCollection: result = new mgAddThisToCollection;break;
diff --git a/vdr_actions.h b/vdr_actions.h
index 72f9caf..5c7a9f8 100644
--- a/vdr_actions.h
+++ b/vdr_actions.h
@@ -40,7 +40,7 @@ enum mgActions {
actAddAllToCollection, //!< add all items of OSD list to default collection
actRemoveAllFromCollection,//!< remove from default collection
actDeleteCollection, //!< delete collection
- actExportTracklist, //!< export track list into a *.m3u file
+ actExportItemlist, //!< export track list into a *.m3u file
actAddCollEntry,
actRemoveCollEntry,
actAddThisToCollection, //!< add selected item to default collection
@@ -109,7 +109,7 @@ class mgAction
* 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 mgSelItem& item=zeroitem)
+ virtual const char *MenuName (const unsigned int idx=0,const mgListItem& item=zeroitem)
{
return strdup(ButtonName());
}
diff --git a/vdr_decoder.c b/vdr_decoder.c
index 0d7bbf1..7a1e00a 100644
--- a/vdr_decoder.c
+++ b/vdr_decoder.c
@@ -93,21 +93,6 @@ mgDecoders::findDecoder (mgContentItem * item)
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;
- }
-
switch (getMediaType (filename))
{
case MT_MP3:
diff --git a/vdr_menu.c b/vdr_menu.c
index 615d77e..b683e0b 100644
--- a/vdr_menu.c
+++ b/vdr_menu.c
@@ -22,24 +22,22 @@
#include <plugin.h>
#if VDRVERSNUM >= 10307
-#include <vdr/interface.h>
-#include <vdr/skins.h>
+#include <interface.h>
+#include <skins.h>
#endif
#include "vdr_setup.h"
#include "vdr_menu.h"
#include "vdr_player.h"
+
+#include "mg_incremental_search.h"
+#include "mg_selection.h"
+
#include "i18n.h"
#define DEBUG
#include "mg_tools.h"
-#include <config.h>
-#if VDRVERSNUM >= 10307
-#include <vdr/interface.h>
-#include <vdr/skins.h>
-#endif
-
void
mgStatus::OsdCurrentItem(const char* Text)
{
@@ -55,7 +53,8 @@ mgStatus::OsdCurrentItem(const char* Text)
void Play(mgSelection *sel,const bool select) {
mgSelection *s = new mgSelection(sel);
if (select) s->select();
- if (s->empty())
+ s->skipItems(0); // make sure we start with a valid item
+ if (s->empty()) // no valid item exists
{
delete s;
return;
@@ -346,9 +345,6 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("",25)
m_root->CollGreenAction = mgActions(nmain.getuint("CollGreenAction"));
m_root->CollYellowAction = mgActions(nmain.getuint("CollYellowAction"));
AddMenu (m_root,posi);
-
- //SetCurrent (Get (posi));
-
forcerefresh = false;
}
@@ -414,13 +410,13 @@ mgMainMenu::LoadExternalCommands()
#if VDRVERSNUM >= 10318
cString cmd_file = AddDirectory (cPlugin::ConfigDirectory ("muggle"),
"playlist_commands.conf");
- mgDebug (1, "mgMuggle::Start: 10318 Looking for file %s", *cmd_file);
+ mgDebug (1, "mgMuggle::Start: %d Looking for file %s",VDRVERSNUM, *cmd_file);
bool have_cmd_file = external_commands->Load (*cmd_file);
#else
const char *
cmd_file = (const char *) AddDirectory (cPlugin::ConfigDirectory ("muggle"),
"playlist_commands.conf");
- mgDebug (1, "mgMuggle::Start: 10317 Looking for file %s", cmd_file);
+ mgDebug (1, "mgMuggle::Start: %d Looking for file %s",VDRVERSNUM, cmd_file);
bool have_cmd_file = external_commands->Load ((const char *) cmd_file);
#endif
@@ -495,20 +491,20 @@ mgMainMenu::AddOrderActions(mgMenu* m)
void
mgMenu::AddSelectionItems (mgSelection *sel,mgActions act)
{
- for (unsigned int i = 0; i < sel->items.size (); i++)
+ for (unsigned int i = 0; i < sel->listitems.size (); i++)
{
mgAction *a = GenerateAction(act, actEntry);
if (!a) continue;
- const char *name = a->MenuName(i+1,sel->items[i]);
+ const char *name = a->MenuName(i+1,sel->listitems[i]);
// add incremental filter here
#if 0
// example:
if (name[0]!='C')
continue;
-#endif
// adapt newposition since it refers to position in mgSelection:
if ((signed int)i==osd()->newposition)
osd()->newposition = osd()->Count();
+#endif
a->SetText(name,false);
a->setHandle(i);
osd()->AddItem(a);
@@ -564,18 +560,25 @@ mgMenu::SetHelpKeys(mgActions on)
void
-mgMenu::InitOsd (const char *title,const bool hashotkeys)
+mgMainMenu::RefreshTitle()
{
- osd ()->InitOsd (title,hashotkeys);
+ SetTitle(Menus.back()->Title().c_str());
+ Display ();
+}
+
+void
+mgMenu::InitOsd (const bool hashotkeys)
+{
+ osd ()->InitOsd (Title(),hashotkeys);
SetHelpKeys(); // Default, will be overridden by the single items
}
void
-mgMainMenu::InitOsd (const char *title,const bool hashotkeys)
+mgMainMenu::InitOsd (string title,const bool hashotkeys)
{
Clear ();
- SetTitle (title);
+ SetTitle (title.c_str());
if (hashotkeys) SetHasHotkeys ();
}
@@ -588,13 +591,19 @@ mgMainMenu::AddItem(mgAction *a)
Add(c);
}
-void
-mgSubmenu::BuildOsd ()
+string
+mgSubmenu::Title() const
{
static char b[100];
snprintf(b,99,tr("Commands:%s"),trim(osd()->selection()->getCurrentValue()).c_str());
+ return b;
+}
+
+void
+mgSubmenu::BuildOsd ()
+{
mgActions on = osd()->CurrentType();
- InitOsd (b);
+ InitOsd ();
if (!osd ()->Parent ())
return;
AddAction(actInstantPlay,on);
@@ -606,7 +615,7 @@ mgSubmenu::BuildOsd ()
AddAction(actDeleteCollection,on);
AddAction(actClearCollection,on);
AddAction(actChooseOrder,on);
- AddAction(actExportTracklist,on);
+ AddAction(actExportItemlist,on);
cCommand *command;
if (osd()->external_commands)
{
@@ -673,8 +682,10 @@ mgMenu::ExecuteButton(eKeys key)
mgTree::mgTree()
{
- TreeBlueAction = actShowCommands;
- CollBlueAction = actShowCommands;
+ TreeBlueAction = actShowCommands;
+ CollBlueAction = actShowCommands;
+ m_incsearch = NULL;
+ m_start_position = 0;
}
eOSState
@@ -683,24 +694,122 @@ mgMenu::Process (eKeys key)
return ExecuteButton(key);
}
-eOSState
-mgTree::Process (eKeys key)
+void
+mgTree::UpdateSearchPosition()
{
- eOSState result = osUnknown;
- if (key!=kNone)
- mgDebug(1,"mgTree::Process(%d)",key);
- switch (key)
+ int position = -1;
+ if( !m_incsearch || m_filter.empty() )
+ position = m_start_position;
+ else
+ {
+ // find the first item starting with m_filter
+ mgSelection::mgListItems& listitems = osd()->selection()->listitems;
+ for (unsigned int idx = 0 ; idx < listitems.size(); idx++)
+ if( strncasecmp( listitems[idx].value().c_str(), m_filter.c_str(), m_filter.size() )>=0 )
+ {
+ position = idx;
+ break;
+ }
+ }
+ osd()->newposition = position;
+ osd()->DisplayGoto();
+}
+
+bool
+mgTree::UpdateIncrementalSearch( eKeys key )
+{
+ bool result; // false if no search active and keystroke was not used
+
+ if( !m_incsearch )
+ {
+ switch( key )
{
- case k0:mgDebug(1,"ich bin k0");break;
- default: result = mgMenu::Process(key);
+ case k0...k9:
+ { // create a new search object as this is the first keystroke
+ m_incsearch = new mgIncrementalSearch();
+
+ // remember the position where we started to search
+ m_start_position = osd()->Current();
+
+ // interprete this keystroke
+ m_filter = m_incsearch->KeyStroke( key - k0 );
+ result = true;
+ UpdateSearchPosition();
+ } break;
+ default:
+ {
+ result = false;
+ }
}
- return result;
+ }
+ else
+ { // an incremental search is already active
+ switch( key )
+ {
+ case kBack:
+ {
+ m_filter = m_incsearch->Backspace();
+
+ if( m_filter.empty() )
+ { // search should be terminated, returning to the previous item
+ TerminateIncrementalSearch( false );
+ }
+ else
+ { // just find the first item for the current search string
+ UpdateSearchPosition();
+ }
+ result = true;
+ } break;
+ case k0...k9:
+ {
+ // evaluate the keystroke
+ m_filter = m_incsearch->KeyStroke( key - k0 );
+ result = true;
+ UpdateSearchPosition();
+ } break;
+ default:
+ {
+ result = false;
+ }
+ }
+ }
+ return result;
+}
+
+void mgTree::TerminateIncrementalSearch( bool remain_on_current )
+{
+ if( m_incsearch )
+ {
+ m_filter = "";
+ delete m_incsearch;
+ m_incsearch = NULL;
+
+ if( remain_on_current )
+ {
+ m_start_position = osd()->Current();
+ }
+
+ UpdateSearchPosition();
+ }
+}
+
+string
+mgTree::Title () const
+{
+ string title = selection ()->getListname ();
+
+ if( !m_filter.empty() )
+ {
+ title += " (" + m_filter + ")";
+ }
+
+ return title;
}
void
mgTree::BuildOsd ()
{
- InitOsd (selection ()->getListname ().c_str (), false);
+ InitOsd (false);
AddSelectionItems (selection());
}
@@ -715,7 +824,6 @@ mgMainMenu::Message1(const char *msg, const char *arg1)
eOSState mgMainMenu::ProcessKey (eKeys key)
{
eOSState result = osContinue;
-
if (Menus.size()<1)
mgError("mgMainMenu::ProcessKey: Menus is empty");
@@ -736,63 +844,65 @@ eOSState mgMainMenu::ProcessKey (eKeys key)
}
else
{
- switch (key)
- {
- case kPause:
- c->Pause ();
- break;
- case kStop:
- if (instant_playing && queue_playing) {
- PlayQueue();
- }
- else
- {
- queue_playing = false;
- c->Stop ();
- }
- break;
- case kChanUp:
- c->Forward ();
- break;
- case kChanDn:
- c->Backward ();
- break;
- default:
- goto otherkeys;
+ switch (key)
+ {
+ case kPause:
+ c->Pause ();
+ break;
+ case kStop:
+ if (instant_playing && queue_playing)
+ {
+ PlayQueue();
+ }
+ else
+ {
+ queue_playing = false;
+ c->Stop ();
+ }
+ break;
+ case kChanUp:
+ c->Forward ();
+ break;
+ case kChanDn:
+ c->Backward ();
+ break;
+ default:
+ goto otherkeys;
}
goto pr_exit;
}
}
else
- if (key==kPlay) {
- PlayQueue();
- goto pr_exit;
- }
+ if (key==kPlay)
+ {
+ PlayQueue();
+ goto pr_exit;
+ }
otherkeys:
newmenu = Menus.back(); // Default: Stay in current menu
newposition = -1;
-
+
{
- mgMenu * oldmenu = newmenu;
-
-// item specific key logic:
- result = cOsdMenu::ProcessKey (key);
-
-// mgMenu specific key logic:
- if (result == osUnknown)
- result = oldmenu->Process (key);
+ mgMenu * oldmenu = newmenu;
+
+ // item specific key logic:
+ result = cOsdMenu::ProcessKey (key);
+
+ // mgMenu specific key logic:
+ if (result == osUnknown)
+ result = oldmenu->Process (key);
}
-// catch osBack for empty OSD lists . This should only happen for playlistitems
-// (because if the list was empty, no mgActions::ProcessKey was ever called)
+ // catch osBack for empty OSD lists . This should only happen for playlistitems
+ // (because if the list was empty, no mgActions::ProcessKey was ever called)
if (result == osBack)
{
- // do as if there was an entry
- mgAction *a = Menus.back()->GenerateAction(actEntry,actEntry);
- if (a)
- {
- result = a->Back();
- delete a;
- }
+ // do as if there was an entry
+ mgAction *a = Menus.back()->GenerateAction(actEntry,actEntry);
+ if (a)
+ {
+ result = a->Back();
+ delete a;
+ }
}
// do nothing for unknown keys:
@@ -873,13 +983,13 @@ showimportcount(unsigned int count,bool final=false)
char b[100];
if (final)
{
- sprintf(b,tr("Import done:Imported %d tracks"),count);
+ sprintf(b,tr("Import done:Imported %d items"),count);
assert(strlen(b)<100);
showmessage(b,1);
}
else
{
- sprintf(b,tr("Imported %d tracks..."),count);
+ sprintf(b,tr("Imported %d items..."),count);
assert(strlen(b)<100);
showmessage(b);
}
@@ -902,6 +1012,7 @@ mgMenu::setosd(mgMainMenu *osd)
{
m_osd = osd;
m_prevUsingCollection = osd->UsingCollection;
+ m_prevpos=osd->selection()->getPosition();
}
mgSubmenu::mgSubmenu()
@@ -910,6 +1021,11 @@ mgSubmenu::mgSubmenu()
CollBlueAction = actShowList;
}
+string
+mgMenuOrders::Title() const
+{
+ return tr("Select an order");
+}
void
mgMenuOrders::BuildOsd ()
@@ -917,7 +1033,7 @@ mgMenuOrders::BuildOsd ()
TreeRedAction = actEditOrder;
TreeGreenAction = actCreateOrder;
TreeYellowAction = actDeleteOrder;
- InitOsd (tr ("Select an order"));
+ InitOsd ();
osd()->AddOrderActions(this);
}
@@ -932,6 +1048,12 @@ mgMenuOrder::~mgMenuOrder()
delete m_order;
}
+string
+mgMenuOrder::Title() const
+{
+ return m_order->Name();
+}
+
void
mgMenuOrder::BuildOsd ()
{
@@ -940,17 +1062,16 @@ mgMenuOrder::BuildOsd ()
m_order = new mgOrder;
*m_order = *(osd()->getOrder(getParentIndex()));
}
- InitOsd (m_order->Name().c_str());
+ InitOsd ();
m_keytypes.clear();
m_keytypes.reserve(mgKeyTypesNr+1);
m_keynames.clear();
m_keynames.reserve(50);
m_orderbycount = m_order->getOrderByCount();
- mgDebug(1,"m_orderbycount wird %d",m_orderbycount);
for (unsigned int i=0;i<m_order->size();i++)
{
unsigned int kt;
- m_keynames.push_back(selection()->choices(m_order,i,&kt));
+ m_keynames.push_back(m_order->Choices(i,&kt));
m_keytypes.push_back(kt);
char buf[20];
sprintf(buf,tr("Key %d"),i+1);
@@ -972,7 +1093,6 @@ mgMenuOrder::ChangeOrder(eKeys key)
newtypes.push_back(ktValue(m_keynames[i][m_keytypes[i]]));
mgOrder n = mgOrder(newtypes);
n.setOrderByCount(m_orderbycount);
- mgDebug(1,"m_orderbycount %d nach n",m_orderbycount);
bool result = !(n == *m_order);
*m_order = n;
if (result)
@@ -1003,6 +1123,13 @@ mgTreeCollSelector::mgTreeCollSelector()
mgTreeCollSelector::~mgTreeCollSelector()
{
osd()->UsingCollection = m_prevUsingCollection;
+ osd()->newposition = m_prevpos;
+}
+
+string
+mgTreeCollSelector::Title () const
+{
+ return m_title;
}
void
@@ -1010,7 +1137,7 @@ mgTreeCollSelector::BuildOsd ()
{
osd()->UsingCollection = true;
mgSelection *coll = osd()->collselection();
- InitOsd (m_title.c_str());
+ InitOsd ();
coll->leave_all();
coll->setPosition(osd()->default_collection);
AddSelectionItems (coll,coll_action());
@@ -1043,7 +1170,6 @@ mgMainMenu::DisplayGoto ()
Display ();
}
-
void
mgMenu::Display ()
{
diff --git a/vdr_menu.h b/vdr_menu.h
index ede022b..a46b4b8 100644
--- a/vdr_menu.h
+++ b/vdr_menu.h
@@ -14,7 +14,7 @@
#define _VDR_MENU_H
#include <string>
-#include <list>
+// #include <list>
#include <vector>
#include <osd.h>
@@ -24,8 +24,6 @@
#include "vdr_player.h"
-using namespace std;
-
//! \brief play a selection, aborting what is currently played
//! \param select if true, play only what the current position selects
void Play(mgSelection *sel,const bool select=false);
@@ -38,6 +36,7 @@ class cCommands;
class mgSelection;
class mgMenu;
class mgMainMenu;
+class mgIncrementalSearch;
//! \brief if a player is running, return it
mgPlayerControl * PlayerControl ();
@@ -145,7 +144,7 @@ class mgMainMenu:public cOsdMenu
}
//! \brief this is the collection things will be added to
- string default_collection;
+ std::string default_collection;
/*! \brief this is the "now playing" collection translated in
* the current language. When changing the OSD language, this
@@ -154,7 +153,7 @@ class mgMainMenu:public cOsdMenu
* previous language will stay, so the user can copy from the
* old one to the new one.
*/
- string play_collection;
+ std::string play_collection;
/*! \brief selects a certain line on the OSD and displays the OSD
*/
@@ -177,13 +176,13 @@ class mgMainMenu:public cOsdMenu
// because that might do forcerefresh which overwrites the message
void Message (const char *msg) { m_message = strdup(msg); }
void Message1 (const char *msg, const char *arg1);
- void Message1 (const char *msg, string arg1) { Message1(msg,arg1.c_str()); }
+ void Message1 (const char *msg, std::string arg1) { Message1(msg,arg1.c_str()); }
//! \brief Actions can request a new position. -1 means none wanted
int newposition;
//! \brief clears the screen, sets a title and the hotkey flag
- void InitOsd (const char *title,const bool hashotkeys);
+ void InitOsd (std::string title,const bool hashotkeys);
#if VDRVERSNUM >= 10307
//! \brief expose the protected DisplayMenu() from cOsdMenu
@@ -200,7 +199,7 @@ class mgMainMenu:public cOsdMenu
}
//! \brief the current selection
- mgSelection* selection ()
+ mgSelection* selection () const
{
if (UsingCollection)
return m_collectionsel;
@@ -209,13 +208,13 @@ class mgMainMenu:public cOsdMenu
}
//! \brief the collection selection
- mgSelection* collselection()
+ mgSelection* collselection() const
{
return m_collectionsel;
}
//! \brief the "now playing" selection
- mgSelection* playselection ()
+ mgSelection* playselection () const
{
return m_playsel;
}
@@ -227,13 +226,15 @@ class mgMainMenu:public cOsdMenu
bool DefaultCollectionSelected();
//! \brief true if the cursor is placed in the default collection
- bool CollectionEntered(string name);
+ bool CollectionEntered(std::string name);
void AddItem(mgAction *a);
- void CollectionChanged(string name);
+ void CollectionChanged(std::string name);
void CloseMenu();
+
+ void RefreshTitle();
};
//! \brief a generic muggle menu
@@ -243,6 +244,7 @@ class mgMenu
mgMainMenu* m_osd;
const char *HKey(const mgActions act,mgActions on);
protected:
+ unsigned int m_prevpos;
bool m_prevUsingCollection;
eOSState ExecuteButton(eKeys key);
//! \brief adds the wanted action to the OSD menu
@@ -258,7 +260,7 @@ class mgMenu
void AddSelectionItems (mgSelection *sel,mgActions act = actEntry);
//! \brief the name of the blue button depends of where we are
int m_parent_index;
- string m_parent_name;
+ std::string m_parent_name;
public:
/*! sets the correct help keys.
* \todo without data from mysql, no key is shown,
@@ -276,22 +278,22 @@ class mgMenu
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; }
+ void setParentName(std::string name) { m_parent_name = name; }
+ std::string getParentName() { return m_parent_name; }
//! \brief the pointer to the owning mgMainMenu
- mgMainMenu* osd ()
+ mgMainMenu* osd () const
{
return m_osd;
}
//! \brief the currently active selection of the owning mgMainMenu
- mgSelection* selection ()
+ mgSelection* selection () const
{
return osd ()->selection ();
}
//! \brief the playselection of the owning mgMainMenu
- mgSelection* playselection ()
+ mgSelection* playselection () const
{
return osd ()->playselection ();
}
@@ -302,8 +304,11 @@ class mgMenu
{
}
+//! \brief computes the title
+ virtual std::string Title() const = 0;
+
//! \brief clears the screen, sets a title and the hotkey flag
- void InitOsd (const char *title,const bool hashotkeys=true);
+ void InitOsd (const bool hashotkeys=true);
//! \brief display OSD and go to osd()->newposition
void Display ();
@@ -342,10 +347,28 @@ class mgMenu
class mgTree:public mgMenu
{
public:
+
mgTree();
- virtual eOSState Process (eKeys Key);
+
+ //! \brief computes the title
+ std::string Title() const;
+
+ bool UpdateIncrementalSearch( eKeys key );
+
+ void TerminateIncrementalSearch( bool remain_on_current );
+
protected:
void BuildOsd ();
+
+ private:
+
+ void UpdateSearchPosition();
+
+ mgIncrementalSearch *m_incsearch;
+
+ std::string m_filter;
+
+ int m_start_position;
};
//! \brief an mgMenu class for submenus
@@ -353,6 +376,8 @@ class mgSubmenu:public mgMenu
{
public:
mgSubmenu::mgSubmenu();
+//! \brief computes the title
+ std::string Title() const;
protected:
void BuildOsd ();
};
@@ -360,6 +385,9 @@ class mgSubmenu:public mgMenu
//! \brief an mgMenu class for selecting an order
class mgMenuOrders:public mgMenu
{
+ public:
+//! \brief computes the title
+ std::string Title() const;
protected:
void BuildOsd ();
};
@@ -369,6 +397,8 @@ class mgMenuOrder : public mgMenu
public:
mgMenuOrder();
~mgMenuOrder();
+//! \brief computes the title
+ std::string Title() const;
bool ChangeOrder(eKeys key);
void SaveOrder();
protected:
@@ -387,16 +417,19 @@ class mgTreeCollSelector:public mgMenu
public:
mgTreeCollSelector();
~mgTreeCollSelector();
+//! \brief computes the title
+ std::string Title() const;
protected:
void BuildOsd ();
virtual mgActions coll_action() = 0;
- string m_title;
+ std::string m_title;
};
class mgTreeAddToCollSelector:public mgTreeCollSelector
{
public:
- mgTreeAddToCollSelector(string title);
+//! \brief computes the title
+ mgTreeAddToCollSelector(std::string title);
protected:
virtual mgActions coll_action() { return actAddCollEntry; }
};
@@ -405,7 +438,8 @@ class mgTreeAddToCollSelector:public mgTreeCollSelector
class mgTreeRemoveFromCollSelector:public mgTreeCollSelector
{
public:
- mgTreeRemoveFromCollSelector(string title);
+//! \brief computes the title
+ mgTreeRemoveFromCollSelector(std::string title);
protected:
virtual mgActions coll_action() { return actRemoveCollEntry; }
};
diff --git a/vdr_player.c b/vdr_player.c
index 01e7337..e31b74a 100644
--- a/vdr_player.c
+++ b/vdr_player.c
@@ -167,15 +167,13 @@ class mgPCMPlayer:public cPlayer, cThread
int m_index;
void Empty ();
- bool SkipFile (int step=0);
+ bool SkipFile (bool skipforward=true);
void PlayTrack();
void StopPlay ();
void SetPlayMode (ePlayMode mode);
void WaitPlayMode (ePlayMode mode, bool inv);
- int skip_direction;
-
protected:
virtual void Activate (bool On);
virtual void Action (void);
@@ -235,7 +233,6 @@ pmAudioOnlyBlack)
m_index = 0;
m_playing = 0;
m_current = 0;
- skip_direction = 1;
}
@@ -250,7 +247,7 @@ mgPCMPlayer::~mgPCMPlayer ()
void
mgPCMPlayer::PlayTrack()
{
- mgContentItem * newcurr = m_playlist->getCurrentTrack ();
+ mgContentItem * newcurr = m_playlist->getCurrentItem ();
if (newcurr)
{
delete m_current;
@@ -303,7 +300,7 @@ mgPCMPlayer::ReloadPlaylist()
m_playlist->clearCache();
if (!m_playing)
{
- SkipFile(1);
+ SkipFile();
Play ();
}
Unlock ();
@@ -772,16 +769,16 @@ mgPCMPlayer::StopPlay ()
}
-bool mgPCMPlayer::SkipFile (int step)
+bool mgPCMPlayer::SkipFile (bool skipforward)
{
MGLOG("mgPCMPlayer::SkipFile");
- mgDebug(1,"SkipFile:step=%d, skip_direction=%d",step,skip_direction);
mgContentItem * newcurr = NULL;
- if (step!=0)
- skip_direction=step;
- if (m_playlist->skipTracks (skip_direction))
+ int skip_direction=1;
+ if (!skipforward)
+ skip_direction=-1;
+ if (m_playlist->skipItems (skip_direction))
{
- newcurr = m_playlist->getCurrentTrack ();
+ newcurr = m_playlist->getCurrentItem ();
if (newcurr) {
delete m_current;
m_current = new mgContentItem(newcurr);
@@ -848,7 +845,7 @@ mgPCMPlayer::Forward ()
MGLOG ("mgPCMPlayer::Forward");
Lock ();
- if (SkipFile (1))
+ if (SkipFile ())
{
StopPlay ();
Play ();
@@ -862,7 +859,7 @@ mgPCMPlayer::Backward (void)
{
MGLOG ("mgPCMPlayer::Backward");
Lock ();
- if (SkipFile (-1))
+ if (SkipFile (false))
{
StopPlay ();
Play ();
@@ -874,8 +871,8 @@ mgPCMPlayer::Backward (void)
void
mgPCMPlayer::Goto (int index, bool still)
{
- m_playlist->setTrackPosition (index - 1);
- mgContentItem *next = m_playlist->getCurrentTrack ();
+ m_playlist->GotoItemPosition (index - 1);
+ mgContentItem *next = m_playlist->getCurrentItem ();
if (next)
{
@@ -1397,10 +1394,10 @@ mgPlayerControl::ShowContents ()
m_menu->SetTitle ("Now playing");
m_menu->SetTabs (25);
- int cur = list->getTrackPosition ();
+ int cur = list->getItemPosition ();
for (int i = 0; i < num_items; i++)
{
- mgContentItem *item = list->getTrack (cur - 3 + i);
+ mgContentItem *item = list->getItem (cur - 3 + i);
if (item)
{
char *buf;
@@ -1437,7 +1434,7 @@ mgPlayerControl::ShowProgress ()
total_frames = SecondsToFrames (list->getLength ());
current_frame += SecondsToFrames (list->getCompletedLength ());
asprintf (&buf, "%s (%d/%d)", list->getListname ().c_str (),
- list->getTrackPosition () + 1, list->getNumTracks ());
+ list->getItemPosition () + 1, list->getNumItems ());
}
}
else
@@ -1767,8 +1764,8 @@ mgPlayerControl::StatusMsgReplaying ()
asprintf (&szBuf, "[%c%c] (%d/%d) %s - %s",
cLoopMode,
cShuffle,
- player->getPlaylist ()->getTrackPosition () + 1,
- player->getPlaylist ()->getNumTracks (),
+ player->getPlaylist ()->getItemPosition () + 1,
+ player->getPlaylist ()->getNumItems (),
player->getCurrent ()->getArtist ().c_str (),
player->getCurrent ()->getTitle ().c_str ());
}
@@ -1777,8 +1774,8 @@ mgPlayerControl::StatusMsgReplaying ()
asprintf (&szBuf, "[%c%c] (%d/%d) %s",
cLoopMode,
cShuffle,
- player->getPlaylist ()->getTrackPosition () + 1,
- player->getPlaylist ()->getNumTracks (),
+ player->getPlaylist ()->getItemPosition () + 1,
+ player->getPlaylist ()->getNumItems (),
player->getCurrent ()->getTitle ().c_str ());
}
}
diff --git a/vdr_player.h b/vdr_player.h
index 5d60344..fd89db5 100644
--- a/vdr_player.h
+++ b/vdr_player.h
@@ -103,6 +103,7 @@ class mgPlayerControl:public cControl
*
* \param index - the position in the playlist to skip to
* \param still - currently unused
+ * \todo Goto is currently unused and has an obvious memory leak
*/
void Goto (int index, bool still = false);
diff --git a/vdr_setup.c b/vdr_setup.c
index df10c0d..d8facbe 100644
--- a/vdr_setup.c
+++ b/vdr_setup.c
@@ -28,37 +28,35 @@
mgMenuSetup::mgMenuSetup ()
{
- m_data = the_setup;
-
SetSection (tr ("Muggle"));
Add (new
cMenuEditBoolItem (tr ("Setup.Muggle$Initial loop mode"),
- &m_data.InitLoopMode));
+ &the_setup.InitLoopMode));
Add (new
cMenuEditBoolItem (tr ("Setup.Muggle$Initial shuffle mode"),
- &m_data.InitShuffleMode));
+ &the_setup.InitShuffleMode));
Add (new
- cMenuEditBoolItem (tr ("Setup.Muggle$Audio mode"), &m_data.AudioMode,
+ cMenuEditBoolItem (tr ("Setup.Muggle$Audio mode"), &the_setup.AudioMode,
tr ("Round"), tr ("Dither")));
Add (new
cMenuEditBoolItem (tr ("Setup.Muggle$Use 48kHz mode only"),
- &m_data.Only48kHz));
+ &the_setup.Only48kHz));
Add (new
cMenuEditIntItem (tr ("Setup.Muggle$Display mode"),
- &m_data.DisplayMode, 1, 3));
+ &the_setup.DisplayMode, 1, 3));
Add (new
cMenuEditBoolItem (tr ("Setup.Muggle$Background mode"),
- &m_data.BackgrMode, tr ("Black"), tr ("Live")));
+ &the_setup.BackgrMode, tr ("Black"), tr ("Live")));
Add (new
cMenuEditIntItem (tr ("Setup.Muggle$Normalizer level"),
- &m_data.TargetLevel, 0, MAX_TARGET_LEVEL));
+ &the_setup.TargetLevel, 0, MAX_TARGET_LEVEL));
Add (new
cMenuEditIntItem (tr ("Setup.Muggle$Limiter level"),
- &m_data.LimiterLevel, MIN_LIMITER_LEVEL, 100));
+ &the_setup.LimiterLevel, MIN_LIMITER_LEVEL, 100));
Add (new
cMenuEditBoolItem (tr ("Setup.Muggle$Delete stale references"),
- &m_data.DeleteStaleReferences));
+ &the_setup.DeleteStaleReferences));
mgAction *a = actGenerate(actSync);
@@ -72,8 +70,6 @@ mgMenuSetup::mgMenuSetup ()
void
mgMenuSetup::Store (void)
{
- the_setup = m_data;
-
SetupStore ("InitLoopMode", the_setup.InitLoopMode);
SetupStore ("InitShuffleMode", the_setup.InitShuffleMode);
SetupStore ("AudioMode", the_setup.AudioMode);
diff --git a/vdr_setup.h b/vdr_setup.h
index cab2ff8..9d4b380 100644
--- a/vdr_setup.h
+++ b/vdr_setup.h
@@ -29,8 +29,6 @@
*/
class mgMenuSetup : public cMenuSetupPage
{
- private:
- mgSetup m_data;
protected:
virtual void Store ();
public: