diff options
author | LarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b> | 2005-04-19 18:01:19 +0000 |
---|---|---|
committer | LarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b> | 2005-04-19 18:01:19 +0000 |
commit | e636aa59d86868039f39b0e39e944871cae5b9db (patch) | |
tree | f44f9f196ce7de52a2dacdff3588ec99e5d03e40 | |
parent | 143d3397960c698935869cae65db8d8937e4d22a (diff) | |
download | vdr-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-- | HISTORY | 15 | ||||
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | i18n.c | 31 | ||||
-rw-r--r-- | mg_content.c | 171 | ||||
-rw-r--r-- | mg_content.h | 38 | ||||
-rw-r--r-- | mg_incremental_search.c | 78 | ||||
-rw-r--r-- | mg_incremental_search.h | 36 | ||||
-rw-r--r-- | mg_mysql.c | 38 | ||||
-rw-r--r-- | mg_mysql.h | 2 | ||||
-rw-r--r-- | mg_order.c | 284 | ||||
-rw-r--r-- | mg_order.h | 74 | ||||
-rw-r--r-- | mg_selection.c | 528 | ||||
-rw-r--r-- | mg_selection.h | 140 | ||||
-rw-r--r-- | mg_setup.c | 34 | ||||
-rw-r--r-- | mg_setup.h | 9 | ||||
-rw-r--r-- | mg_sync.c | 40 | ||||
-rw-r--r-- | mg_sync.h | 8 | ||||
-rw-r--r-- | mg_thread_sync.c | 9 | ||||
-rw-r--r-- | mg_tools.c | 37 | ||||
-rw-r--r-- | mg_tools.h | 31 | ||||
-rw-r--r-- | muggle.c | 30 | ||||
-rw-r--r-- | muggle.doxygen | 2 | ||||
-rwxr-xr-x | mugglei.c | 23 | ||||
-rw-r--r-- | vdr_actions.c | 167 | ||||
-rw-r--r-- | vdr_actions.h | 4 | ||||
-rw-r--r-- | vdr_decoder.c | 15 | ||||
-rw-r--r-- | vdr_menu.c | 310 | ||||
-rw-r--r-- | vdr_menu.h | 80 | ||||
-rw-r--r-- | vdr_player.c | 43 | ||||
-rw-r--r-- | vdr_player.h | 1 | ||||
-rw-r--r-- | vdr_setup.c | 22 | ||||
-rw-r--r-- | vdr_setup.h | 2 |
32 files changed, 1344 insertions, 972 deletions
@@ -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 @@ -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) @@ -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 @@ -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()) @@ -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 @@ -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; +} @@ -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 (); @@ -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; +} + @@ -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); }; @@ -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(); } @@ -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(); } - @@ -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); } @@ -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 */ @@ -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. @@ -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: @@ -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 () { @@ -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: |