Elementary: ctxpopup documentation and example.
[platform/upstream/elementary.git] / src / lib / elc_ctxpopup.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 typedef struct _Widget_Data Widget_Data;
5
6 struct _Elm_Ctxpopup_Item
7 {
8    Elm_Widget_Item base;
9    const char *label;
10    Evas_Object *icon;
11    Evas_Smart_Cb func;
12    Eina_Bool disabled:1;
13 };
14
15 struct _Widget_Data
16 {
17    Evas_Object *parent;
18    Evas_Object *base;
19    Evas_Object *content;
20    Evas_Object *box;
21    Evas_Object *arrow;
22    Evas_Object *scr;
23    Evas_Object *bg;
24    Evas_Object *hover_parent;
25    Eina_List *items;
26    Elm_Ctxpopup_Direction dir;
27    Elm_Ctxpopup_Direction dir_priority[4];
28    Evas_Coord max_sc_w, max_sc_h;
29    Eina_Bool horizontal:1;
30    Eina_Bool visible:1;
31    Eina_Bool finished:1;
32 };
33
34 static const char *widtype = NULL;
35
36 static void _freeze_on(void *data __UNUSED__, Evas_Object *obj,
37                        void *event_info __UNUSED__);
38 static void _freeze_off(void *data __UNUSED__, Evas_Object *obj,
39                         void *event_info __UNUSED__);
40 static void _hold_on(void *data __UNUSED__, Evas_Object *obj,
41                      void *event_info __UNUSED__);
42 static void _hold_off(void *data __UNUSED__, Evas_Object *obj,
43                       void *event_info __UNUSED__);
44 static void _scroller_size_reset(Widget_Data *wd);
45 static void _hover_parent_callbacks_del(Evas_Object *obj);
46 static void _hover_parent_resize(void *data, Evas *e __UNUSED__,
47                                  Evas_Object *obj __UNUSED__,
48                                  void *event_info __UNUSED__);
49 static void _hover_parent_move(void *data, Evas *e __UNUSED__,
50                                Evas_Object *obj __UNUSED__,
51                                void *event_info __UNUSED__);
52 static void _hover_parent_del(void *data, Evas *e __UNUSED__,
53                               Evas_Object *obj __UNUSED__,
54                               void *event_info __UNUSED__);
55 static void _item_sizing_eval(Elm_Ctxpopup_Item *item);
56 static void _adjust_pos_x(Evas_Coord_Point *pos, Evas_Coord_Point *base_size,
57                           Evas_Coord_Rectangle *hover_area);
58 static void _adjust_pos_y(Evas_Coord_Point *pos, Evas_Coord_Point *base_size,
59                           Evas_Coord_Rectangle *hover_area);
60 static void _ctxpopup_changed_size_hints(void *data __UNUSED__,
61                                          Evas *e __UNUSED__, Evas_Object *obj,
62                                          void *event_info __UNUSED__);
63 static Elm_Ctxpopup_Direction _calc_base_geometry(Evas_Object *obj,
64                                                   Evas_Coord_Rectangle *rect);
65 static void _update_arrow(Evas_Object *obj, Elm_Ctxpopup_Direction dir);
66 static void _sizing_eval(Evas_Object *obj);
67 static void _shift_base_by_arrow(Evas_Object *arrow,
68                                  Elm_Ctxpopup_Direction dir,
69                                  Evas_Coord_Rectangle *rect);
70 static void _del_pre_hook(Evas_Object *obj);
71 static void _del_hook(Evas_Object *obj);
72 static void _theme_hook(Evas_Object *obj);
73 static void _bg_clicked_cb(void *data, Evas_Object *obj __UNUSED__,
74                            const char *emission __UNUSED__,
75                            const char *source __UNUSED__);
76 static void _parent_resize(void *data, Evas *e, Evas_Object *obj,
77                            void *event_info __UNUSED__);
78 static void _ctxpopup_show(void *data __UNUSED__, Evas *e __UNUSED__,
79                            Evas_Object *obj, void *event_info __UNUSED__);
80 static void _hide(Evas_Object *obj);
81 static void _ctxpopup_hide(void *data __UNUSED__, Evas *e __UNUSED__,
82                            Evas_Object *obj, void *event_info __UNUSED__);
83 static void _scroller_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj,
84                              void *event_info __UNUSED__);
85 static void _ctxpopup_move(void *data __UNUSED__, Evas *e __UNUSED__,
86                            Evas_Object *obj, void *event_info __UNUSED__);
87 static void _item_select_cb(void *data, Evas_Object *obj __UNUSED__,
88                             const char *emission __UNUSED__,
89                             const char *source __UNUSED__);
90 static void _item_icon_set(Elm_Ctxpopup_Item *item, Evas_Object *icon);
91 static void _item_label_set(Elm_Ctxpopup_Item *item, const char *label);
92 static void _item_new(Elm_Ctxpopup_Item *item, char *group_name);
93 static void _content_del(void *data, Evas *e, Evas_Object *obj __UNUSED__,
94                          void *event_info __UNUSED__);
95 static void _list_del(Widget_Data *wd);
96 static void _list_new(Evas_Object *obj);
97 static void _remove_items(Widget_Data * wd);
98
99 static const char SIG_DISMISSED[] = "dismissed";
100
101 static const Evas_Smart_Cb_Description _signals[] = {
102    {SIG_DISMISSED, ""},
103    {NULL, NULL}
104 };
105
106 #define ELM_CTXPOPUP_ITEM_CHECK_RETURN(it, ...)                        \
107   ELM_WIDGET_ITEM_CHECK_OR_RETURN((Elm_Widget_Item *)it, __VA_ARGS__); \
108   ELM_CHECK_WIDTYPE(item->base.widget, widtype) __VA_ARGS__;
109
110 static void
111 _freeze_on(void *data __UNUSED__, Evas_Object *obj,
112            void *event_info __UNUSED__)
113 {
114    Widget_Data *wd = elm_widget_data_get(obj);
115
116    if ((!wd) || (!wd->scr)) return;
117    elm_object_scroll_freeze_push(wd->scr);
118 }
119
120 static void
121 _freeze_off(void *data __UNUSED__, Evas_Object *obj,
122             void *event_info __UNUSED__)
123 {
124    Widget_Data *wd = elm_widget_data_get(obj);
125
126    if ((!wd) || (!wd->scr)) return;
127    elm_object_scroll_freeze_pop(wd->scr);
128 }
129
130 static void
131 _hold_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
132 {
133    Widget_Data *wd = elm_widget_data_get(obj);
134
135    if ((!wd) || (!wd->scr)) return;
136    elm_object_scroll_hold_push(wd->scr);
137 }
138
139 static void
140 _hold_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
141 {
142    Widget_Data *wd = elm_widget_data_get(obj);
143
144    if ((!wd) || (!wd->scr)) return;
145    elm_object_scroll_hold_pop(wd->scr);
146 }
147
148 static void
149 _scroller_size_reset(Widget_Data *wd)
150 {
151    wd->finished = EINA_FALSE;
152    wd->max_sc_h = -1;
153    wd->max_sc_w = -1;
154 }
155
156 static void
157 _hover_parent_callbacks_del(Evas_Object *obj)
158 {
159    Widget_Data *wd = elm_widget_data_get(obj);
160
161    if ((!wd) || (!wd->hover_parent))
162       return;
163
164    evas_object_event_callback_del_full(wd->hover_parent, EVAS_CALLBACK_DEL,
165                                        _hover_parent_del, obj);
166    evas_object_event_callback_del_full(wd->hover_parent, EVAS_CALLBACK_MOVE,
167                                        _hover_parent_move, obj);
168    evas_object_event_callback_del_full(wd->hover_parent, EVAS_CALLBACK_RESIZE,
169                                        _hover_parent_resize, obj);
170 }
171
172 static void
173 _hover_parent_resize(void *data, Evas *e __UNUSED__,
174                      Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
175 {
176    Widget_Data *wd = elm_widget_data_get(data);
177
178    if (!wd) return;
179
180    if (wd->visible)
181      {
182         _scroller_size_reset(wd);
183         _sizing_eval(data);
184      }
185 }
186
187 static void
188 _hover_parent_move(void *data, Evas *e __UNUSED__,
189                    Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
190 {
191    Widget_Data *wd = elm_widget_data_get(data);
192
193    if (!wd) return;
194
195    if (wd->visible)
196      {
197         _scroller_size_reset(wd);
198         _sizing_eval(obj);
199      }
200 }
201
202 static void
203 _hover_parent_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
204                   void *event_info __UNUSED__)
205 {
206    Widget_Data *wd = elm_widget_data_get(data);
207
208    if (!wd) return;
209
210    wd->hover_parent = NULL;
211 }
212
213 static void
214 _item_sizing_eval(Elm_Ctxpopup_Item *item)
215 {
216    Evas_Coord min_w = -1, min_h = -1, max_w = -1, max_h = -1;
217
218    if (!item) return;
219
220    edje_object_size_min_restricted_calc(item->base.view, &min_w, &min_h, min_w,
221                                         min_h);
222    evas_object_size_hint_min_set(item->base.view, min_w, min_h);
223    evas_object_size_hint_max_set(item->base.view, max_w, max_h);
224 }
225
226 static void
227 _adjust_pos_x(Evas_Coord_Point *pos, Evas_Coord_Point *base_size,
228               Evas_Coord_Rectangle *hover_area)
229 {
230    pos->x -= (base_size->x / 2);
231
232    if (pos->x < hover_area->x)
233       pos->x = hover_area->x;
234    else if ((pos->x + base_size->x) > (hover_area->x + hover_area->w))
235       pos->x = (hover_area->x + hover_area->w) - base_size->x;
236
237    if (base_size->x > hover_area->w)
238       base_size->x -= (base_size->x - hover_area->w);
239
240    if (pos->x < hover_area->x)
241       pos->x = hover_area->x;
242 }
243
244 static void
245 _adjust_pos_y(Evas_Coord_Point *pos, Evas_Coord_Point *base_size,
246               Evas_Coord_Rectangle *hover_area)
247 {
248    pos->y -= (base_size->y / 2);
249
250    if (pos->y < hover_area->y)
251       pos->y = hover_area->y;
252    else if ((pos->y + base_size->y) > (hover_area->y + hover_area->h))
253       pos->y = hover_area->y + hover_area->h - base_size->y;
254
255    if (base_size->y > hover_area->h)
256       base_size->y -= (base_size->y - hover_area->h);
257
258    if (pos->y < hover_area->y)
259       pos->y = hover_area->y;
260 }
261
262 static void
263 _ctxpopup_changed_size_hints(void *data __UNUSED__, Evas *e __UNUSED__,
264                              Evas_Object *obj, void *event_info __UNUSED__)
265 {
266    Widget_Data *wd;
267
268    wd = elm_widget_data_get(obj);
269    if (!wd) return;
270
271    if (wd->visible)
272       _sizing_eval(obj);
273 }
274
275 static Elm_Ctxpopup_Direction
276 _calc_base_geometry(Evas_Object *obj, Evas_Coord_Rectangle *rect)
277 {
278    Widget_Data *wd;
279    Evas_Coord_Point pos = {0, 0};
280    Evas_Coord_Point base_size;
281    Evas_Coord_Point max_size;
282    Evas_Coord_Point min_size;
283    Evas_Coord_Rectangle hover_area;
284    Evas_Coord_Rectangle parent_size;
285    Evas_Coord_Point arrow_size;
286    Elm_Ctxpopup_Direction arrow = ELM_CTXPOPUP_DIRECTION_DOWN;
287    Evas_Coord_Point temp;
288    int idx;
289
290    wd = elm_widget_data_get(obj);
291
292    if ((!wd) || (!rect))
293       return ELM_CTXPOPUP_DIRECTION_DOWN;
294
295    edje_object_part_geometry_get(wd->arrow, "ctxpopup_arrow", NULL, NULL,
296                                  &arrow_size.x, &arrow_size.y);
297    evas_object_resize(wd->arrow, arrow_size.x, arrow_size.y);
298
299    //Initialize Area Rectangle.
300    if (wd->hover_parent)
301       evas_object_geometry_get(wd->hover_parent, &hover_area.x, &hover_area.y,
302                                &hover_area.w, &hover_area.h);
303    else
304      {
305         evas_object_geometry_get(wd->parent, &parent_size.x, &parent_size.y,
306                                  &parent_size.w, &parent_size.h);
307         hover_area.x = parent_size.x;
308         hover_area.y = parent_size.y;
309         hover_area.w = parent_size.w;
310         hover_area.h = parent_size.h;
311      }
312
313    evas_object_geometry_get(obj, &pos.x, &pos.y, NULL, NULL);
314
315    //recalc the edje
316    edje_object_size_min_calc(wd->base, &base_size.x, &base_size.y);
317    evas_object_smart_calculate(wd->base);
318
319    //Limit to Max Size
320    evas_object_size_hint_max_get(obj, &max_size.x, &max_size.y);
321
322    if ((max_size.y > 0) && (base_size.y > max_size.y))
323       base_size.y = max_size.y;
324
325    if ((max_size.x > 0) && (base_size.x > max_size.x))
326       base_size.x = max_size.x;
327
328    //Limit to Min Size
329    evas_object_size_hint_min_get(obj, &min_size.x, &min_size.y);
330
331    if ((min_size.y > 0) && (base_size.y < min_size.y))
332       base_size.y = min_size.y;
333
334    if ((min_size.x > 0) && (base_size.x < min_size.x))
335       base_size.x = min_size.x;
336
337    //Check the Which direction is available.
338    //If find a avaialble direction, it adjusts position and size.
339    for (idx = 0; idx < 4; idx++)
340      {
341         switch (wd->dir_priority[idx])
342           {
343            case ELM_CTXPOPUP_DIRECTION_UP:
344               temp.y = (pos.y - base_size.y);
345               if ((temp.y - arrow_size.y) < hover_area.y)
346                  continue;
347               _adjust_pos_x(&pos, &base_size, &hover_area);
348               pos.y -= base_size.y;
349               arrow = ELM_CTXPOPUP_DIRECTION_DOWN;
350               break;
351            case ELM_CTXPOPUP_DIRECTION_LEFT:
352               temp.x = (pos.x - base_size.x);
353               if ((temp.x - arrow_size.x) < hover_area.x)
354                  continue;
355               _adjust_pos_y(&pos, &base_size, &hover_area);
356               pos.x -= base_size.x;
357               arrow = ELM_CTXPOPUP_DIRECTION_RIGHT;
358               break;
359            case ELM_CTXPOPUP_DIRECTION_RIGHT:
360               temp.x = (pos.x + base_size.x);
361               if ((temp.x + arrow_size.x) >
362                   (hover_area.x + hover_area.w))
363                  continue;
364               _adjust_pos_y(&pos, &base_size, &hover_area);
365               arrow = ELM_CTXPOPUP_DIRECTION_LEFT;
366               break;
367            case ELM_CTXPOPUP_DIRECTION_DOWN:
368               temp.y = (pos.y + base_size.y);
369               if ((temp.y + arrow_size.y) >
370                   (hover_area.y + hover_area.h))
371                  continue;
372               _adjust_pos_x(&pos, &base_size, &hover_area);
373               arrow = ELM_CTXPOPUP_DIRECTION_UP;
374               break;
375            default:
376               break;
377           }
378         break;
379      }
380
381    //In this case, all directions are invalid because of lack of space.
382    if (idx == 4)
383      {
384         //TODO 1: Find the largest space direction.
385         Evas_Coord length[2];
386
387         length[0] = pos.y - hover_area.y;
388         length[1] = (hover_area.y + hover_area.h) - pos.y;
389
390         if (length[0] > length[1])
391            idx = ELM_CTXPOPUP_DIRECTION_UP;
392         else
393            idx = ELM_CTXPOPUP_DIRECTION_DOWN;
394
395         //TODO 2: determine x , y
396         switch (idx)
397           {
398            case ELM_CTXPOPUP_DIRECTION_UP:
399               _adjust_pos_x(&pos, &base_size, &hover_area);
400               pos.y -= base_size.y;
401               arrow = ELM_CTXPOPUP_DIRECTION_DOWN;
402               if (pos.y < hover_area.y + arrow_size.y)
403                 {
404                    base_size.y -= ((hover_area.y + arrow_size.y) - pos.y);
405                    pos.y = hover_area.y + arrow_size.y;
406                 }
407               break;
408            case ELM_CTXPOPUP_DIRECTION_LEFT:
409               _adjust_pos_y(&pos, &base_size, &hover_area);
410               pos.x -= base_size.x;
411               arrow = ELM_CTXPOPUP_DIRECTION_RIGHT;
412               if (pos.x < hover_area.x + arrow_size.x)
413                 {
414                    base_size.x -= ((hover_area.x + arrow_size.x) - pos.x);
415                    pos.x = hover_area.x + arrow_size.x;
416                 }
417               break;
418            case ELM_CTXPOPUP_DIRECTION_RIGHT:
419               _adjust_pos_y(&pos, &base_size, &hover_area);
420               arrow = ELM_CTXPOPUP_DIRECTION_LEFT;
421               if (pos.x + arrow_size.x + base_size.x >
422                   hover_area.x + hover_area.w)
423                  base_size.x -=
424                     ((pos.x + arrow_size.x + base_size.x) -
425                      (hover_area.x + hover_area.w));
426               break;
427            case ELM_CTXPOPUP_DIRECTION_DOWN:
428               _adjust_pos_x(&pos, &base_size, &hover_area);
429               arrow = ELM_CTXPOPUP_DIRECTION_UP;
430               if (pos.y + arrow_size.y + base_size.y >
431                   hover_area.y + hover_area.h)
432                  base_size.y -=
433                     ((pos.y + arrow_size.y + base_size.y) -
434                      (hover_area.y + hover_area.h));
435               break;
436            default:
437               break;
438           }
439      }
440
441    //Final position and size.
442    rect->x = pos.x;
443    rect->y = pos.y;
444    rect->w = base_size.x;
445    rect->h = base_size.y;
446
447    return arrow;
448 }
449
450 static void
451 _update_arrow(Evas_Object *obj, Elm_Ctxpopup_Direction dir)
452 {
453    Evas_Coord x, y;
454    Evas_Coord_Rectangle arrow_size;
455    Evas_Coord_Rectangle base_size;
456    Widget_Data *wd;
457
458    wd = elm_widget_data_get(obj);
459    if (!wd) return;
460
461    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
462    evas_object_geometry_get(wd->arrow, NULL, NULL, &arrow_size.w,
463                             &arrow_size.h);
464    evas_object_geometry_get(wd->base, &base_size.x, &base_size.y,
465                             &base_size.w, &base_size.h);
466
467    switch (dir)
468      {
469       case ELM_CTXPOPUP_DIRECTION_LEFT:
470          edje_object_signal_emit(wd->arrow, "elm,state,left", "elm");
471          edje_object_part_swallow(wd->base, "elm.swallow.arrow_left", wd->arrow);
472          if (base_size.h > 0)
473            {
474               if (y < ((arrow_size.h * 0.5) + base_size.y))
475                 y = 0;
476               else if (y > base_size.y + base_size.h - (arrow_size.h * 0.5))
477                 y = base_size.h - arrow_size.h;
478               else
479                 y = y - base_size.y - (arrow_size.h * 0.5);
480               edje_object_part_drag_value_set(wd->base, "elm.swallow.arrow_left", 1,
481                                               (double) (y) / (double) (base_size.h - arrow_size.h));
482            }
483          break;
484       case ELM_CTXPOPUP_DIRECTION_RIGHT:
485          edje_object_signal_emit(wd->arrow, "elm,state,right", "elm");
486          edje_object_part_swallow(wd->base, "elm.swallow.arrow_right", wd->arrow);
487          if (base_size.h > 0)
488             {
489               if (y < (arrow_size.h * 0.5) + base_size.y)
490                 y = 0;
491               else if (y > (base_size.y + base_size.h - (arrow_size.h * 0.5)))
492                 y = base_size.h - arrow_size.h;
493               else y = y - base_size.y - (arrow_size.h * 0.5);
494               edje_object_part_drag_value_set(wd->base, "elm.swallow.arrow_right", 0,
495                                               (double) (y) / (double) (base_size.h - arrow_size.h));
496             }
497          break;
498       case ELM_CTXPOPUP_DIRECTION_UP:
499          edje_object_signal_emit(wd->arrow, "elm,state,top", "elm");
500          edje_object_part_swallow(wd->base, "elm.swallow.arrow_up", wd->arrow);
501          if (base_size.w > 0)
502            {
503               if (x < (arrow_size.w * 0.5) + base_size.x)
504                 x = 0;
505               else if (x > (base_size.x + base_size.w - (arrow_size.w * 0.5)))
506                 x = base_size.w - arrow_size.w;
507               else
508                 x = x - base_size.x - (arrow_size.w * 0.5);
509               edje_object_part_drag_value_set(wd->base, "elm.swallow.arrow_up",
510                                               (double) (x) / (double) (base_size.w - arrow_size.w), 1);
511            }
512          break;
513       case ELM_CTXPOPUP_DIRECTION_DOWN:
514          edje_object_signal_emit(wd->arrow, "elm,state,bottom", "elm");
515          edje_object_part_swallow(wd->base, "elm.swallow.arrow_down", wd->arrow);
516          if (base_size.w > 0)
517            {
518               if (x < (arrow_size.w * 0.5) + base_size.x)
519                 x = 0;
520               else if (x > (base_size.x + base_size.w - (arrow_size.w * 0.5)))
521                 x = base_size.w - arrow_size.w;
522               else x = x - base_size.x - (arrow_size.w * 0.5);
523               edje_object_part_drag_value_set(wd->base, "elm.swallow.arrow_down",
524                                               (double) (x) / (double) (base_size.w - arrow_size.w), 0);
525            }
526          break;
527       default:
528          break;
529      }
530 }
531
532 static void
533 _sizing_eval(Evas_Object *obj)
534 {
535    Widget_Data *wd;
536    Eina_List *elist;
537    Elm_Ctxpopup_Item *item;
538    Evas_Coord_Rectangle rect = { 0, 0, 1, 1 };
539    Evas_Coord_Point box_size = { 0, 0 };
540    Evas_Coord_Point _box_size = { 0, 0 };
541
542    wd = elm_widget_data_get(obj);
543    if ((!wd) || (!wd->parent)) return;
544
545    //Box, Scroller
546    EINA_LIST_FOREACH(wd->items, elist, item)
547      {
548         _item_sizing_eval(item);
549         evas_object_size_hint_min_get(item->base.view, &_box_size.x, &_box_size.y);
550         if (!wd->horizontal)
551           {
552              if (_box_size.x > box_size.x)
553                 box_size.x = _box_size.x;
554              if (_box_size.y != -1)
555                 box_size.y += _box_size.y;
556           }
557         else
558           {
559              if (_box_size.x != -1)
560                 box_size.x += _box_size.x;
561              if (_box_size.y > box_size.y)
562                 box_size.y = _box_size.y;
563           }
564      }
565
566    if (!wd->content)
567      {
568         evas_object_size_hint_min_set(wd->box, box_size.x, box_size.y);
569         evas_object_size_hint_min_set(wd->scr, box_size.x, box_size.y);
570      }
571
572    //Base
573    wd->dir = _calc_base_geometry(obj, &rect);
574    _update_arrow(obj, wd->dir);
575    _shift_base_by_arrow(wd->arrow, wd->dir, &rect);
576
577    //resize scroller according to final size.
578    if (!wd->content)
579       evas_object_smart_calculate(wd->scr);
580
581    evas_object_move(wd->base, rect.x, rect.y);
582    evas_object_resize(wd->base, rect.w, rect.h);
583 }
584
585 static void
586 _shift_base_by_arrow(Evas_Object *arrow, Elm_Ctxpopup_Direction dir,
587                      Evas_Coord_Rectangle *rect)
588 {
589    Evas_Coord arrow_w, arrow_h;
590
591    evas_object_geometry_get(arrow, NULL, NULL, &arrow_w, &arrow_h);
592
593    switch (dir)
594      {
595       case ELM_CTXPOPUP_DIRECTION_LEFT:
596          rect->x += arrow_w;
597          break;
598       case ELM_CTXPOPUP_DIRECTION_RIGHT:
599          rect->x -= arrow_w;
600          break;
601       case ELM_CTXPOPUP_DIRECTION_UP:
602          rect->y += arrow_h;
603          break;
604       case ELM_CTXPOPUP_DIRECTION_DOWN:
605          rect->y -= arrow_h;
606          break;
607       default:
608          break;
609      }
610 }
611
612 static void
613 _del_pre_hook(Evas_Object *obj)
614 {
615    Widget_Data *wd;
616
617    wd = elm_widget_data_get(obj);
618    if (!wd) return;
619
620    evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_RESIZE,
621                                        _parent_resize, obj);
622
623    _hover_parent_callbacks_del(obj);
624 }
625
626 static void
627 _del_hook(Evas_Object *obj)
628 {
629    Widget_Data *wd;
630
631    wd = elm_widget_data_get(obj);
632    if (!wd) return;
633
634    elm_ctxpopup_clear(obj);
635    evas_object_del(wd->arrow);
636    evas_object_del(wd->base);
637    free(wd);
638 }
639
640 static void
641 _theme_hook(Evas_Object *obj)
642 {
643    Widget_Data *wd;
644    Eina_List *elist;
645    Elm_Ctxpopup_Item *item;
646
647    wd = elm_widget_data_get(obj);
648    if (!wd) return;
649
650    //Items
651    EINA_LIST_FOREACH(wd->items, elist, item)
652      {
653         if (item->label && item->icon)
654            _elm_theme_object_set(obj, item->base.view, "ctxpopup",
655                                  "icon_text_style_item",
656                                  elm_widget_style_get(obj));
657         else if (item->label)
658            _elm_theme_object_set(obj, item->base.view, "ctxpopup", "text_style_item",
659                                  elm_widget_style_get(obj));
660         else if (item->icon)
661            _elm_theme_object_set(obj, item->base.view, "ctxpopup", "icon_style_item",
662                                  elm_widget_style_get(obj));
663         if (item->label)
664            edje_object_part_text_set(item->base.view, "elm.text", item->label);
665
666         if (item->disabled)
667            edje_object_signal_emit(item->base.view, "elm,state,disabled", "elm");
668
669         edje_object_message_signal_process(item->base.view);
670      }
671
672    _elm_theme_object_set(obj, wd->bg, "ctxpopup", "bg",
673                          elm_widget_style_get(obj));
674    _elm_theme_object_set(obj, wd->base, "ctxpopup", "base",
675                          elm_widget_style_get(obj));
676    _elm_theme_object_set(obj, wd->arrow, "ctxpopup", "arrow",
677                          elm_widget_style_get(obj));
678
679    if (wd->scr)
680      {
681         if (!strncmp(elm_object_style_get(obj), "default",
682                      strlen("default")))
683            elm_object_style_set(wd->scr, "ctxpopup");
684         else
685            elm_object_style_set(wd->scr, elm_object_style_get(obj));
686      }
687
688    if (wd->visible)
689      {
690         _scroller_size_reset(wd);
691         _sizing_eval(obj);
692      }
693 }
694
695 static void
696 _bg_clicked_cb(void *data, Evas_Object *obj __UNUSED__,
697                const char *emission __UNUSED__, const char *source __UNUSED__)
698 {
699    evas_object_hide(data);
700 }
701
702 static void
703 _parent_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj,
704                void *event_info __UNUSED__)
705 {
706    Evas_Coord w, h;
707    Widget_Data *wd;
708
709    wd = elm_widget_data_get(data);
710    if (!wd) return;
711
712    evas_object_geometry_get(obj, NULL, NULL, &w, &h);
713    evas_object_resize(wd->bg, w, h);
714
715    if (!wd->visible) return;
716
717    _hide(data);
718 }
719
720 static void
721 _ctxpopup_show(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj,
722                void *event_info __UNUSED__)
723 {
724    Widget_Data *wd;
725
726    wd = elm_widget_data_get(obj);
727    if (!wd) return;
728
729    if ((!wd->items) && (!wd->content)) return;
730
731    wd->visible = EINA_TRUE;
732
733    evas_object_show(wd->bg);
734    evas_object_show(wd->base);
735    evas_object_show(wd->arrow);
736
737    edje_object_signal_emit(wd->bg, "elm,state,show", "elm");
738
739    _sizing_eval(obj);
740 }
741
742 static void
743 _hide(Evas_Object *obj)
744 {
745    Widget_Data *wd = elm_widget_data_get(obj);
746
747    if (!wd) return;
748
749    evas_object_hide(wd->bg);
750    evas_object_hide(wd->arrow);
751    evas_object_hide(wd->base);
752
753    _scroller_size_reset(wd);
754
755    evas_object_smart_callback_call(obj, SIG_DISMISSED, NULL);
756    wd->visible = EINA_FALSE;
757 }
758
759 static void
760 _ctxpopup_hide(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj,
761                void *event_info __UNUSED__)
762 {
763    Widget_Data *wd;
764
765    wd = elm_widget_data_get(obj);
766    if ((!wd) || (!wd->visible))
767       return;
768
769    _hide(obj);
770 }
771
772 static void
773 _scroller_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj,
774                  void *event_info __UNUSED__)
775 {
776    Widget_Data *wd;
777    Evas_Coord w, h;
778
779    wd = elm_widget_data_get(data);
780    if (!wd) return;
781    if (!wd->visible) return;
782    if (wd->finished) return;
783
784    evas_object_geometry_get(obj, 0, 0, &w, &h);
785
786    if (w != 0 && h != 0)
787      {
788         if ((w <= wd->max_sc_w) && (h <= wd->max_sc_h))
789           {
790              _sizing_eval(data);
791              wd->finished = EINA_TRUE;
792              return;
793           }
794      }
795
796    if (wd->max_sc_w < w)
797       wd->max_sc_w = w;
798    if (wd->max_sc_h < h)
799       wd->max_sc_h = h;
800
801    _sizing_eval(data);
802 }
803
804 static void
805 _ctxpopup_move(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj,
806                void *event_info __UNUSED__)
807 {
808    Widget_Data *wd;
809
810    wd = elm_widget_data_get(obj);
811
812    if (!wd) return;
813
814    if (wd->visible)
815       evas_object_show(wd->arrow);
816
817    _scroller_size_reset(wd);
818    _sizing_eval(obj);
819 }
820
821 static void
822 _item_select_cb(void *data, Evas_Object *obj __UNUSED__,
823                 const char *emission __UNUSED__, const char *source __UNUSED__)
824 {
825    Elm_Ctxpopup_Item *item = data;
826
827    if (!item) return;
828    if (item->disabled) return;
829
830    if (item->func)
831       item->func((void*) item->base.data, item->base.widget, data);
832 }
833
834 static void
835 _item_icon_set(Elm_Ctxpopup_Item *item, Evas_Object *icon)
836 {
837    if (item->icon)
838      evas_object_del(item->icon);
839
840    item->icon = icon;
841    if (!icon) return;
842
843    edje_object_part_swallow(item->base.view, "elm.swallow.icon", item->icon);
844    edje_object_message_signal_process(item->base.view);
845 }
846
847 static void
848 _item_label_set(Elm_Ctxpopup_Item *item, const char *label)
849 {
850    if (!eina_stringshare_replace(&item->label, label))
851       return;
852
853    edje_object_part_text_set(item->base.view, "elm.text", label);
854    edje_object_message_signal_process(item->base.view);
855 }
856
857 static void
858 _item_new(Elm_Ctxpopup_Item *item, char *group_name)
859 {
860    Widget_Data *wd;
861
862    wd = elm_widget_data_get(item->base.widget);
863    if (!wd) return;
864
865    item->base.view = edje_object_add(evas_object_evas_get(wd->base));
866    _elm_theme_object_set(item->base.widget, item->base.view, "ctxpopup", group_name,
867                          elm_widget_style_get(item->base.widget));
868    edje_object_signal_callback_add(item->base.view, "elm,action,click", "",
869                                    _item_select_cb, item);
870    evas_object_size_hint_align_set(item->base.view, EVAS_HINT_FILL, EVAS_HINT_FILL);
871    evas_object_show(item->base.view);
872 }
873
874 static void
875 _content_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
876              void *event_info __UNUSED__)
877 {
878    elm_ctxpopup_content_unset(data);
879 }
880
881 static void
882 _list_del(Widget_Data *wd)
883 {
884    if (!wd->scr) return;
885
886    edje_object_part_unswallow(wd->base, wd->scr);
887    evas_object_del(wd->scr);
888    wd->scr = NULL;
889    wd->box = NULL;
890 }
891
892 static void
893 _list_new(Evas_Object *obj)
894 {
895    Widget_Data *wd;
896    wd = elm_widget_data_get(obj);
897    if (!wd) return;
898
899    //scroller
900    wd->scr = elm_scroller_add(obj);
901    elm_object_style_set(wd->scr, "ctxpopup");
902    evas_object_size_hint_align_set(wd->scr, EVAS_HINT_FILL, EVAS_HINT_FILL);
903    evas_object_event_callback_add(wd->scr, EVAS_CALLBACK_RESIZE,
904                                   _scroller_resize, obj);
905    edje_object_part_swallow(wd->base, "elm.swallow.content", wd->scr);
906
907    //box
908    wd->box = elm_box_add(obj);
909    evas_object_size_hint_weight_set(wd->box, EVAS_HINT_EXPAND,
910                                     EVAS_HINT_EXPAND);
911
912    elm_scroller_content_set(wd->scr, wd->box);
913    elm_ctxpopup_horizontal_set(obj, wd->horizontal);
914 }
915
916 static void
917 _remove_items(Widget_Data *wd)
918 {
919    Eina_List *elist;
920    Elm_Ctxpopup_Item *item;
921
922    if (!wd->items) return;
923
924    EINA_LIST_FOREACH(wd->items, elist, item)
925      {
926         if (item->label)
927            eina_stringshare_del(item->label);
928         if (item->icon)
929            evas_object_del(item->icon);
930         wd->items = eina_list_remove(wd->items, item);
931         free(item);
932      }
933
934    wd->items = NULL;
935 }
936
937 EAPI Evas_Object *
938 elm_ctxpopup_add(Evas_Object *parent)
939 {
940    Evas_Object *obj;
941    Evas *e;
942    Widget_Data *wd;
943    Evas_Coord x, y, w, h;
944
945    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
946
947    ELM_SET_WIDTYPE(widtype, "ctxpopup");
948    elm_widget_type_set(obj, "ctxpopup");
949    elm_widget_sub_object_add(parent, obj);
950    elm_widget_data_set(obj, wd);
951    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
952    elm_widget_del_hook_set(obj, _del_hook);
953    elm_widget_theme_hook_set(obj, _theme_hook);
954
955    wd->parent = parent;
956
957    //Background
958    wd->bg = edje_object_add(e);
959    elm_widget_sub_object_add(obj, wd->bg);
960    _elm_theme_object_set(obj, wd->bg, "ctxpopup", "bg", "default");
961    evas_object_geometry_get(parent, &x, &y, &w, &h);
962    evas_object_move(wd->bg, x, y);
963    evas_object_resize(wd->bg, w, h);
964    edje_object_signal_callback_add(wd->bg, "elm,action,click", "",
965                                    _bg_clicked_cb, obj);
966
967    //Base
968    wd->base = edje_object_add(e);
969    elm_widget_sub_object_add(obj, wd->base);
970    _elm_theme_object_set(obj, wd->base, "ctxpopup", "base", "default");
971
972    //Arrow
973    wd->arrow = edje_object_add(e);
974    elm_widget_sub_object_add(obj, wd->arrow);
975    _elm_theme_object_set(obj, wd->arrow, "ctxpopup", "arrow", "default");
976
977    wd->dir_priority[0] = ELM_CTXPOPUP_DIRECTION_UP;
978    wd->dir_priority[1] = ELM_CTXPOPUP_DIRECTION_LEFT;
979    wd->dir_priority[2] = ELM_CTXPOPUP_DIRECTION_RIGHT;
980    wd->dir_priority[3] = ELM_CTXPOPUP_DIRECTION_DOWN;
981
982    evas_object_event_callback_add(parent, EVAS_CALLBACK_RESIZE, _parent_resize,
983                                   obj);
984    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _ctxpopup_show,
985                                   NULL);
986    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _ctxpopup_hide,
987                                   NULL);
988    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _ctxpopup_move,
989                                   NULL);
990    evas_object_event_callback_add(obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
991                                   _ctxpopup_changed_size_hints, NULL);
992    evas_object_smart_callback_add(obj, "scroll-freeze-on", _freeze_on, obj);
993    evas_object_smart_callback_add(obj, "scroll-freeze-off", _freeze_off, obj);
994    evas_object_smart_callback_add(obj, "scroll-hold-on", _hold_on, obj);
995    evas_object_smart_callback_add(obj, "scroll-hold-off", _hold_off, obj);
996
997    evas_object_smart_callbacks_descriptions_set(obj, _signals);
998
999    return obj;
1000 }
1001
1002 EAPI Evas_Object *
1003 elm_ctxpopup_item_icon_get(const Elm_Ctxpopup_Item *item)
1004 {
1005    ELM_CTXPOPUP_ITEM_CHECK_RETURN(item, NULL);
1006    return item->icon;
1007 }
1008
1009 EAPI void
1010 elm_ctxpopup_item_icon_set(Elm_Ctxpopup_Item *item, Evas_Object *icon)
1011 {
1012    ELM_CTXPOPUP_ITEM_CHECK_RETURN(item);
1013
1014    Widget_Data *wd;
1015
1016    wd = elm_widget_data_get(item->base.widget);
1017    if (!wd) return;
1018
1019    _item_icon_set(item, icon);
1020
1021    if (wd->visible)
1022      {
1023         _scroller_size_reset(wd);
1024         _sizing_eval(item->base.widget);
1025      }
1026 }
1027
1028 EAPI const char *
1029 elm_ctxpopup_item_label_get(const Elm_Ctxpopup_Item *item)
1030 {
1031    ELM_CTXPOPUP_ITEM_CHECK_RETURN(item, NULL);
1032    return item->label;
1033 }
1034
1035 EAPI void
1036 elm_ctxpopup_item_label_set(Elm_Ctxpopup_Item *item, const char *label)
1037 {
1038    ELM_CTXPOPUP_ITEM_CHECK_RETURN(item);
1039
1040    Widget_Data *wd;
1041
1042    wd = elm_widget_data_get(item->base.widget);
1043    if (!wd) return;
1044
1045    _item_label_set(item, label);
1046
1047    if (wd->visible)
1048      {
1049         _scroller_size_reset(wd);
1050         _sizing_eval(item->base.widget);
1051      }
1052 }
1053
1054 EAPI void
1055 elm_ctxpopup_hover_parent_set(Evas_Object *obj, Evas_Object *hover_parent)
1056 {
1057    ELM_CHECK_WIDTYPE(obj, widtype);
1058
1059    Widget_Data *wd;
1060
1061    wd = elm_widget_data_get(obj);
1062    if (!wd) return;
1063
1064    _hover_parent_callbacks_del(obj);
1065
1066    if (hover_parent)
1067      {
1068         evas_object_event_callback_add(hover_parent, EVAS_CALLBACK_DEL,
1069                                        _hover_parent_del, obj);
1070         evas_object_event_callback_add(hover_parent, EVAS_CALLBACK_MOVE,
1071                                        _hover_parent_move, obj);
1072         evas_object_event_callback_add(hover_parent, EVAS_CALLBACK_RESIZE,
1073                                        _hover_parent_resize, obj);
1074      }
1075
1076    wd->hover_parent = hover_parent;
1077 }
1078
1079 EAPI Evas_Object *
1080 elm_ctxpopup_hover_parent_get(const Evas_Object *obj)
1081 {
1082    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1083
1084    Widget_Data *wd;
1085
1086    wd = elm_widget_data_get(obj);
1087    if (!wd) return NULL;
1088
1089    return wd->hover_parent;
1090 }
1091
1092 EAPI void
1093 elm_ctxpopup_clear(Evas_Object * obj)
1094 {
1095    ELM_CHECK_WIDTYPE(obj, widtype);
1096
1097    Widget_Data *wd = elm_widget_data_get(obj);
1098    if (!wd) return;
1099
1100    _remove_items(wd);
1101    _list_del(wd);
1102 }
1103
1104 EAPI void
1105 elm_ctxpopup_horizontal_set(Evas_Object *obj, Eina_Bool horizontal)
1106 {
1107    ELM_CHECK_WIDTYPE(obj, widtype);
1108
1109    Widget_Data *wd;
1110
1111    wd = elm_widget_data_get(obj);
1112    if (!wd) return;
1113
1114    wd->horizontal = !!horizontal;
1115
1116    if ((!wd->scr) && (!wd->box))
1117       return;
1118
1119    if (!horizontal)
1120      {
1121         elm_box_horizontal_set(wd->box, EINA_FALSE);
1122         elm_scroller_bounce_set(wd->scr, EINA_FALSE, EINA_TRUE);
1123      }
1124    else
1125      {
1126         elm_box_horizontal_set(wd->box, EINA_TRUE);
1127         elm_scroller_bounce_set(wd->scr, EINA_TRUE, EINA_FALSE);
1128      }
1129
1130    if (wd->visible)
1131       _sizing_eval(obj);
1132 }
1133
1134 EAPI Eina_Bool
1135 elm_ctxpopup_horizontal_get(const Evas_Object *obj)
1136 {
1137    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1138
1139    Widget_Data *wd;
1140
1141    wd = elm_widget_data_get(obj);
1142    if (!wd) return EINA_FALSE;
1143
1144    return wd->horizontal;
1145 }
1146
1147 EAPI Elm_Ctxpopup_Item *
1148 elm_ctxpopup_item_append(Evas_Object *obj, const char *label,
1149                          Evas_Object *icon, Evas_Smart_Cb func,
1150                          const void *data)
1151 {
1152    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1153
1154    Widget_Data *wd;
1155    Elm_Ctxpopup_Item *item;
1156
1157    wd = elm_widget_data_get(obj);
1158    if (!wd) return NULL;
1159
1160    item = elm_widget_item_new(obj, Elm_Ctxpopup_Item);
1161    if (!item) return NULL;
1162
1163    //The first item is appended.
1164    if (wd->content)
1165       evas_object_del(elm_ctxpopup_content_unset(obj));
1166
1167    if (!wd->items)
1168       _list_new(obj);
1169
1170    item->func = func;
1171    item->base.data = data;
1172
1173    if (icon && label)
1174       _item_new(item, "icon_text_style_item");
1175    else if (label)
1176       _item_new(item, "text_style_item");
1177    else
1178       _item_new(item, "icon_style_item");
1179
1180    _item_icon_set(item, icon);
1181    _item_label_set(item, label);
1182    elm_box_pack_end(wd->box, item->base.view);
1183    wd->items = eina_list_append(wd->items, item);
1184
1185    if (wd->visible)
1186      {
1187         _scroller_size_reset(wd);
1188         _sizing_eval(obj);
1189      }
1190
1191    return item;
1192 }
1193
1194 EAPI void
1195 elm_ctxpopup_item_del(Elm_Ctxpopup_Item *item)
1196 {
1197    ELM_CTXPOPUP_ITEM_CHECK_RETURN(item);
1198
1199    Widget_Data *wd;
1200
1201    wd = elm_widget_data_get(item->base.widget);
1202    if (!wd) return;
1203
1204    if (item->icon)
1205       evas_object_del(item->icon);
1206    if (item->base.view)
1207       evas_object_del(item->base.view);
1208
1209    eina_stringshare_del(item->label);
1210
1211    wd->items = eina_list_remove(wd->items, item);
1212
1213    if (eina_list_count(wd->items) < 1)
1214       wd->items = NULL;
1215
1216    if (wd->visible)
1217       _sizing_eval(item->base.widget);
1218
1219    free(item);
1220 }
1221
1222 EAPI void
1223 elm_ctxpopup_item_disabled_set(Elm_Ctxpopup_Item *item, Eina_Bool disabled)
1224 {
1225    ELM_CTXPOPUP_ITEM_CHECK_RETURN(item);
1226
1227    Widget_Data *wd;
1228
1229    wd = elm_widget_data_get(item->base.widget);
1230    if (!wd) return;
1231
1232    if (disabled == item->disabled)
1233       return;
1234
1235    if (disabled)
1236       edje_object_signal_emit(item->base.view, "elm,state,disabled", "elm");
1237    else
1238       edje_object_signal_emit(item->base.view, "elm,state,enabled", "elm");
1239
1240    item->disabled = !!disabled;
1241 }
1242
1243 EAPI Eina_Bool
1244 elm_ctxpopup_item_disabled_get(const Elm_Ctxpopup_Item *item)
1245 {
1246    ELM_CTXPOPUP_ITEM_CHECK_RETURN(item, EINA_FALSE);
1247    return item->disabled;
1248 }
1249
1250 EAPI void
1251 elm_ctxpopup_content_set(Evas_Object *obj, Evas_Object *content)
1252 {
1253    ELM_CHECK_WIDTYPE(obj, widtype);
1254
1255    Widget_Data *wd;
1256
1257    wd = elm_widget_data_get(obj);
1258    if ((!wd) || (!content))
1259       return;
1260
1261    if (wd->items)
1262       elm_ctxpopup_clear(obj);
1263
1264    if (wd->content)
1265       evas_object_del(wd->content);
1266
1267    evas_object_event_callback_add(content, EVAS_CALLBACK_DEL, _content_del,
1268                                   obj);
1269
1270    elm_widget_sub_object_add(obj, content);
1271    edje_object_part_swallow(wd->base, "elm.swallow.content", content);
1272    edje_object_message_signal_process(wd->base);
1273
1274    wd->content = content;
1275
1276    if (wd->visible)
1277       _sizing_eval(obj);
1278 }
1279
1280 EAPI Evas_Object *
1281 elm_ctxpopup_content_unset(Evas_Object *obj)
1282 {
1283    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1284
1285    Widget_Data *wd;
1286    Evas_Object *content;
1287
1288    wd = elm_widget_data_get(obj);
1289    if (!wd) return NULL;
1290
1291    content = wd->content;
1292    if (!content) return NULL;
1293
1294    edje_object_part_unswallow(wd->base, content);
1295    elm_widget_sub_object_del(obj, content);
1296    evas_object_event_callback_del(content, EVAS_CALLBACK_DEL, _content_del);
1297    edje_object_signal_emit(wd->base, "elm,state,content,disable", "elm");
1298
1299    wd->content = NULL;
1300
1301    return content;
1302 }
1303
1304 EAPI void
1305 elm_ctxpopup_direction_priority_set(Evas_Object *obj,
1306                                     Elm_Ctxpopup_Direction first,
1307                                     Elm_Ctxpopup_Direction second,
1308                                     Elm_Ctxpopup_Direction third,
1309                                     Elm_Ctxpopup_Direction fourth)
1310 {
1311    ELM_CHECK_WIDTYPE(obj, widtype);
1312    Widget_Data *wd;
1313
1314    wd = elm_widget_data_get(obj);
1315    if (!wd) return;
1316
1317    wd->dir_priority[0] = first;
1318    wd->dir_priority[1] = second;
1319    wd->dir_priority[2] = third;
1320    wd->dir_priority[3] = fourth;
1321
1322    if (wd->visible)
1323       _sizing_eval(obj);
1324 }
1325
1326 EAPI void
1327 elm_ctxpopup_direction_priority_get(Evas_Object *obj,
1328                                     Elm_Ctxpopup_Direction *first,
1329                                     Elm_Ctxpopup_Direction *second,
1330                                     Elm_Ctxpopup_Direction *third,
1331                                     Elm_Ctxpopup_Direction *fourth)
1332 {
1333    ELM_CHECK_WIDTYPE(obj, widtype);
1334    Widget_Data *wd;
1335
1336    wd = elm_widget_data_get(obj);
1337    if (!wd) return;
1338
1339    if (first) *first = wd->dir_priority[0];
1340    if (second) *second = wd->dir_priority[1];
1341    if (third) *third = wd->dir_priority[2];
1342    if (fourth) *fourth = wd->dir_priority[3];
1343 }