changeset 23393:a5e55cb59bbc

Rework the property API to allow sub properties such as metadata/title, etc.
author albeu
date Tue, 29 May 2007 21:49:39 +0000
parents 85c5ea3543ff
children d4e8613ddc95
files command.c libmenu/menu_param.c m_property.c m_property.h
diffstat 4 files changed, 145 insertions(+), 120 deletions(-) [+]
line wrap: on
line diff
--- a/command.c	Tue May 29 18:52:20 2007 +0000
+++ b/command.c	Tue May 29 21:49:39 2007 +0000
@@ -1410,17 +1410,17 @@
 };
 
 
-m_option_t *mp_property_find(const char *name)
+int mp_property_do(const char *name, int action, void *val, void *ctx)
 {
-    return m_option_list_find(mp_properties, name);
+    return m_property_do(mp_properties, name, action, val, ctx);
 }
 
-int mp_property_do(const char *name, int action, void *val, void *ctx)
+char* mp_property_print(const char *name, void* ctx)
 {
-    m_option_t *p = mp_property_find(name);
-    if (!p)
-	return M_PROPERTY_UNAVAILABLE;
-    return m_property_do(p, action, val, ctx);
+    char* ret = NULL;
+    if(mp_property_do(name,M_PROPERTY_PRINT,&ret,ctx) <= 0)
+        return NULL;
+    return ret;
 }
 
 char *property_expand_string(MPContext * mpctx, char *str)
@@ -1511,43 +1511,42 @@
 static int set_property_command(MPContext * mpctx, mp_cmd_t * cmd)
 {
     int i, r;
-    m_option_t *prop;
+    m_option_t* prop;
+    const char *pname;
 
     // look for the command
     for (i = 0; set_prop_cmd[i].name; i++)
 	if (set_prop_cmd[i].cmd == cmd->id)
 	    break;
-    if (!set_prop_cmd[i].name)
+    if (!(pname = set_prop_cmd[i].name))
 	return 0;
 
-    // get the property
-    prop = mp_property_find(set_prop_cmd[i].name);
-    if (!prop)
-	return 0;
+    if (mp_property_do(pname,M_PROPERTY_GET_TYPE,&prop,mpctx) <= 0 || !prop)
+        return 0;
 
     // toggle command
     if (set_prop_cmd[i].toggle) {
 	// set to value
 	if (cmd->nargs > 0 && cmd->args[0].v.i >= prop->min)
-	    r = m_property_do(prop, M_PROPERTY_SET, &cmd->args[0].v.i, mpctx);
+	    r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v.i, mpctx);
 	else
-	    r = m_property_do(prop, M_PROPERTY_STEP_UP, NULL, mpctx);
+	    r = mp_property_do(pname, M_PROPERTY_STEP_UP, NULL, mpctx);
     } else if (cmd->args[1].v.i)	//set
-	r = m_property_do(prop, M_PROPERTY_SET, &cmd->args[0].v, mpctx);
+	r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v, mpctx);
     else			// adjust
-	r = m_property_do(prop, M_PROPERTY_STEP_UP, &cmd->args[0].v, mpctx);
+	r = mp_property_do(pname, M_PROPERTY_STEP_UP, &cmd->args[0].v, mpctx);
 
     if (r <= 0)
 	return 1;
 
     if (set_prop_cmd[i].osd_progbar) {
 	if (prop->type == CONF_TYPE_INT) {
-	    if (m_property_do(prop, M_PROPERTY_GET, &r, mpctx) > 0)
+	    if (mp_property_do(pname, M_PROPERTY_GET, &r, mpctx) > 0)
 		set_osd_bar(set_prop_cmd[i].osd_progbar,
 			    set_prop_cmd[i].osd_msg, prop->min, prop->max, r);
 	} else if (prop->type == CONF_TYPE_FLOAT) {
 	    float f;
-	    if (m_property_do(prop, M_PROPERTY_GET, &f, mpctx) > 0)
+	    if (mp_property_do(pname, M_PROPERTY_GET, &f, mpctx) > 0)
 		set_osd_bar(set_prop_cmd[i].osd_progbar,
 			    set_prop_cmd[i].osd_msg, prop->min, prop->max, f);
 	} else
@@ -1557,7 +1556,7 @@
     }
 
     if (set_prop_cmd[i].osd_msg) {
-	char *val = m_property_print(prop, mpctx);
+	char *val = mp_property_print(pname, mpctx);
 	if (val) {
 	    set_osd_msg(set_prop_cmd[i].osd_id >=
 			0 ? set_prop_cmd[i].osd_id : OSD_MSG_PROPERTY + i,
@@ -1603,11 +1602,12 @@
 	    break;
 
 	case MP_CMD_SET_PROPERTY:{
-		m_option_t *prop = mp_property_find(cmd->args[0].v.s);
-		if (!prop)
+		int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_PARSE,
+				       cmd->args[1].v.s, mpctx);
+		if (r == M_PROPERTY_UNKNOWN)
 		    mp_msg(MSGT_CPLAYER, MSGL_WARN,
 			   "Unknown property: '%s'\n", cmd->args[0].v.s);
-		else if (m_property_parse(prop, cmd->args[1].v.s, mpctx) <= 0)
+		else if (r <= 0)
 		    mp_msg(MSGT_CPLAYER, MSGL_WARN,
 			   "Failed to set property '%s' to '%s'.\n",
 			   cmd->args[0].v.s, cmd->args[1].v.s);
@@ -1615,14 +1615,14 @@
 	    break;
 
 	case MP_CMD_STEP_PROPERTY:{
-		m_option_t *prop = mp_property_find(cmd->args[0].v.s);
 		float arg = cmd->args[1].v.f;
-		if (!prop)
+		int r = mp_property_do
+			 (cmd->args[0].v.s, M_PROPERTY_STEP_UP,
+			  arg ? &arg : NULL, mpctx);
+		if (r == M_PROPERTY_UNKNOWN)
 		    mp_msg(MSGT_CPLAYER, MSGL_WARN,
 			   "Unknown property: '%s'\n", cmd->args[0].v.s);
-		else if (m_property_do
-			 (prop, M_PROPERTY_STEP_UP,
-			  arg ? &arg : NULL, mpctx) <= 0)
+		else if (r <= 0)
 		    mp_msg(MSGT_CPLAYER, MSGL_WARN,
 			   "Failed to increment property '%s' by %f.\n",
 			   cmd->args[0].v.s, arg);
@@ -1630,30 +1630,14 @@
 	    break;
 
 	case MP_CMD_GET_PROPERTY:{
-		m_option_t *prop;
-		void *val;
 		char *tmp;
-		prop = mp_property_find(cmd->args[0].v.s);
-		if (!prop) {
-		    mp_msg(MSGT_CPLAYER, MSGL_WARN,
-			   "Unknown property: '%s'\n", cmd->args[0].v.s);
-		    break;
-		}
-		/* Use m_option_print directly to get easily parseable values. */
-		val = calloc(1, prop->type->size);
-		if (m_property_do(prop, M_PROPERTY_GET, val, mpctx) <= 0) {
+		if (mp_property_do(cmd->args[0].v.s, M_PROPERTY_TO_STRING,
+				   &tmp, mpctx) <= 0) {
 		    mp_msg(MSGT_CPLAYER, MSGL_WARN,
 			   "Failed to get value of property '%s'.\n",
 			   cmd->args[0].v.s);
 		    break;
 		}
-		tmp = m_option_print(prop, val);
-		if (!tmp || tmp == (char *) -1) {
-		    mp_msg(MSGT_CPLAYER, MSGL_WARN,
-			   "Failed to print value of property '%s'.\n",
-			   cmd->args[0].v.s);
-		    break;
-		}
 		mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_%s=%s\n",
 		       cmd->args[0].v.s, tmp);
 		free(tmp);
--- a/libmenu/menu_param.c	Tue May 29 18:52:20 2007 +0000
+++ b/libmenu/menu_param.c	Tue May 29 21:49:39 2007 +0000
@@ -30,6 +30,7 @@
 struct list_entry_s {
   struct list_entry p;
   char* name;
+  char* prop;
   m_option_t* opt;
   char* menu;
 };
@@ -76,10 +77,8 @@
 #define OPT_INFO_TRACK "track"
 #define OPT_INFO_GENRE "genre"
 
-m_option_t*  mp_property_find(const char* name);
-
 static void entry_set_text(menu_t* menu, list_entry_t* e) {
-  char* val = m_property_print(e->opt, menu->ctx);
+  char* val = mp_property_print(e->prop, menu->ctx);
   int l,edit = (mpriv->edit && e == mpriv->p.current);
   if(!val) {
     if(mpriv->hide_na) {
@@ -99,7 +98,7 @@
 static void update_entries(menu_t* menu) {
   list_entry_t* e;
   for(e = mpriv->p.menu ; e ; e = e->p.next)
-    if(e->opt) entry_set_text(menu,e);
+    if(e->prop) entry_set_text(menu,e);
 }
 
 static int parse_args(menu_t* menu,char* args) {
@@ -195,13 +194,13 @@
     }
     
     name = asx_get_attrib("property",attribs);
-    opt = name ? mp_property_find(name) : NULL;
-    if(!opt) {
+    if(!name || mp_property_do(name,M_PROPERTY_GET_TYPE,&opt,menu->ctx) <= 0) {
       mp_msg(MSGT_OSD_MENU,MSGL_WARN,MSGTR_LIBMENU_PrefMenuEntryDefinitionsNeed,parser->line);
       goto next_element;
     }
     m = calloc(1,sizeof(struct list_entry_s));
     m->opt = opt;
+    m->prop = strdup(name);
     m->name = asx_get_attrib("name",attribs);
     if(!m->name) m->name = strdup(opt->name);
     entry_set_text(menu,m);
@@ -227,22 +226,22 @@
     case MENU_CMD_UP:
       if(!mpriv->edit) break;
     case MENU_CMD_RIGHT:
-      if(m_property_do(e->opt,M_PROPERTY_STEP_UP,NULL,menu->ctx) > 0)
+      if(mp_property_do(e->prop,M_PROPERTY_STEP_UP,NULL,menu->ctx) > 0)
         update_entries(menu);
       return;
     case MENU_CMD_DOWN:
       if(!mpriv->edit) break;
     case MENU_CMD_LEFT:
-      if(m_property_do(e->opt,M_PROPERTY_STEP_DOWN,NULL,menu->ctx) > 0)
+      if(mp_property_do(e->prop,M_PROPERTY_STEP_DOWN,NULL,menu->ctx) > 0)
         update_entries(menu);
       return;
       
     case MENU_CMD_OK:
       // check that the property is writable
-      if(m_property_do(e->opt,M_PROPERTY_SET,NULL,menu->ctx) < 0) return;
+      if(mp_property_do(e->prop,M_PROPERTY_SET,NULL,menu->ctx) < 0) return;
       // shortcut for flags
       if(e->opt->type == CONF_TYPE_FLAG) {
-	if(m_property_do(e->opt,M_PROPERTY_STEP_UP,NULL,menu->ctx) > 0)
+	if(mp_property_do(e->prop,M_PROPERTY_STEP_UP,NULL,menu->ctx) > 0)
           update_entries(menu);
         return;
       }
@@ -291,6 +290,7 @@
 static void free_entry(list_entry_t* entry) {
   free(entry->p.txt);
   if(entry->name) free(entry->name);
+  if(entry->prop) free(entry->prop);
   if(entry->menu) free(entry->menu);
   free(entry);
 }
--- a/m_property.c	Tue May 29 18:52:20 2007 +0000
+++ b/m_property.c	Tue May 29 21:49:39 2007 +0000
@@ -17,55 +17,84 @@
 
 #define ROUND(x) ((int)((x)<0 ? (x)-0.5 : (x)+0.5))
 
-int m_property_do(m_option_t* prop, int action, void* arg, void *ctx) {
+static int do_action(m_option_t* prop_list, const char* name,
+                     int action, void* arg, void *ctx) {
+    const char* sep;
+    m_option_t* prop;
+    m_property_action_t ka;
+    int r;
+    if((sep = strchr(name,'/')) && sep[1]) {
+        int len = sep-name;
+        char base[len+1];
+        memcpy(base,name,len);
+        base[len] = 0;
+        prop = m_option_list_find(prop_list, base);
+        ka.key = sep+1;
+        ka.action = action;
+        ka.arg = arg;
+        action = M_PROPERTY_KEY_ACTION;
+        arg = &ka;
+    } else
+        prop = m_option_list_find(prop_list, name);
     if(!prop) return M_PROPERTY_UNKNOWN;
-    return ((m_property_ctrl_f)prop->p)(prop,action,arg,ctx);
+    r = ((m_property_ctrl_f)prop->p)(prop,action,arg,ctx);
+    if(action == M_PROPERTY_GET_TYPE && r == M_PROPERTY_NOT_IMPLEMENTED) {
+        if(!arg) return M_PROPERTY_ERROR;
+        *(m_option_t**)arg = prop;
+        return M_PROPERTY_OK;
+    }
+    return r;
 }
 
-
-char* m_property_print(m_option_t* prop, void *ctx) {
-    m_property_ctrl_f ctrl;
+int m_property_do(m_option_t* prop_list, const char* name,
+                  int action, void* arg, void *ctx) {
+    m_option_t* opt;
     void* val;
-    char* ret;
-    
-    if(!prop) return NULL;
+    char* str;
+    int r;
 
-    ctrl = prop->p;
-    // look if the property have it's own print func
-    if(ctrl(prop,M_PROPERTY_PRINT,&ret, ctx) >= 0)
-        return ret;
-    // fallback on the default print for this type
-    val = calloc(1,prop->type->size);    
-    if(ctrl(prop,M_PROPERTY_GET,val,ctx) <= 0) {
+    switch(action) {
+    case M_PROPERTY_PRINT:
+        if((r = do_action(prop_list,name,M_PROPERTY_PRINT,arg,ctx)) >= 0)
+            return r;
+        // fallback on the default print for this type
+    case M_PROPERTY_TO_STRING:
+        if((r = do_action(prop_list,name,M_PROPERTY_TO_STRING,arg,ctx)) !=
+           M_PROPERTY_NOT_IMPLEMENTED)
+            return r;
+        // fallback on the options API. Get the type, value and print.
+        if((r = do_action(prop_list,name,M_PROPERTY_GET_TYPE,&opt,ctx)) <= 0)
+            return r;
+        val = calloc(1,opt->type->size);
+        if((r = do_action(prop_list,name,M_PROPERTY_GET,val,ctx)) <= 0) {
+            free(val);
+            return r;
+        }
+        if(!arg) return M_PROPERTY_ERROR;
+        str = m_option_print(opt,val);
         free(val);
-        return NULL;
-    }
-    ret = m_option_print(prop,val);
-    free(val);
-    return ret == (char*)-1 ? NULL : ret;
-}
-
-int m_property_parse(m_option_t* prop, char* txt, void *ctx) {
-    m_property_ctrl_f ctrl;
-    void* val;
-    int r;
-    
-    if(!prop) return M_PROPERTY_UNKNOWN;
-
-    ctrl = prop->p;
-    // try the property own parsing func
-    if((r = ctrl(prop,M_PROPERTY_PARSE,txt,ctx)) !=  M_PROPERTY_NOT_IMPLEMENTED)
-        return r;
-    // fallback on the default
-    val = calloc(1,prop->type->size);
-    if((r = m_option_parse(prop,prop->name,txt,val,M_CONFIG_FILE)) <= 0) {
+        *(char**)arg = str == (char*)-1 ? NULL : str;
+        return str != (char*)-1;
+    case M_PROPERTY_PARSE:
+        // try the property own parsing func
+        if((r = do_action(prop_list,name,M_PROPERTY_PARSE,arg,ctx)) !=
+           M_PROPERTY_NOT_IMPLEMENTED)
+            return r;
+        // fallback on the options API, get the type and parse.
+        if((r = do_action(prop_list,name,M_PROPERTY_GET_TYPE,&opt,ctx)) <= 0)
+            return r;
+        if(!arg) return M_PROPERTY_ERROR;
+        val = calloc(1,opt->type->size);
+        if((r = m_option_parse(opt,opt->name,arg,val,M_CONFIG_FILE)) <= 0) {
+            free(val);
+            return r;
+        }
+        r = do_action(prop_list,name,M_PROPERTY_SET,val,ctx);
+        m_option_free(opt,val);
         free(val);
         return r;
     }
-    r = ctrl(prop,M_PROPERTY_SET,val,ctx);
-    m_option_free(prop,val);
-    free(val);
-    return r;
+    return do_action(prop_list,name,action,arg,ctx);
 }
 
 char* m_properties_expand_string(m_option_t* prop_list,char* str, void *ctx) {
@@ -106,11 +135,10 @@
         } else if(str[0] == '$' && str[1] == '{' && (e = strchr(str+2,'}'))) {
             int pl = e-str-2;
             char pname[pl+1];
-            m_option_t* prop;
             memcpy(pname,str+2,pl);
             pname[pl] = 0;
-            if((prop = m_option_list_find(prop_list,pname)) &&
-               (p = m_property_print(prop, ctx)))
+            if(m_property_do(prop_list, pname,
+                             M_PROPERTY_PRINT, &p, ctx) >= 0 && p)
                 l = strlen(p), fr = 1;
             else
                 l = 0;
@@ -118,13 +146,11 @@
         } else if(str[0] == '?' && str[1] == '(' && (e = strchr(str+2,':'))) {
             int pl = e-str-2;
             char pname[pl+1];
-            m_option_t* prop;
             lvl++;
             if(!skip) {            
                 memcpy(pname,str+2,pl);
                 pname[pl] = 0;
-                if(!(prop = m_option_list_find(prop_list,pname)) ||
-                   m_property_do(prop,M_PROPERTY_GET,NULL, ctx) < 0)
+                if(m_property_do(prop_list,pname,M_PROPERTY_GET,NULL,ctx) < 0)
                     skip = 1, skip_lvl = lvl;
             }
             str = e+1, l = 0;
--- a/m_property.h	Tue May 29 18:52:20 2007 +0000
+++ b/m_property.h	Tue May 29 21:49:39 2007 +0000
@@ -48,6 +48,32 @@
  */
 #define M_PROPERTY_STEP_DOWN   5
 
+/// Get a string containg a parsable representation.
+/** Set the variable to a newly allocated string or NULL.
+ *  \param arg Pointer to a char* variable.
+ */
+#define M_PROPERTY_TO_STRING   6
+
+/// Pass down an action to a sub-property.
+#define M_PROPERTY_KEY_ACTION  7
+
+/// Get a m_option describing the property.
+#define M_PROPERTY_GET_TYPE    8
+
+///@}
+
+/// \defgroup PropertyActionsArg Property actions argument type
+/// \ingroup Properties
+/// \brief  Types used as action argument.
+///@{
+
+/// Argument for \ref M_PROPERTY_KEY_ACTION
+typedef struct {
+    const char* key;
+    int action;
+    void* arg;
+} m_property_action_t;
+
 ///@}
 
 /// \defgroup PropertyActionsReturn Property actions return code
@@ -81,25 +107,14 @@
 typedef int(*m_property_ctrl_f)(m_option_t* prop,int action,void* arg,void *ctx);
 
 /// Do an action on a property.
-/** \param prop The property.
+/** \param prop_list The list of properties.
+ *  \param prop The path of the property.
  *  \param action See \ref PropertyActions.
  *  \param arg Argument, usually a pointer to the data type used by the property.
  *  \return See \ref PropertyActionsReturn.
  */
-int m_property_do(m_option_t* prop, int action, void* arg, void *ctx);
-
-/// Print the current value of a property.
-/** \param prop The property.
- *  \return A newly allocated string with the current value or NULL on error.
- */
-char* m_property_print(m_option_t* prop, void *ctx);
-
-/// Set a property.
-/** \param prop The property.
- *  \param txt The value to set.
- *  \return 1 on success, 0 on error.
- */
-int m_property_parse(m_option_t* prop, char* txt, void *ctx);
+int m_property_do(m_option_t* prop_list, const char* prop,
+                  int action, void* arg, void *ctx);
 
 /// Print a list of properties.
 void m_properties_print_help_list(m_option_t* list);
@@ -119,12 +134,12 @@
 
 // Helpers to use MPlayer's properties
 
-/// Get an MPlayer property.
-m_option_t*  mp_property_find(const char* name);
-
 /// Do an action with an MPlayer property.
 int mp_property_do(const char* name,int action, void* val, void *ctx);
 
+/// Get the value of a property as a string suitable for display in an UI.
+char* mp_property_print(const char *name, void* ctx);
+
 /// \defgroup PropertyImplHelper Property implementation helpers
 /// \ingroup Properties
 /// \brief Helper functions for common property types.