changeset 225:5c70883c40d5

merge with changeset ad5c7a5c2c39
author Thinker K.F. Li <thinker@branda.to>
date Mon, 15 Dec 2008 10:17:20 +0800
parents 29e1b2bffe4c (diff) ad5c7a5c2c39 (current diff)
children b24d7889cbda
files
diffstat 20 files changed, 688 insertions(+), 246 deletions(-) [+]
line wrap: on
line diff
--- a/examples/drag/main.c	Sun Dec 14 11:23:34 2008 +0800
+++ b/examples/drag/main.c	Mon Dec 15 10:17:20 2008 +0800
@@ -27,6 +27,7 @@
 
     en->rt = rt;
     en->rdman =  X_MB_rdman(rt);
+    en->state = 0;
     return en;
 }
 void engine_close(engine_t *en)
@@ -48,13 +49,6 @@
 #define COORD_HIDE(group) coord_hide(group);rdman_coord_changed(X_MB_rdman(ex_rt->rt), group)
 
 
-void coord_move(coord_t *c, co_aix x, co_aix y)
-{
-    c->matrix[2] = x;
-    c->matrix[5] = y;
-}
-
-
 static void cursor_press_handler(event_t *evt, void *arg) {
     engine_t *en = (engine_t *) arg;
     mouse_event_t *mev = (mouse_event_t *) evt;
--- a/examples/dynamic/button.svg	Sun Dec 14 11:23:34 2008 +0800
+++ b/examples/dynamic/button.svg	Mon Dec 15 10:17:20 2008 +0800
@@ -8,13 +8,14 @@
    xmlns="http://www.w3.org/2000/svg"
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="744.09448819"
-   height="1052.3622047"
+   width="720"
+   height="480"
    id="svg2"
    sodipodi:version="0.32"
    inkscape:version="0.46"
    sodipodi:docname="button.svg"
-   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
   <sodipodi:namedview
      id="base"
      pagecolor="#ffffff"
@@ -26,10 +27,10 @@
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
      inkscape:zoom="0.7"
-     inkscape:cx="239.91999"
-     inkscape:cy="667.92389"
+     inkscape:cx="257.06285"
+     inkscape:cy="365.06674"
      inkscape:document-units="px"
-     inkscape:current-layer="g2430"
+     inkscape:current-layer="layer1"
      showgrid="false"
      inkscape:window-width="1400"
      inkscape:window-height="978"
@@ -40,43 +41,43 @@
     <linearGradient
        id="linearGradient4066">
       <stop
-         style="stop-color:#000000;stop-opacity:0;"
+         id="stop4068"
          offset="0"
-         id="stop4068" />
+         style="stop-color:#000000;stop-opacity:0;" />
       <stop
-         style="stop-color:#000000;stop-opacity:1"
+         id="stop4070"
          offset="1"
-         id="stop4070" />
+         style="stop-color:#000000;stop-opacity:1" />
     </linearGradient>
     <linearGradient
        id="linearGradient4026">
       <stop
-         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4028"
          offset="0"
-         id="stop4028" />
+         style="stop-color:#000000;stop-opacity:1;" />
       <stop
-         style="stop-color:#ffffff;stop-opacity:0;"
+         id="stop4030"
          offset="1"
-         id="stop4030" />
+         style="stop-color:#ffffff;stop-opacity:0;" />
     </linearGradient>
     <linearGradient
        id="linearGradient4018">
       <stop
-         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4020"
          offset="0"
-         id="stop4020" />
+         style="stop-color:#000000;stop-opacity:1;" />
       <stop
-         style="stop-color:#0000ff;stop-opacity:0;"
+         id="stop4022"
          offset="1"
-         id="stop4022" />
+         style="stop-color:#0000ff;stop-opacity:0;" />
     </linearGradient>
     <inkscape:perspective
-       id="perspective10"
-       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
        inkscape:vp_z="744.09448 : 526.18109 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 526.18109 : 1"
-       sodipodi:type="inkscape:persp3d" />
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
   </defs>
   <metadata
      id="metadata7">
@@ -90,110 +91,107 @@
     </rdf:RDF>
   </metadata>
   <g
-     id="layer1"
-     inkscape:groupmode="layer"
+     style="display:inline"
      inkscape:label="bg"
-     style="display:inline">
+     inkscape:groupmode="layer"
+     id="layer1">
     <text
-       xml:space="preserve"
-       style="font-size:30px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
-       x="235.71429"
+       id="text4380"
        y="282.36218"
-       id="text4380"><tspan
-         sodipodi:role="line"
-         id="tspan4382"
+       x="235.71429"
+       style="font-size:30px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       xml:space="preserve"><tspan
+         y="282.36218"
          x="235.71429"
-         y="282.36218" /></text>
+         id="tspan4382"
+         sodipodi:role="line" /></text>
     <g
-       transform="translate(20.000001,42.857139)"
-       id="btn"
-       frame="click"
-       mbname="btn">
+       mbname="btn"
+       transform="translate(148.57143,98.571429)"
+       id="btn">
+      <g
+         style="display:none"
+         mbname="btn_normal"
+         transform="translate(148.57143,98.571429)"
+         id="btn_normal"
+         frame="normal">
+        <rect
+           style="fill:#0000ff"
+           id="rect2651"
+           width="122.85714"
+           height="47.142857"
+           x="-1.4285715"
+           y="1.4285747"
+           rx="6.0995407"
+           ry="5.0559778" />
+        <text
+           xml:space="preserve"
+           style="font-size:24px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+           x="10.000002"
+           y="32.85714"
+           id="text2653"><tspan
+             sodipodi:role="line"
+             id="tspan2655"
+             x="10.000002"
+             y="32.85714">Click Me</tspan></text>
+      </g>
       <g
-         id="g2430"
-         mbname="btn"
-         transform="translate(-78.571429,44.285714)">
-        <g
-           frame="normal"
-           id="btn_normal"
-           mbname="btn_normal"
-           transform="translate(1.4285713e-7,-60)">
-          <rect
-             style="fill:#0000ff"
-             id="rect2493"
-             width="82.85714"
-             height="42.857143"
-             x="220"
-             y="69.505035"
-             rx="5.4543843"
-             ry="7.1427469" />
-          <text
-             xml:space="preserve"
-             style="font-size:24px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
-             x="232.85715"
-             y="99.505043"
-             id="text2495"><tspan
-               sodipodi:role="line"
-               id="tspan2497"
-               x="232.85715"
-               y="99.505043">Rect</tspan></text>
-        </g>
-        <g
-           frame="active"
-           id="btn_active"
-           mbname="btn_active"
-           transform="translate(-1.4285757,30.000005)">
-          <rect
-             style="fill:#00ffff"
-             id="rect2481"
-             width="82.85714"
-             height="42.857143"
-             x="220"
-             y="69.505035"
-             rx="5.4543843"
-             ry="7.1427469" />
-          <text
-             xml:space="preserve"
-             style="font-size:24px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
-             x="232.85715"
-             y="99.505043"
-             id="text2483"><tspan
-               sodipodi:role="line"
-               id="tspan2485"
-               x="232.85715"
-               y="99.505043">Rect</tspan></text>
-        </g>
-        <g
-           frame="click"
-           id="btn_click"
-           mbname="btn_click"
-           transform="translate(-1.4285715,117.14286)">
-          <rect
-             style="fill:#0000ff"
-             id="rect2488"
-             width="82.85714"
-             height="42.857143"
-             x="220"
-             y="69.505035"
-             rx="5.4543843"
-             ry="7.1427469" />
-          <text
-             xml:space="preserve"
-             style="font-size:24px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
-             x="232.85715"
-             y="99.505043"
-             id="text2490"><tspan
-               sodipodi:role="line"
-               id="tspan2492"
-               x="232.85715"
-               y="99.505043">Rect</tspan></text>
-        </g>
+         style=""
+         mbname="btn_active"
+         transform="translate(148.57143,98.571429)"
+         id="btn_active"
+         frame="active">
+        <rect
+           style="fill:#00ffff"
+           id="rect2727"
+           width="122.85714"
+           height="47.142857"
+           x="-1.4285715"
+           y="1.4285747"
+           rx="6.0995407"
+           ry="5.0559778" />
+        <text
+           xml:space="preserve"
+           style="font-size:24px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+           x="10.000002"
+           y="32.85714"
+           id="text2729"><tspan
+             sodipodi:role="line"
+             id="tspan2731"
+             x="10.000002"
+             y="32.85714">Click Me</tspan></text>
+      </g>
+      <g
+         style="display:none"
+         mbname="btn_click"
+         transform="translate(148.57143,98.571429)"
+         id="btn_click"
+         frame="click">
+        <rect
+           style="fill:#0000ff"
+           id="rect2734"
+           width="122.85714"
+           height="47.142857"
+           x="-1.4285715"
+           y="1.4285747"
+           rx="6.0995407"
+           ry="5.0559778" />
+        <text
+           xml:space="preserve"
+           style="font-size:24px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+           x="10.000002"
+           y="32.85714"
+           id="text2736"><tspan
+             sodipodi:role="line"
+             id="tspan2738"
+             x="10.000002"
+             y="32.85714">Click Me</tspan></text>
       </g>
     </g>
   </g>
   <g
-     inkscape:groupmode="layer"
-     id="layer2"
+     style="display:inline"
      inkscape:label="text"
-     style="display:inline" />
+     id="layer2"
+     inkscape:groupmode="layer" />
 </svg>
--- a/examples/dynamic/main.c	Sun Dec 14 11:23:34 2008 +0800
+++ b/examples/dynamic/main.c	Mon Dec 15 10:17:20 2008 +0800
@@ -34,6 +34,7 @@
     coord_t *click;
     void (*press)();
     void *arg;
+    observer_t *obs_move,*obs_out,*obs_press;
 } mb_button_t;
 
 
@@ -43,6 +44,8 @@
 #define CMOUSE(e) (coord_get_mouse_event(e))
 
 
+static void mb_button_pressed(event_t *evt, void *arg);
+static void mb_button_out(event_t *evt, void *arg);
 
 static void mb_button_move(event_t *evt, void *arg) 
 {
@@ -52,7 +55,9 @@
     
     printf("Mouse move\n");
     COORD_SHOW(btn->active);
+#if 0
     rdman_coord_changed(btn->en->rdman,btn->root);
+#endif
     rdman_redraw_changed(btn->en->rdman);
 }
 static void mb_button_out(event_t *evt, void *arg) 
@@ -62,16 +67,18 @@
 
     printf("mouse out\n");
     COORD_HIDE(btn->active);
+#if 0
     rdman_coord_changed(btn->en->rdman,btn->root);
+#endif
     rdman_redraw_changed(btn->en->rdman);
 }
 
-void mb_button_show_active(const mb_timeval_t *tmo, const mb_timeval_t *now, void *arg)
+void mb_button_show_active(event_t *evt, void *arg)
 {
     mb_button_t *btn = (mb_button_t *) arg;
     engine_t *en = btn->en;
 
-    COORD_HIDE(btn->click);
+    COORD_SHOW(btn->active);
     rdman_coord_changed(btn->en->rdman,btn->root);
     rdman_redraw_changed(btn->en->rdman);
 }
@@ -80,17 +87,25 @@
 {
     mb_button_t *btn = (mb_button_t *) arg;
     engine_t *en = btn->en;
-    mb_timeval_t now,to;
+    mb_timeval_t start, playing, now;
+    mb_progm_t *progm;
+    mb_word_t *word;
 
     printf("Pressed\n");
     COORD_SHOW(btn->click);
+    COORD_HIDE(btn->active);
     rdman_coord_changed(en->rdman,en->button->root_coord);
     rdman_redraw_changed(en->rdman);
+
+    progm = mb_progm_new(1, en->rdman);
+    MB_TIMEVAL_SET(&start, 0, 500000);
+    MB_TIMEVAL_SET(&playing, 0, 0);
+    word = mb_progm_next_word(progm, &start, &playing);
+    mb_visibility_new(VIS_HIDDEN, btn->click, word);
+    mb_visibility_new(VIS_VISIBLE, btn->active, word);
+    mb_progm_free_completed(progm);
     get_now(&now);
-    MB_TIMEVAL_SET(&to, 0, 500);
-    MB_TIMEVAL_ADD(&to,&now);
-
-    mb_tman_timeout(X_MB_tman(btn->en->rt), &to, mb_button_show_active, arg);
+    mb_progm_start(progm, X_MB_tman(en->rt), &now);
 }
 mb_button_t *mb_button_new(engine_t *en,mb_sprite_t *sp, char *name)
 {
@@ -117,6 +132,7 @@
     // Show only the normal button
     COORD_HIDE(btn->active);
     COORD_HIDE(btn->click);
+    COORD_SHOW(btn->normal);
     // Move to the same position
     btn->active->matrix[2] = 200;
     btn->active->matrix[5] = 200;
@@ -125,9 +141,10 @@
     btn->click->matrix[2] = 200;
     btn->click->matrix[5] = 200;
     btn->en = en;
-    subject_add_event_observer(CMOUSE(btn->root), EVT_MOUSE_MOVE, mb_button_move,btn);
-    subject_add_event_observer(CMOUSE(btn->root), EVT_MOUSE_OUT, mb_button_out,btn);
-    subject_add_event_observer(CMOUSE(btn->root), EVT_MOUSE_BUT_RELEASE, mb_button_pressed,btn);
+    btn->obs_move = subject_add_event_observer(CMOUSE(btn->root), EVT_MOUSE_MOVE, mb_button_move,btn);
+    btn->obs_press = subject_add_event_observer(CMOUSE(btn->root), EVT_MOUSE_BUT_PRESS, mb_button_pressed,btn);
+    btn->obs_out = subject_add_event_observer(CMOUSE(btn->root), EVT_MOUSE_OUT, mb_button_out,btn);
+    rdman_redraw_changed(en->rdman);
     return btn;
 }
 
@@ -150,7 +167,7 @@
     return en;
 }
 
-void engine_close(engine_t *en)
+void engine_mainloop(engine_t *en)
 {
     /*
      * Start handle connections, includes one to X server.
@@ -167,15 +184,6 @@
 }
 
 
-
-void coord_move(coord_t *c, co_aix x, co_aix y)
-{
-    c->matrix[2] = x;
-    c->matrix[5] = y;
-}
-
-
-
 static void add_rect_move(event_t *evt, void *arg) 
 {
     engine_t *en = (engine_t *) arg;
@@ -269,7 +277,7 @@
     subject_add_event_observer(subject,  EVT_MOUSE_BUT_RELEASE, add_rect, en);
 
 
-    engine_close(en);
+    engine_mainloop(en);
 
     return 0;
 }
--- a/examples/dynamic/menu.svg	Sun Dec 14 11:23:34 2008 +0800
+++ b/examples/dynamic/menu.svg	Mon Dec 15 10:17:20 2008 +0800
@@ -72,7 +72,8 @@
        ry="7.1427469" />
     <g
        id="rect"
-       inkscape:label="#g2389">
+       inkscape:label="#g2389"
+       transform="translate(442.85714,7.1428571)">
       <rect
          ry="7.1427469"
          rx="5.4543843"
--- a/include/Makefile.am	Sun Dec 14 11:23:34 2008 +0800
+++ b/include/Makefile.am	Mon Dec 15 10:17:20 2008 +0800
@@ -8,4 +8,5 @@
 	mb_redraw_man.h	\
 	mb_shapes.h	\
 	mb_tools.h	\
+	mb_prop.h	\
 	mb_X_supp.h
--- a/include/mb_observer.h	Sun Dec 14 11:23:34 2008 +0800
+++ b/include/mb_observer.h	Mon Dec 15 10:17:20 2008 +0800
@@ -7,14 +7,25 @@
 typedef struct _observer observer_t;
 typedef struct _subject subject_t;
 typedef struct _mouse_event mouse_event_t;
+typedef struct _monitor_event monitor_event_t;
 typedef struct _ob_factory ob_factory_t;
 typedef void (*evt_handler)(event_t *event, void *arg);
 
 struct _event {
-    int type;			/*!< event type (a.k.a. EVT_*  */
+    int type;			/*!< event type (a.k.a. EVT_*)  */
     subject_t *tgt, *cur_tgt;
+    int flags;
 };
 
+/*! \brief Observer mark event with EVTF_STOP_PROPAGATE flag
+ *	   to stop propagation.
+ */
+#define EVTF_STOP_PROPAGATE 0x1
+/*! \brief Observer mark event with EVTF_STOP_NOTIFY flag to stop
+ *	   stop notification the event immediately.
+ */
+#define EVTF_STOP_NOTIFY 0x2
+
 /*! \brief Observer of observer pattern.
  *
  * A target for receiving events.
@@ -31,15 +42,21 @@
  * Observer is a pattern to decouple caller and callee,
  * especial for multiple callee.
  * \see http://en.wikipedia.org/wiki/Observer_pattern
+ *
+ * This implementation add a monitor facility to monitor adding/removing
+ * observers from subjects.  Monitor is another subject that monitor events
+ * will be sent to if it is existed.
  */
 struct _subject {
     int obj_type;		/*!< \brief type of object (a.k.a. OBJT_*). */
     void *obj;			/*!< \brief the object this subject for. */
     int flags;
+    subject_t *monitor_sub;	/*!< \brief Monitor adding/removing
+				 *          obervers on this subject. */
     ob_factory_t *factory;
     STAILQ(observer_t) observers;
 };
-/*! \brief Flag that make a subject to propagate events to parents. */
+/*! \brief Flag that make a subject to stop propagate events to parents. */
 #define SUBF_STOP_PROPAGATE 0x1
 #define SUBF_BUSY 0x2		/*!< \brief in subject_notify() */
 #define SUBF_FREE 0x4		/*!< \brief in postponding subject_free() */
@@ -57,6 +74,12 @@
 #define MOUSE_BUT2 0x2
 #define MOUSE_BUT3 0x4
 
+struct _monitor_event {
+    event_t event;
+    subject_t *subject;	  /*!< \brief Subject been monitored. */
+    observer_t *observer; /*!< \brief Observer been added or removed. */
+};
+
 /*! \brief Observer factory.
  *
  * It provides functions for allocation of subject and observer objects,
@@ -75,18 +98,39 @@
 enum {EVT_ANY,EVT_MOUSE_OVER, EVT_MOUSE_OUT, EVT_MOUSE_MOVE,
       EVT_MOUSE_BUT_PRESS, EVT_MOUSE_BUT_RELEASE,
       EVT_KB_PRESS, EVT_KB_RELEASE, EVT_PROGM_COMPLETE,
-      EVT_RDMAN_REDRAW };
+      EVT_RDMAN_REDRAW,
+      EVT_MONITOR_ADD, EVT_MONITOR_REMOVE, EVT_MONITOR_FREE,
+      EVT_MOUSE_MOVE_RAW
+};
 
 extern subject_t *subject_new(ob_factory_t *factory,
 			      void *obj, int obj_type);
 extern void subject_free(subject_t *subject);
 extern void subject_notify(subject_t *subject, event_t *evt);
-extern observer_t *subject_add_observer(subject_t *subject,
-					evt_handler hdr, void *arg);
 extern observer_t *subject_add_event_observer(subject_t *subject, int type,
 					      evt_handler hdr, void *arg);
+/*! \brief Add an observer for any type of events. */
+#define subject_add_observer(s, h, a)			\
+    subject_add_event_observer(s, EVT_ANY, h, a)
+extern observer_t *subject_add_event_observer_head(subject_t *subject,
+						   int type,
+						   evt_handler hdr,
+						   void *arg);
+/*! \brief Add an observer for any type of events at head. */
+#define subject_add_observer_head(s, h, a)		\
+    subject_add_event_observer_head(s, EVT_ANY, h, a)
 extern void subject_remove_observer(subject_t *subject,
 				    observer_t *observer);
+#define subject_get_object(s) ((s)->obj)
 
+/*! \brief Set monitor for the subject.
+ *
+ * Monitor of a subject is another subject that would be notified when
+ * add/remove a observer to/from the subject.  It can be used to efficiently
+ * implement translator to translate events.
+ */
+#define subject_set_monitor(subject, monitor)	\
+    do { (subject)->monitor_sub = monitor; } while(0)
+#define subject_monitor(subject) ((subject)->monitor_sub)
 
 #endif /* __OBSERVER_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/mb_prop.h	Mon Dec 15 10:17:20 2008 +0800
@@ -0,0 +1,53 @@
+#ifndef __MB_PROP_H_
+#define __MB_PROP_H_
+
+#include "mb_tools.h"
+
+/*! \defgroup mb_prop_grp Property
+ * \brief A way the modules can set up their own properties on objects.
+ *
+ * Properties are key-value pairs that are associated with objects.
+ * MadButterfly associate a property store for each object (coord or shape)
+ * to keep property values.  Every property is identified by a ID; an
+ * integer.  Programmer can use a ID to set/get value to/from a property
+ * store.  The ID should be unique in a property store.
+ * @{
+ */
+
+typedef struct _mb_prop_entry mb_prop_entry_t;
+typedef struct _mb_prop_store mb_prop_store_t;
+
+struct _mb_prop_entry {
+    int id;
+    void *value;
+    mb_prop_entry_t *next;
+};
+
+/*! \brief Property IDs. */
+enum {
+    PROP_DUMMY,
+    PROP_MEVT_OB_CNT,
+    PROP_MEVT_OBSERVER,
+    PROP_LAST
+};
+
+struct _mb_prop_store {
+    elmpool_t *entry_pool;
+    STAILQ(mb_prop_entry_t) entries;
+};
+
+#define mb_prop_store_init(prop_store, pent_pool)		\
+    do {							\
+	(prop_store)->entry_pool = pent_pool;			\
+	STAILQ_INIT((prop_store)->entries);			\
+    } while(0)
+
+void mb_prop_store_destroy(mb_prop_store_t *prop_store);
+void *mb_prop_set(mb_prop_store_t *prop_store, int id, void *value);
+void *mb_prop_get(mb_prop_store_t *prop_store, int id);
+void mb_prop_del(mb_prop_store_t *prop_store, int id);
+int mb_prop_has(mb_prop_store_t *prop_store, int id);
+
+/* @} */
+
+#endif /* __MB_PROP_H_ */
--- a/include/mb_redraw_man.h	Sun Dec 14 11:23:34 2008 +0800
+++ b/include/mb_redraw_man.h	Mon Dec 15 10:17:20 2008 +0800
@@ -50,6 +50,7 @@
     elmpool_t *observer_pool;
     elmpool_t *subject_pool;
     elmpool_t *paint_color_pool;
+    elmpool_t *pent_pool;
 
     coords_t dirty_coords;
     geos_t dirty_geos;
@@ -68,6 +69,10 @@
     ob_factory_t ob_factory;
 
     subject_t *redraw;		/*!< \brief Notified after redrawing. */
+    subject_t *addrm_monitor;	/*!< \brief Monitor adding/removing observers
+				 *	    to/from mouse event subjects.
+				 */
+    mb_obj_t *last_mouse_over;
 };
 
 extern int redraw_man_init(redraw_man_t *rdman, cairo_t *cr,
@@ -79,8 +84,14 @@
 extern int rdman_add_shape(redraw_man_t *rdman,
 			   shape_t *shape, coord_t *coord);
 /*! \brief Make a shape been managed by a redraw manager. */
-#define rdman_shape_man(rdman, shape)				\
-    STAILQ_INS_TAIL(rdman->shapes, shape_t, sh_next, shape)
+#define rdman_shape_man(rdman, shape)					\
+    do {								\
+	mb_prop_store_init(&((mb_obj_t *)(shape))->props,		\
+			   (rdman)->pent_pool);				\
+	STAILQ_INS_TAIL(rdman->shapes, shape_t, sh_next, shape);	\
+	if(rdman->last_mouse_over == (mb_obj_t *)(shape))		\
+	    rdman->last_mouse_over = NULL;				\
+    } while(0)
 extern int rdman_shape_free(redraw_man_t *rdman, shape_t *shape);
 
 #define rdman_paint_man(rdman, paint)		\
@@ -155,6 +166,7 @@
 				  co_aix x, co_aix y, int *in_stroke);
 #define rdman_get_ob_factory(rdman) (&(rdman)->ob_factory)
 #define rdman_get_redraw_subject(rdman) ((rdman)->redraw)
+#define rdman_get_root(rdman) ((rdman)->root_coord)
 
 
 #endif /* __REDRAW_MAN_H_ */
--- a/include/mb_types.h	Sun Dec 14 11:23:34 2008 +0800
+++ b/include/mb_types.h	Mon Dec 15 10:17:20 2008 +0800
@@ -4,6 +4,7 @@
 #include <cairo.h>
 #include "mb_tools.h"
 #include "mb_observer.h"
+#include "mb_prop.h"
 
 typedef float co_aix;
 typedef struct _shape shape_t;
@@ -16,21 +17,27 @@
 
 struct _redraw_man;
 
+/* \defgroup mb_obj_grp Object type
+ * @{
+ */
 /*! \brief MadButterfly object.
  *
  * All objects (coord and shapes) should have mb_obj_t as first member
  * variable.  obj_type is used to identify type of an object.  Please,
  * use MBO_TYPE() to return this value.  MBO_TYPE() will type-casting the
- * object to mb_obj_t and return obj_type.  MBO_TYPE() is a left-side
- * value.
+ * object to mb_obj_t and return obj_type.
+ *
+ * mb_obj_t should be initialized with mb_obj_init() and destroied with
+ * mb_obj_destroy().
  */
 struct _mb_obj {
     int obj_type;		/*!< \brief Type of a MadButterfly object. */
+    mb_prop_store_t props;	/*!< Initialized by rdman. */
 };
 
 enum { MBO_DUMMY,
        MBO_COORD,
-       MBO_SHAPES=0x1000,
+       MBO_SHAPES=0x1000,	/*! \note Don't touch this.  */
        MBO_PATH,
        MBO_TEXT,
        MBO_RECT
@@ -40,7 +47,15 @@
 /*! \brief Return type of a MadBufferly object. */
 #define MBO_TYPE(x) (((mb_obj_t *)(x))->obj_type)
 #define IS_MBO_SHAPES(obj) (MBO_CLASS(obj) == MBO_SHAPES)
-#define IS_MBO_COORD(obj) (MBO_TYPE(obj) == MB_COORD)
+#define IS_MBO_COORD(obj) (MBO_TYPE(obj) == MBO_COORD)
+#define mb_obj_init(obj, type)					\
+    do {							\
+	((mb_obj_t *)(obj))->obj_type = type;			\
+    } while(0)
+#define mb_obj_destroy(obj)
+#define mb_obj_prop_store(obj) (&(obj)->props)
+
+/* @} */
 
 /*! \brief Base of paint types.
  *
@@ -176,6 +191,18 @@
 #define coord_show(co) do { co->flags &= ~COF_HIDDEN; } while(0)
 #define coord_get_mouse_event(coord) ((coord)->mouse_event)
 
+/*! \brief Coord operation function
+ * These functions are used to move and scale the coord_t. Programmers should use these functions instead of using the matrix directly.
+ * The x,y,sx,sy are all in co_aix type.
+ *
+ */
+#define coord_move(co,x,y) do {(co)->matrix[2] = (x); (co)->matrix[5] = (y);} while(0)
+#define coord_set_scalex(ci,sx) do {(co)->matrix[0] = sx;} while(0)
+#define coord_set_scaley(ci,sy) do {(co)->matrux[3] = sy;} while(0)
+#define coord_scalex(ci) ((co)->matrix[0])
+#define coord_scaley(ci) ((co)->matrix[3])
+#define coord_x(ci) ((co)->matrix[2])
+#define coord_y(ci) ((co)->matrix[5])
 
 /*! \brief A grahpic shape.
  *
--- a/src/Makefile.am	Sun Dec 14 11:23:34 2008 +0800
+++ b/src/Makefile.am	Mon Dec 15 10:17:20 2008 +0800
@@ -7,7 +7,7 @@
 libmbfly_la_SOURCES = animate.c chgcolor.c coord.c event.c geo.c	\
 	observer.c paint.c redraw_man.c rotate.c shape_path.c		\
 	shape_rect.c shape_text.c shift.c subtree_free.c timer.c 	\
-	timertool.c tools.c visibility.c X_supp.c
+	timertool.c tools.c visibility.c X_supp.c prop.c
 libmbfly_la_CPPFLAGS = @cairo_CFLAGS@
 libmbfly_la_LDFLAGS = @cairo_LIBS@
 
--- a/src/Makefile.pmake	Sun Dec 14 11:23:34 2008 +0800
+++ b/src/Makefile.pmake	Mon Dec 15 10:17:20 2008 +0800
@@ -1,10 +1,11 @@
 SRCS =	coord.c geo.c shape_path.c shape_text.c shape_rect.c \
 	redraw_man.c timer.c animate.c paint.c event.c observer.c \
 	X_supp.c timertool.c tools.c shift.c chgcolor.c \
-	visibility.c rotate.c
+	visibility.c rotate.c prop.c
 OBJS = ${SRCS:C/(.*)\.c/\1.o/g}
 TESTCASE_OBJS = ${SRCS:C/(.*)\.c/testcase-\1.o/g}
-CFLAGS+=	-Wall -I/usr/local/include `pkg-config --cflags cairo`
+CFLAGS+=	-Wall -I../include -I/usr/local/include \
+		`pkg-config --cflags cairo`
 LDFLAGS =	`pkg-config --libs cairo`
 BINS =	libmbfly.a X_main
 PREFIX?=/usr/local/
--- a/src/X_supp.c	Sun Dec 14 11:23:34 2008 +0800
+++ b/src/X_supp.c	Mon Dec 15 10:17:20 2008 +0800
@@ -12,6 +12,8 @@
 #define ERR -1
 #define OK 0
 
+#define ONLY_MOUSE_MOVE_RAW 1
+
 /*! \ingroup xkb
  * @{
  */
@@ -37,8 +39,10 @@
 
     X_kb_info_t kbinfo;
 
+#ifndef ONLY_MOUSE_MOVE_RAW
     /* States */
     shape_t *last;
+#endif
 };
 
 /*! \defgroup xkb X Keyboard Handling
@@ -145,11 +149,11 @@
  * with SUBF_STOP_PROPAGATE flag.  The subject of mouse event
  * for a shape is returned by sh_get_mouse_event_subject().
  */
-static void notify_shapes(redraw_man_t *rdman,
-			  shape_t *shape,
-			  co_aix x, co_aix y, int etype,
-			  unsigned int state,
-			  unsigned int button) {
+static void notify_coord_or_shape(redraw_man_t *rdman,
+				   mb_obj_t *obj,
+				   co_aix x, co_aix y, int etype,
+				   unsigned int state,
+				   unsigned int button) {
     mouse_event_t mouse_event;
     subject_t *subject;
 
@@ -159,7 +163,10 @@
     mouse_event.but_state = state;
     mouse_event.button = button;
     
-    subject = sh_get_mouse_event_subject(shape);
+    if(IS_MBO_SHAPES(obj))
+	subject = sh_get_mouse_event_subject((shape_t *)obj);
+    else
+	subject = coord_get_mouse_event((coord_t *)obj);
     
     subject_notify(subject, (event_t *)&mouse_event);
 }
@@ -180,6 +187,7 @@
     int ex1=0, ey1=0, ex2=0, ey2=0;
 
     shape_t *shape;
+    coord_t *root;
 
     unsigned int state, button;
     int in_stroke;
@@ -201,8 +209,9 @@
 	    shape = find_shape_at_pos(rdman, x, y,
 				      &in_stroke);
 	    if(shape)
-		notify_shapes(rdman, shape, x, y, EVT_MOUSE_BUT_PRESS,
-			      state, button);
+		notify_coord_or_shape(rdman, (mb_obj_t *)shape,
+				      x, y, EVT_MOUSE_BUT_PRESS,
+				      state, button);
 	    break;
 
 	case ButtonRelease:
@@ -215,7 +224,8 @@
 	    shape = find_shape_at_pos(rdman, x, y,
 				      &in_stroke);
 	    if(shape)
-		notify_shapes(rdman, shape, x, y, EVT_MOUSE_BUT_RELEASE,
+		notify_coord_or_shape(rdman, (mb_obj_t *)shape,
+			      x, y, EVT_MOUSE_BUT_RELEASE,
 			      state, button);
 	    break;
 
@@ -227,24 +237,35 @@
 
 	    shape = find_shape_at_pos(rdman, x, y,
 				      &in_stroke);
+#ifdef ONLY_MOUSE_MOVE_RAW
+	    if(shape != NULL) {
+		notify_coord_or_shape(rdman, (mb_obj_t *)shape,
+			      x, y, EVT_MOUSE_MOVE_RAW, state, 0);
+	    } else {
+		root = rdman_get_root(rdman);
+		notify_coord_or_shape(rdman, (mb_obj_t *)root,
+			      x, y, EVT_MOUSE_MOVE_RAW, state, 0);
+	    }
+#else
 	    if(shape != NULL) {
 		if(rt->last != shape) {
 		    if(rt->last)
-			notify_shapes(rdman, rt->last, x, y,
+			notify_coord_or_shape(rdman, rt->last, x, y,
 				      EVT_MOUSE_OUT, state, 0);
-		    notify_shapes(rdman, shape, x, y,
+		    notify_coord_or_shape(rdman, shape, x, y,
 				  EVT_MOUSE_OVER, state, 0);
 		    rt->last = shape;
 		} else
-		    notify_shapes(rdman, shape, x, y,
+		    notify_coord_or_shape(rdman, shape, x, y,
 				  EVT_MOUSE_MOVE, state, 0);
 	    } else {
 		if(rt->last) {
-		    notify_shapes(rdman, rt->last, x, y,
+		    notify_coord_or_shape(rdman, rt->last, x, y,
 				  EVT_MOUSE_OUT, state, 0);
 		    rt->last = NULL;
 		}
 	    }
+#endif
 	    break;
 
 	case KeyPress:
@@ -424,7 +445,9 @@
 
     xmb_rt->tman = mb_tman_new();
 
+#ifndef ONLY_MOUSE_MOVE_RAW
     xmb_rt->last = NULL;
+#endif
 
     X_kb_init(&xmb_rt->kbinfo, xmb_rt->display, xmb_rt->rdman);
 
--- a/src/coord.c	Sun Dec 14 11:23:34 2008 +0800
+++ b/src/coord.c	Mon Dec 15 10:17:20 2008 +0800
@@ -90,7 +90,7 @@
 	co->parent = parent;
 	STAILQ_INS_TAIL(parent->children, coord_t, sibling, co);
     }
-    MBO_TYPE(co) = MBO_COORD;
+    mb_obj_init(co, MBO_COORD);
     co->matrix[0] = 1;
     co->matrix[4] = 1;
     co->aggr_matrix[0] = 1;
--- a/src/observer.c	Sun Dec 14 11:23:34 2008 +0800
+++ b/src/observer.c	Mon Dec 15 10:17:20 2008 +0800
@@ -17,6 +17,7 @@
     subject->obj = obj;
     subject->obj_type = obj_type;
     subject->flags = 0;
+    subject->monitor_sub = NULL;
     STAILQ_INIT(subject->observers);
 
     subject->factory = factory;
@@ -30,6 +31,7 @@
 void subject_free(subject_t *subject) {
     ob_factory_t *factory = subject->factory;
     observer_t *observer;
+    monitor_event_t mevt;
 
     ASSERT(!(subject->flags & SUBF_FREE));
     if(subject->flags & SUBF_BUSY) {
@@ -39,7 +41,14 @@
 	subject->flags |= SUBF_FREE;
 	return;
     }
-	
+    
+    if(subject->monitor_sub) {
+	mevt.event.type = EVT_MONITOR_FREE;
+	mevt.subject = subject;
+	mevt.observer = NULL;
+	subject_notify(subject->monitor_sub, (event_t *)&mevt);
+    }
+
     while((observer = STAILQ_HEAD(subject->observers))) {
 	STAILQ_REMOVE(subject->observers, observer_t, next, observer);
 	factory->observer_free(factory, observer);
@@ -52,62 +61,60 @@
     ob_factory_t *factory = subject->factory;
     observer_t *observer;
     subject_t *old_subject;
+    int stop_propagate = 0;
+    int old_busy;
 
     evt->tgt = subject;
+    evt->flags = 0;
     while(subject) {
 	/*!
 	 * \note What is happend when the subject is freed by observer?
 	 *		Postponding the request of free until notification
 	 *		been finished. (\ref SUBF_BUSY / \ref SUBF_FREE)
 	 */
+	old_busy = subject->flags & SUBF_BUSY;
 	subject->flags |= SUBF_BUSY;
 
-	evt->cur_tgt = subject->obj;
+	evt->cur_tgt = subject;
 	for(observer = STAILQ_HEAD(subject->observers);
 	    observer != NULL;
 	    observer = STAILQ_NEXT(observer_t, next, observer)) {
-	    if (observer->type == EVT_ANY || observer->type == evt->type)
-		    observer->hdr(evt, observer->arg);
+	    if (observer->type == EVT_ANY || observer->type == evt->type) {
+		observer->hdr(evt, observer->arg);
+		
+		if(evt->flags & EVTF_STOP_NOTIFY) {
+		    stop_propagate = 1;
+			break;
+		}
+		if(evt->flags & EVTF_STOP_PROPAGATE)
+		    stop_propagate = 1;
+	    }
 	}
 
-	subject->flags &= ~SUBF_BUSY;
+	if(!old_busy)
+	    subject->flags &= ~SUBF_BUSY;
 
 	old_subject = subject;
 	subject = factory->get_parent_subject(factory, subject);
 
+	if(old_subject->flags & SUBF_STOP_PROPAGATE)
+	    stop_propagate = 1;
+
 	if(old_subject->flags & SUBF_FREE)
 	    subject_free(old_subject);
 
-	if(old_subject->flags & SUBF_STOP_PROPAGATE)
+	if(stop_propagate)
 	    break;
     }
 }
 
-/*! \brief Add an observer for any type of events.
- */
-observer_t *subject_add_observer(subject_t *subject,
-				 evt_handler hdr, void *arg) {
-    ob_factory_t *factory = subject->factory;
-    observer_t *observer;
-
-    observer = factory->observer_alloc(factory);
-    if(observer == NULL)
-	return NULL;
-    observer->hdr = hdr;
-    observer->arg = arg;
-    observer->type = EVT_ANY;
-
-    STAILQ_INS_TAIL(subject->observers, observer_t, next, observer);
-
-    return observer;
-}
-
 /*! \brief Add an observer for specified type of events.
  */
 observer_t *subject_add_event_observer(subject_t *subject, int type,
 				 evt_handler hdr, void *arg) {
     ob_factory_t *factory = subject->factory;
     observer_t *observer;
+    monitor_event_t mevt;
 
     observer = factory->observer_alloc(factory);
     if(observer == NULL)
@@ -118,14 +125,57 @@
 
     STAILQ_INS_TAIL(subject->observers, observer_t, next, observer);
 
+    if(subject->monitor_sub) {
+	mevt.event.type = EVT_MONITOR_ADD;
+	mevt.subject = subject;
+	mevt.observer = observer;
+	subject_notify(subject->monitor_sub, (event_t *)&mevt);
+    }
+
+    return observer;
+}
+
+/*! \brief Add an observer for specified type of events at head.
+ */
+observer_t *subject_add_event_observer_head(subject_t *subject, int type,
+					    evt_handler hdr, void *arg) {
+    ob_factory_t *factory = subject->factory;
+    observer_t *observer;
+    monitor_event_t mevt;
+
+    observer = factory->observer_alloc(factory);
+    if(observer == NULL)
+	return NULL;
+    observer->hdr = hdr;
+    observer->arg = arg;
+    observer->type = type;
+
+    STAILQ_INS(subject->observers, observer_t, next, observer);
+
+    if(subject->monitor_sub) {
+	mevt.event.type = EVT_MONITOR_ADD;
+	mevt.subject = subject;
+	mevt.observer = observer;
+	subject_notify(subject->monitor_sub, (event_t *)&mevt);
+    }
+
     return observer;
 }
 
 void subject_remove_observer(subject_t *subject,
 			     observer_t *observer) {
     ob_factory_t *factory = subject->factory;
+    monitor_event_t mevt;
 
     STAILQ_REMOVE(subject->observers, observer_t, next, observer);
+    
+    if(subject->monitor_sub) {
+	mevt.event.type = EVT_MONITOR_REMOVE;
+	mevt.subject = subject;
+	mevt.observer = observer;
+	subject_notify(subject->monitor_sub, (event_t *)&mevt);
+    }
+
     factory->observer_free(factory, observer);
 }
 
@@ -176,7 +226,7 @@
     (*cnt)++;
 }
 
-void test_observer(void) {
+static void test_observer(void) {
     subject_t *subject;
     observer_t *observer[2];
     event_t evt;
@@ -198,11 +248,41 @@
     subject_free(subject);
 }
 
+static void monitor_handler(event_t *evt, void *arg) {
+    int *cnt = (int *)arg;
+
+    (*cnt)++;
+}
+
+static void test_monitor(void) {
+    subject_t *subject, *monitor;
+    observer_t *observer[2];
+    int cnt = 0;
+
+    subject = subject_new(&test_factory, NULL, 0);
+    monitor = subject_new(&test_factory, NULL, 0);
+    subject_set_monitor(subject, monitor);
+
+    observer[0] = subject_add_observer(monitor, monitor_handler, &cnt);
+    observer[1] = subject_add_observer(subject, NULL, NULL);
+    CU_ASSERT(cnt == 1);
+
+    subject_remove_observer(subject, observer[1]);
+    CU_ASSERT(cnt == 2);
+
+    subject_free(subject);
+    CU_ASSERT(cnt == 3);
+
+    subject_remove_observer(monitor, observer[0]);
+    subject_free(monitor);
+}
+
 CU_pSuite get_observer_suite(void) {
     CU_pSuite suite;
 
     suite = CU_add_suite("Suite_observer", NULL, NULL);
     CU_ADD_TEST(suite, test_observer);
+    CU_ADD_TEST(suite, test_monitor);
 
     return suite;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/prop.c	Mon Dec 15 10:17:20 2008 +0800
@@ -0,0 +1,84 @@
+#include "mb_prop.h"
+
+#define ASSERT(x)
+
+
+static
+mb_prop_entry_t *_mb_prop_find(mb_prop_store_t *prop_store, int id) {
+    mb_prop_entry_t *entry;
+
+    for(entry = STAILQ_HEAD(prop_store->entries);
+	entry != NULL;
+	entry = STAILQ_NEXT(mb_prop_entry_t, next, entry)) {
+	if(entry->id == id)
+	    return entry;
+    }
+    
+    return NULL;
+}
+
+void mb_prop_store_destroy(mb_prop_store_t *prop_store) {
+    mb_prop_entry_t *entry, *last;
+
+    last = STAILQ_HEAD(prop_store->entries);
+    if(last == NULL)
+	return;
+
+    for(entry = STAILQ_NEXT(mb_prop_entry_t, next, entry);
+	entry != NULL;
+	entry = STAILQ_NEXT(mb_prop_entry_t, next, entry)) {
+	STAILQ_REMOVE(prop_store->entries, mb_prop_entry_t, next, last);
+	elmpool_elm_free(prop_store->entry_pool, last);
+	last = entry;
+    }
+    STAILQ_REMOVE(prop_store->entries, mb_prop_entry_t, next, last);
+    elmpool_elm_free(prop_store->entry_pool, last);
+}
+
+void *mb_prop_set(mb_prop_store_t *prop_store, int id, void *value) {
+    mb_prop_entry_t *entry;
+    void *old;
+
+    entry = _mb_prop_find(prop_store, id);
+    if(entry) {
+	old = entry->value;
+	entry->value = value;
+	return old;
+    }
+
+    entry = elmpool_elm_alloc(prop_store->entry_pool);
+    ASSERT(entry != NULL);
+    entry->id = id;
+    entry->value = value;
+    STAILQ_INS(prop_store->entries, mb_prop_entry_t, next, entry);
+
+    return NULL;
+}
+
+void *mb_prop_get(mb_prop_store_t *prop_store, int id) {
+    mb_prop_entry_t *entry;
+
+    entry = _mb_prop_find(prop_store, id);
+    if(entry)
+	return entry->value;
+
+    return NULL;
+}
+
+void mb_prop_del(mb_prop_store_t *prop_store, int id) {
+    mb_prop_entry_t *entry;
+
+    entry = _mb_prop_find(prop_store, id);
+    if(entry)
+	STAILQ_REMOVE(prop_store->entries, mb_prop_entry_t, next, entry);
+}
+
+int mb_prop_has(mb_prop_store_t *prop_store, int id) {
+    mb_prop_entry_t *entry;
+
+    entry = _mb_prop_find(prop_store, id);
+    if(entry)
+	return 1;
+    
+    return 0;
+}
--- a/src/redraw_man.c	Sun Dec 14 11:23:34 2008 +0800
+++ b/src/redraw_man.c	Mon Dec 15 10:17:20 2008 +0800
@@ -7,7 +7,11 @@
 #include "mb_tools.h"
 #include "mb_redraw_man.h"
 #include "mb_observer.h"
+#include "mb_prop.h"
 
+#ifndef ASSERT
+#define ASSERT(x)
+#endif
 
 /* NOTE: bounding box should also consider width of stroke.
  */
@@ -235,55 +239,116 @@
     coord->num_members--;
 }
 
+static void mouse_event_root_dummy(event_t *evt, void *arg) {
+}
+
+static void mouse_event_interpreter(event_t *evt, void *arg) {
+    mouse_event_t *mevt = (mouse_event_t *)evt;
+    redraw_man_t *rdman = (redraw_man_t *)arg;
+    mb_obj_t *obj;
+    mouse_event_t new_evt;
+    coord_t *coord;
+    shape_t *shape;
+    
+    ASSERT(evt->type == EVT_MOUSE_MOVE_RAW);
+    
+    obj = (mb_obj_t *)subject_get_object(evt->cur_tgt);
+    if(rdman->last_mouse_over == obj) {
+	evt->type = EVT_MOUSE_MOVE;
+	return;
+    }
+    
+    new_evt.x = mevt->x;
+    new_evt.y = mevt->y;
+    new_evt.but_state = mevt->but_state;
+    new_evt.button = mevt->button;
+    
+    if(rdman->last_mouse_over != NULL) {
+	new_evt.event.type = EVT_MOUSE_OUT;
+	if(IS_MBO_COORD(rdman->last_mouse_over)) {
+	    coord = (coord_t *)rdman->last_mouse_over;
+	    subject_notify(coord->mouse_event, (event_t *)&new_evt);
+	} else if(IS_MBO_SHAPES(rdman->last_mouse_over)) {
+	    shape = (shape_t *)rdman->last_mouse_over;
+	    ASSERT(shape->geo != NULL);
+	    subject_notify(shape->geo->mouse_event, (event_t *)&new_evt); 
+	}
+    }
+
+    new_evt.event.type = EVT_MOUSE_OVER;
+    subject_notify(evt->cur_tgt, (event_t *)&new_evt);
+    rdman->last_mouse_over = obj;
+    
+    evt->flags |= EVTF_STOP_NOTIFY;
+}
+
+static void addrm_monitor_hdlr(event_t *evt, void *arg) {
+    monitor_event_t *mevt;
+    redraw_man_t *rdman;
+    mb_obj_t *obj;
+    mb_prop_store_t *props;
+    observer_t *observer;
+    int cnt = 0;
+    
+    mevt = (monitor_event_t *)evt;
+    rdman = (redraw_man_t *)evt->tgt;
+    obj = (mb_obj_t *)subject_get_object(mevt->subject);
+    props = mb_obj_prop_store(obj);
+
+    switch(evt->type) {
+    case EVT_MONITOR_ADD:
+	if(!mb_prop_has(props, PROP_MEVT_OB_CNT))
+	    cnt = 0;
+	else
+	    cnt = (int)mb_prop_get(props, PROP_MEVT_OB_CNT);
+	
+	cnt++;
+	mb_prop_set(props, PROP_MEVT_OB_CNT, (void *)cnt);
+	if(cnt == 1) {
+	    observer =
+		subject_add_event_observer_head(mevt->subject,
+						EVT_MOUSE_MOVE_RAW,
+						mouse_event_interpreter,
+						rdman);
+	    ASSERT(observer != NULL);
+	    mb_prop_set(props, PROP_MEVT_OBSERVER, observer);
+	}
+	break;
+	
+    case EVT_MONITOR_REMOVE:
+	cnt = (int)mb_prop_get(props, PROP_MEVT_OB_CNT);
+	cnt--;
+	mb_prop_set(props, PROP_MEVT_OB_CNT, (void *)cnt);
+	if(cnt == 1) {
+	    observer = (observer_t *)mb_prop_get(props, PROP_MEVT_OBSERVER);
+	    subject_remove_observer(mevt->subject, observer);
+	    mb_prop_del(props, PROP_MEVT_OBSERVER);
+	}
+	break;
+	
+    case EVT_MONITOR_FREE:
+	break;
+    }
+}
+
 int redraw_man_init(redraw_man_t *rdman, cairo_t *cr, cairo_t *backend) {
     extern void redraw_man_destroy(redraw_man_t *rdman);
     extern int _paint_color_size;
+    observer_t *addrm_ob;
 
     memset(rdman, 0, sizeof(redraw_man_t));
 
     rdman->geo_pool = elmpool_new(sizeof(geo_t), 128);
-    if(rdman->geo_pool == NULL)
-	return ERR;
-
     rdman->coord_pool = elmpool_new(sizeof(coord_t), 16);
-    if(rdman->coord_pool == NULL) {
-	elmpool_free(rdman->geo_pool);
-	return ERR;
-    }
-
     rdman->shnode_pool = elmpool_new(sizeof(shnode_t), 16);
-    if(rdman->shnode_pool == NULL) {
-	elmpool_free(rdman->geo_pool);
-	elmpool_free(rdman->coord_pool);
-	return ERR;
-    }
-
     rdman->observer_pool = elmpool_new(sizeof(observer_t), 32);
-    if(rdman->observer_pool == NULL) {
-	elmpool_free(rdman->geo_pool);
-	elmpool_free(rdman->coord_pool);
-	elmpool_free(rdman->shnode_pool);
-	return ERR;
-    }
-
     rdman->subject_pool = elmpool_new(sizeof(subject_t), 32);
-    if(rdman->subject_pool == NULL) {
-	elmpool_free(rdman->geo_pool);
-	elmpool_free(rdman->coord_pool);
-	elmpool_free(rdman->shnode_pool);
-	elmpool_free(rdman->observer_pool);
-	return ERR;
-    }
-
     rdman->paint_color_pool = elmpool_new(_paint_color_size, 64);
-    if(rdman->subject_pool == NULL) {
-	elmpool_free(rdman->geo_pool);
-	elmpool_free(rdman->coord_pool);
-	elmpool_free(rdman->shnode_pool);
-	elmpool_free(rdman->observer_pool);
-	elmpool_free(rdman->subject_pool);
-	return ERR;
-    }
+    rdman->pent_pool = elmpool_new(sizeof(mb_prop_entry_t), 128);
+    if(!(rdman->geo_pool && rdman->coord_pool && rdman->shnode_pool &&
+	 rdman->observer_pool && rdman->subject_pool &&
+	 rdman->paint_color_pool))
+	goto err;
 
     rdman->ob_factory.subject_alloc = ob_subject_alloc;
     rdman->ob_factory.subject_free = ob_subject_free;
@@ -293,12 +358,24 @@
 
     rdman->redraw =
 	subject_new(&rdman->ob_factory, rdman, OBJT_RDMAN);
+    rdman->addrm_monitor =
+	subject_new(&rdman->ob_factory, rdman, OBJT_RDMAN);
+    if(!(rdman->redraw && rdman->addrm_monitor))
+	goto err;
+
+    addrm_ob = subject_add_observer(rdman->addrm_monitor,
+				    addrm_monitor_hdlr, NULL);
+    if(addrm_ob == NULL)
+	goto err;
+
+    rdman->last_mouse_over = NULL;
 
     rdman->root_coord = elmpool_elm_alloc(rdman->coord_pool);
     if(rdman->root_coord == NULL)
 	redraw_man_destroy(rdman);
     rdman->n_coords = 1;
     coord_init(rdman->root_coord, NULL);
+    mb_prop_store_init(&rdman->root_coord->obj.props, rdman->pent_pool);
     rdman->root_coord->mouse_event = subject_new(&rdman->ob_factory,
 						 rdman->root_coord,
 						 OBJT_COORD);
@@ -311,8 +388,33 @@
 
     STAILQ_INIT(rdman->shapes);
     STAILQ_INIT(rdman->paints);
+    
+    /* \note To make root coord always have at leat one observer.
+     * It triggers mouse interpreter to be installed on root.
+     */
+    subject_set_monitor(rdman->root_coord->mouse_event,
+			rdman->addrm_monitor);
+    subject_add_observer(rdman->root_coord->mouse_event,
+			 mouse_event_root_dummy, NULL);
 
     return OK;
+
+ err:
+    if(rdman->geo_pool)
+	elmpool_free(rdman->geo_pool);
+    if(rdman->coord_pool)
+	elmpool_free(rdman->coord_pool);
+    if(rdman->shnode_pool)
+	elmpool_free(rdman->shnode_pool);
+    if(rdman->observer_pool)
+	elmpool_free(rdman->observer_pool);
+    if(rdman->subject_pool)
+	elmpool_free(rdman->subject_pool);
+    if(rdman->paint_color_pool)
+	elmpool_free(rdman->paint_color_pool);
+    if(rdman->pent_pool)
+	elmpool_free(rdman->pent_pool);
+    return ERR;
 }
 
 void redraw_man_destroy(redraw_man_t *rdman) {
@@ -332,9 +434,11 @@
 	}
 	rdman_coord_free(rdman, saved_coord);
     }
+#if 0
     FORMEMBERS(saved_coord, member) {
 	rdman_shape_free(rdman, member->shape);
     }
+#endif
     /* Resources of root_coord is free by elmpool_free() or
      * caller; for canvas
      */
@@ -356,6 +460,7 @@
     elmpool_free(rdman->observer_pool);
     elmpool_free(rdman->subject_pool);
     elmpool_free(rdman->paint_color_pool);
+    elmpool_free(rdman->pent_pool);
 
     DARRAY_DESTROY(&rdman->dirty_coords);
     DARRAY_DESTROY(&rdman->dirty_geos);
@@ -397,6 +502,7 @@
     
     geo_init(geo);
     geo->mouse_event = subject_new(&rdman->ob_factory, geo, OBJT_GEO);
+    subject_set_monitor(geo->mouse_event, rdman->addrm_monitor);
 
     geo_attach_coord(geo, coord);
 
@@ -442,14 +548,18 @@
     }
 
     if(geo != NULL) {
+	subject_free(geo->mouse_event);
 	geo_detach_coord(geo, shape->coord);
 	sh_detach_coord(shape);
 	sh_detach_geo(shape);
-	subject_free(geo->mouse_event);
 	elmpool_elm_free(rdman->geo_pool, geo);
     }
     STAILQ_REMOVE(rdman->shapes, shape_t, sh_next, shape);
     shape->free(shape);
+
+    if(rdman->last_mouse_over == (mb_obj_t *)shape)
+	rdman->last_mouse_over = NULL;
+    
     return OK;
 }
 
@@ -517,9 +627,11 @@
 	return NULL;
 
     coord_init(coord, parent);
+    mb_prop_store_init(&coord->obj.props, rdman->pent_pool);
     coord->mouse_event = subject_new(&rdman->ob_factory,
 				     coord,
 				     OBJT_COORD);
+    subject_set_monitor(coord->mouse_event, rdman->addrm_monitor);
     /*! \note default opacity == 1 */
     coord->opacity = 1;
     if(parent)
@@ -613,7 +725,7 @@
     return OK;
 }
 
-static _rdman_coord_free_members(redraw_man_t *rdman, coord_t *coord) {
+static int _rdman_coord_free_members(redraw_man_t *rdman, coord_t *coord) {
     geo_t *member;
     shape_t *shape;
     int r;
@@ -1468,7 +1580,7 @@
     dummy->trans_cnt = 0;
     dummy->draw_cnt = 0;
     dummy->shape.free = sh_dummy_free;
-
+    
     rdman_shape_man(rdman, (shape_t *)dummy);
 
     return (shape_t *)dummy;
--- a/src/shape_path.c	Sun Dec 14 11:23:34 2008 +0800
+++ b/src/shape_path.c	Mon Dec 15 10:17:20 2008 +0800
@@ -682,7 +682,7 @@
     path = (sh_path_t *)malloc(sizeof(sh_path_t));
     /*! \todo Remove this memset()? */
     memset(&path->shape, 0, sizeof(shape_t));
-    MBO_TYPE(path) = MBO_PATH;
+    mb_obj_init(path, MBO_PATH);
     path->cmd_len = cmd_cnt;
     path->arg_len = arg_cnt;
     path->fix_arg_len = fix_arg_cnt;
@@ -722,7 +722,7 @@
     path = (sh_path_t *)malloc(sizeof(sh_path_t));
     /*! \todo Remove this memset()? */
     memset(&path->shape, 0, sizeof(shape_t));
-    MBO_TYPE(path) = MBO_PATH;
+    mb_obj_init(path, MBO_PATH);
     path->cmd_len = strlen(commands);
     path->arg_len = arg_cnt;
     path->fix_arg_len = fix_arg_cnt;
--- a/src/shape_rect.c	Sun Dec 14 11:23:34 2008 +0800
+++ b/src/shape_rect.c	Mon Dec 15 10:17:20 2008 +0800
@@ -27,7 +27,7 @@
 
     memset(rect, 0, sizeof(sh_rect_t));
 
-    MBO_TYPE(rect) = MBO_RECT;
+    mb_obj_init(rect, MBO_RECT);
     rect->x = x;
     rect->y = y;
     rect->w = w;
--- a/src/shape_text.c	Sun Dec 14 11:23:34 2008 +0800
+++ b/src/shape_text.c	Mon Dec 15 10:17:20 2008 +0800
@@ -42,7 +42,7 @@
 	return NULL;
 
     memset(text, 0, sizeof(sh_text_t));
-    MBO_TYPE(text) = MBO_TEXT;
+    mb_obj_init(text, MBO_TEXT);
     text->data = strdup(txt);
     if(text->data == NULL) {
 	free(text);
--- a/tools/mb_c_source.m4	Sun Dec 14 11:23:34 2008 +0800
+++ b/tools/mb_c_source.m4	Mon Dec 15 10:17:20 2008 +0800
@@ -355,6 +355,9 @@
 
 #ifndef MB_LSYM_GET_OBJ_WITH_NAME
 #define MB_LSYM_GET_OBJ_WITH_NAME
+
+#define MB_SPRITE_OFF_2_PTR(x, off) (((void *)(x)) + (off))
+
 static
 mb_obj_t *mb_lsym_get_obj_with_name(mb_sprite_lsym_t *lsym, const char *sym) {
     int i;
@@ -362,7 +365,8 @@
     for(i = 0; i < lsym->num_entries; i++) {
 	if(strcmp(lsym->entries[i].sym, sym) != 0)
 	    continue;
-	return (mb_obj_t *)*((int*)(((void *)lsym) + lsym->entries[i].offset));
+	return *(mb_obj_t **)MB_SPRITE_OFF_2_PTR(lsym,
+					         lsym->entries[i].offset);
     }
     return NULL;
 }