changeset 7849:3249f64f69b2

Initial low-level support for property listeners. * * * Make listeners work for all properties.
author Michael Goffioul <michael.goffioul@gmail.com>
date Fri, 29 Feb 2008 17:38:32 +0100
parents 6bb2bbc2bf45
children 56254a8d4d59
files src/ChangeLog src/genprops.awk src/graphics.cc src/graphics.h.in
diffstat 4 files changed, 343 insertions(+), 103 deletions(-) [+]
line wrap: on
line diff
--- a/src/ChangeLog	Wed Feb 27 21:45:12 2008 +0100
+++ b/src/ChangeLog	Fri Feb 29 17:38:32 2008 +0100
@@ -27,6 +27,36 @@
 
 2008-06-04  Michael Goffioul <michael.goffioul@gmail.com>
 
+	* graphics.h.in (base_properties::get_property): Make virtual and
+	remove const modifier.
+	* graphics.cc (base_properties::get_property): Can return built-in
+	properties. Generate an error if the property does not exist.
+	(base_properties::add_listener): Use get_property.
+	(Fadd_listener): Add documentation.
+	* genprops.awk (emit_common_declarations, emit_source): Emit code for
+	overloaded get_property method.
+
+	* genprops.awk (emit_declarations): Run listeners in the property
+	setter methods.
+	* graphics.h.in (enum listener_mode): New enum type for listeners.
+	(base_property::set): Make non-virtual and handle listeners execution.
+	(base_property::listeners): New field holding a map of listeners.
+	(base_property::add_listener, base_property::run_listeners,
+	base_property::do_set): New methods to handle listeners.
+	(property::add_listener, property::run_listeners): Likewise.
+	(base_properties::add_listener): Likewise.
+	(base_graphics_object::add_property_listener,
+	graphics_object::add_property_listener): Likewise.
+	(all property classes): Rename set to do_set and make it protected.
+	* graphics.cc (base_property::run_listeners): New method to execute
+	listeners.
+	(color_property::set, double_radio_property::set,
+	handle_property::set): Rename to do_set.
+	(base_properties::add_listener): New method to handle listener
+	additio. Only handle dynamic properties.
+	(Fadd_listener): New octave interface to add property listeners to
+	graphics object.
+
 	* genprops.awk (emit_get_data): Remove.
 	(emit_declarations): Treat row_vector_property as array_property and
 	remove data_property references.
--- a/src/genprops.awk	Wed Feb 27 21:45:12 2008 +0100
+++ b/src/genprops.awk	Fri Feb 29 17:38:32 2008 +0100
@@ -244,6 +244,7 @@
   printf ("  void set (const caseless_str& pname, const octave_value& val);\n\n");
   printf ("  octave_value get (bool all = false) const;\n\n");
   printf ("  octave_value get (const caseless_str& pname) const;\n\n");
+  printf ("  property get_property (const caseless_str& pname);\n\n");
   printf ("  std::string graphics_object_name (void) const { return go_name; }\n\n");
   printf ("  static property_list::pval_map_type factory_defaults (void);\n\n");
   printf ("private:\n  static std::string go_name;\n\n");
@@ -312,14 +313,21 @@
 
       if (emit_set[i] == "definition")
       {
-        printf ("\n  {\n    if (! error_state)\n      {\n        %s = val;\n",
-          name[i]);
+	if (updaters[i] || limits[i] || mode[i])
+	  has_builtin_listeners = 1;
+	else
+	  has_builtin_listeners = 0;
+
+        printf ("\n  {\n    if (! error_state)\n      {\n        %s.set (val, %s);\n",
+          name[i], (has_builtin_listeners ? "false" : "true"));
         if (updater[i])
           printf ("        update_%s ();\n", name[i]);
         if (limits[i])
           printf ("        update_axis_limits (\"%s\");\n", name[i]);
         if (mode[i])
           printf ("        set_%smode (\"manual\");\n", name[i]);
+	if (has_builtin_listeners)
+	  printf ("        %s.run_listeners (POSTSET);\n", name[i]);
         printf ("        mark_modified ();\n      }\n  }\n\n");
       }
       else
@@ -429,6 +437,22 @@
     printf ("  else\n    retval = base_properties::get (pname);\n\n") >> filename;
     printf ("  return retval;\n}\n\n") >> filename;
 
+    ## get_property method
+
+    printf ("property\n%s::properties::get_property (const caseless_str& pname)\n{\n",
+            class_name) >> filename;
+
+    for (i = 1; i<= idx; i++)
+    {
+      printf ("  %sif (pname.compare (\"%s\"))\n",
+              (i > 1 ? "else " : ""), name[i]) >> filename;
+      printf ("    return property (&%s, true);\n", name[i]) >> filename;
+    }
+
+    printf ("  else\n    return base_properties::get_property (pname);\n") >> filename;
+    printf ("}\n\n") >> filename;
+
+
     ## factory defaults method
 
     printf ("property_list::pval_map_type\n%s::properties::factory_defaults (void)\n{\n",
--- a/src/graphics.cc	Wed Feb 27 21:45:12 2008 +0100
+++ b/src/graphics.cc	Fri Feb 29 17:38:32 2008 +0100
@@ -456,6 +456,20 @@
 
 // ---------------------------------------------------------------------
 
+void
+base_property::run_listeners (listener_mode mode)
+{
+  const octave_value_list& l = listeners[mode];
+
+  for (int i = 0; i < l.length (); i++)
+    {
+      execute_callback (l(i), parent, octave_value ());
+
+      if (error_state)
+	break;
+    }
+}
+
 radio_values::radio_values (const std::string& opt_string)
 {
   size_t beg = 0;
@@ -529,7 +543,7 @@
 }
 
 void
-color_property::set (const octave_value& val)
+color_property::do_set (const octave_value& val)
 {
   if (val.is_string ())
     {
@@ -582,7 +596,7 @@
 }
 
 void
-double_radio_property::set (const octave_value& val)
+double_radio_property::do_set (const octave_value& val)
 {
   if (val.is_string ())
     {
@@ -690,7 +704,7 @@
 }
 
 void
-handle_property::set (const octave_value& v)
+handle_property::do_set (const octave_value& v)
 {
   double dv = v.double_value ();
 
@@ -1367,14 +1381,54 @@
 }
 
 property
-base_properties::get_property (const caseless_str& name) const
+base_properties::get_property (const caseless_str& name)
 {
-  std::map<caseless_str, property>::const_iterator it = all_props.find (name);
-
-  if (it == all_props.end ())
-    return property ();
+  if (name.compare ("beingdeleted"))
+    return property (&beingdeleted, true);
+  else if (name.compare ("busyaction"))
+    return property (&busyaction, true);
+  else if (name.compare ("buttondownfcn"))
+    return property (&buttondownfcn, true);
+  else if (name.compare ("clipping"))
+    return property (&clipping, true);
+  else if (name.compare ("createfcn"))
+    return property (&createfcn, true);
+  else if (name.compare ("deletefcn"))
+    return property (&deletefcn, true);
+  else if (name.compare ("handlevisibility"))
+    return property (&handlevisibility, true);
+  else if (name.compare ("hittest"))
+    return property (&hittest, true);
+  else if (name.compare ("interruptible"))
+    return property (&interruptible, true);
+  else if (name.compare ("parent"))
+    return property (&parent, true);
+  else if (name.compare ("selected"))
+    return property (&selected, true);
+  else if (name.compare ("selectionhighlight"))
+    return property (&selectionhighlight, true);
+  else if (name.compare ("tag"))
+    return property (&tag, true);
+  else if (name.compare ("type"))
+    return property (&userdata, true);
+  else if (name.compare ("userdata"))
+    return property (&visible, true);
+  else if (name.compare ("visible"))
+    return property (&visible, true);
+  else if (name.compare ("__modified__"))
+    return property (&__modified__, true);
   else
-    return it->second;
+    {
+      std::map<caseless_str, property>::const_iterator it = all_props.find (name);
+
+      if (it == all_props.end ())
+	{
+	  error ("get_property: unknown property \"%s\"", name.c_str ());
+	  return property ();
+	}
+      else
+	return it->second;
+    }
 }
 
 void
@@ -1494,6 +1548,16 @@
     }
 }
 
+void
+base_properties::add_listener (const caseless_str& nm, const octave_value& v,
+			       listener_mode mode)
+{
+  property p = get_property (nm);
+
+  if (! error_state && p.ok ())
+    p.add_listener (v, mode);
+}
+
 // ---------------------------------------------------------------------
 
 class gnuplot_backend : public base_graphics_backend
@@ -3652,6 +3716,71 @@
   return retval;
 }
 
+DEFUN (add_listener, args, ,
+   "-*- texinfo -*-\n\
+@deftypefn {Built-in Function} {} add_listener (@var{h}, @var{prop}, @var{fcn})\n\
+Register @var{fcn} as listener for the property @var{prop} of the graphics\n\
+object @var{h}. Property listeners are executed (in order of registration)\n\
+when the property is set. The new value is already available when the\n\
+listeners are executed.\n\
+\n\
+@var{prop} must be a string naming a valid property in @var{h}.\n\
+\n\
+@var{fcn} can be a function handle, a string or a cell array whose first\n\
+element is a function handle. If @var{fcn} is a function handle, the\n\
+corresponding function should accept at least 2 arguments, that will be\n\
+set to the object handle and the empty matrix respectively. If @var{fcn}\n\
+is a string, it must be any valid octave expression. If @var{fcn} is a cell\n\
+array, the first element must be a function handle with the same signature\n\
+as described above. The next elements of the cell array are passed\n\
+as additional arguments to the function.\n\
+\n\
+@example\n\
+function my_listener (h, dummy, p1)\n\
+  fprintf (\"my_listener called with p1=%s\\n\", p1);\n\
+endfunction\n\
+\n\
+add_listener (gcf, \"position\", @{@@my_listener, \"my string\"@})\n\
+@end example\n\
+\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  if (args.length () == 3)
+    {
+      double h = args(0).double_value ();
+
+      if (! error_state)
+	{
+	  std::string pname = args(1).string_value ();
+
+	  if (! error_state)
+	    {
+	      graphics_handle gh = gh_manager::lookup (h);
+
+	      if (gh.ok ())
+		{
+		  graphics_object go = gh_manager::get_object (gh);
+
+		  go.add_property_listener (pname, args(2), POSTSET);
+		}
+	      else
+		error ("add_listener: invalid graphics object (= %g)",
+		       h);
+	    }
+	  else
+	    error ("add_listener: invalid property name, expected a string value");
+	}
+      else
+	error ("add_listener: invalid handle");
+    }
+  else
+    print_usage ();
+
+  return retval;
+}
+
 octave_value
 get_property_from_handle (double handle, const std::string& property,
 			  const std::string& func)
--- a/src/graphics.h.in	Wed Feb 27 21:45:12 2008 +0100
+++ b/src/graphics.h.in	Fri Feb 29 17:38:32 2008 +0100
@@ -339,6 +339,8 @@
 
 class property;
 
+enum listener_mode { POSTSET };
+
 class base_property
 {
 public:
@@ -369,8 +371,13 @@
 
   void set_hidden (bool flag) { hidden = flag; }
 
-  virtual void set (const octave_value&)
-    { error ("set: invalid property \"%s\"", name.c_str ()); }
+  void set (const octave_value& v, bool do_run = true)
+    {
+      do_set (v);
+
+      if (do_run && ! error_state)
+	run_listeners (POSTSET);
+    }
 
   virtual octave_value get (void) const
     {
@@ -384,11 +391,29 @@
       return *this;
     }
 
+  void add_listener (const octave_value& v, listener_mode mode = POSTSET)
+    {
+      octave_value_list& l = listeners[mode];
+      l.resize (l.length () + 1, v);
+    }
+
+  OCTINTERP_API void run_listeners (listener_mode mode = POSTSET);
+
+protected:
+  virtual void do_set (const octave_value&)
+    { error ("set: invalid property \"%s\"", name.c_str ()); }
+
+private:
+  typedef std::map<listener_mode, octave_value_list> listener_map;
+  typedef std::map<listener_mode, octave_value_list>::iterator listener_map_iterator;
+  typedef std::map<listener_mode, octave_value_list>::const_iterator listener_map_const_iterator;
+
 private:
   int count;
   std::string name;
   graphics_handle parent;
   bool hidden;
+  listener_map listeners;
 };
 
 // ---------------------------------------------------------------------
@@ -403,15 +428,6 @@
   string_property (const string_property& p)
     : base_property (p), str (p.str) { }
 
-  void set (const octave_value& val)
-    {
-      if (val.is_string ())
-        str = val.string_value ();
-      else
-        error ("set: invalid string property value for \"%s\"",
-               get_name ().c_str ());
-    }
-
   octave_value get (void) const
     { return octave_value (str); }
 
@@ -423,6 +439,16 @@
       return *this;
     }
 
+protected:
+  void do_set (const octave_value& val)
+    {
+      if (val.is_string ())
+        str = val.string_value ();
+      else
+        error ("set: invalid string property value for \"%s\"",
+               get_name ().c_str ());
+    }
+
 private:
   std::string str;
 };
@@ -495,7 +521,21 @@
   radio_property (const radio_property& p)
     : base_property (p), vals (p.vals), current_val (p.current_val) { }
 
-  void set (const octave_value& newval) 
+  octave_value get (void) const { return octave_value (current_val); }
+
+  const std::string& current_value (void) const { return current_val; }
+
+  bool is (const caseless_str& v) const
+    { return v.compare (current_val); }
+
+  radio_property& operator = (const octave_value& val)
+    {
+      set (val);
+      return *this;
+    }
+
+protected:
+  void do_set (const octave_value& newval) 
   {
     if (newval.is_string ())
       {
@@ -511,19 +551,6 @@
           get_name ().c_str ());
   }
 
-  octave_value get (void) const { return octave_value (current_val); }
-
-  const std::string& current_value (void) const { return current_val; }
-
-  bool is (const caseless_str& v) const
-    { return v.compare (current_val); }
-
-  radio_property& operator = (const octave_value& val)
-    {
-      set (val);
-      return *this;
-    }
-
 private:
   radio_values vals;
   std::string current_val;
@@ -636,8 +663,6 @@
     return current_val;
   }
 
-  OCTINTERP_API void set (const octave_value& newval);
-
   bool is_rgb (void) const { return (current_type == color_t); }
 
   bool is_radio (void) const { return (current_type == radio_t); }
@@ -669,6 +694,9 @@
 
   operator octave_value (void) const { return get (); }
 
+protected:
+  OCTINTERP_API void do_set (const octave_value& newval);
+
 private:
   enum current_enum { color_t, radio_t } current_type;
   color_values color_val;
@@ -689,15 +717,6 @@
   double_property (const double_property& p)
     : base_property (p), current_val (p.current_val) { }
 
-  void set (const octave_value& v)
-    {
-      if (v.is_scalar_type () && v.is_real_type ())
-        current_val = v.double_value ();
-      else
-        error ("set: invalid value for double property \"%s\"",
-               get_name ().c_str ());
-    }
-
   octave_value get (void) const { return octave_value (current_val); }
 
   double double_value (void) const { return current_val; }
@@ -708,6 +727,16 @@
       return *this;
     }
 
+protected:
+  void do_set (const octave_value& v)
+    {
+      if (v.is_scalar_type () && v.is_real_type ())
+        current_val = v.double_value ();
+      else
+        error ("set: invalid value for double property \"%s\"",
+               get_name ().c_str ());
+    }
+
 private:
   double current_val;
 };
@@ -745,8 +774,6 @@
     return current_val;
   }
 
-  OCTINTERP_API void set (const octave_value& v);
-
   bool is_double (void) const { return (current_type == double_t); }
 
   bool is_radio (void) const { return (current_type == radio_t); }
@@ -778,6 +805,9 @@
 
   operator octave_value (void) const { return get (); }
 
+protected:
+  OCTINTERP_API void do_set (const octave_value& v);
+
 private:
   enum current_enum { double_t, radio_t } current_type;
   double dval;
@@ -812,19 +842,6 @@
 
   octave_value get (void) const { return data; }
 
-  void set (const octave_value& v)
-    {
-      if (validate (v))
-	{
-	  data = v;
-
-	  get_data_limits ();
-	}
-      else
-        error ("invalid value for array property \"%s\"",
-               get_name ().c_str ());
-    }
-
   void add_constraint (const std::string& type)
     { type_constraints.push_back (type); }
 
@@ -841,6 +858,20 @@
       return *this;
     }
 
+protected:
+  void do_set (const octave_value& v)
+    {
+      if (validate (v))
+	{
+	  data = v;
+
+	  get_data_limits ();
+	}
+      else
+        error ("invalid value for array property \"%s\"",
+               get_name ().c_str ());
+    }
+
 private:
   OCTINTERP_API bool validate (const octave_value& v);
 
@@ -866,25 +897,6 @@
     add_constraint (dim_vector (1, -1));
   }
 
-  void set (const octave_value& v)
-  {
-    array_property::set (v);
-
-    if (! error_state)
-      {
-	dim_vector dv = data.dims ();
-
-	if (dv(0) > 1 && dv(1) == 1)
-	  {
-	    int tmp = dv(0);
-	    dv(0) = dv(1);
-	    dv(1) = tmp;
-
-	    data = data.reshape (dv);
-	  }
-      }
-  }
-
   void add_constraint (const std::string& type)
   {
     array_property::add_constraint (type);
@@ -910,6 +922,26 @@
     return *this;
   }
 
+protected:
+  void do_set (const octave_value& v)
+  {
+    array_property::do_set (v);
+
+    if (! error_state)
+      {
+	dim_vector dv = data.dims ();
+
+	if (dv(0) > 1 && dv(1) == 1)
+	  {
+	    int tmp = dv(0);
+	    dv(0) = dv(1);
+	    dv(1) = tmp;
+
+	    data = data.reshape (dv);
+	  }
+      }
+  }
+
 private:
   OCTINTERP_API bool validate (const octave_value& v);
 };
@@ -932,14 +964,6 @@
   bool_property (const bool_property& p)
     : radio_property (p) { }
 
-  void set (const octave_value& val)
-    {
-      if (val.is_bool_scalar ())
-        radio_property::set (val.bool_value () ? "on" : "off");
-      else
-        radio_property::set (val);
-    }
-
   bool is_on (void) const { return is ("on"); }
   
   bool_property& operator = (const octave_value& val)
@@ -947,6 +971,15 @@
       set (val);
       return *this;
     }
+
+protected:
+  void do_set (const octave_value& val)
+    {
+      if (val.is_bool_scalar ())
+        radio_property::do_set (val.bool_value () ? "on" : "off");
+      else
+        radio_property::do_set (val);
+    }
 };
 
 // ---------------------------------------------------------------------
@@ -962,8 +995,6 @@
   handle_property (const handle_property& p)
     : base_property (p), current_val (p.current_val) { }
 
-  OCTINTERP_API void set (const octave_value& v);
-
   octave_value get (void) const { return current_val.as_octave_value (); }
 
   graphics_handle handle_value (void) const { return current_val; }
@@ -980,6 +1011,9 @@
       return *this;
     }
 
+protected:
+  OCTINTERP_API void do_set (const octave_value& v);
+
 private:
   graphics_handle current_val;
 };
@@ -995,14 +1029,15 @@
 
   octave_value get (void) const { return data; }
 
-  void set (const octave_value& v) { data = v; }
-
   any_property& operator = (const octave_value& val)
     {
       set (val);
       return *this;
     }
 
+protected:
+  void do_set (const octave_value& v) { data = v; }
+
 private:
   octave_value data;
 };
@@ -1018,15 +1053,6 @@
 
   octave_value get (void) const { return callback; }
 
-  void set (const octave_value& v)
-    {
-      if (validate (v))
-        callback = v;
-      else
-        error ("invalid value for callback property \"%s\"",
-               get_name ().c_str ());
-    }
-
   OCTINTERP_API void execute (const octave_value& data = octave_value ()) const;
 
   OCTINTERP_API static
@@ -1040,6 +1066,16 @@
       return *this;
     }
 
+protected:
+  void do_set (const octave_value& v)
+    {
+      if (validate (v))
+        callback = v;
+      else
+        error ("invalid value for callback property \"%s\"",
+               get_name ().c_str ());
+    }
+
 private:
   OCTINTERP_API bool validate (const octave_value& v) const;
 
@@ -1114,6 +1150,12 @@
       return *this;
     }
 
+  void add_listener (const octave_value& v, listener_mode mode = POSTSET)
+    { rep->add_listener (v, mode); }
+
+  void run_listeners (listener_mode mode = POSTSET)
+    { rep->run_listeners (mode); }
+
   /*
   const string_property& as_string_property (void) const
     { return *(dynamic_cast<string_property*> (rep)); }
@@ -1412,7 +1454,7 @@
 
   virtual octave_value get (bool all = false) const;
 
-  property get_property (const caseless_str&) const;
+  virtual property get_property (const caseless_str&);
 
   std::string get_tag (void) const { return tag.string_value (); }
 
@@ -1477,6 +1519,9 @@
 
   virtual void update_boundingbox (void);
 
+  virtual void add_listener (const caseless_str&, const octave_value&,
+			     listener_mode = POSTSET);
+
   void set_tag (const octave_value& val) { tag = val; }
 
   void set_parent (const octave_value& val);
@@ -1885,6 +1930,14 @@
       }
   }
 
+  virtual void add_property_listener (const std::string& nm,
+				      const octave_value& v,
+				      listener_mode mode = POSTSET)
+    {
+      if (valid_object ())
+	get_properties ().add_listener (nm, v, mode);
+    }
+
 protected:
   // A reference count.
   int count;
@@ -2051,6 +2104,10 @@
 
   graphics_backend get_backend (void) const { return rep->get_backend (); }
 
+  void add_property_listener (const std::string& nm, const octave_value& v,
+			      listener_mode mode = POSTSET)
+    { rep->add_property_listener (nm, v, mode); }
+
 private:
   base_graphics_object *rep;
 };