kuuko pushed a commit to branch master.

http://git.enlightenment.org/bindings/python/python-efl.git/commit/?id=3661a78c96a6a264f131d5b9d344248180b68c6b

commit 3661a78c96a6a264f131d5b9d344248180b68c6b
Author: Kai Huuhko <kai.huu...@gmail.com>
Date:   Tue Feb 2 20:29:21 2016 +0200

    Elm.MultiButtonEntry: Attempt to fix leaks, add more docs
---
 efl/elementary/multibuttonentry.pxi          | 150 +++++++++++++++++++++------
 efl/elementary/multibuttonentry_cdef.pxi     |   4 +-
 examples/elementary/test_multibuttonentry.py |  46 +++++---
 3 files changed, 153 insertions(+), 47 deletions(-)

diff --git a/efl/elementary/multibuttonentry.pxi 
b/efl/elementary/multibuttonentry.pxi
index aa874bc..13c28e3 100644
--- a/efl/elementary/multibuttonentry.pxi
+++ b/efl/elementary/multibuttonentry.pxi
@@ -18,24 +18,51 @@
 
 include "multibuttonentry_cdef.pxi"
 
+class MultiButtonEntryFilterOut(Exception):
+    """An exception you may raise in an item filter callback if you wish to 
prevent addition of the item"""
+    pass
+
 cdef Eina_Bool _multibuttonentry_filter_callback(Evas_Object *obj, \
-    const char *item_label, void *item_data, void *data) with gil:
+    char *item_label, void *item_data, void *data) with gil:
 
     cdef:
         MultiButtonEntry mbe = object_from_instance(obj)
-        bint ret
-
-    (callback, a, ka) = <object>data
-
-    try:
-        ret = callback(mbe, _ctouni(item_label), *a, **ka)
-    except Exception:
-        traceback.print_exc()
-
-    return ret
+        object ret
+        list callbacks = mbe._item_filters
+
+    for func, args, kargs in callbacks:
+        try:
+            # raise MultiButtonEntryFilterOut -> cancels item add
+            # ret is None -> no change, continue to next filter
+            # ret is not None -> change label to value of ret, continue to 
next filter
+            ret = func(mbe, _ctouni(item_label), *args, **kargs)
+        except MultiButtonEntryFilterOut:
+            #free(item_label) # FIXME: This will result in a double free, find 
out if it's bad elm documentation
+                              #        or wrong ref handling on our side.
+
+            #item_label = NULL
+            return 0
+        except Exception:
+            traceback.print_exc()
+            continue
+
+        if ret:
+            if not isinstance(ret, basestring):
+                EINA_LOG_DOM_WARN(PY_EFL_ELM_LOG_DOMAIN,
+                    "Ignoring invalid return value from MultiButtonEntry item 
filter callback!", NULL)
+                continue
+            if isinstance(ret, unicode): ret = PyUnicode_AsUTF8String(ret)
+            #free(item_label) # FIXME: This will result in a double free, find 
out if it's bad elm documentation
+                              #        or wrong ref handling on our side.
+
+            item_label = strdup(ret) # Elm will manage the string
+                                     # FIXME: This doesn't apply, why?
+
+    return 1
 
 cdef char * _multibuttonentry_format_cb(int count, void *data) with gil:
-    (callback, a, ka) = <object>data
+    cdef MultiButtonEntry obj = <MultiButtonEntry>data
+    (callback, a, ka) = obj.internal_data["multibuttonentry_format_cb"]
 
     try:
         s = callback(count, *a, **ka)
@@ -44,8 +71,7 @@ cdef char * _multibuttonentry_format_cb(int count, void 
*data) with gil:
         traceback.print_exc()
         return NULL
 
-    # FIXME: leak here
-    return strdup(<char *>s)
+    return strdup(s) # Elm will manage the string
 
 
 cdef class MultiButtonEntryItem(ObjectItem):
@@ -198,6 +224,8 @@ cdef class MultiButtonEntry(Object):
 
     """
 
+    cdef list _item_filters
+
     def __init__(self, evasObject parent, *args, **kwargs):
         """MultiButtonEntry(...)
 
@@ -213,6 +241,7 @@ cdef class MultiButtonEntry(Object):
             _py_elm_mbe_item_added_cb, NULL
             )
         self._set_properties_from_keyword_args(kwargs)
+        self._item_filters = list()
 
     property entry:
         """The Entry object child of the multibuttonentry.
@@ -227,7 +256,10 @@ cdef class MultiButtonEntry(Object):
         return object_from_instance(elm_multibuttonentry_entry_get(self.obj))
 
     property expanded:
-        """The expanded state of the multibuttonentry.
+        """Control the multibuttonentry to expanded state.
+
+        In expanded state, the complete entry will be displayed.
+        Otherwise, only single line of the entry will be displayed.
 
         :type: bool
 
@@ -342,6 +374,11 @@ cdef class MultiButtonEntry(Object):
             return None
 
     property items:
+        """List of items in the multibuttonentry
+
+        :type: list
+
+        """
         def __get__(self):
             return 
_object_item_list_to_python(elm_multibuttonentry_items_get(self.obj))
 
@@ -349,6 +386,11 @@ cdef class MultiButtonEntry(Object):
         return 
_object_item_list_to_python(elm_multibuttonentry_items_get(self.obj))
 
     property first_item:
+        """The first item in the multibuttonentry
+
+        :type: :class:`MultiButtonEntryItem`
+
+        """
         def __get__(self):
             return 
_object_item_to_python(elm_multibuttonentry_first_item_get(self.obj))
 
@@ -356,6 +398,11 @@ cdef class MultiButtonEntry(Object):
         return 
_object_item_to_python(elm_multibuttonentry_first_item_get(self.obj))
 
     property last_item:
+        """The last item in the multibuttonentry
+
+        :type: :class:`MultiButtonEntryItem`
+
+        """
         def __get__(self):
             return 
_object_item_to_python(elm_multibuttonentry_last_item_get(self.obj))
 
@@ -363,6 +410,11 @@ cdef class MultiButtonEntry(Object):
         return 
_object_item_to_python(elm_multibuttonentry_last_item_get(self.obj))
 
     property selected_item:
+        """The selected item in the multibuttonentry
+
+        :type: :class:`MultiButtonEntryItem`
+
+        """
         def __get__(self):
             return 
_object_item_to_python(elm_multibuttonentry_selected_item_get(self.obj))
 
@@ -370,29 +422,66 @@ cdef class MultiButtonEntry(Object):
         return 
_object_item_to_python(elm_multibuttonentry_selected_item_get(self.obj))
 
     def clear(self):
+        """Remove all items in the multibuttonentry"""
         elm_multibuttonentry_clear(self.obj)
 
     def filter_append(self, func, *args, **kwargs):
-        cbdata = (func, args, kwargs)
+        """Append an item filter function for text inserted in the 
Multibuttonentry
+
+        Append the given callback to the list. This function will be called
+        whenever any text is inserted into the Multibuttonentry, with the text 
to be inserted
+        as a parameter. The callback function is free to alter the text in any 
way
+        it wants with the modified text passed back as return value.
+        If the new text is to be left intact, the function can return ``None``.
+        If the new text is to be discarded, the function must raise
+        :class:`MultiButtonEntryFilterOut`.
+        This will also prevent any following filters from being called.
 
-        elm_multibuttonentry_item_filter_append(self.obj,
-            _multibuttonentry_filter_callback, <void *>cbdata)
+        Callback signature::
 
-        # FIXME: leak here
-        Py_INCREF(cbdata)
+            func(obj, text, *args, **kwargs) -> modified text or None
+
+        """
+        if not self._item_filters:
+            elm_multibuttonentry_item_filter_append(
+                self.obj,
+                _multibuttonentry_filter_callback,
+                NULL)
+
+        cbdata = (func, args, kwargs)
+        self._item_filters.append(cbdata)
 
     def filter_prepend(self, func, *args, **kwargs):
+        """Prepend a filter function for text inserted in the Multibuttonentry
+
+        Prepend the given callback to the list. See :meth:`filter_append`
+        for more information
+
+        """
+        if not self._item_filters:
+            elm_multibuttonentry_item_filter_append(
+                self.obj,
+                _multibuttonentry_filter_callback,
+                NULL)
+
         cbdata = (func, args, kwargs)
+        self._item_filters[0] = cbdata
 
-        elm_multibuttonentry_item_filter_prepend(self.obj,
-            _multibuttonentry_filter_callback, <void *>cbdata)
+    def filter_remove(self, func, *args, **kwargs):
+        """Remove a filter from the list
 
-        # FIXME: leak here
-        Py_INCREF(cbdata)
+        Removes the given callback from the filter list. See 
:meth:`filter_append`
+        for more information.
 
-    #TODO
-    #def filter_remove(self, func, *args, **kwargs):
-        #pass
+        """
+        cbdata = (func, args, kwargs)
+        self._item_filters.remove(cbdata)
+
+        if not self._item_filters:
+            elm_multibuttonentry_item_filter_remove(
+                self.obj,
+                _multibuttonentry_filter_callback,
+                NULL)
 
     property editable:
         """Whether the multibuttonentry is to be editable or not.
@@ -428,15 +517,16 @@ cdef class MultiButtonEntry(Object):
 
         """
         if func is None:
+            self.internal_data["multibuttonentry_format_cb"] = None
             elm_multibuttonentry_format_function_set(self.obj, NULL, NULL)
             return
 
         cbdata = (func, args, kwargs)
+        self.internal_data["multibuttonentry_format_cb"] = cbdata
+
         elm_multibuttonentry_format_function_set(self.obj,
                                                 _multibuttonentry_format_cb,
-                                                <void *>cbdata)
-        # FIXME: leak here
-        Py_INCREF(cbdata)
+                                                <void *>self)
 
     def callback_item_selected_add(self, func, *args, **kwargs):
         self._callback_add_full("item,selected", _cb_object_item_conv, func, 
args, kwargs)
diff --git a/efl/elementary/multibuttonentry_cdef.pxi 
b/efl/elementary/multibuttonentry_cdef.pxi
index 1168b48..5b2aa34 100644
--- a/efl/elementary/multibuttonentry_cdef.pxi
+++ b/efl/elementary/multibuttonentry_cdef.pxi
@@ -1,6 +1,6 @@
 cdef extern from "Elementary.h":
 
-    ctypedef Eina_Bool (*Elm_Multibuttonentry_Item_Filter_Cb)(Evas_Object 
*obj, const char *item_label, void *item_data, void *data)
+    ctypedef Eina_Bool (*Elm_Multibuttonentry_Item_Filter_Cb)(Evas_Object 
*obj, char *item_label, void *item_data, void *data)
     ctypedef char * (*Elm_Multibuttonentry_Format_Cb)(int count, void *data)
 
     Evas_Object             *elm_multibuttonentry_add(Evas_Object *parent)
@@ -22,7 +22,7 @@ cdef extern from "Elementary.h":
     Elm_Object_Item         *elm_multibuttonentry_item_next_get(const 
Elm_Object_Item *it)
     void                     
elm_multibuttonentry_item_filter_append(Evas_Object *obj, 
Elm_Multibuttonentry_Item_Filter_Cb func, void *data)
     void                     
elm_multibuttonentry_item_filter_prepend(Evas_Object *obj, 
Elm_Multibuttonentry_Item_Filter_Cb func, void *data)
-    # TODO: void                     
elm_multibuttonentry_item_filter_remove(Evas_Object *obj, 
Elm_Multibuttonentry_Item_Filter_Cb func, void *data)
+    void                     
elm_multibuttonentry_item_filter_remove(Evas_Object *obj, 
Elm_Multibuttonentry_Item_Filter_Cb func, void *data)
     void                    elm_multibuttonentry_editable_set(Evas_Object 
*obj, Eina_Bool editable)
     Eina_Bool               elm_multibuttonentry_editable_get(const 
Evas_Object *obj)
     void                    
elm_multibuttonentry_format_function_set(Evas_Object *obj, 
Elm_Multibuttonentry_Format_Cb f_func, const void *data)
diff --git a/examples/elementary/test_multibuttonentry.py 
b/examples/elementary/test_multibuttonentry.py
index b8ad5b6..989598f 100644
--- a/examples/elementary/test_multibuttonentry.py
+++ b/examples/elementary/test_multibuttonentry.py
@@ -1,16 +1,13 @@
 #!/usr/bin/env python
 # encoding: utf-8
 
-from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL, \
-    EXPAND_BOTH, FILL_BOTH, EXPAND_HORIZ, FILL_HORIZ
+from efl.evas import EXPAND_BOTH, FILL_BOTH, EXPAND_HORIZ, FILL_HORIZ
 from efl import elementary
-from efl.elementary.window import StandardWindow
-from efl.elementary.box import Box
-from efl.elementary.button import Button
-from efl.elementary.entry import Entry
-from efl.elementary.multibuttonentry import MultiButtonEntry
-from efl.elementary.scroller import Scroller, ELM_SCROLLER_POLICY_OFF, \
-    ELM_SCROLLER_POLICY_AUTO
+from efl.elementary import StandardWindow
+from efl.elementary import Box
+from efl.elementary import Button
+from efl.elementary import MultiButtonEntry, MultiButtonEntryFilterOut
+from efl.elementary import Scroller, ELM_SCROLLER_POLICY_OFF, 
ELM_SCROLLER_POLICY_AUTO
 
 SCROLL_POLICY_VERT = ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_AUTO
 
@@ -30,27 +27,27 @@ def cb_btn_item_prepend(btn, mbe):
     global counter
 
     counter += 1
-    item = mbe.item_prepend("item #%d" % (counter), cb_item_selected)
+    mbe.item_prepend("item #%d" % (counter), cb_item_selected)
 
 def cb_btn_item_append(btn, mbe):
     global counter
 
     counter += 1
-    item = mbe.item_append("item #%d" % (counter), cb_item_selected)
+    mbe.item_append("item #%d" % (counter), cb_item_selected)
 
 def cb_btn_item_insert_after(btn, mbe):
     global counter
 
     counter += 1
     after = mbe.selected_item
-    item = mbe.item_insert_after(after, "item #%d" % (counter), 
cb_item_selected)
+    mbe.item_insert_after(after, "item #%d" % (counter), cb_item_selected)
 
 def cb_btn_item_insert_before(btn, mbe):
     global counter
 
     counter += 1
     before = mbe.selected_item
-    item = mbe.item_insert_before(before, "item #%d" % (counter), 
cb_item_selected)
+    mbe.item_insert_before(before, "item #%d" % (counter), cb_item_selected)
 
 def cb_btn_clear2(btn, mbe):
     for item in mbe.items:
@@ -58,7 +55,13 @@ def cb_btn_clear2(btn, mbe):
 
 def cb_filter1(mbe, text):
     print(text)
-    return True
+
+def cb_filter2(mbe, text):
+    return text[:-2]
+
+def cb_filter3(mbe, text):
+    print(text)
+    #raise MultiButtonEntryFilterOut
 
 def cb_print(btn, mbe):
     for i in mbe.items:
@@ -85,6 +88,8 @@ def multibuttonentry_clicked(obj, item=None):
     mbe.callback_item_longpressed_add(cb_item_longpressed)
     mbe.part_text_set("guide", "Tap to add recipient")
     mbe.filter_append(cb_filter1)
+    mbe.filter_append(cb_filter2)
+    mbe.filter_append(cb_filter3)
     mbe.show()
 
     sc = Scroller(win, bounce=(False, True), policy=SCROLL_POLICY_VERT,
@@ -181,7 +186,18 @@ def multibuttonentry_clicked(obj, item=None):
 
 if __name__ == "__main__":
 
+    import logging
+    elog = logging.getLogger("efl")
+    elog.setLevel(logging.INFO)
+
+    elog_form = logging.Formatter(
+        "[%(name)s] %(levelname)s in %(funcName)s:%(lineno)d - %(message)s"
+        )
+    elog_hdlr = logging.StreamHandler()
+    elog_hdlr.setFormatter(elog_form)
+
+    elog.addHandler(elog_hdlr)
+
     multibuttonentry_clicked(None)
 
     elementary.run()
-

-- 


Reply via email to