1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4 #define EFL_UI_FOCUS_OBJECT_PROTECTED
5 
6 
7 #include <Elementary.h>
8 #include "elm_priv.h"
9 
10 #include "efl_ui_focus_graph.h"
11 
12 #define MY_CLASS EFL_UI_FOCUS_MANAGER_CALC_CLASS
13 #define FOCUS_DATA(obj) Efl_Ui_Focus_Manager_Calc_Data *pd = efl_data_scope_get(obj, MY_CLASS);
14 
15 #define NODE_DIRECTIONS_COUNT 4
16 
17 #define DIRECTION_IS_LOGICAL(dir) (dir >= EFL_UI_FOCUS_DIRECTION_PREVIOUS && dir < EFL_UI_FOCUS_DIRECTION_UP)
18 #define DIRECTION_IS_2D(dir) (dir >= EFL_UI_FOCUS_DIRECTION_UP && dir < EFL_UI_FOCUS_DIRECTION_LAST)
19 #define DIRECTION_CHECK(dir) (dir >= EFL_UI_FOCUS_DIRECTION_PREVIOUS && dir < EFL_UI_FOCUS_DIRECTION_LAST)
20 
21 //#define CALC_DEBUG
22 #define DEBUG_TUPLE(obj) efl_name_get(obj), efl_class_name_get(obj)
23 
24 static int _focus_log_domain = -1;
25 
26 #define F_CRI(...) EINA_LOG_DOM_CRIT(_focus_log_domain, __VA_ARGS__)
27 #define F_ERR(...) EINA_LOG_DOM_ERR(_focus_log_domain, __VA_ARGS__)
28 #define F_WRN(...) EINA_LOG_DOM_WARN(_focus_log_domain, __VA_ARGS__)
29 #define F_INF(...) EINA_LOG_DOM_INFO(_focus_log_domain, __VA_ARGS__)
30 #define F_DBG(...) EINA_LOG_DOM_DBG(_focus_log_domain, __VA_ARGS__)
31 
32 #define DIRECTION_ACCESS(V, ID) ((V)->graph.directions[(ID) - 2])
33 
34 typedef enum {
35     DIMENSION_X = 0,
36     DIMENSION_Y = 1,
37 } Dimension;
38 
39 typedef struct _Border Border;
40 typedef struct _Node Node;
41 
42 struct _Border {
43   Eina_List *one_direction; //partners that are linked in one direction
44   Eina_List *cleanup_nodes; //a list of nodes that needs to be cleaned up when this node is deleted
45 };
46 
47 typedef enum {
48   NODE_TYPE_NORMAL = 0,
49   NODE_TYPE_ONLY_LOGICAL = 2,
50 } Node_Type;
51 
52 struct _Node{
53   Node_Type type; //type of the node
54 
55   Efl_Ui_Focus_Object *focusable;
56   Efl_Ui_Focus_Manager *manager;
57   Efl_Ui_Focus_Manager *redirect_manager;
58 
59   struct _Tree_Node{
60     Node *parent; //the parent of the tree
61     Eina_List *children; //this saves the original set of elements
62     Eina_List *saved_order;
63     Eina_Bool clean_apply; //set if there was no new registration after a "update_order" call
64   }tree;
65 
66   struct _Graph_Node {
67     Border directions[NODE_DIRECTIONS_COUNT];
68   } graph;
69 
70   Eina_Bool on_list : 1;
71   Eina_Bool unused : 1;
72   Eina_Bool this_is_root : 1;
73 };
74 
75 #define T(n) (n->tree)
76 #define G(n) (n->graph)
77 
78 typedef struct {
79     Eina_List *focus_stack;
80     Eina_Hash *node_hash;
81     Efl_Ui_Focus_Manager *redirect;
82     Efl_Ui_Focus_Object *redirect_entry;
83     Eina_List *dirty;
84     Efl_Ui_Focus_Graph_Context graph_ctx;
85     int freeze;
86 
87     Node *root;
88     Eina_Bool border_elements_changed;
89 } Efl_Ui_Focus_Manager_Calc_Data;
90 
91 static Eina_Mempool *_node_mempool;
92 
93 static Node*
node_mem_get(void)94 node_mem_get(void)
95 {
96    return eina_mempool_calloc(_node_mempool, sizeof(Node));
97 }
98 
99 static void
node_mem_free(Node * n)100 node_mem_free(Node *n)
101 {
102    eina_mempool_free(_node_mempool, n);
103 }
104 
105 static Node* _request_subchild(Node *node);
106 static void dirty_add(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Node *dirty);
107 static Node* _next(Node *node);
108 static void _prepare_node(Node *root);
109 
110 static Eina_Bool
_focus_manager_active_get(Eo * obj)111 _focus_manager_active_get(Eo *obj)
112 {
113    Eo *root, *manager, *comp_parent, *redirect;
114 
115    root = efl_ui_focus_manager_root_get(obj);
116 
117    if (efl_isa(root, EFL_UI_FOCUS_MANAGER_WINDOW_ROOT_INTERFACE))
118      return EINA_TRUE;
119 
120    manager = efl_ui_focus_object_focus_manager_get(root);
121 
122    if (!manager) return EINA_FALSE;
123 
124    redirect = efl_ui_focus_manager_redirect_get(manager);
125 
126    if (!redirect) return EINA_FALSE;
127 
128    if (efl_composite_part_is(obj))
129      comp_parent = efl_parent_get(obj); //a focus manager can be attached to something via composition
130    else
131      comp_parent = NULL;
132 
133    if (redirect == obj ||
134       (redirect == comp_parent))
135      return EINA_TRUE;
136 
137    return EINA_FALSE;
138 }
139 
140 static void
_manager_in_chain_set(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)141 _manager_in_chain_set(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
142 {
143    Eo *manager, *root;
144 
145    root = efl_ui_focus_manager_root_get(obj);
146    manager = efl_ui_focus_object_focus_manager_get(pd->root->focusable);
147 
148    EINA_SAFETY_ON_NULL_RETURN(root);
149 
150    if (!efl_isa(root, EFL_UI_FOCUS_MANAGER_WINDOW_ROOT_INTERFACE))
151      EINA_SAFETY_ON_NULL_RETURN(manager);
152 
153    //so we dont run infinitly this does not fix it, but at least we only have a error
154    EINA_SAFETY_ON_TRUE_RETURN(manager == obj);
155 
156    if (manager)
157      efl_ui_focus_manager_focus_set(manager, root);
158    else
159       DBG("No focus manager for focusable %s@%p",
160           efl_class_name_get(pd->root->focusable), root);
161 }
162 
163 
164 static void
border_onedirection_set(Node * node,Efl_Ui_Focus_Direction direction,Eina_List * list)165 border_onedirection_set(Node *node, Efl_Ui_Focus_Direction direction, Eina_List *list)
166 {
167    Node *partner;
168    Eina_List *lnode;
169    Border *border;
170    Efl_Ui_Focus_Direction complement;
171 
172    EINA_SAFETY_ON_FALSE_RETURN(DIRECTION_IS_2D(direction));
173    complement = efl_ui_focus_util_direction_complement(direction);
174    //this is basically a nop. The complement of 2D will *always* be 2D
175    EINA_SAFETY_ON_FALSE_RETURN(DIRECTION_IS_2D(complement));
176 
177    border = &DIRECTION_ACCESS(node, direction);
178 
179    EINA_LIST_FREE(border->one_direction, partner)
180      {
181         Border *b = &DIRECTION_ACCESS(partner, complement);
182         b->cleanup_nodes = eina_list_remove(b->cleanup_nodes, node);
183      }
184 
185    border->one_direction = list;
186 
187    EINA_LIST_FOREACH(border->one_direction, lnode, partner)
188      {
189         Border *comp_border = &DIRECTION_ACCESS(partner, complement);
190 
191         comp_border->cleanup_nodes = eina_list_append(comp_border->cleanup_nodes, node);
192      }
193 }
194 
195 static void
border_onedirection_cleanup(Node * node,Efl_Ui_Focus_Direction direction)196 border_onedirection_cleanup(Node *node, Efl_Ui_Focus_Direction direction)
197 {
198    Node *partner;
199    Border *border;
200    Efl_Ui_Focus_Direction complement;
201 
202    EINA_SAFETY_ON_FALSE_RETURN(DIRECTION_IS_2D(direction));
203    complement = efl_ui_focus_util_direction_complement(direction);
204    //this is basically a nop. The complement of 2D will *always* be 2D
205    EINA_SAFETY_ON_FALSE_RETURN(DIRECTION_IS_2D(complement));
206 
207    border = &DIRECTION_ACCESS(node, direction);
208 
209    EINA_LIST_FREE(border->cleanup_nodes, partner)
210      {
211         Border *b = &DIRECTION_ACCESS(partner, complement);
212         b->one_direction = eina_list_remove(b->one_direction, node);
213      }
214 }
215 /**
216  * Create a new node
217  */
218 static Node*
node_new(Efl_Ui_Focus_Object * focusable,Efl_Ui_Focus_Manager * manager)219 node_new(Efl_Ui_Focus_Object *focusable, Efl_Ui_Focus_Manager *manager)
220 {
221     Node *node;
222 
223     node = node_mem_get();
224 
225     node->focusable = focusable;
226     node->manager = manager;
227 
228     return node;
229 }
230 
231 /**
232  * Looks up given focus object from the focus manager.
233  *
234  * @returns node found, or NULL if focusable was not found in the manager.
235  */
236 static Node*
node_get(Efl_Ui_Focus_Manager * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * focusable)237 node_get(Efl_Ui_Focus_Manager *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *focusable)
238 {
239    Node *ret;
240 
241    ret = eina_hash_find(pd->node_hash, &focusable);
242 
243    if (ret) return ret;
244 
245    ERR("Focusable %p (%s) not registered in manager %p", focusable, efl_class_name_get(focusable), obj);
246 
247    return NULL;
248 }
249 
250 /**
251  * Free a node item and unlink this item from all direction
252  */
253 static void
node_item_free(Node * item)254 node_item_free(Node *item)
255 {
256    Node *n;
257    Eina_List *l;
258    Eo *obj = item->manager;
259    FOCUS_DATA(obj);
260 
261    /*cleanup graph parts*/
262 
263    //add all neighbors of the node to the dirty list
264    if (!item->unused)
265      {
266         for(int i = EFL_UI_FOCUS_DIRECTION_UP; i < EFL_UI_FOCUS_DIRECTION_LAST; i++)
267           {
268              Node *partner;
269 
270 #define MAKE_LIST_DIRTY(node, field) \
271              EINA_LIST_FOREACH(DIRECTION_ACCESS(node, i).field, l, partner) \
272                { \
273                   if (partner->type != NODE_TYPE_ONLY_LOGICAL) \
274                     { \
275                        dirty_add(obj, pd, partner); \
276                        pd->border_elements_changed = EINA_TRUE; \
277                     } \
278                }
279 
280              MAKE_LIST_DIRTY(item, one_direction)
281              MAKE_LIST_DIRTY(item, cleanup_nodes)
282 
283              border_onedirection_cleanup(item, i);
284              border_onedirection_set(item, i, NULL);
285           }
286 
287      }
288 
289    //the unregistering of a item should ever result in atleast a coords_dirty call
290    if (pd->border_elements_changed)
291      efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_COORDS_DIRTY, NULL);
292 
293    /*cleanup manager householdings*/
294 
295    //remove from the focus stack
296 
297    if (!item->unused)
298      pd->focus_stack = eina_list_remove(pd->focus_stack, item);
299 
300    //if this is the entry for redirect, NULL them out!
301    if (pd->redirect_entry == item->focusable)
302      pd->redirect_entry = NULL;
303 
304    //remove from the dirty parts
305    if (item->on_list)
306      pd->dirty = eina_list_remove(pd->dirty, item);
307    item->on_list = EINA_FALSE;
308 
309    /*merge tree items*/
310 
311    //free the tree items
312    if (!item->tree.parent && item->tree.children)
313      {
314         ERR("Freeing the root with children is going to break the logical tree!");
315      }
316 
317    if (item->tree.parent && item->tree.children)
318      {
319         Node *parent;
320 
321         parent = item->tree.parent;
322         //reparent everything into the next layer
323         EINA_LIST_FOREACH(item->tree.children, l, n)
324           {
325              n->tree.parent = item->tree.parent;
326           }
327         parent->tree.children = eina_list_merge(parent->tree.children , item->tree.children);
328      }
329 
330    if (item->tree.parent)
331      {
332         Node *parent;
333 
334         parent = item->tree.parent;
335         T(parent).children = eina_list_remove(T(parent).children, item);
336      }
337 
338    //free the safed order
339    ELM_SAFE_FREE(T(item).saved_order, eina_list_free);
340 
341    node_mem_free(item);
342 }
343 //FOCUS-STACK HELPERS
344 
345 static Efl_Ui_Focus_Object*
_focus_stack_unfocus_last(Efl_Ui_Focus_Manager_Calc_Data * pd)346 _focus_stack_unfocus_last(Efl_Ui_Focus_Manager_Calc_Data *pd)
347 {
348    Efl_Ui_Focus_Object *focusable = NULL;
349    Node *n;
350 
351    n = eina_list_last_data_get(pd->focus_stack);
352 
353    if (n)
354      focusable = n->focusable;
355 
356    pd->focus_stack = eina_list_remove(pd->focus_stack, n);
357 
358    if (n)
359      efl_ui_focus_object_focus_set(n->focusable, EINA_FALSE);
360 
361    return focusable;
362 }
363 
364 #ifdef CALC_DEBUG
365 static void
_debug_node(Node * node)366 _debug_node(Node *node)
367 {
368    Eina_List *tmp = NULL;
369 
370    if (!node) return;
371 
372    printf("NODE %s-%s\n", DEBUG_TUPLE(node->focusable));
373 
374 #define DIR_LIST(dir) DIRECTION_ACCESS(node,dir).partners
375 
376 #define DIR_OUT(dir)\
377    tmp = DIR_LIST(dir); \
378    { \
379       Eina_List *list_node; \
380       Node *partner; \
381       printf("-"#dir"-> ("); \
382       EINA_LIST_FOREACH(tmp, list_node, partner) \
383         printf("%s-%s,", DEBUG_TUPLE(partner->focusable)); \
384       printf(")\n"); \
385    }
386 
387    DIR_OUT(EFL_UI_FOCUS_DIRECTION_RIGHT)
388    DIR_OUT(EFL_UI_FOCUS_DIRECTION_LEFT)
389    DIR_OUT(EFL_UI_FOCUS_DIRECTION_UP)
390    DIR_OUT(EFL_UI_FOCUS_DIRECTION_DOWN)
391 
392 }
393 #endif
394 
395 static Eina_Bool
_no_logical(const void * iter EINA_UNUSED,void * data,void * fdata EINA_UNUSED)396 _no_logical(const void *iter EINA_UNUSED, void *data, void *fdata EINA_UNUSED)
397 {
398    Node *n = data;
399 
400    return n->type != NODE_TYPE_ONLY_LOGICAL;
401 }
402 
403 static void
dirty_flush_node(Efl_Ui_Focus_Manager * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd,Node * node)404 dirty_flush_node(Efl_Ui_Focus_Manager *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd, Node *node)
405 {
406    Efl_Ui_Focus_Graph_Calc_Result result;
407 
408    efl_ui_focus_graph_calc(&pd->graph_ctx, eina_iterator_filter_new(eina_hash_iterator_data_new(pd->node_hash), _no_logical, NULL, NULL) ,  (Opaque_Graph_Member*) node, &result);
409    node->unused = EINA_FALSE;
410    for (int i = 0; i < 4; ++i)
411      {
412         Efl_Ui_Focus_Direction direction = -1;
413         Efl_Ui_Focus_Graph_Calc_Direction_Result *res = NULL;
414         Node *partner;
415         Eina_List *n;
416 
417         if (i == 0)
418           {
419              direction = EFL_UI_FOCUS_DIRECTION_RIGHT;
420              res = &result.right;
421           }
422         else if (i == 1)
423           {
424              direction = EFL_UI_FOCUS_DIRECTION_LEFT;
425              res = &result.left;
426           }
427         else if (i == 2)
428           {
429              direction = EFL_UI_FOCUS_DIRECTION_UP;
430              res = &result.top;
431           }
432         else if (i == 3)
433           {
434              direction = EFL_UI_FOCUS_DIRECTION_DOWN;
435              res = &result.bottom;
436           }
437 
438        EINA_LIST_FOREACH(res->relation, n, partner)
439          partner->unused = EINA_FALSE;
440 
441        border_onedirection_set(node, direction, res->relation);
442      }
443 
444 #ifdef CALC_DEBUG
445    _debug_node(node);
446 #endif
447 }
448 
449 static void
dirty_flush(Efl_Ui_Focus_Manager * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Node * node)450 dirty_flush(Efl_Ui_Focus_Manager *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Node *node)
451 {
452    if (!pd->dirty && node->type == NODE_TYPE_NORMAL) return;
453 
454    efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_FLUSH_PRE, NULL);
455 
456    dirty_flush_node(obj, pd, node);
457 }
458 
459 static void
dirty_flush_all(Efl_Ui_Focus_Manager * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)460 dirty_flush_all(Efl_Ui_Focus_Manager *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
461 {
462    Node *node;
463 
464    efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_FLUSH_PRE, NULL);
465 
466    EINA_LIST_FREE(pd->dirty, node)
467      {
468         node->on_list = EINA_FALSE;
469         dirty_flush_node(obj, pd, node);
470      }
471 }
472 
473 static void
dirty_add(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Node * dirty)474 dirty_add(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Node *dirty)
475 {
476    if (dirty->type == NODE_TYPE_ONLY_LOGICAL)
477      {
478         ERR("Only not only logical nodes can be marked dirty");
479         return;
480      }
481    if (dirty->on_list) return;
482 
483    //if (eina_list_data_find(pd->dirty, dirty)) return;
484    pd->dirty = eina_list_append(pd->dirty, dirty);
485    dirty->on_list = EINA_TRUE;
486 
487    if (!pd->border_elements_changed)
488      efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_COORDS_DIRTY, NULL);
489    pd->border_elements_changed = EINA_TRUE;
490 }
491 
492 
493 static void
_node_new_geometry_cb(void * data,const Efl_Event * event)494 _node_new_geometry_cb(void *data, const Efl_Event *event)
495 {
496    Node *node;
497    FOCUS_DATA(data)
498 
499    if (pd->freeze > 0) {
500      return;
501    }
502 
503    node = node_get(data, pd, event->object);
504    if (!node)
505       return;
506 
507    dirty_add(data, pd, node);
508 
509    return;
510 }
511 
512 static void
_object_del_cb(void * data,const Efl_Event * event)513 _object_del_cb(void *data, const Efl_Event *event)
514 {
515    /*
516     * Lets just implicitly delete items that are deleted
517     * Otherwise we have later just a bunch of errors
518     */
519    efl_ui_focus_manager_calc_unregister(data, event->object);
520 }
521 
522 EFL_CALLBACKS_ARRAY_DEFINE(regular_node,
523     {EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_GEOMETRY_CHANGED, _node_new_geometry_cb},
524     {EFL_EVENT_INVALIDATE, _object_del_cb},
525 );
526 
527 EFL_CALLBACKS_ARRAY_DEFINE(logical_node,
528     {EFL_EVENT_INVALIDATE, _object_del_cb},
529 );
530 
531 
532 //=============================
533 
534 static Node*
_register(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child,Node * parent,Node_Type type,Efl_Ui_Focus_Manager * redirect)535 _register(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child, Node *parent, Node_Type type, Efl_Ui_Focus_Manager *redirect)
536 {
537    Node *node;
538    node = eina_hash_find(pd->node_hash, &child);
539    if (node && !(node->type == type && T(node).parent == parent && node->redirect_manager == redirect))
540      {
541         ERR("Child %p is already registered in the graph (%s)", child, node->type == NODE_TYPE_ONLY_LOGICAL ? "logical" : "regular");
542         return NULL;
543      }
544 
545    node = node_new(child, obj);
546    eina_hash_add(pd->node_hash, &child, node);
547    node->unused = EINA_TRUE;
548    //add the parent
549    if (parent)
550      {
551         T(node).parent = parent;
552         T(parent).children = eina_list_append(T(parent).children, node);
553         T(parent).clean_apply = EINA_FALSE;
554      }
555    node->type = NODE_TYPE_ONLY_LOGICAL;
556    node->redirect_manager = redirect;
557 
558    return node;
559 }
560 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_register_logical(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child,Efl_Ui_Focus_Object * parent,Efl_Ui_Focus_Manager * redirect)561 _efl_ui_focus_manager_calc_register_logical(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child, Efl_Ui_Focus_Object *parent,  Efl_Ui_Focus_Manager *redirect)
562 {
563    Node *node = NULL;
564    Node *pnode = NULL;
565 
566    EINA_SAFETY_ON_NULL_RETURN_VAL(child, EINA_FALSE);
567    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, EINA_FALSE);
568 
569    if (redirect)
570      EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(redirect, EFL_UI_FOCUS_MANAGER_INTERFACE), EINA_FALSE);
571 
572    F_DBG("Manager: %p register %p %p %p", obj, child, parent, redirect);
573 
574    pnode = node_get(obj, pd, parent);
575    if (!pnode) return EINA_FALSE;
576 
577    node = _register(obj, pd, child, pnode, NODE_TYPE_ONLY_LOGICAL, redirect);
578    if (!node) return EINA_FALSE;
579 
580    //listen to deletion
581    efl_event_callback_array_add(child, logical_node(), obj);
582 
583    //set again
584    if (T(pnode).saved_order)
585      {
586         Eina_List *tmp;
587 
588         tmp = eina_list_clone(T(pnode).saved_order);
589         efl_ui_focus_manager_calc_update_order(obj, parent, tmp);
590      }
591 
592    return EINA_TRUE;
593 }
594 
595 
596 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_register(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child,Efl_Ui_Focus_Object * parent,Efl_Ui_Focus_Manager * redirect)597 _efl_ui_focus_manager_calc_register(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child, Efl_Ui_Focus_Object *parent, Efl_Ui_Focus_Manager *redirect)
598 {
599    Node *node = NULL;
600    Node *pnode = NULL;
601 
602    EINA_SAFETY_ON_NULL_RETURN_VAL(child, EINA_FALSE);
603    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, EINA_FALSE);
604 
605    if (redirect)
606      EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(redirect, EFL_UI_FOCUS_MANAGER_INTERFACE), EINA_FALSE);
607 
608    F_DBG("Manager: %p register %p %p %p", obj, child, parent, redirect);
609 
610    pnode = node_get(obj, pd, parent);
611    if (!pnode) return EINA_FALSE;
612 
613    node = _register(obj, pd, child, pnode, NODE_TYPE_NORMAL, redirect);
614    if (!node) return EINA_FALSE;
615 
616    //listen to changes
617    efl_event_callback_array_add(child, regular_node(), obj);
618 
619    node->type = NODE_TYPE_NORMAL;
620    node->redirect_manager = redirect;
621 
622    //mark dirty
623    dirty_add(obj, pd, node);
624 
625    return EINA_TRUE;
626 }
627 
628 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_update_redirect(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child,Efl_Ui_Focus_Manager * redirect)629 _efl_ui_focus_manager_calc_update_redirect(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child, Efl_Ui_Focus_Manager *redirect)
630 {
631    Node *node = node_get(obj, pd, child);
632    if (!node) return EINA_FALSE;
633 
634    if (redirect)
635      EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(redirect, MY_CLASS), EINA_FALSE);
636 
637    node->redirect_manager = redirect;
638 
639    return EINA_TRUE;
640 }
641 
642 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_update_parent(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child,Efl_Ui_Focus_Object * parent_obj)643 _efl_ui_focus_manager_calc_update_parent(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child, Efl_Ui_Focus_Object *parent_obj)
644 {
645    Node *node;
646    Node *parent;
647 
648    EINA_SAFETY_ON_NULL_RETURN_VAL(parent_obj, EINA_FALSE);
649    EINA_SAFETY_ON_NULL_RETURN_VAL(child, EINA_FALSE);
650 
651    node = node_get(obj, pd, child);
652    parent = node_get(obj, pd, parent_obj);
653 
654    if (!node || !parent) return EINA_FALSE;
655 
656    if (T(node).parent)
657      {
658         Node *old_parent;
659 
660         old_parent = T(node).parent;
661 
662         T(old_parent).children = eina_list_remove(T(old_parent).children, node);
663      }
664 
665    T(node).parent = parent;
666 
667    if (T(node).parent)
668      {
669         T(parent).children = eina_list_append(T(parent).children, node);
670      }
671 
672    return EINA_TRUE;
673 }
674 
675 static Eina_List*
_set_a_without_b(Eina_List * a,Eina_List * b)676 _set_a_without_b(Eina_List *a, Eina_List *b)
677 {
678    Eina_List *a_out = NULL, *node;
679    void *data;
680 
681    a_out = eina_list_clone(a);
682 
683    EINA_LIST_FOREACH(b, node, data)
684      {
685         a_out = eina_list_remove(a_out, data);
686      }
687 
688    return a_out;
689 }
690 
691 static Eina_Bool
_equal_set(Eina_List * none_nodes,Eina_List * nodes)692 _equal_set(Eina_List *none_nodes, Eina_List *nodes)
693 {
694    Eina_List *n;
695    Node *node;
696 
697    if (eina_list_count(nodes) != eina_list_count(none_nodes)) return EINA_FALSE;
698 
699    EINA_LIST_FOREACH(nodes, n, node)
700      {
701         if (!eina_list_data_find(none_nodes, node))
702           return EINA_FALSE;
703      }
704 
705    return EINA_TRUE;
706 }
707 
708 EOLIAN static void
_efl_ui_focus_manager_calc_update_order(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * parent,Eina_List * order)709 _efl_ui_focus_manager_calc_update_order(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *parent, Eina_List *order)
710 {
711    Node *pnode;
712    Efl_Ui_Focus_Object *o;
713    Eina_List *node_order = NULL, *not_ordered, *n;
714 
715    F_DBG("Manager_update_order on %p %p", obj, parent);
716 
717    pnode = node_get(obj, pd, parent);
718    if (!pnode)
719      return;
720 
721    ELM_SAFE_FREE(T(pnode).saved_order, eina_list_free);
722    T(pnode).saved_order = order;
723    T(pnode).clean_apply = EINA_TRUE;
724 
725    //get all nodes from the subset
726    EINA_LIST_FOREACH(order, n, o)
727      {
728         Node *tmp;
729 
730         tmp = eina_hash_find(pd->node_hash, &o);
731 
732         if (!tmp) continue;
733         if (T(tmp).parent != pnode) continue;
734 
735         node_order = eina_list_append(node_order, tmp);
736      }
737    if (eina_list_count(node_order) == eina_list_count(T(pnode).children))
738      {
739         eina_list_free(T(pnode).children);
740         T(pnode).children = node_order;
741      }
742    else
743      {
744         not_ordered = _set_a_without_b(T(pnode).children, node_order);
745         eina_list_free(T(pnode).children);
746         T(pnode).children = eina_list_merge(node_order, not_ordered);
747      }
748    T(pnode).clean_apply = EINA_TRUE;
749 
750    return;
751 }
752 
753 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_update_children(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * parent,Eina_List * order)754 _efl_ui_focus_manager_calc_update_children(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *parent, Eina_List *order)
755 {
756    Node *pnode;
757    Efl_Ui_Focus_Object *o;
758    Eina_Bool fail = EINA_FALSE;
759    Eina_List *node_order = NULL;
760 
761    pnode = node_get(obj, pd, parent);
762    if (!pnode)
763      return EINA_FALSE;
764 
765    //get all nodes from the subset
766    EINA_LIST_FREE(order, o)
767      {
768         Node *tmp;
769 
770         tmp = node_get(obj, pd, o);
771         if (!tmp)
772           fail = EINA_TRUE;
773         node_order = eina_list_append(node_order, tmp);
774      }
775 
776    if (fail)
777      {
778         eina_list_free(node_order);
779         return EINA_FALSE;
780      }
781 
782    if (!_equal_set(node_order, T(pnode).children))
783      {
784         ERR("Set of children is not equal");
785         return EINA_FALSE;
786      }
787 
788    eina_list_free(T(pnode).children);
789    T(pnode).children = node_order;
790 
791    return EINA_TRUE;
792 }
793 
794 static inline Node*
_request_subchild_except(Node * n,Eo * except)795 _request_subchild_except(Node *n, Eo *except)
796 {
797    n = _request_subchild(n);
798    while (n && n->focusable == except)
799      {
800         n = _next(n);
801      }
802 
803    return n;
804 }
805 
806 EOLIAN static void
_efl_ui_focus_manager_calc_unregister(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child)807 _efl_ui_focus_manager_calc_unregister(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child)
808 {
809    Node *node;
810 
811    node = eina_hash_find(pd->node_hash, &child);
812 
813    if (!node) return;
814 
815    F_DBG("Manager: %p unregister %p", obj, child);
816 
817    if (eina_list_last_data_get(pd->focus_stack) == node)
818      {
819         if (!efl_invalidated_get(pd->root->focusable) && _focus_manager_active_get(obj))
820           {
821              Node *n = NULL;
822 
823              // if there is no redirect manager, then try to recover the focus property to a different element
824              //allow the removal of the redirect when we are removing the child that is the redirect manager
825              if (!pd->redirect || pd->redirect == child)
826                {
827                   n = eina_list_nth(pd->focus_stack, eina_list_count(pd->focus_stack) - 2);
828                   if (!n)
829                     n = _request_subchild_except(pd->root, node->focusable);
830 
831                   if (n)
832                     efl_ui_focus_manager_focus_set(obj, n->focusable);
833                }
834              // if there is a redirect manager, then remove the flag from the child
835              if (!n)
836                {
837                   efl_ui_focus_object_focus_set(child, EINA_FALSE);
838                }
839           }
840      }
841 
842    eina_hash_del_by_key(pd->node_hash, &child);
843 }
844 
845 static void
_redirect_del(void * data,const Efl_Event * event EINA_UNUSED)846 _redirect_del(void *data, const Efl_Event *event EINA_UNUSED)
847 {
848    efl_ui_focus_manager_redirect_set(data, NULL);
849 }
850 
851 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_redirect_set(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Manager * redirect)852 _efl_ui_focus_manager_calc_efl_ui_focus_manager_redirect_set(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Manager *redirect)
853 {
854    Efl_Ui_Focus_Manager *old_manager;
855 
856    if (pd->redirect == redirect) return;
857 
858    F_DBG("Manager: %p setting redirect from %p to %p", obj, pd->redirect, redirect);
859 
860    old_manager = pd->redirect;
861 
862    if (old_manager)
863      efl_event_callback_del(old_manager, EFL_EVENT_DEL, _redirect_del, obj);
864 
865    pd->redirect = redirect;
866 
867    if (pd->redirect)
868      efl_event_callback_add(pd->redirect, EFL_EVENT_DEL, _redirect_del, obj);
869 
870    if (old_manager)
871      efl_ui_focus_manager_reset_history(old_manager);
872 
873    //adjust focus property of the most upper element
874    if (_focus_manager_active_get(obj))
875      {
876         Node *n = NULL;
877 
878         n = eina_list_last_data_get(pd->focus_stack);
879 
880         if (!pd->redirect && old_manager)
881           {
882              if (n)
883                {
884                   efl_ui_focus_object_focus_set(n->focusable, EINA_TRUE);
885                }
886              else
887                {
888                   n = _request_subchild_except(pd->root, pd->redirect_entry);
889                   if (n)
890                     efl_ui_focus_manager_focus_set(obj, n->focusable);
891                }
892           }
893         else if (pd->redirect && !old_manager)
894           {
895              if (n)
896                efl_ui_focus_object_focus_set(n->focusable, EINA_FALSE);
897           }
898      }
899 
900    efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_REDIRECT_CHANGED , old_manager);
901 
902    //just set the root of the new redirect as focused, so it is in a known state
903    if (redirect)
904      {
905         efl_ui_focus_manager_setup_on_first_touch(redirect, EFL_UI_FOCUS_DIRECTION_LAST, NULL);
906      }
907 }
908 
909 EOLIAN static Efl_Ui_Focus_Manager *
_efl_ui_focus_manager_calc_efl_ui_focus_manager_redirect_get(const Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)910 _efl_ui_focus_manager_calc_efl_ui_focus_manager_redirect_get(const Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
911 {
912    return pd->redirect;
913 }
914 
915 static void
_free_node(void * data)916 _free_node(void *data)
917 {
918    Node *node = data;
919 
920    if (node->type == NODE_TYPE_ONLY_LOGICAL)
921      efl_event_callback_array_del(node->focusable, logical_node(), node->manager);
922    else
923      efl_event_callback_array_del(node->focusable, regular_node(), node->manager);
924 
925    if (!node->this_is_root)
926      {
927         node_item_free(node);
928      }
929 }
930 
931 EOLIAN static Efl_Object *
_efl_ui_focus_manager_calc_efl_object_constructor(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)932 _efl_ui_focus_manager_calc_efl_object_constructor(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
933 {
934    obj = efl_constructor(efl_super(obj, MY_CLASS));
935    pd->node_hash = eina_hash_pointer_new(_free_node);
936 
937    pd->graph_ctx.offset_focusable = offsetof(Node, focusable);
938    pd->border_elements_changed = EINA_TRUE;
939 
940    return obj;
941 }
942 
943 EOLIAN static Efl_Object *
_efl_ui_focus_manager_calc_efl_object_provider_find(const Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd EINA_UNUSED,const Efl_Object * klass)944 _efl_ui_focus_manager_calc_efl_object_provider_find(const Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd EINA_UNUSED, const Efl_Object *klass)
945 {
946    if (klass == MY_CLASS)
947      return (Efl_Object *) obj;
948 
949    return efl_provider_find(efl_super(obj, MY_CLASS), klass);
950 }
951 
952 EOLIAN static void
_efl_ui_focus_manager_calc_efl_object_destructor(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)953 _efl_ui_focus_manager_calc_efl_object_destructor(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
954 {
955    Node *n;
956 
957    pd->focus_stack = eina_list_free(pd->focus_stack);
958    EINA_LIST_FREE(pd->dirty, n)
959      {
960         n->on_list = EINA_FALSE;
961      }
962    eina_hash_free(pd->node_hash);
963 
964    efl_ui_focus_manager_redirect_set(obj, NULL);
965 
966    if (pd->root)
967      node_item_free(pd->root);
968    pd->root = NULL;
969 
970    efl_destructor(efl_super(obj, MY_CLASS));
971 }
972 
973 typedef struct {
974    Eina_Iterator iterator;
975    Eina_Iterator *real_iterator;
976    Efl_Ui_Focus_Manager *object;
977    Eina_Each_Cb filter_cb;
978    Eina_Rect viewport;
979    Eina_Bool use_viewport;
980 } Border_Elements_Iterator;
981 
982 static Eina_Bool
_border_filter_cb(Node * node,Border_Elements_Iterator * pd EINA_UNUSED)983 _border_filter_cb(Node *node, Border_Elements_Iterator *pd EINA_UNUSED)
984 {
985    for(int i = EFL_UI_FOCUS_DIRECTION_UP ;i < EFL_UI_FOCUS_DIRECTION_LAST; i++)
986      {
987         if (!DIRECTION_ACCESS(node, i).one_direction)
988           {
989              return EINA_TRUE;
990           }
991      }
992    return EINA_FALSE;
993 }
994 
995 static Eina_Bool
eina_rectangle_real_inside(Eina_Rect rect,Eina_Rect geom)996 eina_rectangle_real_inside(Eina_Rect rect, Eina_Rect geom)
997 {
998    int min_x, max_x, min_y, max_y;
999 
1000    min_x = geom.rect.x;
1001    min_y = geom.rect.y;
1002    max_x = eina_rectangle_max_x(&geom.rect);
1003    max_y = eina_rectangle_max_y(&geom.rect);
1004 
1005    Eina_Bool inside = eina_rectangle_coords_inside(&rect.rect, min_x, min_y) &&
1006                       eina_rectangle_coords_inside(&rect.rect, min_x, max_y) &&
1007                       eina_rectangle_coords_inside(&rect.rect, max_x, min_y) &&
1008                       eina_rectangle_coords_inside(&rect.rect, max_x, max_y);
1009 
1010    return inside;
1011 }
1012 
1013 static Eina_Bool
_viewport_filter_cb(Node * node,Border_Elements_Iterator * pd)1014 _viewport_filter_cb(Node *node, Border_Elements_Iterator *pd)
1015 {
1016    Node *partner;
1017    Eina_Rect geom;
1018 
1019    if (node->type == NODE_TYPE_ONLY_LOGICAL) return EINA_FALSE;
1020 
1021    geom = efl_ui_focus_object_focus_geometry_get(node->focusable);
1022 
1023    if (eina_rectangle_real_inside(pd->viewport, geom))
1024      {
1025         for (int i = 0; i < 4; ++i)
1026           {
1027              Eina_List *n, *lst = G(node).directions[i].one_direction;
1028 
1029              if (!lst)
1030                return EINA_TRUE;
1031 
1032              EINA_LIST_FOREACH(lst, n, partner)
1033                {
1034                   Eina_Rect partner_geom;
1035                   partner_geom = efl_ui_focus_object_focus_geometry_get(partner->focusable);
1036                   if (!eina_rectangle_real_inside(pd->viewport, partner_geom))
1037                     return EINA_TRUE;
1038                }
1039           }
1040      }
1041    return EINA_FALSE;
1042 }
1043 
1044 
1045 static Eina_Bool
_iterator_next(Border_Elements_Iterator * it,void ** data)1046 _iterator_next(Border_Elements_Iterator *it, void **data)
1047 {
1048    Node *node;
1049 
1050    EINA_ITERATOR_FOREACH(it->real_iterator, node)
1051      {
1052         Eina_Bool use = EINA_FALSE;
1053         if (node->type == NODE_TYPE_ONLY_LOGICAL) continue;
1054 
1055         if (!it->use_viewport)
1056           use = _border_filter_cb(node, it);
1057         else
1058           use = _viewport_filter_cb(node, it);
1059 
1060         if (use)
1061           {
1062              *data = node->focusable;
1063              return EINA_TRUE;
1064           }
1065      }
1066 
1067    return EINA_FALSE;
1068 }
1069 
1070 static Eo *
_iterator_get_container(Border_Elements_Iterator * it)1071 _iterator_get_container(Border_Elements_Iterator *it)
1072 {
1073    return it->object;
1074 }
1075 
1076 static void
_iterator_free(Border_Elements_Iterator * it)1077 _iterator_free(Border_Elements_Iterator *it)
1078 {
1079    eina_iterator_free(it->real_iterator);
1080    free(it);
1081 }
1082 
1083 static void
_prepare_node(Node * root)1084 _prepare_node(Node *root)
1085 {
1086    Eina_List *n;
1087    Node *node;
1088 
1089    efl_ui_focus_object_setup_order(root->focusable);
1090 
1091    EINA_LIST_FOREACH(root->tree.children, n, node)
1092      {
1093         _prepare_node(node);
1094      }
1095 }
1096 
1097 static Border_Elements_Iterator*
_elements_iterator_new(const Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)1098 _elements_iterator_new(const Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
1099 {
1100    Border_Elements_Iterator *it;
1101 
1102    /* XXX const */
1103    dirty_flush_all((Eo *)obj, pd);
1104 
1105    //now call prepare on every node
1106    _prepare_node(pd->root);
1107 
1108    it = calloc(1, sizeof(Border_Elements_Iterator));
1109 
1110    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
1111 
1112    it->real_iterator = eina_hash_iterator_data_new(pd->node_hash);
1113    it->iterator.version = EINA_ITERATOR_VERSION;
1114    it->iterator.next = FUNC_ITERATOR_NEXT(_iterator_next);
1115    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_iterator_get_container);
1116    it->iterator.free = FUNC_ITERATOR_FREE(_iterator_free);
1117    it->object = (Eo *)obj;
1118 
1119    return it;
1120 }
1121 
1122 EOLIAN static Eina_Iterator*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_border_elements_get(const Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)1123 _efl_ui_focus_manager_calc_efl_ui_focus_manager_border_elements_get(const Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
1124 {
1125    pd->border_elements_changed = EINA_FALSE;
1126    return (Eina_Iterator*) _elements_iterator_new(obj, pd);
1127 }
1128 
1129 
1130 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_efl_ui_focus_manager_border_elements_changed_get(const Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)1131 _efl_ui_focus_manager_calc_efl_ui_focus_manager_border_elements_changed_get(const Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
1132 {
1133    return pd->border_elements_changed;
1134 }
1135 
1136 
1137 EOLIAN static Eina_Iterator*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_viewport_elements_get(const Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Eina_Rect viewport)1138 _efl_ui_focus_manager_calc_efl_ui_focus_manager_viewport_elements_get(const Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Eina_Rect viewport)
1139 {
1140    Border_Elements_Iterator *it = _elements_iterator_new(obj, pd);
1141 
1142    it->use_viewport = EINA_TRUE;
1143    it->viewport = viewport;
1144 
1145    return (Eina_Iterator*) it;
1146 }
1147 
1148 static Node*
_no_history_element(Eina_Hash * node_hash)1149 _no_history_element(Eina_Hash *node_hash)
1150 {
1151    //nothing is selected yet, just try to use the first element in the iterator
1152    Eina_Iterator *iter;
1153    Node *upper;
1154 
1155    iter = eina_hash_iterator_data_new(node_hash);
1156 
1157    EINA_ITERATOR_FOREACH(iter, upper)
1158      {
1159         if (upper->type == NODE_TYPE_NORMAL)
1160           break;
1161      }
1162 
1163    eina_iterator_free(iter);
1164 
1165    if (upper->type != NODE_TYPE_NORMAL)
1166      return NULL;
1167 
1168    return upper;
1169 }
1170 
1171 static void
_get_middle(Evas_Object * obj,Eina_Vector2 * elem)1172 _get_middle(Evas_Object *obj, Eina_Vector2 *elem)
1173 {
1174    Eina_Rect geom;
1175 
1176    geom = efl_ui_focus_object_focus_geometry_get(obj);
1177    elem->x = geom.x + geom.w/2;
1178    elem->y = geom.y + geom.h/2;
1179 }
1180 
1181 static Node*
_coords_movement(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Node * upper,Efl_Ui_Focus_Direction direction)1182 _coords_movement(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Node *upper, Efl_Ui_Focus_Direction direction)
1183 {
1184    Node *candidate;
1185    Eina_List *node_list;
1186    Eina_List *lst;
1187 
1188    EINA_SAFETY_ON_FALSE_RETURN_VAL(DIRECTION_IS_2D(direction), NULL);
1189 
1190    //flush the node and prepare all the nodes
1191    _prepare_node(pd->root);
1192    dirty_flush(obj, pd, upper);
1193 
1194    //decide which direction we take
1195    lst = DIRECTION_ACCESS(upper, direction).one_direction;
1196 
1197    //we are searching which of the partners is lower to the history
1198    EINA_LIST_REVERSE_FOREACH(pd->focus_stack, node_list, candidate)
1199      {
1200         //we only calculate partners for normal nodes
1201         if (candidate->type == NODE_TYPE_NORMAL) continue;
1202 
1203         if (eina_list_data_find(lst, candidate))
1204           {
1205              //this is the next accessible part
1206              return candidate;
1207           }
1208      }
1209 
1210    //if we haven't found anything in the history, use the widget with the smallest distance
1211    {
1212       Eina_List *n;
1213       Node *node, *min = NULL;
1214       Eina_Vector2 elem, other;
1215       float min_distance = 0.0;
1216 
1217       _get_middle(upper->focusable, &elem);
1218 
1219       EINA_LIST_FOREACH(lst, n, node)
1220         {
1221            _get_middle(node->focusable, &other);
1222            float tmp = eina_vector2_distance_get(&other, &elem);
1223            if (!min || tmp < min_distance)
1224              {
1225                 min = node;
1226                 min_distance = tmp;
1227              }
1228         }
1229       candidate = min;
1230    }
1231    return candidate;
1232 }
1233 
1234 
1235 static Node*
_prev_item(Node * node)1236 _prev_item(Node *node)
1237 {
1238    Node *parent;
1239    Eina_List *lnode;
1240 
1241    parent = T(node).parent;
1242 
1243    //we are accessing the parents children, prepare!
1244    efl_ui_focus_object_setup_order(parent->focusable);
1245 
1246    lnode = eina_list_data_find_list(T(parent).children, node);
1247    lnode = eina_list_prev(lnode);
1248 
1249    if (lnode)
1250      return eina_list_data_get(lnode);
1251    return NULL;
1252 }
1253 
1254 static Node*
_next(Node * node)1255 _next(Node *node)
1256 {
1257    Node *n;
1258    if (!node) return NULL;
1259 
1260    //Case 1 we are having children
1261    //But only enter the children if it does NOT have a redirect manager
1262    if (T(node).children && !node->redirect_manager)
1263      {
1264         return eina_list_data_get(T(node).children);
1265      }
1266 
1267    //case 2 we are the root and we don't have children, return ourself
1268    if (!T(node).parent)
1269      {
1270         return node;
1271      }
1272 
1273    //case 3 we are not at the end of the parents list
1274    n = node;
1275    while(T(n).parent)
1276      {
1277         Node *parent;
1278         Eina_List *lnode;
1279 
1280         parent = T(n).parent;
1281 
1282         //we are accessing the parents children, prepare!
1283         efl_ui_focus_object_setup_order(parent->focusable);
1284 
1285         lnode = eina_list_data_find_list(T(parent).children, n);
1286         lnode = eina_list_next(lnode);
1287 
1288         if (lnode)
1289           {
1290              return eina_list_data_get(lnode);
1291           }
1292 
1293         n = parent;
1294      }
1295 
1296    //this is then the root again
1297    return NULL;
1298 }
1299 
1300 static Node*
_prev(Node * node)1301 _prev(Node *node)
1302 {
1303    Node *n = NULL;
1304 
1305    //this is the root there is no parent
1306    if (!T(node).parent)
1307      return NULL;
1308 
1309    n =_prev_item(node);
1310 
1311    //we are accessing prev items children, prepare them!
1312    if (n && n->focusable)
1313      efl_ui_focus_object_setup_order(n->focusable);
1314 
1315    //case 1 there is a item in the parent previous to node, which has children
1316    if (n && T(n).children && !n->redirect_manager)
1317      {
1318         do
1319           {
1320               n = eina_list_last_data_get(T(n).children);
1321           }
1322         while (T(n).children && !n->redirect_manager);
1323 
1324         return n;
1325      }
1326 
1327    //case 2 there is a item in the parent previous to node, which has no children
1328    if (n)
1329      return n;
1330 
1331    //case 3 there is a no item in the parent previous to this one
1332    return T(node).parent;
1333 }
1334 
1335 
1336 static Node*
_logical_movement(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd EINA_UNUSED,Node * upper,Efl_Ui_Focus_Direction direction,Eina_Bool accept_logical)1337 _logical_movement(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd EINA_UNUSED, Node *upper, Efl_Ui_Focus_Direction direction, Eina_Bool accept_logical)
1338 {
1339    Node* (*deliver)(Node *n);
1340    Node *result;
1341    Eina_List *stack = NULL;
1342 
1343    EINA_SAFETY_ON_FALSE_RETURN_VAL(DIRECTION_IS_LOGICAL(direction), NULL);
1344 
1345    if (direction == EFL_UI_FOCUS_DIRECTION_NEXT)
1346      deliver = _next;
1347    else
1348      deliver = _prev;
1349 
1350    //search as long as we have a none logical parent
1351    result = upper;
1352    do
1353      {
1354         //give up, if we have already been here
1355         if (!!eina_list_data_find(stack, result))
1356           {
1357              eina_list_free(stack);
1358              ERR("Warning cycle detected\n");
1359              return NULL;
1360           }
1361 
1362         stack = eina_list_append(stack, result);
1363 
1364         if (direction == EFL_UI_FOCUS_DIRECTION_NEXT)
1365           {
1366              //set again
1367              if (T(result).saved_order && !T(result).clean_apply)
1368                {
1369                   Eina_List *tmp;
1370 
1371                   tmp = eina_list_clone(T(result).saved_order);
1372                   efl_ui_focus_manager_calc_update_order(obj, result->focusable, tmp);
1373                }
1374              efl_ui_focus_object_setup_order(result->focusable);
1375           }
1376 
1377         result = deliver(result);
1378         if (accept_logical)
1379           break;
1380    } while(result && result->type != NODE_TYPE_NORMAL && !result->redirect_manager);
1381 
1382    eina_list_free(stack);
1383 
1384    return result;
1385 }
1386 
1387 static Efl_Ui_Focus_Object*
_request_move(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Direction direction,Node * upper,Eina_Bool accept_logical)1388 _request_move(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Direction direction, Node *upper, Eina_Bool accept_logical)
1389 {
1390    Node *dir = NULL;
1391 
1392    if (!upper)
1393      upper = eina_list_last_data_get(pd->focus_stack);
1394 
1395    if (!upper)
1396      {
1397         upper = _no_history_element(pd->node_hash);
1398         if (upper)
1399           return upper->focusable;
1400         return NULL;
1401 
1402      }
1403 
1404    if (direction == EFL_UI_FOCUS_DIRECTION_PREVIOUS
1405     || direction == EFL_UI_FOCUS_DIRECTION_NEXT)
1406       dir = _logical_movement(obj, pd, upper, direction, accept_logical);
1407    else
1408       dir = _coords_movement(obj, pd, upper, direction);
1409 
1410    //return the widget
1411    if (dir)
1412      return dir->focusable;
1413    else
1414      return NULL;
1415 }
1416 
1417 EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_request_move(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Direction direction,Efl_Ui_Focus_Object * child,Eina_Bool logical)1418 _efl_ui_focus_manager_calc_efl_ui_focus_manager_request_move(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Direction direction, Efl_Ui_Focus_Object *child, Eina_Bool logical)
1419 {
1420    Node *child_node;
1421    EINA_SAFETY_ON_FALSE_RETURN_VAL(DIRECTION_CHECK(direction), NULL);
1422 
1423    if (!child)
1424      child_node = eina_list_last_data_get(pd->focus_stack);
1425    else
1426      child_node = node_get(obj, pd, child);
1427 
1428    if (!child_node)
1429      return NULL;
1430 
1431    return _request_move(obj, pd, direction, child_node, logical);
1432 }
1433 
1434 static int
_node_depth(Node * node)1435 _node_depth(Node *node)
1436 {
1437   int i = 0;
1438 
1439   while (node && node->tree.parent)
1440     {
1441       node = node->tree.parent;
1442       i++;
1443     }
1444 
1445   return i;
1446 }
1447 
1448 static Node*
_request_subchild(Node * node)1449 _request_subchild(Node *node)
1450 {
1451    //important! if there are no children _next would return the parent of node which will exceed the limit of children of node
1452    Node *target = NULL;
1453    if (!node) return target;
1454 
1455    if (node->tree.children)
1456      {
1457         int new_depth, old_depth = _node_depth(node);
1458 
1459         target = node;
1460         //try to find a child that is not logical or has a redirect manager
1461         do
1462           {
1463              if (target != node)
1464                efl_ui_focus_object_setup_order(target->focusable);
1465 
1466              target = _next(target);
1467              //abort if we are exceeding the childrens of node
1468              new_depth = _node_depth(target);
1469 
1470              if (new_depth <= old_depth) target = NULL;
1471           }
1472         while (target && target->type == NODE_TYPE_ONLY_LOGICAL && !target->redirect_manager);
1473         F_DBG("Found node %p", target);
1474      }
1475 
1476    return target;
1477 }
1478 
1479 static inline void
_current_focused_parent_to_array(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Eina_Array * array)1480 _current_focused_parent_to_array(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Eina_Array *array)
1481 {
1482    Eo *focused = efl_ui_focus_manager_focus_get(obj);
1483    Node *f;
1484 
1485    if (!focused) return;
1486 
1487    for (f = node_get(obj, pd, focused); f && efl_ui_focus_manager_root_get(obj) != f->focusable; f = f->tree.parent)
1488      {
1489         eina_array_push(array, f->focusable);
1490      }
1491 }
1492 
1493 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_manager_focus_set(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * focus)1494 _efl_ui_focus_manager_calc_efl_ui_focus_manager_manager_focus_set(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *focus)
1495 {
1496    Node *node, *last;
1497    Efl_Ui_Focus_Object *last_focusable = NULL, *new_focusable;
1498    Efl_Ui_Focus_Manager *redirect_manager;
1499    Node_Type node_type;
1500    Eina_Array *old_chain = eina_array_new(5), *chain = eina_array_new(5);
1501 
1502    EINA_SAFETY_ON_NULL_GOTO(focus, end);
1503 
1504    _current_focused_parent_to_array(obj, pd, old_chain);
1505 
1506    //check if node is part of this manager object
1507    node = node_get(obj, pd, focus);
1508    if (!node)
1509      goto end;
1510 
1511    if (node->type == NODE_TYPE_ONLY_LOGICAL && !node->redirect_manager)
1512      {
1513         Node *target = NULL;
1514 
1515         F_DBG(" %p is logical, fetching the next subnode that is either a redirect or a regular", obj);
1516 
1517         //important! if there are no children _next would return the parent of node which will exceed the limit of children of node
1518         efl_ui_focus_object_setup_order(node->focusable);
1519 
1520         target = _request_subchild(node);
1521 
1522         //check if we have found anything
1523         if (target)
1524           {
1525              node = target;
1526           }
1527         else
1528           {
1529              ERR("Could not fetch a node located at %p", node->focusable);
1530              goto end;
1531           }
1532      }
1533 
1534    F_DBG("Manager: %p focusing object %p %s", obj, node->focusable, efl_class_name_get(node->focusable));
1535 
1536 
1537    if (eina_list_last_data_get(pd->focus_stack) == node)
1538      {
1539         //the correct one is focused
1540         if (node->redirect_manager == pd->redirect)
1541           goto end;
1542      }
1543 
1544    //make sure this manager is in the chain of redirects
1545    _manager_in_chain_set(obj, pd);
1546 
1547    node->unused = EINA_FALSE;
1548    node_type = node->type;
1549    new_focusable = node->focusable;
1550 
1551    redirect_manager = node->redirect_manager;
1552 
1553    last = eina_list_last_data_get(pd->focus_stack);
1554    if (last)
1555      last_focusable = last->focusable;
1556 
1557    //remove the object from the list and add it again
1558    if (node_type == NODE_TYPE_NORMAL)
1559      {
1560         EINA_SAFETY_ON_TRUE_RETURN(node->unused);
1561         pd->focus_stack = eina_list_remove(pd->focus_stack, node);
1562         pd->focus_stack = eina_list_append(pd->focus_stack, node);
1563      }
1564 
1565    //capture how we came to the redirect manager
1566    if (redirect_manager)
1567      {
1568         pd->redirect_entry = new_focusable;
1569      }
1570 
1571    //set to NULL here, from the event earlier this pointer could be dead.
1572    node = NULL;
1573 
1574    //unset redirect manager for the case that its a different one to the one we want
1575    if (pd->redirect && pd->redirect != redirect_manager)
1576      {
1577         Efl_Ui_Focus_Manager *m = obj;
1578 
1579         //completely unset the current redirect chain
1580         while (efl_ui_focus_manager_redirect_get(m))
1581          {
1582             Efl_Ui_Focus_Manager *old = m;
1583 
1584             m = efl_ui_focus_manager_redirect_get(m);
1585             efl_ui_focus_manager_redirect_set(old, NULL);
1586           }
1587      }
1588 
1589    //now check if this is also a listener object
1590    if (redirect_manager)
1591      {
1592         //set the redirect
1593         efl_ui_focus_manager_redirect_set(obj, redirect_manager);
1594      }
1595 
1596    /*
1597      Only emit those signals if we are already at the top of the focus stack.
1598      Otherwise focus_get in the callback to that signal might return false.
1599     */
1600    if (node_type == NODE_TYPE_NORMAL)
1601      {
1602         //populate the new change
1603         if (last_focusable)
1604           efl_ui_focus_object_focus_set(last_focusable, EINA_FALSE);
1605         if (new_focusable)
1606           efl_ui_focus_object_focus_set(new_focusable, EINA_TRUE);
1607         efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_MANAGER_FOCUS_CHANGED, last_focusable);
1608      }
1609 
1610     _current_focused_parent_to_array(obj, pd, chain);
1611     //first pop off all elements that are the same
1612     while (eina_array_count(chain) > 0 && eina_array_count(old_chain) > 0 &&
1613          eina_array_data_get(chain, (int)eina_array_count(chain) -1) == eina_array_data_get(old_chain, (int)eina_array_count(old_chain) - 1))
1614      {
1615         eina_array_pop(chain);
1616         eina_array_pop(old_chain);
1617      }
1618 
1619    while (eina_array_count(chain) > 1)
1620      {
1621         Eina_Bool child_focus_changed = EINA_TRUE;
1622         efl_event_callback_call(eina_array_pop(chain), EFL_UI_FOCUS_OBJECT_EVENT_CHILD_FOCUS_CHANGED , &child_focus_changed);
1623      }
1624 
1625    while (eina_array_count(old_chain) > 1)
1626      {
1627         Eina_Bool child_focus_changed = EINA_FALSE;
1628         efl_event_callback_call(eina_array_pop(old_chain), EFL_UI_FOCUS_OBJECT_EVENT_CHILD_FOCUS_CHANGED, &child_focus_changed);
1629      }
1630 
1631 end:
1632    eina_array_free(old_chain);
1633    eina_array_free(chain);
1634 }
1635 
1636 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_setup_on_first_touch(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Direction direction,Efl_Ui_Focus_Object * entry)1637 _efl_ui_focus_manager_calc_efl_ui_focus_manager_setup_on_first_touch(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Direction direction, Efl_Ui_Focus_Object *entry)
1638 {
1639    if (direction == EFL_UI_FOCUS_DIRECTION_PREVIOUS && entry)
1640      {
1641         Efl_Ui_Focus_Manager_Logical_End_Detail last;
1642         Efl_Ui_Focus_Manager *rec_manager = obj;
1643         do
1644           {
1645              last = efl_ui_focus_manager_logical_end(rec_manager);
1646              EINA_SAFETY_ON_NULL_RETURN(last.element);
1647              efl_ui_focus_manager_focus_set(rec_manager, last.element);
1648 
1649              rec_manager = efl_ui_focus_manager_redirect_get(rec_manager);
1650           }
1651         while (rec_manager);
1652      }
1653    else if (DIRECTION_IS_2D(direction) && entry)
1654      efl_ui_focus_manager_focus_set(obj, entry);
1655    else
1656      efl_ui_focus_manager_focus_set(obj, pd->root->focusable);
1657 }
1658 
1659 
1660 EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_move(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Direction direction)1661 _efl_ui_focus_manager_calc_efl_ui_focus_manager_move(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Direction direction)
1662 {
1663    Efl_Ui_Focus_Object *candidate = NULL;
1664    Efl_Ui_Focus_Manager *early, *late;
1665 
1666    // for the case that focus is set to a new element, a new redirect
1667    // manager could have been set, to adjust everything
1668    // in the new reidirect manager we have to call the first touch function.
1669    Eina_Bool adjust_redirect_manager = EINA_FALSE;
1670 
1671    EINA_SAFETY_ON_FALSE_RETURN_VAL(DIRECTION_CHECK(direction), NULL);
1672 
1673    early = efl_ui_focus_manager_redirect_get(obj);
1674 
1675    if (pd->redirect)
1676      {
1677         Efl_Ui_Focus_Object *old_candidate = NULL;
1678         candidate = efl_ui_focus_manager_move(pd->redirect, direction);
1679 
1680         if (!candidate)
1681           {
1682              Efl_Ui_Focus_Object *new_candidate = NULL;
1683 
1684              if (DIRECTION_IS_LOGICAL(direction))
1685                {
1686                   // lets just take the redirect_entry
1687                   Node *n = node_get(obj, pd, pd->redirect_entry);
1688                   new_candidate = _request_move(obj, pd, direction, n, EINA_FALSE);
1689 
1690                   if (new_candidate)
1691                     {
1692                        efl_ui_focus_manager_focus_set(obj, new_candidate);
1693                        adjust_redirect_manager = EINA_TRUE;
1694                     }
1695                   else
1696                     {
1697                        //we set the redirect to NULL since it cannot
1698                        //help us, later on the redirect manager can be
1699                        //set to the same again, and it is strictly new setted up.
1700                        efl_ui_focus_manager_redirect_set(obj, NULL);
1701                        pd->redirect_entry = NULL;
1702                     }
1703 
1704                   candidate = new_candidate;
1705                }
1706              else
1707                {
1708                   Node *n;
1709 
1710                   old_candidate = efl_ui_focus_manager_focus_get(pd->redirect);
1711                   n = eina_hash_find(pd->node_hash, &old_candidate);
1712 
1713                   if (n)
1714                     new_candidate = _request_move(obj, pd, direction, n, EINA_FALSE);
1715 
1716                   if (new_candidate)
1717                     {
1718                        //redirect does not have smth. but we do have.
1719                        efl_ui_focus_manager_focus_set(obj, new_candidate);
1720                        adjust_redirect_manager = EINA_TRUE;
1721                     }
1722                   else
1723                     {
1724                        /*
1725                          DO NOT unset the redirect here, the value has to stay,
1726                          if one direction is not continuing in one direction, the
1727                          other can continue thus we need to safe the redirect for this case.
1728                         */
1729 
1730                     }
1731 
1732                   candidate = new_candidate;
1733                }
1734           }
1735      }
1736    else
1737      {
1738         Efl_Ui_Focus_Object *child = NULL;
1739 
1740         if (!pd->focus_stack)
1741           {
1742              Node *child_node;
1743 
1744              child_node = _no_history_element(pd->node_hash);
1745              if (child_node)
1746                child = child_node->focusable;
1747           }
1748 
1749         candidate = efl_ui_focus_manager_request_move(obj, direction, child, EINA_FALSE);
1750 
1751         F_DBG("Manager: %p moved to %p %s in direction %d", obj, candidate, efl_class_name_get(candidate), direction);
1752 
1753         if (candidate)
1754           {
1755              efl_ui_focus_manager_focus_set(obj, candidate);
1756              adjust_redirect_manager = EINA_TRUE;
1757           }
1758      }
1759 
1760    if (adjust_redirect_manager)
1761      {
1762         late = efl_ui_focus_manager_redirect_get(obj);
1763 
1764         if (early != late)
1765           {
1766              //this is a new manager, we have to init its case!
1767              if (late)
1768                efl_ui_focus_manager_setup_on_first_touch(late, direction, candidate);
1769           }
1770      }
1771 
1772    return candidate;
1773 }
1774 
1775 EOLIAN static Eina_Bool
_efl_ui_focus_manager_calc_efl_ui_focus_manager_root_set(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * root)1776 _efl_ui_focus_manager_calc_efl_ui_focus_manager_root_set(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *root)
1777 {
1778    Node *node;
1779 
1780    if (pd->root)
1781      {
1782         ERR("Root element can only be set once!");
1783         return EINA_FALSE;
1784      }
1785 
1786    node = _register(obj, pd, root, NULL, NODE_TYPE_ONLY_LOGICAL, NULL);
1787 
1788    pd->root = node;
1789    pd->root->this_is_root = 1;
1790 
1791    return EINA_TRUE;
1792 }
1793 
1794 EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_root_get(const Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)1795 _efl_ui_focus_manager_calc_efl_ui_focus_manager_root_get(const Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
1796 {
1797    if (!pd->root) return NULL;
1798 
1799    return pd->root->focusable;
1800 }
1801 
1802 EOLIAN static Efl_Object*
_efl_ui_focus_manager_calc_efl_object_finalize(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)1803 _efl_ui_focus_manager_calc_efl_object_finalize(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
1804 {
1805    Efl_Object *result;
1806 
1807    if (!pd->root)
1808      {
1809         ERR("Constructing failed. No root element set.");
1810         return NULL;
1811      }
1812 
1813    result = efl_finalize(efl_super(obj, MY_CLASS));
1814 
1815    return result;
1816 }
1817 
1818 EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_manager_focus_get(const Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)1819 _efl_ui_focus_manager_calc_efl_ui_focus_manager_manager_focus_get(const Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
1820 {
1821    Node *upper = NULL;
1822 
1823    if (pd->redirect && pd->redirect_entry) return pd->redirect_entry;
1824 
1825    upper = eina_list_last_data_get(pd->focus_stack);
1826 
1827    if (!upper)
1828      return NULL;
1829    return upper->focusable;
1830 }
1831 
1832 typedef struct _Eina_Iterator_Focusable Eina_Iterator_Focusable;
1833 struct _Eina_Iterator_Focusable
1834 {
1835    Eina_Iterator iterator;
1836 
1837    Eina_Iterator *redirect;
1838 };
1839 
1840 static Eina_Bool
_node_focusable_iterator_next(Eina_Iterator_Focusable * it,void ** data)1841 _node_focusable_iterator_next(Eina_Iterator_Focusable *it, void **data)
1842 {
1843    Node *node = NULL;
1844    Eina_Bool r;
1845 
1846    if (!it->redirect) return EINA_FALSE;
1847 
1848    r = eina_iterator_next(it->redirect, (void **) &node);
1849    if (r && data) *data = node->focusable;
1850 
1851    return r;
1852 }
1853 
1854 static Eina_List *
_node_focusable_iterator_get_container(Eina_Iterator_Focusable * it)1855 _node_focusable_iterator_get_container(Eina_Iterator_Focusable *it)
1856 {
1857    if (!it->redirect) return NULL;
1858 
1859    return eina_iterator_container_get(it->redirect);
1860 }
1861 
1862 static void
_node_focusable_iterator_free(Eina_Iterator_Focusable * it)1863 _node_focusable_iterator_free(Eina_Iterator_Focusable *it)
1864 {
1865    eina_iterator_free(it->redirect);
1866    EINA_MAGIC_SET(&it->iterator, 0);
1867    free(it);
1868 }
1869 
1870 static Eina_Iterator *
_node_focusable_iterator_new(Eina_List * nodes)1871 _node_focusable_iterator_new(Eina_List *nodes)
1872 {
1873    Eina_Iterator_Focusable *it;
1874 
1875    it = calloc(1, sizeof (Eina_Iterator_Focusable));
1876    if (!it) return NULL;
1877 
1878    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
1879    it->redirect = eina_list_iterator_new(nodes);
1880 
1881    it->iterator.version = EINA_ITERATOR_VERSION;
1882    it->iterator.next = FUNC_ITERATOR_NEXT(_node_focusable_iterator_next);
1883    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_node_focusable_iterator_get_container);
1884    it->iterator.free = FUNC_ITERATOR_FREE(_node_focusable_iterator_free);
1885 
1886    return &it->iterator;
1887 }
1888 
1889 EOLIAN static Efl_Ui_Focus_Relations*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_fetch(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child)1890 _efl_ui_focus_manager_calc_efl_ui_focus_manager_fetch(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child)
1891 {
1892    Efl_Ui_Focus_Relations *res;
1893    Node *n, *tmp;
1894 
1895    n = node_get(obj, pd, child);
1896    if (!n)
1897       return NULL;
1898 
1899    res = calloc(1, sizeof(Efl_Ui_Focus_Relations));
1900 
1901    dirty_flush(obj, pd, n);
1902 
1903    //make sure to setup_order so next and prev are correctly
1904    if (n->tree.parent)
1905      efl_ui_focus_object_setup_order(n->tree.parent->focusable);
1906    efl_ui_focus_object_setup_order(n->focusable);
1907 
1908    // FIXME: the iterator must actually return the (Node*)->focusable object in it
1909    // Just redirect to default eina list iterator but offset the returned pointer?
1910 #define DIR_ITERATOR(dir) _node_focusable_iterator_new(DIRECTION_ACCESS(n,dir).one_direction);
1911 
1912    res->right = DIR_ITERATOR(EFL_UI_FOCUS_DIRECTION_RIGHT);
1913    res->left = DIR_ITERATOR(EFL_UI_FOCUS_DIRECTION_LEFT);
1914    res->top = DIR_ITERATOR(EFL_UI_FOCUS_DIRECTION_UP);
1915    res->down = DIR_ITERATOR(EFL_UI_FOCUS_DIRECTION_DOWN);
1916    res->next = (tmp = _next(n)) ? tmp->focusable : NULL;
1917    res->prev = (tmp = _prev(n)) ? tmp->focusable : NULL;
1918    res->position_in_history = eina_list_data_idx(pd->focus_stack, n);
1919    res->node = child;
1920 
1921    res->logical = (n->type == NODE_TYPE_ONLY_LOGICAL);
1922 
1923    if (T(n).parent)
1924      res->parent = T(n).parent->focusable;
1925    res->redirect = n->redirect_manager;
1926 #undef DIR_ITERATOR
1927 
1928    return res;
1929 }
1930 
1931 EOLIAN static void
_efl_ui_focus_manager_calc_class_constructor(Efl_Class * c EINA_UNUSED)1932 _efl_ui_focus_manager_calc_class_constructor(Efl_Class *c EINA_UNUSED)
1933 {
1934    const char *choice, *tmp;
1935 
1936 #ifdef EINA_DEFAULT_MEMPOOL
1937    choice = "pass_through";
1938 #else
1939    choice = "chained_mempool";
1940 #endif
1941    tmp = getenv("EINA_MEMPOOL");
1942    if (tmp && tmp[0])
1943      choice = tmp;
1944 
1945    _node_mempool = eina_mempool_add
1946       (choice, "Focus-Node mempool", NULL, sizeof(Node), 20);
1947 
1948    _focus_log_domain = eina_log_domain_register("elementary-focus", EINA_COLOR_CYAN);
1949 }
1950 
1951 EOLIAN static void
_efl_ui_focus_manager_calc_class_destructor(Efl_Class * c EINA_UNUSED)1952 _efl_ui_focus_manager_calc_class_destructor(Efl_Class *c EINA_UNUSED)
1953 {
1954    eina_log_domain_unregister(_focus_log_domain);
1955    _focus_log_domain = -1;
1956 }
1957 
1958 EOLIAN static Efl_Ui_Focus_Manager_Logical_End_Detail
_efl_ui_focus_manager_calc_efl_ui_focus_manager_logical_end(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)1959 _efl_ui_focus_manager_calc_efl_ui_focus_manager_logical_end(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
1960 {
1961    Node *child = pd->root;
1962    Efl_Ui_Focus_Manager_Logical_End_Detail ret = { 0, NULL};
1963    EINA_SAFETY_ON_NULL_RETURN_VAL(child, ret);
1964 
1965    //we need to return the most lower right element
1966 
1967    while ((child) && (T(child).children) && (!child->redirect_manager))
1968      child = eina_list_last_data_get(T(child).children);
1969    while ((child) && (child->type != NODE_TYPE_NORMAL) && (!child->redirect_manager))
1970      child = _prev(child);
1971 
1972    if (child)
1973      {
1974         ret.is_regular_end = child->type == NODE_TYPE_NORMAL;
1975         ret.element = child->focusable;
1976      }
1977    return ret;
1978 }
1979 
1980 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_reset_history(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)1981 _efl_ui_focus_manager_calc_efl_ui_focus_manager_reset_history(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
1982 {
1983   Efl_Ui_Focus_Object *last_focusable;
1984 
1985   if (!pd->focus_stack) return;
1986 
1987   last_focusable = _focus_stack_unfocus_last(pd);
1988 
1989   pd->focus_stack = eina_list_free(pd->focus_stack);
1990 
1991   efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_MANAGER_FOCUS_CHANGED, last_focusable);
1992 }
1993 
1994 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_pop_history_stack(Eo * obj EINA_UNUSED,Efl_Ui_Focus_Manager_Calc_Data * pd)1995 _efl_ui_focus_manager_calc_efl_ui_focus_manager_pop_history_stack(Eo *obj EINA_UNUSED, Efl_Ui_Focus_Manager_Calc_Data *pd)
1996 {
1997   Efl_Ui_Focus_Object *last_focusable;
1998   Node *last = NULL;
1999 
2000   if (pd->redirect)
2001     {
2002        Eina_List *n;
2003 
2004        last_focusable = pd->redirect_entry;
2005        n = eina_list_last(pd->focus_stack);
2006 
2007        while (n && (eina_list_data_get(n) != last_focusable))
2008          {
2009             n = eina_list_prev(n);
2010          }
2011 
2012        last = eina_list_data_get(n);
2013     }
2014   else
2015     {
2016        last_focusable = _focus_stack_unfocus_last(pd);
2017        //get now the highest, and unfocus that!
2018        last = eina_list_last_data_get(pd->focus_stack);
2019     }
2020 
2021   if (last)
2022     {
2023        efl_ui_focus_object_focus_set(last->focusable, EINA_TRUE);
2024     }
2025   else
2026     {
2027        last = _request_subchild_except(pd->root, last_focusable);
2028        if (last)
2029          efl_ui_focus_manager_focus_set(obj, last->focusable);
2030     }
2031 
2032   efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_MANAGER_FOCUS_CHANGED, last_focusable);
2033 }
2034 
2035 EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_focus_manager_calc_efl_ui_focus_manager_request_subchild(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Ui_Focus_Object * child_obj)2036 _efl_ui_focus_manager_calc_efl_ui_focus_manager_request_subchild(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Ui_Focus_Object *child_obj)
2037 {
2038    Node *child, *target;
2039 
2040    child = node_get(obj, pd, child_obj);
2041    if (!child) return NULL;
2042 
2043    target = _request_subchild(child);
2044 
2045    if (target) return target->focusable;
2046    return NULL;
2047 }
2048 
2049 EOLIAN static void
_efl_ui_focus_manager_calc_efl_object_dbg_info_get(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd,Efl_Dbg_Info * root)2050 _efl_ui_focus_manager_calc_efl_object_dbg_info_get(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd, Efl_Dbg_Info *root)
2051 {
2052    efl_dbg_info_get(efl_super(obj, MY_CLASS), root);
2053    Efl_Dbg_Info *append, *group = EFL_DBG_INFO_LIST_APPEND(root, "Efl.Ui.Focus.Manager");
2054    Eina_Iterator *iter;
2055    Node *node;
2056 
2057    append = EFL_DBG_INFO_LIST_APPEND(group, "children");
2058 
2059    iter = eina_hash_iterator_data_new(pd->node_hash);
2060    EINA_ITERATOR_FOREACH(iter, node)
2061      {
2062         EFL_DBG_INFO_APPEND(append, "-", EINA_VALUE_TYPE_UINT64, node->focusable);
2063      }
2064    eina_iterator_free(iter);
2065 }
2066 
2067 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_dirty_logic_freeze(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)2068 _efl_ui_focus_manager_calc_efl_ui_focus_manager_dirty_logic_freeze(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
2069 {
2070   pd->freeze ++;
2071   if (pd->freeze == 1)
2072     {
2073        Eina_Bool event_info = EINA_TRUE;
2074        efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_DIRTY_LOGIC_FREEZE_CHANGED, &event_info);
2075     }
2076 
2077 }
2078 
2079 EOLIAN static void
_efl_ui_focus_manager_calc_efl_ui_focus_manager_dirty_logic_unfreeze(Eo * obj,Efl_Ui_Focus_Manager_Calc_Data * pd)2080 _efl_ui_focus_manager_calc_efl_ui_focus_manager_dirty_logic_unfreeze(Eo *obj, Efl_Ui_Focus_Manager_Calc_Data *pd)
2081 {
2082   pd->freeze --;
2083   if (!pd->freeze)
2084     {
2085        Eina_Bool event_info = EINA_FALSE;
2086        efl_event_callback_call(obj, EFL_UI_FOCUS_MANAGER_EVENT_DIRTY_LOGIC_FREEZE_CHANGED, &event_info);
2087     }
2088 }
2089 
2090 static void
_efl_ui_focus_manager_calc_update_children_ownership_fallback(Efl_Ui_Focus_Object * parent,Eina_List * children)2091 _efl_ui_focus_manager_calc_update_children_ownership_fallback(Efl_Ui_Focus_Object *parent, Eina_List *children)
2092 {
2093    (void)parent;
2094    eina_list_free(children);
2095 }
2096 
2097 EOAPI EFL_FUNC_BODYV_FALLBACK(efl_ui_focus_manager_calc_update_children, Eina_Bool, 0, _efl_ui_focus_manager_calc_update_children_ownership_fallback(parent, children);, EFL_FUNC_CALL(parent, children), Efl_Ui_Focus_Object *parent, Eina_List *children);
2098 
2099 static void
_efl_ui_focus_manager_calc_update_order_ownership_fallback(Efl_Ui_Focus_Object * parent,Eina_List * children)2100 _efl_ui_focus_manager_calc_update_order_ownership_fallback(Efl_Ui_Focus_Object *parent, Eina_List *children)
2101 {
2102    (void)parent;
2103    eina_list_free(children);
2104 }
2105 
2106 EOAPI EFL_VOID_FUNC_BODYV_FALLBACK(efl_ui_focus_manager_calc_update_order, _efl_ui_focus_manager_calc_update_order_ownership_fallback(parent, children);, EFL_FUNC_CALL(parent, children), Efl_Ui_Focus_Object *parent, Eina_List *children);
2107 
2108 #define EFL_UI_FOCUS_MANAGER_CALC_EXTRA_OPS \
2109   EFL_OBJECT_OP_FUNC(efl_dbg_info_get, _efl_ui_focus_manager_calc_efl_object_dbg_info_get), \
2110   EFL_OBJECT_OP_FUNC(efl_ui_focus_manager_calc_update_children, _efl_ui_focus_manager_calc_update_children), \
2111   EFL_OBJECT_OP_FUNC(efl_ui_focus_manager_calc_update_order, _efl_ui_focus_manager_calc_update_order)
2112 
2113 #include "efl_ui_focus_manager_calc.eo.c"
2114