1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 
5 #include <Elementary.h>
6 #include "elm_priv.h"
7 
8 // This class rely on the parent class to do all the individual holding of value,
9 // it only compute the average size of an item and answer for that property alone.
10 
11 // FIXME: handle child being removed
12 typedef struct _Efl_Ui_Average_Model_Data Efl_Ui_Average_Model_Data;
13 struct _Efl_Ui_Average_Model_Data
14 {
15    Efl_Ui_Average_Model_Data *parent;
16 
17    struct {
18       unsigned long long width;
19       unsigned long long height;
20       unsigned long long wseen;
21       unsigned long long hseen;
22    } total;
23 
24    Eina_Bool wseen : 1;
25    Eina_Bool hseen : 1;
26 };
27 
28 typedef struct _Efl_Ui_Average_Model_Update Efl_Ui_Average_Model_Update;
29 struct _Efl_Ui_Average_Model_Update
30 {
31    unsigned long long *total;
32    unsigned long long *seen;
33    unsigned int previous;
34 };
35 
36 static Eina_Value
_efl_ui_average_model_update(Eo * obj EINA_UNUSED,void * data,const Eina_Value v)37 _efl_ui_average_model_update(Eo *obj EINA_UNUSED, void *data, const Eina_Value v)
38 {
39    Efl_Ui_Average_Model_Update *request = data;
40    unsigned int now;
41 
42    if (!eina_value_uint_convert(&v, &now))
43      goto on_error;
44 
45    *(request->total) += now - request->previous;
46    if (request->seen) *(request->seen) += 1;
47 
48  on_error:
49    return v;
50 }
51 
52 static void
_efl_ui_average_model_clean(Eo * obj,void * data,const Eina_Future * dead_future EINA_UNUSED)53 _efl_ui_average_model_clean(Eo *obj, void *data, const Eina_Future *dead_future EINA_UNUSED)
54 {
55    free(data);
56 
57    efl_unref(obj);
58 }
59 
60 static Eina_Future *
_efl_ui_average_model_prepare(Eo * obj,unsigned long long * total,unsigned long long * seen,const char * property,Eina_Value * value)61 _efl_ui_average_model_prepare(Eo *obj,
62                               unsigned long long *total, unsigned long long *seen,
63                               const char *property, Eina_Value *value)
64 {
65    Efl_Ui_Average_Model_Update *update;
66    Eina_Value *previous;
67    Eina_Future *f;
68 
69    update = calloc(1, sizeof (Efl_Ui_Average_Model_Update));
70    if (!update) return efl_loop_future_rejected(obj, ENOMEM);
71 
72    previous = efl_model_property_get(obj, property);
73    if (eina_value_type_get(previous) == EINA_VALUE_TYPE_ERROR)
74      {
75         Eina_Error err;
76 
77         // Check the case when that property hasn't been set before
78         if (!eina_value_error_convert(previous, &err))
79           goto on_error;
80         if (err != EAGAIN) goto on_error;
81      }
82    else if (!eina_value_uint_convert(previous, &update->previous))
83      goto on_error;
84    eina_value_free(previous);
85 
86    update->total = total;
87    update->seen = seen;
88 
89    // As we are operating asynchronously and we want to make sure that the object
90    // survive until the transaction is commited, we will ref the object here
91    // and unref on clean. This is necessary so that a nested call of property_set
92    // on a model returned by children_slice_get, the user doesn't have to keep a
93    // reference around to do the same. It shouldn't create any problem as this
94    // future would be cancelled automatically when the parent object get destroyed.
95    efl_ref(obj);
96 
97    // We have to make the change after we fetch the old value, otherwise, well, no old value left
98    f = efl_model_property_set(efl_super(obj, EFL_UI_AVERAGE_MODEL_CLASS), property, value);
99 
100    return efl_future_then(obj, f,
101                           .success = _efl_ui_average_model_update,
102                           .free = _efl_ui_average_model_clean,
103                           .data = update);
104  on_error:
105    eina_value_free(previous);
106    free(update);
107    return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE);
108 }
109 
110 static Eina_Future *
_efl_ui_average_model_efl_model_property_set(Eo * obj,Efl_Ui_Average_Model_Data * pd,const char * property,Eina_Value * value)111 _efl_ui_average_model_efl_model_property_set(Eo *obj, Efl_Ui_Average_Model_Data *pd, const char *property, Eina_Value *value)
112 {
113    Eina_Future *f = NULL;
114 
115    if (!pd->parent) goto end;
116 
117    // In vertical list mode we do not need to compute the average width size
118    /* if (eina_streq(property, _efl_model_property_selfw)) */
119    /*   { */
120    /*      f = _efl_ui_average_model_prepare(obj, &pd->parent->total.width, */
121    /*                                        pd->wseen ? NULL : &pd->parent->total.wseen, */
122    /*                                        property, value, EINA_TRUE); */
123    /*      pd->wseen = EINA_TRUE; */
124    /*   } */
125    if (eina_streq(property, _efl_model_property_selfh))
126      {
127         f = _efl_ui_average_model_prepare(obj, &pd->parent->total.height,
128                                           pd->hseen ? NULL : &pd->parent->total.hseen,
129                                           property, value);
130         pd->hseen = EINA_TRUE;
131      }
132 
133  end:
134    if (!f)
135      f = efl_model_property_set(efl_super(obj, EFL_UI_AVERAGE_MODEL_CLASS), property, value);
136 
137    return f;
138 }
139 
140 static inline Eina_Value *
_efl_ui_average_model_compute(const Eo * obj,Eina_Value * r,unsigned long long total,unsigned long long seen)141 _efl_ui_average_model_compute(const Eo *obj, Eina_Value *r, unsigned long long total, unsigned long long seen)
142 {
143    unsigned int count;
144 
145    eina_value_free(r);
146 
147    // Protection against divide by zero
148    if (!seen) return eina_value_uint_new(0);
149 
150    count = efl_model_children_count_get(obj);
151    // We are doing the multiply first in an attempt to not reduce the precision to early on.
152    return eina_value_uint_new((total * count) / seen);
153 }
154 
155 static Eina_Value *
_efl_ui_average_model_efl_model_property_get(const Eo * obj,Efl_Ui_Average_Model_Data * pd,const char * property)156 _efl_ui_average_model_efl_model_property_get(const Eo *obj, Efl_Ui_Average_Model_Data *pd, const char *property)
157 {
158    const Eina_Value_Type *t;
159    Eina_Value *r;
160 
161    r = efl_model_property_get(efl_super(obj, EFL_UI_AVERAGE_MODEL_CLASS), property);
162    if (!r) return r;
163 
164    // We are checking that the parent class was able to provide an answer to the request for property "Total.Width"
165    // or "Total.Height" which means that we are an object that should compute its size. This avoid computing the
166    // pointer to the parent object.
167    t = eina_value_type_get(r);
168    if (t == EINA_VALUE_TYPE_UINT)
169      {
170         if (eina_streq(property, _efl_model_property_totalh))
171           r = _efl_ui_average_model_compute(obj, r, pd->total.height, pd->total.hseen);
172         // We do not need to average the width in vertical list mode as this is done by the parent class
173         /* if (eina_streq(property, _efl_model_property_totalw)) */
174         /*   r = _efl_ui_average_model_compute(obj, r, pd->total.width, pd->total.wseen); */
175      }
176 
177    return r;
178 }
179 
180 static Efl_Object *
_efl_ui_average_model_efl_object_constructor(Eo * obj,Efl_Ui_Average_Model_Data * pd)181 _efl_ui_average_model_efl_object_constructor(Eo *obj, Efl_Ui_Average_Model_Data *pd)
182 {
183    Eo *parent = efl_parent_get(obj);
184 
185    if (parent && efl_isa(parent, EFL_UI_AVERAGE_MODEL_CLASS))
186      pd->parent = efl_data_scope_get(efl_parent_get(obj), EFL_UI_AVERAGE_MODEL_CLASS);
187 
188    return efl_constructor(efl_super(obj, EFL_UI_AVERAGE_MODEL_CLASS));
189 }
190 
191 #include "efl_ui_average_model.eo.c"
192