88defd6b179a070ac3440f895f9f83859eef2427
[framework/uifw/evas.git] / src / lib / canvas / evas_clip.c
1 #include "evas_common.h"
2 #include "evas_private.h"
3
4 void
5 evas_object_clip_dirty(Evas_Object *obj)
6 {
7    Eina_List *l;
8    Evas_Object *data;
9
10    obj->cur.cache.clip.dirty = 1;
11    EINA_LIST_FOREACH(obj->clip.clipees, l, data)
12      evas_object_clip_dirty(data);
13 }
14
15 void
16 evas_object_recalc_clippees(Evas_Object *obj)
17 {
18    Eina_List *l;
19    Evas_Object *data;
20
21    if (obj->cur.cache.clip.dirty)
22      {
23         evas_object_clip_recalc(obj);
24         EINA_LIST_FOREACH(obj->clip.clipees, l, data)
25           evas_object_recalc_clippees(data);
26      }
27 }
28
29 int
30 evas_object_clippers_was_visible(Evas_Object *obj)
31 {
32    if (obj->prev.visible)
33      {
34         if (obj->prev.clipper)
35           return evas_object_clippers_is_visible(obj->prev.clipper);
36         return 1;
37      }
38    return 0;
39 }
40
41 /* aaaaargh (pirate voice) ... notes!
42  * 
43  * we have a big problem until now that's gone undetected... until yesterday.
44  * that problem involves clips and maps and smart objects. hooray! 3 of the
45  * more complex bits of evas - and maps and smart objects  being one of the
46  * nastiest ones.
47  * 
48  * what is the problem? when a clip crosses a map boundary. that is to say
49  * that when the clipper and clippee are not within the child tree of the
50  * mapped object. in this case "bad stuff" happens. basically as clips are
51  * then used to render objects, but they no longer apply as you'd expect as
52  * the map transfomr the objects to-be-clipped separately from the objects
53  * that clip them and this whole relationship is broken by maps. it somehow
54  * managed to not break with the advent of smart objects. lucky me... but
55  * maps killed it. now... what do we do? that is a good question. detect
56  * such a broken link and "turn off clipping" in that event - sure. but this
57  * isn't going to be cheap as ANY addition or deletion of a map to an object
58  * or any change in clipper of an object or any change in smart object
59  * membership needs to walk the obj tree both up and down from the changed
60  * object and probably walk entire object trees to find these and mark them.
61  * thats silly-expensive and i was about to fix it that way but it has since
62  * dawned on me that that is just going to kill performance in some critical
63  * areas like during object setup and manipulation, as well as teardown.
64  * 
65  * aaaaagh! best for now is to document this as a "don't do it damnit!" thing
66  * and have the apps avoid it. but even then - how to do this? this is not
67  * easy. everywhere i turn so far i come up to either expensive operations,
68  * breaks in logic, or nasty re-work of apps or4 the whole concept of clipping,
69  * smart objects and maps... and that will have to wait for evas 2.0
70  * 
71  * the below does clip fixups etc. in the even a clip spans a map boundary.
72  * not pretty, but necessary.
73  */
74
75 #define MAP_ACROSS 1
76 static void
77 evas_object_child_map_across_mark(Evas_Object *obj, Evas_Object *map_obj, Eina_Bool force)
78 {
79 #ifdef MAP_ACROSS
80    if ((obj->cur.map_parent != map_obj) || force)
81      {
82         obj->cur.map_parent = map_obj;
83         obj->cur.cache.clip.dirty = 1;
84         evas_object_clip_recalc(obj);
85         if (obj->smart.smart)
86           {
87              Evas_Object *obj2;
88              
89              EINA_INLIST_FOREACH(evas_object_smart_members_get_direct(obj), obj2)
90                {
91                   // if obj has its own map - skip it. already done
92                   if ((obj2->cur.map) && (obj2->cur.usemap)) continue;
93                   evas_object_child_map_across_mark(obj2, map_obj, force);
94                }
95           }
96         else if (obj->clip.clipees)
97           {
98              Eina_List *l;
99              Evas_Object *obj2;
100              
101              EINA_LIST_FOREACH(obj->clip.clipees, l, obj2)
102                 evas_object_child_map_across_mark(obj2, map_obj, force);
103           }
104      }
105 #endif   
106 }
107
108 void
109 evas_object_clip_across_check(Evas_Object *obj)
110 {
111 #ifdef MAP_ACROSS
112    if (!obj->cur.clipper) return;
113    if (obj->cur.clipper->cur.map_parent != obj->cur.map_parent)
114       evas_object_child_map_across_mark(obj, obj->cur.map_parent, 1);
115 #endif   
116 }
117
118 void
119 evas_object_clip_across_clippees_check(Evas_Object *obj)
120 {
121 #ifdef MAP_ACROSS
122    Eina_List *l;
123    Evas_Object *obj2;
124
125    if (!obj->clip.clipees) return;
126 // schloooooooooooow:
127 //   evas_object_child_map_across_mark(obj, obj->cur.map_parent, 1);
128 // buggy:
129    evas_object_child_map_across_mark(obj, obj->cur.map_parent, 0);
130    if (obj->cur.cache.clip.dirty)
131      {
132         EINA_LIST_FOREACH(obj->clip.clipees, l, obj2)
133            evas_object_clip_across_clippees_check(obj2);
134      }
135 #endif   
136 }
137
138 // this function is called on an object when map is enabled or disabled on it
139 // thus creating a "map boundary" at that point.
140 // 
141 // FIXME: flip2 test broken in elm - might be show/hide of clips
142 void
143 evas_object_mapped_clip_across_mark(Evas_Object *obj)
144 {
145 #ifdef MAP_ACROSS
146    if ((obj->cur.map) && (obj->cur.usemap))
147       evas_object_child_map_across_mark(obj, obj, 0);
148    else
149      {
150         if (obj->smart.parent)
151            evas_object_child_map_across_mark
152            (obj, obj->smart.parent->cur.map_parent, 0);
153         else
154            evas_object_child_map_across_mark(obj, NULL, 0); 
155     }
156 #endif   
157 }
158
159 /* public functions */
160
161 /**
162  * @addtogroup Evas_Object_Group_Basic
163  * @{
164  */
165
166 /**
167  * Clip one object to another.
168  * @param obj The object to be clipped
169  * @param clip The object to clip @p obj by
170  *
171  * This function will clip the object @p obj to the area occupied by the
172  * object @p clipper. This means the object @p obj will only be visible within
173  * the area occupied by the clipping object (@p clip). The color of the object
174  * being clipped will be multiplied by the color of the clipping object, so
175  * the resulting color for the clipped object is
176  * RESULT = (OBJ * CLIP) / (255 * 255) per color element (red, green, blue and
177  * alpha). Clipping is recursive, so clip objects may be clipped by other
178  * objects, and their color will in tern be multiplied. You may NOT set up
179  * circular clipping lists (i.e. object 1 clips object 2 which clips object 1).
180  * The behavior of Evas is undefined in this case. Objects which do not clip
181  * others are visible as normal, those that clip 1 or more objects become
182  * invisible themselves, only affecting what they clip. If an object ceases to
183  * have other objects being clipped by it, it will become visible again. The
184  * visibility of an object affects the objects that are clipped by it, so if
185  * the object clipping others is not shown, the objects clipped will not be
186  * shown either. If the object was being clipped by another object when this
187  * function is called, it is implicitly removed from the clipper it is being
188  * clipped to, and now is made to clip its new clipper.
189  *
190  * At the moment the only objects that can validly be used to clip other
191  * objects are rectangle objects. All other object types are invalid and the
192  * result of using them is undefined.
193  *
194  * The clip object @p clip must be a valid object, but may also be NULL in
195  * which case the effect of this function is the same as calling
196  * evas_object_clip_unset() on the @p obj object.
197  *
198  * Example:
199  * @code
200  * extern Evas *evas;
201  * extern Evas_Object *obj;
202  * Evas_Object *clipper;
203  *
204  * clipper = evas_object_rectangle_add(evas);
205  * evas_object_color_set(clipper, 255, 255, 255, 255);
206  * evas_object_move(clipper, 10, 10);
207  * evas_object_resize(clipper, 20, 50);
208  * evas_object_clip_set(obj, clipper);
209  * evas_object_show(clipper);
210  * @endcode
211  *
212  */
213 EAPI void
214 evas_object_clip_set(Evas_Object *obj, Evas_Object *clip)
215 {
216    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
217    return;
218    MAGIC_CHECK_END();
219    if (!clip)
220      {
221         evas_object_clip_unset(obj);
222         return;
223      }
224    MAGIC_CHECK(clip, Evas_Object, MAGIC_OBJ);
225    return;
226    MAGIC_CHECK_END();
227    if (obj->cur.clipper == clip) return;
228    if (obj == clip) return;
229    if (evas_object_intercept_call_clip_set(obj, clip)) return;
230    if (obj->smart.smart)
231      {
232        if (obj->smart.smart->smart_class->clip_set)
233           obj->smart.smart->smart_class->clip_set(obj, clip);
234      }
235    if (obj->cur.clipper)
236      {
237         /* unclip */
238         obj->cur.clipper->clip.clipees = eina_list_remove(obj->cur.clipper->clip.clipees, obj);
239         if (!obj->cur.clipper->clip.clipees)
240           {
241              obj->cur.clipper->cur.have_clipees = 0;
242              if (obj->cur.clipper->cur.visible)
243                evas_damage_rectangle_add(obj->cur.clipper->layer->evas,
244                                          obj->cur.clipper->cur.geometry.x,
245                                          obj->cur.clipper->cur.geometry.y,
246                                          obj->cur.clipper->cur.geometry.w,
247                                          obj->cur.clipper->cur.geometry.h);
248           }
249         evas_object_change(obj->cur.clipper);
250         evas_object_change(obj);
251         obj->cur.clipper = NULL;
252      }
253    /* clip me */
254    if ((!clip->clip.clipees) && (clip->cur.visible))
255      {
256         /* Basically it just went invisible */
257         clip->changed = 1;
258         clip->layer->evas->changed = 1;
259         evas_damage_rectangle_add(clip->layer->evas,
260                                   clip->cur.geometry.x, clip->cur.geometry.y,
261                                   clip->cur.geometry.w, clip->cur.geometry.h);
262      }
263    obj->cur.clipper = clip;
264    clip->clip.clipees = eina_list_append(clip->clip.clipees, obj);
265    if (clip->clip.clipees) clip->cur.have_clipees = 1;
266    evas_object_change(clip);
267    evas_object_change(obj);
268    evas_object_clip_dirty(obj);
269    evas_object_recalc_clippees(obj);
270    if ((!obj->smart.smart) && 
271        (!((obj->cur.map) && (obj->cur.usemap))))
272      {
273         if (evas_object_is_in_output_rect(obj,
274                                           obj->layer->evas->pointer.x,
275                                           obj->layer->evas->pointer.y, 1, 1))
276           evas_event_feed_mouse_move(obj->layer->evas,
277                                      obj->layer->evas->pointer.x,
278                                      obj->layer->evas->pointer.y,
279                                      obj->layer->evas->last_timestamp,
280                                      NULL);
281      }
282    evas_object_clip_across_check(obj);
283 }
284
285 /**
286  * Get the object clipping this one (if any).
287  * @param obj The object to get the clipper from
288  *
289  * This function returns the the object clipping @p obj. If @p obj not being
290  * clipped, NULL is returned. The object @p obj must be a valid object.
291  *
292  * See also evas_object_clip_set(), evas_object_clip_unset() and
293  * evas_object_clipees_get().
294  *
295  * Example:
296  * @code
297  * extern Evas_Object *obj;
298  * Evas_Object *clipper;
299  *
300  * clipper = evas_object_clip_get(obj);
301  * if (clipper) evas_object_show(clipper);
302  * @endcode
303  *
304  */
305 EAPI Evas_Object *
306 evas_object_clip_get(const Evas_Object *obj)
307 {
308    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
309    return NULL;
310    MAGIC_CHECK_END();
311    return obj->cur.clipper;
312 }
313
314 /**
315  * Disable clipping for an object.
316  *
317  * @param obj The object to cease clipping on
318  *
319  * This function disables clipping for the object @p obj, if it was already
320  * clipped. If it wasn't, this has no effect. The object @p obj must be a
321  * valid object.
322  *
323  * See also evas_object_clip_set(), evas_object_clipees_get() and
324  * evas_object_clip_get().
325  *
326  * Example:
327  * @code
328  * extern Evas_Object *obj;
329  * Evas_Object *clipper;
330  *
331  * clipper = evas_object_clip_get(obj);
332  * if (clipper)
333  *   {
334  *     evas_object_clip_unset(obj);
335  *     evas_object_hide(obj);
336  *   }
337  * @endcode
338  *
339  */
340 EAPI void
341 evas_object_clip_unset(Evas_Object *obj)
342 {
343    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
344    return;
345    MAGIC_CHECK_END();
346    if (!obj->cur.clipper) return;
347    /* unclip */
348    if (evas_object_intercept_call_clip_unset(obj)) return;
349    if (obj->smart.smart)
350      {
351        if (obj->smart.smart->smart_class->clip_unset)
352           obj->smart.smart->smart_class->clip_unset(obj);
353      }
354    if (obj->cur.clipper)
355      {
356         obj->cur.clipper->clip.clipees = eina_list_remove(obj->cur.clipper->clip.clipees, obj);
357         if (!obj->cur.clipper->clip.clipees)
358           {
359              obj->cur.clipper->cur.have_clipees = 0;
360              if (obj->cur.clipper->cur.visible)
361                evas_damage_rectangle_add(obj->cur.clipper->layer->evas,
362                                          obj->cur.clipper->cur.geometry.x,
363                                          obj->cur.clipper->cur.geometry.y,
364                                          obj->cur.clipper->cur.geometry.w,
365                                          obj->cur.clipper->cur.geometry.h);
366           }
367         evas_object_change(obj->cur.clipper);
368      }
369    obj->cur.clipper = NULL;
370    evas_object_change(obj);
371    evas_object_clip_dirty(obj);
372    evas_object_recalc_clippees(obj);
373    if ((!obj->smart.smart) && 
374        (!((obj->cur.map) && (obj->cur.usemap))))
375      {
376         if (evas_object_is_in_output_rect(obj,
377                                           obj->layer->evas->pointer.x,
378                                           obj->layer->evas->pointer.y, 1, 1))
379           evas_event_feed_mouse_move(obj->layer->evas,
380                                      obj->layer->evas->pointer.x,
381                                      obj->layer->evas->pointer.y,
382                                      obj->layer->evas->last_timestamp,
383                                      NULL);
384      }
385    evas_object_clip_across_check(obj);
386 }
387
388 /**
389  * Return a list of objects currently clipped by a specific object.
390  *
391  * @param obj The object to get a list of clippees from
392  *
393  * This returns the inernal list handle that contains all objects clipped by
394  * the object @p obj. If none are clipped, it returns NULL. This list is only
395  * valid until the clip list is changed and should be fetched again with another
396  * call to evas_object_clipees_get() if any objects being clipped by this object
397  * are unclipped, clipped by a new object, are deleted or the clipper is
398  * deleted.  These operations will invalidate the list returned so it should
399  * not be used anymore after that point. Any use of the list after this may have
400  * undefined results, not limited just to strange behavior but possible
401  * segfaults and other strange memory errors. The object @p obj must be a valid
402  * object.
403  *
404  * See also evas_object_clip_set(), evas_object_clip_unset() and
405  * evas_object_clip_get().
406  *
407  * Example:
408  * @code
409  * extern Evas_Object *obj;
410  * Evas_Object *clipper;
411  *
412  * clipper = evas_object_clip_get(obj);
413  * if (clipper)
414  *   {
415  *     Eina_List *clippees, *l;
416  *     Evas_Object *obj_tmp;
417  *
418  *     clippees = evas_object_clipees_get(clipper);
419  *     printf("Clipper clips %i objects\n", eina_list_count(clippees));
420  *     EINA_LIST_FOREACH(clippees, l, obj_tmp)
421  *         evas_object_show(obj_tmp);
422  *   }
423  * @endcode
424  */
425 EAPI const Eina_List *
426 evas_object_clipees_get(const Evas_Object *obj)
427 {
428    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
429    return NULL;
430    MAGIC_CHECK_END();
431    return obj->clip.clipees;
432 }
433
434 /**
435  * @}
436  */