Evas_Smart_Interface support.
authorglima <glima@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Wed, 4 Jul 2012 21:23:03 +0000 (21:23 +0000)
committerglima <glima@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Wed, 4 Jul 2012 21:23:03 +0000 (21:23 +0000)
This gives Evas simple (smart object) interfaces, to extend the object behaviorextension beyond the limits of mere sub-classing.

Patch by: Gustavo Lima Chaves <glima@profusion.mobi>

git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/evas@73302 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

src/lib/Evas.h
src/lib/canvas/evas_object_smart.c
src/lib/canvas/evas_smart.c
src/lib/include/evas_private.h

index f4b0c4d..9f15764 100644 (file)
@@ -574,6 +574,22 @@ typedef struct _Evas_Precision_Position    Evas_Precision_Position;   /**< assoc
 typedef struct _Evas_Smart_Class Evas_Smart_Class;
 
 /**
+ * @typedef Evas_Smart_Interface
+ *
+ * A smart object's @b base interface definition
+ *
+ * An Evas interface is exactly like the OO-concept: an 'contract' or
+ * API a given object is declared to support. A smart object may have
+ * more than one interface, thus extending the behavior it gets from
+ * sub-classing.
+ *
+ * @since 1.3
+ *
+ * @ingroup Evas_Smart_Group
+ */
+typedef struct _Evas_Smart_Interface         Evas_Smart_Interface;
+
+/**
  * @typedef Evas_Smart_Cb_Description
  *
  * A smart object callback description, used to provide introspection
@@ -9633,11 +9649,34 @@ struct _Evas_Smart_Class
 
    const Evas_Smart_Class          *parent; /**< this class inherits from this parent */
    const Evas_Smart_Cb_Description *callbacks; /**< callbacks at this level, @c NULL terminated */
-   void                            *interfaces; /**< to be used in a future near you */
+   const Evas_Smart_Interface     **interfaces; /**< #Evas_Smart_Interface pointers array, @c NULL terminated. These will be the interfaces supported at this level for an object (parents may have others) @since 1.3 */
    const void                      *data;
 };
 
 /**
+ * @struct _Evas_Smart_Interface
+ *
+ * A smart object's @b base interface definition
+ *
+ * Every Evas interface must have a name field, pointing to a global,
+ * constant string variable. This string pointer will be the only way
+ * of retrieving back a given interface from a smart object. Two
+ * function pointers must be defined, too, which will be called at
+ * object creation and deletion times.
+ *
+ * @since 1.3
+ *
+ * @ingroup Evas_Smart_Group
+ */
+struct _Evas_Smart_Interface
+{
+   const char *name; /**< Name of the given interface */
+   unsigned    private_size; /**< Size, in bytes, of the interface's private dada blob. This will be allocated and freed automatically for you. Get it with evas_object_smart_interface_data_get(). */
+   Eina_Bool   (*add)(Evas_Object *obj); /**< Function to be called at object creation time */
+   void        (*del)(Evas_Object *obj); /**< Function to be called at object deletion time */
+};
+
+/**
  * @struct _Evas_Smart_Cb_Description
  *
  * Describes a callback issued by a smart object
@@ -9847,6 +9886,96 @@ struct _Evas_Smart_Cb_Description
   }
 
 /**
+ * @def EVAS_SMART_SUBCLASS_IFACE_NEW
+ *
+ * @since 1.3
+ *
+ * Convenience macro to subclass a given Evas smart class. This is the
+ * same as #EVAS_SMART_SUBCLASS_NEW, but now <b>declaring smart
+ * interfaces</b> besides the smart callbacks.
+ *
+ * @param smart_name The name used for the smart class. e.g:
+ *                   @c "Evas_Object_Box".
+ * @param prefix Prefix used for all variables and functions defined
+ *               and referenced by this macro.
+ * @param api_type Type of the structure used as API for the smart
+ *                 class. Either #Evas_Smart_Class or something
+ *                 derived from it.
+ * @param parent_type Type of the parent class API.
+ * @param parent_func Function that gets the parent class. e.g:
+ *                    evas_object_box_smart_class_get().
+ * @param cb_desc Array of smart callback descriptions for this smart
+ *                class.
+ * @param ifaces Array of Evas smart interafaces for this smart
+ *               class.
+ *
+ * This macro saves some typing when writing a smart class derived
+ * from another one. In order to work, the user @b must provide some
+ * functions adhering to the following guidelines:
+ *  - @<prefix@>_smart_set_user(): the @b internal @c _smart_set
+ *    function (defined by this macro) will call this one, provided by
+ *    the user, after inheriting everything from the parent, which
+ *    should <b>take care of setting the right member functions for
+ *    the class</b>, both overrides and extensions, if any.
+ *  - If this new class should be subclassable as well, a @b public
+ *    @c _smart_set() function is desirable to fill in the class used as
+ *    parent by the children. It's up to the user to provide this
+ *    interface, which will most likely call @<prefix@>_smart_set() to
+ *    get the job done.
+ *
+ * After the macro's usage, the following will be defined for use:
+ *  - @<prefix@>_parent_sc: A pointer to the @b parent smart
+ *    class. When calling parent functions from overloaded ones, use
+ *    this global variable.
+ *  - @<prefix@>_smart_class_new(): this function returns the
+ *    #Evas_Smart needed to create smart objects with this class,
+ *    which should be passed to evas_object_smart_add().
+ *
+ * @warning @p smart_name has to be a pointer to a globally available
+ * string! The smart class created here will just have a pointer set
+ * to that, and all object instances will depend on it for smart class
+ * name lookup.
+ *
+ * @ingroup Evas_Smart_Group
+ */
+#define EVAS_SMART_SUBCLASS_IFACE_NEW(smart_name,          \
+                                      prefix,              \
+                                      api_type,            \
+                                      parent_type,         \
+                                      parent_func,         \
+                                      cb_desc,             \
+                                      ifaces)              \
+  static const parent_type * prefix##_parent_sc = NULL;    \
+  static void prefix##_smart_set_user(api_type * api);     \
+  static void prefix##_smart_set(api_type * api)           \
+  {                                                        \
+     Evas_Smart_Class *sc;                                 \
+     if (!(sc = (Evas_Smart_Class *)api))                  \
+       return;                                             \
+     if (!prefix##_parent_sc)                              \
+       prefix##_parent_sc = parent_func();                 \
+     evas_smart_class_inherit(sc, prefix##_parent_sc);     \
+     prefix##_smart_set_user(api);                         \
+  }                                                        \
+  static Evas_Smart *prefix##_smart_class_new(void)        \
+  {                                                        \
+     static Evas_Smart *smart = NULL;                      \
+     static api_type api;                                  \
+     if (!smart)                                           \
+       {                                                   \
+          Evas_Smart_Class *sc = (Evas_Smart_Class *)&api; \
+          memset(&api, 0, sizeof(api_type));               \
+          sc->version = EVAS_SMART_CLASS_VERSION;          \
+          sc->name = smart_name;                           \
+          sc->callbacks = cb_desc;                         \
+          sc->interfaces = ifaces;                         \
+          prefix##_smart_set(&api);                        \
+          smart = evas_smart_class_new(sc);                \
+       }                                                   \
+     return smart;                                         \
+  }
+
+/**
  * @def EVAS_SMART_DATA_ALLOC
  *
  * Convenience macro to allocate smart data only if needed.
@@ -10462,6 +10591,34 @@ EAPI void         evas_object_smart_callbacks_descriptions_get(const Evas_Object
 EAPI void         evas_object_smart_callback_description_find(const Evas_Object *obj, const char *name, const Evas_Smart_Cb_Description **class_description, const Evas_Smart_Cb_Description **instance_description) EINA_ARG_NONNULL(1, 2);
 
 /**
+ * Retrieve an Evas smart object's interface, by name string pointer.
+ *
+ * @param obj An Evas smart object.
+ * @param name Name string of the desired interface, which must be the
+ *             same pointer used at the interface's declarion, when
+ *             creating the smart object @a obj.
+ *
+ * @since 1.3
+ *
+ * @return The interface's handle pointer, if found, @c NULL
+ * otherwise.
+ */
+const void       *evas_object_smart_interface_get(const Evas_Object *obj, const char *name);
+
+/**
+ * Retrieve an Evas smart object interface's <b>private data</b>.
+ *
+ * @param obj An Evas smart object.
+ * @param iface The given object's interface handle.
+ *
+ * @since 1.3
+ *
+ * @return The object interface's private data blob pointer, if found,
+ * @c NULL otherwise.
+ */
+void             *evas_object_smart_interface_data_get(const Evas_Object *obj, const Evas_Smart_Interface *iface);
+
+/**
  * Mark smart object as changed, dirty.
  *
  * @param obj The given Evas smart object
index 3d9c89d..28ed4cb 100644 (file)
@@ -100,6 +100,54 @@ evas_object_smart_data_get(const Evas_Object *obj)
    return o->data;
 }
 
+EAPI const void *
+evas_object_smart_interface_get(const Evas_Object *obj,
+                                const char *name)
+{
+   unsigned int i;
+   Evas_Smart *s;
+
+   MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
+   return NULL;
+   MAGIC_CHECK_END();
+
+   s = evas_object_smart_smart_get(obj);
+
+   for (i = 0; i < s->interfaces.size; i++)
+     {
+        const Evas_Smart_Interface *iface;
+
+        iface = s->interfaces.array[i];
+
+        if (iface->name == name)
+          return iface;
+     }
+
+   return NULL;
+}
+
+EAPI void *
+evas_object_smart_interface_data_get(const Evas_Object *obj,
+                                     const Evas_Smart_Interface *iface)
+{
+   unsigned int i;
+   Evas_Smart *s;
+
+   MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
+   return NULL;
+   MAGIC_CHECK_END();
+
+   s = evas_object_smart_smart_get(obj);
+
+   for (i = 0; i < s->interfaces.size; i++)
+     {
+        if (iface == s->interfaces.array[i])
+          return obj->interface_privates[i];
+     }
+
+   return NULL;
+}
+
 EAPI Evas_Smart *
 evas_object_smart_smart_get(const Evas_Object *obj)
 {
@@ -311,10 +359,76 @@ _evas_object_smart_members_all_del(Evas_Object *obj)
      }
 }
 
+static void
+_evas_smart_class_ifaces_private_data_alloc(Evas_Object *obj,
+                                            Evas_Smart *s)
+{
+   unsigned int i, total_priv_sz = 0;
+   const Evas_Smart_Class *sc;
+   unsigned char *ptr;
+
+   /* get total size of interfaces private data */
+   for (sc = s->smart_class; sc; sc = sc->parent)
+     {
+        const Evas_Smart_Interface **ifaces_array = sc->interfaces;
+        if (!ifaces_array) continue;
+
+        while (*ifaces_array)
+          {
+             const Evas_Smart_Interface *iface = *ifaces_array;
+
+             if (!iface->name) break;
+
+             if (iface->private_size > 0)
+               {
+                  unsigned int size = iface->private_size;
+
+                  if (size % sizeof(void *) != 0)
+                    size += sizeof(void *) - (size % sizeof(void *));
+                  total_priv_sz += size;
+               }
+
+             ifaces_array++;
+          }
+     }
+
+   obj->interface_privates = malloc
+       (s->interfaces.size * sizeof(void *) + total_priv_sz);
+   if (!obj->interface_privates)
+     {
+        ERR("malloc failed!");
+        return;
+     }
+
+   /* make private data array ptrs point to right places, WHICH LIE ON
+    * THE SAME STRUCT, AFTER THE # OF INTERFACES COUNT */
+   ptr = (unsigned char *)(obj->interface_privates + s->interfaces.size);
+   for (i = 0; i < s->interfaces.size; i++)
+     {
+        unsigned int size;
+
+        size = s->interfaces.array[i]->private_size;
+
+        if (size == 0)
+          {
+             obj->interface_privates[i] = NULL;
+             continue;
+          }
+
+        obj->interface_privates[i] = ptr;
+        memset(ptr, 0, size);
+
+        if (size % sizeof(void *) != 0)
+          size += sizeof(void *) - (size % sizeof(void *));
+        ptr += size;
+     }
+}
+
 EAPI Evas_Object *
 evas_object_smart_add(Evas *e, Evas_Smart *s)
 {
    Evas_Object *obj;
+   unsigned int i;
 
    MAGIC_CHECK(e, Evas, MAGIC_EVAS);
    return NULL;
@@ -332,8 +446,26 @@ evas_object_smart_add(Evas *e, Evas_Smart *s)
 
    evas_object_smart_use(s);
 
+   _evas_smart_class_ifaces_private_data_alloc(obj, s);
+
    if (s->smart_class->add) s->smart_class->add(obj);
 
+   for (i = 0; i < s->interfaces.size; i++)
+     {
+        const Evas_Smart_Interface *iface;
+
+        iface = s->interfaces.array[i];
+        if (iface->add)
+          {
+             if (!iface->add(obj))
+               {
+                  ERR("failed to create interface %s\n", iface->name);
+                  evas_object_del(obj);
+                  return NULL;
+               }
+          }
+     }
+
    return obj;
 }
 
@@ -753,11 +885,25 @@ void
 evas_object_smart_del(Evas_Object *obj)
 {
    Evas_Smart *s;
+   unsigned int i;
 
    if (obj->delete_me) return;
    s = obj->smart.smart;
+
    if ((s) && (s->smart_class->del)) s->smart_class->del(obj);
    if (obj->smart.parent) evas_object_smart_member_del(obj);
+
+   for (i = 0; i < s->interfaces.size; i++)
+     {
+        const Evas_Smart_Interface *iface;
+
+        iface = s->interfaces.array[i];
+        if (iface->del) iface->del(obj);
+     }
+
+   free(obj->interface_privates);
+   obj->interface_privates = NULL;
+
    if (s) evas_object_smart_unuse(s);
 }
 
index 0cfba05..f39721d 100644 (file)
@@ -3,6 +3,7 @@
 
 
 static void _evas_smart_class_callbacks_create(Evas_Smart *s);
+static void _evas_smart_class_interfaces_create(Evas_Smart *s);
 
 /* all public */
 
@@ -16,6 +17,8 @@ evas_smart_free(Evas_Smart *s)
    if (s->usage > 0) return;
    if (s->class_allocated) free((void *)s->smart_class);
    free(s->callbacks.array);
+   free(s->interfaces.array);
+
    free(s);
 }
 
@@ -36,6 +39,7 @@ evas_smart_class_new(const Evas_Smart_Class *sc)
 
    s->smart_class = sc;
    _evas_smart_class_callbacks_create(s);
+   _evas_smart_class_interfaces_create(s);
 
    return s;
 }
@@ -238,6 +242,7 @@ _evas_smart_class_callbacks_create(Evas_Smart *s)
 
    if (n == 0) return;
    if (!evas_smart_cb_descriptions_resize(&s->callbacks, n)) return;
+   s->callbacks.size = n;
    for (n = 0, sc = s->smart_class; sc; sc = sc->parent)
      {
         const Evas_Smart_Cb_Description *d;
@@ -247,6 +252,67 @@ _evas_smart_class_callbacks_create(Evas_Smart *s)
    evas_smart_cb_descriptions_fix(&s->callbacks);
 }
 
+static void
+_evas_smart_class_interfaces_create(Evas_Smart *s)
+{
+   unsigned int i, total_priv_sz;
+   const Evas_Smart_Class *sc;
+
+   /* get number of interfaces on the smart */
+   for (i = 0, sc = s->smart_class; sc; sc = sc->parent)
+     {
+        const Evas_Smart_Interface **ifaces_array = sc->interfaces;
+        if (!ifaces_array) continue;
+
+        while (*ifaces_array)
+          {
+             const Evas_Smart_Interface *iface = *ifaces_array;
+
+             if (!iface->name) break;
+
+             i++;
+
+             if (iface->private_size > 0)
+               {
+                  unsigned int size = iface->private_size;
+
+                  if (size % sizeof(void *) != 0)
+                    size += sizeof(void *) - (size % sizeof(void *));
+                  total_priv_sz += size;
+               }
+
+             ifaces_array++;
+          }
+     }
+
+   if (!i) return;
+
+   s->interfaces.array = malloc(i * sizeof(Evas_Smart_Interface *));
+   if (!s->interfaces.array)
+     {
+        ERR("malloc failed!");
+        return;
+     }
+
+   s->interfaces.size = i;
+
+   for (i = 0, sc = s->smart_class; sc; sc = sc->parent)
+     {
+        const Evas_Smart_Interface **ifaces_array = sc->interfaces;
+        if (!ifaces_array) continue;
+
+        while (*ifaces_array)
+          {
+             const Evas_Smart_Interface *iface = *ifaces_array;
+
+             if (!iface->name) break;
+
+             s->interfaces.array[i++] = iface;
+             ifaces_array++;
+          }
+     }
+}
+
 static int
 _evas_smart_cb_description_cmp_search(const void *p1, const void *p2)
 {
index 86dab87..18d440a 100644 (file)
@@ -45,6 +45,7 @@ typedef struct _Evas_Callbacks              Evas_Callbacks;
 typedef struct _Evas_Format                 Evas_Format;
 typedef struct _Evas_Map_Point              Evas_Map_Point;
 typedef struct _Evas_Smart_Cb_Description_Array Evas_Smart_Cb_Description_Array;
+typedef struct _Evas_Smart_Interfaces_Array Evas_Smart_Interfaces_Array;
 typedef struct _Evas_Post_Callback          Evas_Post_Callback;
 typedef struct _Evas_Coord_Touch_Point      Evas_Coord_Touch_Point;
 
@@ -245,6 +246,12 @@ struct _Evas_Smart_Cb_Description_Array
    const Evas_Smart_Cb_Description **array;
 };
 
+struct _Evas_Smart_Interfaces_Array
+{
+   unsigned int                 size;
+   const Evas_Smart_Interface **array;
+};
+
 struct _Evas_Smart
 {
    DATA32            magic;
@@ -254,6 +261,7 @@ struct _Evas_Smart
    const Evas_Smart_Class *smart_class;
 
    Evas_Smart_Cb_Description_Array callbacks;
+   Evas_Smart_Interfaces_Array interfaces;
 
    unsigned char     delete_me : 1;
    unsigned char     class_allocated : 1;
@@ -587,6 +595,10 @@ struct _Evas_Object
         int                      in_move, in_resize;
    } doing;
 
+  /* ptr array + data blob holding all interfaces private data for
+   * this object */
+   void                      **interface_privates;
+
    unsigned int                ref;
 
    unsigned char               delete_me;