Fix signal MOVE for elm_win
[platform/core/uifw/eail.git] / eail / eail / eail_window.c
1 /*
2  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * @file eail_window.c
22  * @brief EailWindow Implementation
23  */
24
25 #include <Elementary.h>
26 #include "eail_window.h"
27 #include "eail_factory.h"
28 #include "eail_utils.h"
29 #include "eail_dynamic_content.h"
30 #include "eail_priv.h"
31
32 static void eail_window_actions_init(EailActionWidget *widget);
33 static void atk_window_interface_init (AtkWindowIface *iface);
34 static void atk_component_interface_init(AtkComponentIface *iface);
35 static void eail_dynamic_content_interface_init(EailDynamicContentIface *iface);
36
37 /**
38  * @brief Focus signal name
39  */
40 #define EAIL_WINDOW_FOCUS_NAME "focus,in"
41
42 #define EAIL_WIN_ACTION_MAXIMIZE "maximize" /**< 'maximize' action name */
43 #define EAIL_WIN_ACTION_MINIMIZE "minimize" /**< 'minimize' action name */
44 #define EAIL_WIN_ACTION_RESTORE "restore" /**< 'restore' action name */
45
46 /**
47  * @brief EailWindow type definition
48  */
49 G_DEFINE_TYPE_WITH_CODE(EailWindow,
50                         eail_window,
51                         EAIL_TYPE_ACTION_WIDGET,
52                         G_IMPLEMENT_INTERFACE(ATK_TYPE_WINDOW,
53                                               atk_window_interface_init)
54                         G_IMPLEMENT_INTERFACE(ATK_TYPE_COMPONENT,
55                                             atk_component_interface_init)
56                         G_IMPLEMENT_INTERFACE(EAIL_TYPE_DYNAMIC_CONTENT,
57                                           eail_dynamic_content_interface_init));
58
59 /**
60  * @brief Maximizes event handler
61  *
62  * @param data data passed to callback
63  * @param obj Evas_Object instance that raised event
64  * @param event_info additional event info
65  */
66 void
67 _eail_window_handle_maximize_event(void *data,
68                                    Evas_Object *obj,
69                                    void *event_info)
70 {
71    eail_emit_atk_signal(ATK_OBJECT(data), "maximize", EAIL_TYPE_WINDOW);
72 }
73
74 /**
75  * @brief Minimizes event handler
76  *
77  * @param data data passed to callback
78  * @param obj Evas_Object instance that raised event
79  * @param event_info additional event info
80  */
81 void
82 _eail_window_handle_minimize_event(void *data,
83                                    Evas_Object *obj,
84                                    void *event_info)
85 {
86    eail_emit_atk_signal(ATK_OBJECT(data), "minimize", EAIL_TYPE_WINDOW);
87 }
88
89 /**
90  * @brief Moves event handler
91  *
92  * @param data data passed to callback
93  * @param obj Evas_Object instance that raised event
94  * @param event_info additional event info
95  */
96 void
97 eail_window_on_move(void *data, Evas *e, Evas_Object *obj, void *event_info)
98 {
99    g_return_if_fail(ATK_IS_OBJECT(data));
100
101    eail_emit_atk_signal(ATK_OBJECT(data), "move", EAIL_TYPE_WINDOW);
102 }
103
104 /**
105  * @brief Callback used for tracking resize-changes for window
106  *
107  * @param data data passed to callback
108  * @param e Evas instance that has been shown
109  * @param obj Evas_Object instance that has been shown
110  * @param event_info additional event info
111  */
112 void
113 eail_window_on_resize(void *data, Evas *e, Evas_Object *obj, void *event_info)
114 {
115    g_return_if_fail(ATK_IS_OBJECT(data));
116
117    eail_emit_atk_signal(ATK_OBJECT(data), "resize", EAIL_TYPE_WINDOW);
118 }
119
120 /**
121  * @brief Destroyed event handler for window
122  *
123  * @param data passed to callback
124  * @param obj object that raised event
125  * @param event_info additional event info
126  */
127 void
128 _eail_window_handle_delete_event(void *data,
129                                Evas_Object *obj,
130                                void *event_info)
131 {
132    atk_object_notify_state_change(ATK_OBJECT(data), ATK_STATE_DEFUNCT, TRUE);
133    eail_emit_atk_signal(ATK_OBJECT(data), "destroy", EAIL_TYPE_WINDOW);
134    eail_factory_unregister_wdgt_from_cache(obj);
135 }
136
137 /**
138  * @brief Restore event handler
139  *
140  * @param data passed to callback
141  * @param obj object that raised event
142  * @param event_info additional event info
143  */
144 void
145 _eail_window_handle_restore_event(void *data,
146                                    Evas_Object *obj,
147                                    void *event_info)
148 {
149    eail_emit_atk_signal(ATK_OBJECT(data), "restore", EAIL_TYPE_WINDOW);
150 }
151
152 /**
153  * @brief Initializes window focus handler
154  *
155  * @param obj AtkObject instance
156  */
157 static void
158 eail_window_init_focus_handler(AtkObject *obj)
159 {
160    Evas_Object *nested_widget = NULL;
161    g_return_if_fail(EAIL_IS_WIDGET(obj));
162
163    nested_widget = eail_widget_get_widget(EAIL_WIDGET(obj));
164    if (!nested_widget)
165      {
166         ERR("No evas object inside EailWidget was found");
167         return;
168      }
169
170    evas_object_smart_callback_add(nested_widget, "maximized",
171                                   _eail_window_handle_maximize_event, obj);
172    evas_object_smart_callback_add(nested_widget, "iconified",
173                                   _eail_window_handle_minimize_event, obj);
174    evas_object_smart_callback_add(nested_widget, "delete,request",
175                                   _eail_window_handle_delete_event, obj);
176    evas_object_smart_callback_add(nested_widget, "unmaximized",
177                                   _eail_window_handle_restore_event, obj);
178    evas_object_smart_callback_add(nested_widget, "normal",
179                                   _eail_window_handle_restore_event, obj);
180
181    /* evas object events (not smart callbacks) */
182    evas_object_event_callback_add(nested_widget, EVAS_CALLBACK_RESIZE,
183                                   eail_window_on_resize, obj);
184    evas_object_event_callback_add(nested_widget, EVAS_CALLBACK_MOVE,
185                                   eail_window_on_move, obj);
186 }
187
188 /**
189  * @brief EailWindow initializer
190  *
191  * @param obj AtkObject instance
192  * @param data initialization data
193  */
194 static void
195 eail_window_initialize(AtkObject *obj, gpointer data)
196 {
197    EailWindow *eail_win = NULL;
198    ATK_OBJECT_CLASS(eail_window_parent_class)->initialize(obj, data);
199    EAIL_WIDGET(obj)->layer = ATK_LAYER_WINDOW;
200
201    if (!elm_object_widget_check((Evas_Object *) data)) return;
202
203    obj->name = g_strdup(elm_win_title_get((Evas_Object *)data));
204    obj->role = ATK_ROLE_WINDOW;
205    obj->accessible_parent = atk_get_root();
206
207    eail_window_init_focus_handler(obj);
208    eail_window_actions_init(EAIL_ACTION_WIDGET(obj));
209
210    eail_win = EAIL_WINDOW(obj);
211    /* storing last numbers of children to be for checking if children-changed
212     * signal has to be propagated */
213    eail_win->child_count_last = atk_object_get_n_accessible_children(obj);
214 }
215
216 /**
217  * @brief Gets the children list from given edje
218  *
219  * @param edje lowest (stacked) Evas object
220  * @return Eina_List representing the children list
221  */
222 static Eina_List *
223 _parse_edje(const Evas_Object *edje)
224 {
225    const Evas_Object *content_part = NULL;
226    const Evas_Object *menu_part = NULL;
227    Eina_List *list = NULL;
228
229    if (edje_object_part_exists(edje, "elm.swallow.menu"))
230      menu_part = edje_object_part_swallow_get(edje, "elm.swallow.menu");
231    if (edje_object_part_exists(edje, "elm.swallow.contents"))
232      content_part = edje_object_part_swallow_get(edje, "elm.swallow.contents");
233    if ((menu_part) &&
234        (!strcmp(evas_object_type_get(menu_part), "Evas_Object_Box")))
235         list = evas_object_box_children_get(menu_part);
236    if ((content_part) &&
237        (!strcmp(evas_object_type_get(content_part), "Evas_Object_Box")))
238      {
239         if (list)
240           list = eina_list_merge(list,
241                                  evas_object_box_children_get(content_part));
242         else
243           list = evas_object_box_children_get(content_part);
244      }
245
246    return list;
247 }
248
249 /**
250  * @brief Gets widget's children
251  *
252  * @param widget EailWidget instance
253  * @return Eina_List representing the list of widget's children
254  */
255 static Eina_List *
256 eail_window_get_widget_children(EailWidget *widget)
257 {
258    Evas_Object *o, *obj = eail_widget_get_widget(widget);
259    Eina_List *win_childs = NULL;
260    Eina_List *widgets = NULL;
261    Evas *e;
262    Eina_List *l;
263    Evas_Object *item;
264
265    /*in elementary >= 1.7.99 we get edje object if object are stacked
266      in containers like box, grid etc we need to get children from this
267      edje*/
268
269    e = evas_object_evas_get(obj);
270    o = evas_object_bottom_get(e);
271    if (!g_strcmp0(evas_object_type_get(o), "edje"))
272      {
273         widgets = _parse_edje(o);
274         EINA_LIST_FOREACH(widgets, l, item)
275           {
276              if(!elm_object_widget_check(item))
277                widgets = eina_list_remove(widgets, item);
278           }
279      }
280    /* Sometimes we have a mix of widgets grouped in containters with
281     * those directly on elm_win objct. So list evas objects laying on
282     * window to be sure we get everything */
283    o = obj;
284    while ((o = evas_object_below_get(o)))
285      {
286         if (elm_object_widget_check(o))
287           {
288              /*be sure that object belongs to window and not to other
289               * container*/
290              if (obj == elm_object_parent_widget_get(o))
291                win_childs = eina_list_append(win_childs, o);
292           }
293      }
294
295    if (win_childs)
296      {
297         /*reverse list to get correct order of widgets tree*/
298         win_childs = eina_list_reverse(win_childs);
299         /*merge window childs together with containers*/
300         if (widgets)
301           widgets = eina_list_merge(widgets, win_childs);
302         else
303           widgets = win_childs;
304      }
305
306    return widgets;
307 }
308
309 /**
310  * @brief Gets the state set of the accessible
311  *
312  * @param obj AtkObject instance
313  * @return AtkStateSet representing the state set of the accessible
314  */
315 static AtkStateSet *
316 eail_window_ref_state_set(AtkObject *obj)
317 {
318    double x, y;
319    AtkStateSet *state_set;
320    Eina_List *l, *children;
321    Eina_Bool resizable = EINA_TRUE;
322    Evas_Object *child, *widget = eail_widget_get_widget(EAIL_WIDGET(obj));
323
324    if (!widget) return NULL;
325
326    state_set= ATK_OBJECT_CLASS(eail_window_parent_class)->ref_state_set(obj);
327
328    if (elm_win_focus_get(widget))
329      {
330         atk_state_set_add_state(state_set, ATK_STATE_ACTIVE);
331      }
332
333    if (elm_win_iconified_get(widget))
334      {
335         atk_state_set_add_state(state_set, ATK_STATE_ICONIFIED);
336      }
337
338    if (elm_win_modal_get(widget))
339      {
340         atk_state_set_add_state(state_set, ATK_STATE_MODAL);
341      }
342
343    children = eail_widget_get_widget_children(EAIL_WIDGET(obj));
344    EINA_LIST_FOREACH(children, l, child)
345      {
346         evas_object_size_hint_weight_get(child, &x, &y);
347         if (!float_equal(x, EVAS_HINT_EXPAND) || !float_equal(y, EVAS_HINT_EXPAND))
348           {
349              resizable = EINA_FALSE;
350              break;
351           }
352      }
353    eina_list_free(children);
354
355    if (resizable)
356      {
357         atk_state_set_add_state(state_set, ATK_STATE_RESIZABLE);
358      }
359
360    return state_set;
361 }
362
363 /**
364  * @brief Get the index in parent of the accessible
365  *
366  * @param obj AtkObject instance
367  * @return Integer representing the index of the object in parent
368  */
369 static int
370 eail_window_get_index_in_parent(AtkObject *obj)
371 {
372     AtkObject *parent = atk_object_get_parent(obj);
373
374     if(atk_object_get_n_accessible_children(parent) == 1)
375       {
376         return 0;
377       }
378     else
379       {
380         int i;
381         for(i = 0; i < atk_object_get_n_accessible_children(parent); i++)
382           {
383             AtkObject *child = atk_object_ref_accessible_child(parent, i);
384             if(child == obj)
385               {
386                 g_object_unref(child);
387                 return i;
388               }
389             g_object_unref(child);
390           }
391       }
392
393     return -1;
394 }
395
396 /**
397  * @brief EailWindow instance initializer
398  *
399  * @param window EailWindow instance
400  */
401 static void
402 eail_window_init(EailWindow *window)
403 {
404 }
405
406 /**
407  * @brief EailWindow class initializer
408  *
409  * @param klass EailWindowClass instance
410  */
411 static void
412 eail_window_class_init(EailWindowClass *klass)
413 {
414    AtkObjectClass *atk_class = ATK_OBJECT_CLASS(klass);
415    EailWidgetClass *widget_class = EAIL_WIDGET_CLASS(klass);
416
417    widget_class->get_widget_children = eail_window_get_widget_children;
418
419    atk_class->initialize = eail_window_initialize;
420    atk_class->ref_state_set = eail_window_ref_state_set;
421    atk_class->get_index_in_parent = eail_window_get_index_in_parent;
422 }
423
424 /**
425  * @brief Handle for minimize action
426  *
427  * @param action AtkAction instance
428  * @param data additional action data (not used here)
429  *
430  * @return TRUE if action was triggered successfully, FALSE otherwise
431  */
432 static gboolean
433 eail_action_minimize(AtkAction *action, void *data)
434 {
435    Evas_Object *widget;
436    AtkObject *obj;
437
438    g_return_val_if_fail(EAIL_IS_WINDOW(action), FALSE);
439
440    widget = eail_widget_get_widget(EAIL_WIDGET(action));
441
442    elm_win_iconified_set(widget, EINA_TRUE);
443
444    obj = ATK_OBJECT(action);
445
446    eail_emit_atk_signal(obj, "minimize", EAIL_TYPE_WINDOW);
447
448    return TRUE;
449 }
450
451 /**
452  * @brief Handle for maximize action
453  *
454  * @param action AtkAction instance
455  * @param data additional action data (not used here)
456  *
457  * @return TRUE if action was triggered successfully, FALSE otherwise
458  */
459 static gboolean
460 eail_action_maximize(AtkAction *action, void *data)
461 {
462    Evas_Object *widget;
463    AtkObject *obj;
464
465    g_return_val_if_fail(EAIL_IS_WINDOW(action), FALSE);
466
467    widget = eail_widget_get_widget(EAIL_WIDGET(action));
468
469    elm_win_maximized_set(widget, EINA_TRUE);
470
471    obj = ATK_OBJECT(action);
472
473    eail_emit_atk_signal(obj, "maximize", EAIL_TYPE_WINDOW);
474    return TRUE;
475 }
476
477 /**
478  * @brief Handle for action restore
479  *
480  * @param action an AtkAction
481  * @param data additional action data (not used here)
482  *
483  * @return TRUE if action was triggered successfully, FALSE otherwise
484  */
485 static gboolean
486 eail_action_restore(AtkAction *action, void *data)
487 {
488    Evas_Object *widget;
489    AtkObject *obj;
490
491    g_return_val_if_fail(EAIL_IS_WINDOW(action), FALSE);
492
493    widget = eail_widget_get_widget(EAIL_WIDGET(action));
494
495    if(elm_win_maximized_get(widget))
496      {
497         elm_win_maximized_set(widget, EINA_FALSE);
498      }
499
500    if(elm_win_iconified_get(widget))
501      {
502         elm_win_activate(widget);
503      }
504
505    obj = ATK_OBJECT(action);
506
507    eail_emit_atk_signal(obj, "restore", EAIL_TYPE_WINDOW);
508    return TRUE;
509 }
510
511 /**
512  * @brief Adds window actions to the actions table
513  *
514  * @param action_widget widget that implements EailActionWidget interface
515  */
516 static void
517 eail_window_actions_init(EailActionWidget *action_widget)
518 {
519    eail_action_widget_action_append(action_widget,
520                                     EAIL_WIN_ACTION_MAXIMIZE, NULL,
521                                     eail_action_maximize);
522    eail_action_widget_action_append(action_widget,
523                                     EAIL_WIN_ACTION_MINIMIZE, NULL,
524                                     eail_action_minimize);
525    eail_action_widget_action_append(action_widget,
526                                     EAIL_WIN_ACTION_RESTORE, NULL,
527                                     eail_action_restore);
528 }
529
530 /**
531  * @brief AtkWindow interface initializer
532  *
533  * It is empty because at the moment
534  * AtkWindow is just about signals
535  *
536  * @param iface AtkWindowIface instance
537  */
538 static void
539 atk_window_interface_init(AtkWindowIface *iface)
540 {
541 }
542
543 /**
544  * @brief Returns the first found widget from EvasObjectBox
545  *
546  * @param evas_obj EvasObjectBox instance to search in
547  *
548  * @returns Evas_Object that represents the found widget
549  * or NULL if none has been found
550  */
551 static Evas_Object*
552 _eail_get_first_widget_from_evas_box(Evas_Object *evas_obj)
553 {
554    Eina_List *children = NULL, *l;
555    Evas_Object *it_evas_obj = NULL;
556
557    children = evas_object_box_children_get(evas_obj);
558
559    EINA_LIST_FOREACH(children, l, it_evas_obj)
560      {
561       if (elm_object_widget_check (it_evas_obj))
562          {
563             DBG("Widget has been found %s", evas_object_type_get(it_evas_obj));
564             return it_evas_obj;
565          }
566      }
567
568    return NULL;
569 }
570
571 /**
572  * @brief Returns the first found widget from given edje object
573  *
574  * @param evas_obj edje object to search in
575  *
576  * @returns Evas_Object that represents the found widget
577  * or NULL if none has been found
578  */
579 static Evas_Object *
580 _eail_get_first_widget_from_edje(Evas_Object *evas_obj)
581 {
582    Eina_List *members = NULL, *l;
583    Evas_Object *it_evas_obj = NULL;
584
585    members = evas_object_smart_members_get(evas_obj);
586    EINA_LIST_FOREACH(members, l, it_evas_obj)
587    {
588       const char *type_name = evas_object_type_get(it_evas_obj);
589
590       /* if widget found, then returning immediately. If not widget, then
591        * looking for nested box*/
592       if (elm_object_widget_check (it_evas_obj))
593         return it_evas_obj;
594       else if (0 == g_strcmp0(type_name, "Evas_Object_Box") )
595         {
596             Evas_Object *widget_from_box = _eail_get_first_widget_from_evas_box(it_evas_obj);
597             if (widget_from_box)
598               return widget_from_box;
599         }
600    }
601
602    return NULL;
603 }
604
605 /**
606  * @brief Gets a reference to the accessible child, if one exists, at the
607  * coordinate point specified by x and y
608  *
609  * @param component AtkComponent instance
610  * @param x x coordinate
611  * @param y y coordinate
612  * @param coord_type specifies whether the coordinates are relative to the
613  * screen or to the components top level window
614  *
615  * @returns AtkObject representing the accessible child, if one exists
616  */
617 static AtkObject *
618 eail_window_ref_accessible_at_point(AtkComponent *component,
619                                     gint x,
620                                     gint y,
621                                     AtkCoordType coord_type)
622 {
623    Evas_Object *widget = NULL, *evas_obj_at_coords = NULL;
624    Evas *wdgt_evas = NULL;
625    const char * found_obj_type = NULL;
626
627    widget = eail_widget_get_widget(EAIL_WIDGET(component));
628
629    if (!widget) return NULL;
630
631    if (coord_type == ATK_XY_SCREEN) {
632            int ee_x, ee_y;
633            Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(widget));
634
635            ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL);
636            x -= ee_x;
637            y -= ee_y;
638        }
639
640    /* NOTE: it is crucial to get object from evas, NOT Evas_Object*/
641    wdgt_evas = evas_object_evas_get(widget);
642
643    evas_obj_at_coords = evas_object_top_at_xy_get
644                               (wdgt_evas, x, y, EINA_FALSE, EINA_FALSE);
645    if (!evas_obj_at_coords)
646      {
647        DBG("Not found any object at coords %d,%d", x, y);
648        return NULL;
649      }
650
651    /* if edje object, then we have to parse that and extract widget from it*/
652    found_obj_type = evas_object_type_get(evas_obj_at_coords);
653    if (0 == g_strcmp0(found_obj_type, "edje"))
654      {
655          DBG("Edje object found...");
656          evas_obj_at_coords = _eail_get_first_widget_from_edje(evas_obj_at_coords);
657      }
658
659    return eail_factory_get_accessible(evas_obj_at_coords);
660 }
661
662 /**
663  * @brief AtkComponent interface initialization
664  *
665  * Initialization for ref_accessible_at_point callback used by clients to
666  * reference object at given coordinates. Rest of implementation is default
667  * so no overriding-callbacks is needed.
668  *
669  * @param iface EailWindow instance
670  */
671 static void
672 atk_component_interface_init(AtkComponentIface *iface)
673 {
674    iface->ref_accessible_at_point = eail_window_ref_accessible_at_point;
675 }
676
677 /**
678  * @param dynamic_content_holder an EailDynamicContent object (EailWindow)
679  */
680 void
681 eail_window_update_descendants(EailDynamicContent *dynamic_content_holder)
682 {
683    gint n_children = 0;
684    EailWindow *window = NULL;
685
686    if (!EAIL_IS_WINDOW(dynamic_content_holder))
687      {
688         DBG("No EailWindow found. Returning");
689         return;
690      }
691
692    window = EAIL_WINDOW(dynamic_content_holder);
693
694    n_children = atk_object_get_n_accessible_children(ATK_OBJECT(window));
695    if (n_children && n_children > window->child_count_last)
696      {
697         eail_emit_children_changed(TRUE, ATK_OBJECT(window), n_children - 1);
698
699      }
700    else if (n_children < window->child_count_last)
701      {
702          eail_emit_children_changed
703                      (FALSE, ATK_OBJECT(window), window->child_count_last);
704      }
705
706    window->child_count_last = n_children;
707 }
708
709 /**
710  * @brief Initializer for dynamic content interface, used for handling objects
711  * children hierarchy changes
712  *
713  * @param iface an EailDynamicContentIface
714  */
715 static void
716 eail_dynamic_content_interface_init(EailDynamicContentIface *iface)
717 {
718    iface->update_hierarchy = eail_window_update_descendants;
719 }