1 #include "private.h"
2 
3 #include <assert.h>
4 #include <Elementary.h>
5 #include <Elementary_Cursor.h>
6 #include <Ecore_Input.h>
7 #include <Ecore_IMF.h>
8 #include <Ecore_IMF_Evas.h>
9 #include "win.h"
10 #include "termcmd.h"
11 #include "config.h"
12 #include "main.h"
13 #include "miniview.h"
14 #include "gravatar.h"
15 #include "media.h"
16 #include "termio.h"
17 #include "theme.h"
18 #include "sel.h"
19 #include "controls.h"
20 #include "keyin.h"
21 #include "term_container.h"
22 
23 
24 /**
25  * Design:
26  * A terminal widget is Term. It hosts various Evas_Object, like a `termio`
27  * handling the textgrid.
28  * It is hosted in a Term_Container of type Solo.
29  * On Term_Container:
30  *   It is a generic structure with a set of function pointers. It is a simple
31  *   way to objectify and have genericity between a Window, a Split or Tabs.
32  * Solo, Win, Split, Tabs have a Term_Container as their first field and thus
33  * can be casted to Term_Container to have access to those APIs.
34  *
35  * Solo is the simplest container, hosting just a Term.
36  * Win is a window and has only one container child.
37  * Split is a widget to separate an area of the screen in 2 and thus has 2
38  * children that can be either Solo or Tabs.
39  * Tabs is a Term_Container containing many containers (at the moment, only
40  * Solo ones) and have a system of tabs.
41  *
42  * All the windows are in the `wins` list.
43  */
44 
45 
46 
47 
48 /* specific log domain to help debug code in that file */
49 int _win_log_dom = -1;
50 
51 #undef CRITICAL
52 #undef ERR
53 #undef WRN
54 #undef INF
55 #undef DBG
56 
57 #define CRITICAL(...) EINA_LOG_DOM_CRIT(_win_log_dom, __VA_ARGS__)
58 #define ERR(...)      EINA_LOG_DOM_ERR(_win_log_dom, __VA_ARGS__)
59 #define WRN(...)      EINA_LOG_DOM_WARN(_win_log_dom, __VA_ARGS__)
60 #define INF(...)      EINA_LOG_DOM_INFO(_win_log_dom, __VA_ARGS__)
61 #define DBG(...)      EINA_LOG_DOM_DBG(_win_log_dom, __VA_ARGS__)
62 
63 #define PANES_TOP "top"
64 #define PANES_BOTTOM "bottom"
65 
66 #define DRAG_TIMEOUT 0.4
67 
68 /* {{{ Structs */
69 
70 typedef struct _Split Split;
71 typedef struct _Tabbar Tabbar;
72 typedef struct _Solo Solo;
73 typedef struct _Tabs Tabs;
74 typedef struct _Tab_Item Tab_Item;
75 typedef struct _Tab_Drag Tab_Drag;
76 
77 
78 struct _Tab_Drag
79 {
80    Evas_Coord mdx;     /* Mouse-down x */
81    Evas_Coord mdy;     /* Mouse-down y */
82    Split_Direction split_direction;
83    Term *term_over;
84    Term *term;
85    Evas_Object *icon;
86    Evas_Object *img;
87    Evas *e;
88    Ecore_Timer *timer;
89    /* To be able to restore */
90    Term_Container_Type parent_type;
91    union {
92         struct {
93              int previous_position;
94              Term_Container *tabs_child;
95         };
96         struct {
97              Term_Container *other;
98              double left_size;
99              Eina_Bool is_horizontal;
100              Eina_Bool is_first_child;
101         }; };
102 };
103 
104 struct _Tabbar
105 {
106    struct {
107       Evas_Object *box;
108    } l, r;
109 };
110 
111 struct _Term
112 {
113    Win         *wn;
114    Config      *config;
115 
116    Term_Container *container;
117    Evas_Object *bg;
118    Evas_Object *bg_edj;
119    Evas_Object *core;
120    Evas_Object *termio;
121    Evas_Object *media;
122    Evas_Object *popmedia;
123    Evas_Object *miniview;
124    Evas_Object *sel;
125    Evas_Object *sendfile_request;
126    Evas_Object *sendfile_progress;
127    Evas_Object *sendfile_progress_bar;
128    Evas_Object *tab_spacer;
129    Evas_Object *tab_region_base;
130    Evas_Object *tab_region_bg;
131    Evas_Object *tab_inactive;
132    Tab_Item    *tab_item;
133    Eina_List   *popmedia_queue;
134    Ecore_Timer *sendfile_request_hide_timer;
135    Ecore_Timer *sendfile_progress_hide_timer;
136    const char  *sendfile_dir;
137    Media_Type   poptype, mediatype;
138    Tabbar       tabbar;
139    int          step_x, step_y, min_w, min_h, req_w, req_h;
140    struct {
141       int       x, y;
142    } down;
143    int refcnt;
144    unsigned char hold : 1;
145    unsigned char unswallowed : 1;
146    unsigned char missed_bell : 1;
147    unsigned char miniview_shown : 1;
148    unsigned char popmedia_deleted : 1;
149    unsigned char has_bg_cursor : 1;
150    unsigned char core_cursor_set: 1;
151 
152    Eina_Bool sendfile_request_enabled : 1;
153    Eina_Bool sendfile_progress_enabled : 1;
154 };
155 
156 struct _Solo {
157      Term_Container tc;
158      Term *term;
159 };
160 
161 struct _Tab_Item {
162      Term_Container *tc;
163      Evas_Object *obj;
164      void *selector_entry;
165 };
166 
167 struct _Tabs {
168      Term_Container tc;
169      Evas_Object *selector;
170      Evas_Object *selector_bg;
171      Eina_List *tabs; // Tab_Item
172      Tab_Item *current;
173      double v1_orig;
174      double v2_orig;
175 };
176 
177 struct _Split
178 {
179    Term_Container tc;
180    Term_Container *tc1, *tc2; // left/right or top/bottom child splits, null if leaf
181    Evas_Object *panes;
182    Term_Container *last_focus;
183 
184    unsigned char is_horizontal : 1;
185 };
186 
187 
188 
189 
190 struct _Win
191 {
192    Term_Container tc; /* has to be first field */
193 
194    Keys_Handler khdl;
195    const char *preedit_str;
196    Term_Container *child;
197    Evas_Object *win;
198    Evas_Object *conform;
199    Evas_Object *backbg;
200    Evas_Object *base;
201    Config      *config;
202    Eina_List   *terms;
203    Split       *split;
204    Ecore_Job   *size_job;
205    Evas_Object *cmdbox;
206    Ecore_Timer *cmdbox_del_timer;
207    Ecore_Timer *hide_cursor_timer;
208    Ecore_Event_Handler *config_elm_handler;
209    unsigned char focused : 1;
210    unsigned char cmdbox_up : 1;
211    unsigned char group_input : 1;
212    unsigned char group_only_visible : 1;
213    unsigned char group_once_handled : 1;
214    unsigned char translucent : 1;
215 
216    unsigned int  on_popover;
217 };
218 
219 /* }}} */
220 /* {{{ static */
221 static Eina_List   *wins = NULL;
222 static Tab_Drag *_tab_drag = NULL;
223 
224 static Eina_Bool _win_is_focused(Win *wn);
225 static Term_Container *_solo_new(Term *term, Win *wn);
226 static Term_Container *_split_new(Term_Container *tc1, Term_Container *tc2,
227                                   double left_size, Eina_Bool is_horizontal);
228 static Term_Container *_tabs_new(Term_Container *child, Term_Container *parent);
229 static void _term_free(Term *term);
230 static void _term_media_update(Term *term, const Config *config);
231 static void _term_miniview_check(Term *term);
232 static void _popmedia_queue_process(Term *term);
233 static void _cb_size_hint(void *data, Evas *_e EINA_UNUSED, Evas_Object *obj, void *_event EINA_UNUSED);
234 static void _tab_new_cb(void *data, Evas_Object *_obj EINA_UNUSED, void *_event_info EINA_UNUSED);
235 static Tab_Item* tab_item_new(Tabs *tabs, Term_Container *child);
236 static void _tabs_recreate(Tabs *tabs);
237 static void _tab_drag_free(void);
238 static void _term_tabregion_free(Term *term);
239 static void _imf_event_commit_cb(void *data, Ecore_IMF_Context *_ctx EINA_UNUSED, void *event);
240 
241 /* }}} */
242 /* {{{ Utils */
243 #ifndef NDEBUG
244 static void
_focus_validator(void)245 _focus_validator(void)
246 {
247    Win *wn;
248    Term *term;
249    Eina_List *l, *ll;
250 
251    EINA_LIST_FOREACH(wins, l, wn)
252      {
253         Term_Container *focused_found = NULL;
254 
255         if (wn->group_input)
256           continue;
257 
258         EINA_LIST_FOREACH(wn->terms, ll, term)
259           {
260              Term_Container *tc = term->container;
261 
262              if (focused_found)
263                {
264                   assert(!tc->is_focused);
265                }
266              else
267                {
268                   if (tc->is_focused)
269                     {
270                        Term *term_focused;
271                        Term_Container *tc_parent = tc;
272 
273                        focused_found = tc;
274                        do
275                          {
276                             assert (tc_parent->is_focused);
277                             tc_parent = tc_parent->parent;
278                             if (tc_parent->type == TERM_CONTAINER_TYPE_TABS)
279                               {
280                                  Tabs *tabs = (Tabs*) tc_parent;
281                                  if (tabs->selector_bg)
282                                    return;
283                               }
284                          }
285                        while (tc_parent->type != TERM_CONTAINER_TYPE_WIN);
286                        assert (tc_parent->is_focused);
287                        term_focused = tc_parent->focused_term_get(tc_parent);
288                        assert(term_focused == term);
289                     }
290                }
291           }
292      }
293 }
294 #else
295 static void
_focus_validator(void)296 _focus_validator(void)
297 {}
298 #endif
299 
300 #define GROUPED_INPUT_TERM_FOREACH(_wn, _list, _term) \
301    EINA_LIST_FOREACH(_wn->terms, _list, _term) \
302      if (!_wn->group_only_visible || term_is_visible(_term))
303 
304 /* }}} */
305 /* {{{ Scale */
306 static void
_scale_round(void * data EINA_UNUSED,Evas_Object * obj,void * event_info EINA_UNUSED)307 _scale_round(void *data       EINA_UNUSED,
308              Evas_Object     *obj,
309              void *event_info EINA_UNUSED)
310 {
311    double val = elm_slider_value_get(obj);
312    double v;
313 
314    v = ((double)((int)(val * 10.0))) / 10.0;
315    if (v != val) elm_slider_value_set(obj, v);
316 }
317 
318 static void
_scale_change(void * data EINA_UNUSED,Evas_Object * obj,void * event_info EINA_UNUSED)319 _scale_change(void *data       EINA_UNUSED,
320               Evas_Object     *obj,
321               void *event_info EINA_UNUSED)
322 {
323    double scale = elm_config_scale_get();
324    double val = elm_slider_value_get(obj);
325 
326    if (scale == val)
327      return;
328    elm_config_scale_set(val);
329    elm_config_all_flush();
330 }
331 
332 typedef struct _Scale_Ctx
333 {
334    Evas_Object *hv;
335    Term *term;
336 } Scale_Ctx;
337 
338 static void
_scale_done(void * data,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)339 _scale_done(void *data,
340             Evas_Object *obj EINA_UNUSED,
341             void *event_info EINA_UNUSED)
342 {
343    Scale_Ctx *ctx = data;
344 
345    evas_object_smart_callback_del_full(ctx->hv, "dismissed",
346                                        _scale_done, ctx);
347    evas_object_del(ctx->hv);
348    ctx->term->wn->on_popover--;
349    term_unref(ctx->term);
350    elm_config_save();
351    config_save(ctx->term->config);
352    free(ctx);
353 }
354 
355 void
win_scale_wizard(Evas_Object * win,Term * term)356 win_scale_wizard(Evas_Object *win, Term *term)
357 {
358    Evas_Object *bx, *lbl, *sl, *fr, *bt;
359    const char *txt;
360    Scale_Ctx *ctx;
361 
362    EINA_SAFETY_ON_NULL_RETURN(term);
363 
364    ctx = calloc(1, sizeof(*ctx));
365    if (!ctx)
366      return;
367 
368    ctx->term = term;
369 
370    term->wn->on_popover++;
371 
372    term_ref(term);
373 
374    ctx->hv = elm_hover_add(win);
375    evas_object_size_hint_weight_set(ctx->hv, EVAS_HINT_EXPAND, 0.0);
376    evas_object_size_hint_align_set(ctx->hv, EVAS_HINT_FILL, 0.5);
377    elm_hover_parent_set(ctx->hv, win);
378    elm_hover_target_set(ctx->hv, win);
379    evas_object_smart_callback_add(ctx->hv, "dismissed", _scale_done, ctx);
380 
381    fr = elm_frame_add(win);
382    evas_object_size_hint_weight_set(fr, EVAS_HINT_EXPAND, 0.0);
383    evas_object_size_hint_align_set(fr, EVAS_HINT_FILL, 0.5);
384    elm_object_text_set(fr, _("Scale"));
385    elm_object_part_content_set(ctx->hv, "middle", fr);
386    evas_object_show(fr);
387 
388    bx = elm_box_add(win);
389    evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, 0.0);
390    evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, 0.5);
391    elm_object_content_set(fr, bx);
392    evas_object_show(bx);
393 
394    fr = elm_frame_add(win);
395    evas_object_size_hint_weight_set(fr, EVAS_HINT_EXPAND, 0.0);
396    evas_object_size_hint_align_set(fr, EVAS_HINT_FILL, 0.5);
397    elm_object_style_set(fr, "pad_medium");
398    elm_box_pack_end(bx, fr);
399    evas_object_show(fr);
400 
401    lbl = elm_label_add(win);
402    evas_object_size_hint_weight_set(lbl, EVAS_HINT_EXPAND, 0.0);
403    evas_object_size_hint_align_set(lbl, EVAS_HINT_FILL, 0.5);
404    txt = eina_stringshare_printf("<hilight>%s</>",_("Scale"));
405    elm_object_text_set(lbl, txt);
406    eina_stringshare_del(txt);
407    elm_object_content_set(fr, lbl);
408    elm_box_pack_end(bx, lbl);
409    evas_object_show(lbl);
410 
411    sl = elm_slider_add(win);
412    evas_object_size_hint_weight_set(sl, EVAS_HINT_EXPAND, 0.0);
413    evas_object_size_hint_align_set(sl, EVAS_HINT_FILL, 0.5);
414    elm_slider_span_size_set(sl, 120);
415    elm_slider_unit_format_set(sl, "%1.2f");
416    elm_slider_indicator_format_set(sl, "%1.2f");
417    elm_slider_min_max_set(sl, 0.25, 5.0);
418    elm_slider_value_set(sl, elm_config_scale_get());
419    elm_box_pack_end(bx, sl);
420    evas_object_show(sl);
421    evas_object_smart_callback_add(sl, "changed", _scale_round, NULL);
422    evas_object_smart_callback_add(sl, "delay,changed", _scale_change, NULL);
423 
424    bt = elm_button_add(win);
425    elm_object_text_set(bt, _("Done"));
426    elm_box_pack_end(bx, bt);
427    evas_object_smart_callback_add(bt, "clicked", _scale_done, ctx);
428    evas_object_show(bt);
429 
430    lbl = elm_label_add(win);
431    evas_object_size_hint_weight_set(lbl, EVAS_HINT_EXPAND, 0.0);
432    evas_object_size_hint_align_set(lbl, EVAS_HINT_FILL, 0.5);
433    elm_object_text_set(lbl, _("Select preferred size so that this text is readable"));
434    elm_label_line_wrap_set(lbl, ELM_WRAP_WORD);
435    elm_box_pack_end(bx, lbl);
436    evas_object_show(lbl);
437 
438    lbl = elm_label_add(win);
439    evas_object_size_hint_weight_set(lbl, EVAS_HINT_EXPAND, 0.0);
440    evas_object_size_hint_align_set(lbl, EVAS_HINT_FILL, 0.5);
441    elm_object_text_set(lbl, _("The scale configuration can be changed in the Settings (right click on the terminal) →  Toolkit, or by starting the command <keyword>elementary_config</keyword>"));
442    elm_label_line_wrap_set(lbl, ELM_WRAP_WORD);
443    elm_box_pack_end(bx, lbl);
444    evas_object_show(lbl);
445 
446    evas_object_show(ctx->hv);
447 
448    elm_object_focus_set(ctx->hv, EINA_TRUE);
449    elm_object_focus_set(bt, EINA_TRUE);
450 }
451 
452 /* }}} */
453 /* {{{ Solo */
454 
455 static Evas_Object *
_solo_get_evas_object(const Term_Container * tc)456 _solo_get_evas_object(const Term_Container *tc)
457 {
458    Solo *solo;
459    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
460    solo = (Solo*) tc;
461 
462    return solo->term->bg;
463 }
464 
465 static Term *
_solo_focused_term_get(const Term_Container * tc)466 _solo_focused_term_get(const Term_Container *tc)
467 {
468    Solo *solo;
469    Term *term = NULL;
470 
471    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
472    solo = (Solo*)tc;
473 
474    if (tc->is_focused)
475      term = solo->term;
476    return term;
477 }
478 
479 static Term *
_solo_find_term_at_coords(const Term_Container * tc,Evas_Coord _mx EINA_UNUSED,Evas_Coord _my EINA_UNUSED)480 _solo_find_term_at_coords(const Term_Container *tc,
481                           Evas_Coord _mx EINA_UNUSED,
482                           Evas_Coord _my EINA_UNUSED)
483 {
484    Solo *solo;
485    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
486    solo = (Solo*) tc;
487 
488    return solo->term;
489 }
490 
491 static void
_solo_size_eval(Term_Container * container,Sizeinfo * info)492 _solo_size_eval(Term_Container *container, Sizeinfo *info)
493 {
494    Term *term;
495    int mw = 0, mh = 0;
496    Solo *solo;
497    assert (container->type == TERM_CONTAINER_TYPE_SOLO);
498    solo = (Solo*)container;
499 
500    term = solo->term;
501 
502    info->min_w = term->min_w;
503    info->min_h = term->min_h;
504    info->step_x = term->step_x;
505    info->step_y = term->step_y;
506    info->req_w = term->req_w;
507    info->req_h = term->req_h;
508    if (!evas_object_data_get(term->termio, "sizedone"))
509      {
510         evas_object_data_set(term->termio, "sizedone", term->termio);
511         info->req = 1;
512      }
513    evas_object_size_hint_min_get(term->bg, &mw, &mh);
514    info->bg_min_w = mw;
515    info->bg_min_h = mh;
516 }
517 
518 static void
_solo_close(Term_Container * tc,Term_Container * _child EINA_UNUSED)519 _solo_close(Term_Container *tc,
520             Term_Container *_child EINA_UNUSED)
521 {
522    Solo *solo;
523    Term *term;
524 
525    DBG("close");
526    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
527    solo = (Solo*) tc;
528    tc->parent->close(tc->parent, tc);
529 
530    eina_stringshare_del(tc->title);
531 
532    term = solo->term;
533    term->container = NULL;
534 
535    free(tc);
536 }
537 
538 static void
_solo_tabs_new(Term_Container * tc)539 _solo_tabs_new(Term_Container *tc)
540 {
541    if (tc->parent->type != TERM_CONTAINER_TYPE_TABS)
542      _tabs_new(tc, tc->parent);
543    _tab_new_cb(tc->parent, NULL, NULL);
544 }
545 
546 static void
_solo_split(Term_Container * tc,Term_Container * _child EINA_UNUSED,Term * from,const char * cmd,Eina_Bool is_horizontal)547 _solo_split(Term_Container *tc,
548             Term_Container *_child EINA_UNUSED,
549             Term *from,
550             const char *cmd,
551             Eina_Bool is_horizontal)
552 {
553    tc->parent->split(tc->parent, tc, from, cmd, is_horizontal);
554 }
555 
556 static int
_solo_split_direction(Term_Container * tc,Term_Container * child_orig EINA_UNUSED,Term_Container * child_new,Split_Direction direction)557 _solo_split_direction(Term_Container *tc,
558                       Term_Container *child_orig EINA_UNUSED,
559                       Term_Container *child_new,
560                       Split_Direction direction)
561 {
562    return tc->parent->split_direction(tc->parent, tc, child_new, direction);
563 }
564 
565 static Term *
_solo_term_next(const Term_Container * tc,const Term_Container * _child EINA_UNUSED)566 _solo_term_next(const Term_Container *tc,
567                 const Term_Container *_child EINA_UNUSED)
568 {
569    return tc->parent->term_next(tc->parent, tc);
570 }
571 
572 static Term *
_solo_term_prev(const Term_Container * tc,const Term_Container * _child EINA_UNUSED)573 _solo_term_prev(const Term_Container *tc,
574                 const Term_Container *_child EINA_UNUSED)
575 {
576    return tc->parent->term_prev(tc->parent, tc);
577 }
578 
579 static Term *
_solo_term_up(const Term_Container * tc,const Term_Container * _child EINA_UNUSED)580 _solo_term_up(const Term_Container *tc,
581               const Term_Container *_child EINA_UNUSED)
582 {
583    return tc->parent->term_up(tc->parent, tc);
584 }
585 
586 static Term *
_solo_term_down(const Term_Container * tc,const Term_Container * _child EINA_UNUSED)587 _solo_term_down(const Term_Container *tc,
588                 const Term_Container *_child EINA_UNUSED)
589 {
590    return tc->parent->term_down(tc->parent, tc);
591 }
592 
593 static Term *
_solo_term_left(const Term_Container * tc,const Term_Container * _child EINA_UNUSED)594 _solo_term_left(const Term_Container *tc,
595                 const Term_Container *_child EINA_UNUSED)
596 {
597    return tc->parent->term_left(tc->parent, tc);
598 }
599 
600 static Term *
_solo_term_right(const Term_Container * tc,const Term_Container * _child EINA_UNUSED)601 _solo_term_right(const Term_Container *tc,
602                  const Term_Container *_child EINA_UNUSED)
603 {
604    return tc->parent->term_right(tc->parent, tc);
605 }
606 
607 static Term *
_solo_term_first(const Term_Container * tc)608 _solo_term_first(const Term_Container *tc)
609 {
610    Solo *solo;
611    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
612    solo = (Solo*) tc;
613    return solo->term;
614 }
615 
616 static Term *
_solo_term_last(const Term_Container * tc)617 _solo_term_last(const Term_Container *tc)
618 {
619    Solo *solo;
620    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
621    solo = (Solo*) tc;
622    return solo->term;
623 }
624 
625 static void
_solo_set_title(Term_Container * tc,Term_Container * _child EINA_UNUSED,const char * title)626 _solo_set_title(Term_Container *tc,
627                 Term_Container *_child EINA_UNUSED,
628                 const char *title)
629 {
630    Solo *solo;
631    Term *term;
632 
633    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
634    solo = (Solo*) tc;
635    term = solo->term;
636 
637    eina_stringshare_del(tc->title);
638    tc->title = eina_stringshare_add(title);
639    if (term->config->show_tabs)
640      {
641         elm_layout_text_set(term->bg, "terminology.tab.title", title);
642      }
643    if (_tab_drag && _tab_drag->term == term && _tab_drag->icon)
644      {
645         elm_layout_text_set(_tab_drag->icon,
646                             "terminology.title", title);
647      }
648    tc->parent->set_title(tc->parent, tc, title);
649 }
650 
651 static void
_solo_bell(Term_Container * tc,Term_Container * _child EINA_UNUSED)652 _solo_bell(Term_Container *tc,
653            Term_Container *_child EINA_UNUSED)
654 {
655    Solo *solo;
656    Term *term;
657 
658    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
659    solo = (Solo*) tc;
660    term = solo->term;
661 
662    if (!tc->is_focused)
663      term->missed_bell = EINA_TRUE;
664 
665    if (!tc->wn->config->disable_visual_bell)
666      {
667         elm_layout_signal_emit(term->bg, "bell", "terminology");
668         elm_layout_signal_emit(term->core, "bell", "terminology");
669         if (tc->wn->config->bell_rings)
670           {
671              elm_layout_signal_emit(term->bg, "bell,ring", "terminology");
672              elm_layout_signal_emit(term->core, "bell,ring", "terminology");
673           }
674         if ((_tab_drag != NULL) && (_tab_drag->term == term))
675           {
676              elm_layout_signal_emit(_tab_drag->icon, "bell", "terminology");
677           }
678      }
679    if ((term->missed_bell) && (term->config->show_tabs)
680        && (tc->parent->type == TERM_CONTAINER_TYPE_SPLIT))
681      {
682         elm_layout_signal_emit(term->bg, "tab,bell,on", "terminology");
683      }
684    edje_object_message_signal_process(term->bg_edj);
685    tc->parent->bell(tc->parent, tc);
686 }
687 
688 static void
_solo_unfocus(Term_Container * tc,Term_Container * relative)689 _solo_unfocus(Term_Container *tc, Term_Container *relative)
690 {
691    Solo *solo;
692    Term *term;
693 
694    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
695    solo = (Solo*) tc;
696    term = solo->term;
697 
698    DBG("tc:%p tc->is_focused:%d from_parent:%d term:%p",
699        tc, tc->is_focused, tc->parent == relative, term);
700    if (!tc->is_focused)
701      return;
702 
703    tc->is_focused = EINA_FALSE;
704    termio_focus_out(term->termio);
705 
706    if (tc->parent != relative)
707      tc->parent->unfocus(tc->parent, tc);
708 
709    if (!term->config->disable_focus_visuals)
710      {
711         elm_layout_signal_emit(term->bg, "focus,out", "terminology");
712         elm_layout_signal_emit(term->core, "focus,out", "terminology");
713      }
714 }
715 
716 static void
_solo_focus(Term_Container * tc,Term_Container * relative)717 _solo_focus(Term_Container *tc, Term_Container *relative)
718 {
719    Solo *solo;
720    Term *term;
721    const char *title;
722 
723    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
724    solo = (Solo*) tc;
725    term = solo->term;
726 
727    if (!tc->parent)
728      return;
729 
730    DBG("tc:%p tc->is_focused:%d from_parent:%d term:%p",
731        tc, tc->is_focused, tc->parent == relative, term);
732    if (tc->is_focused)
733      return;
734 
735    term->missed_bell = EINA_FALSE;
736    if ((term->config->show_tabs)
737        && (tc->parent->type == TERM_CONTAINER_TYPE_SPLIT))
738      {
739         elm_layout_signal_emit(term->bg, "tab,bell,off", "terminology");
740      }
741 
742    if (term->config->disable_focus_visuals)
743      {
744         elm_layout_signal_emit(term->bg, "focused,set", "terminology");
745         elm_layout_signal_emit(term->core, "focused,set", "terminology");
746      }
747    else
748      {
749         elm_layout_signal_emit(term->bg, "focus,in", "terminology");
750         elm_layout_signal_emit(term->core, "focus,in", "terminology");
751      }
752    termio_event_feed_mouse_in(term->termio);
753    termio_focus_in(term->termio);
754 
755    title = termio_title_get(term->termio);
756    if (title)
757       tc->set_title(tc, tc, title);
758 
759    if (term->missed_bell)
760      term->missed_bell = EINA_FALSE;
761    edje_object_message_signal_process(term->bg_edj);
762    if (!tc->is_focused && relative != tc->parent)
763      {
764        tc->is_focused = EINA_TRUE;
765        tc->parent->focus(tc->parent, tc);
766      }
767    tc->is_focused = EINA_TRUE;
768    _focus_validator();
769 }
770 
771 static Eina_Bool
_solo_is_visible(const Term_Container * tc,const Term_Container * _child EINA_UNUSED)772 _solo_is_visible(const Term_Container *tc, const Term_Container *_child EINA_UNUSED)
773 {
774    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
775    return tc->parent->is_visible(tc->parent, tc);
776 }
777 
778 static void
_solo_tab_show(Term_Container * tc)779 _solo_tab_show(Term_Container *tc)
780 {
781    Solo *solo;
782    Term *term;
783 
784    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
785    solo = (Solo*) tc;
786    term = solo->term;
787    DBG("tab show tc:%p", tc);
788 
789    if (!term->tab_spacer)
790      {
791         Evas_Coord w = 0, h = 0;
792 
793         term->tab_spacer = evas_object_rectangle_add(
794            evas_object_evas_get(term->bg));
795         evas_object_color_set(term->tab_spacer, 0, 0, 0, 0);
796         elm_coords_finger_size_adjust(1, &w, 1, &h);
797         evas_object_size_hint_min_set(term->tab_spacer, w, h);
798         elm_layout_content_set(term->bg, "terminology.tab", term->tab_spacer);
799         edje_object_part_drag_value_set(term->bg_edj,
800                                         "terminology.tabl", 0.0, 0.0);
801         edje_object_part_drag_value_set(term->bg_edj,
802                                         "terminology.tabr", 1.0, 0.0);
803         elm_layout_text_set(term->bg, "terminology.tab.title",
804                             solo->tc.title);
805         elm_layout_signal_emit(term->bg, "tabbar,on", "terminology");
806         edje_object_message_signal_process(term->bg_edj);
807      }
808    else
809      {
810         edje_object_part_drag_value_set(term->bg_edj, "terminology.tabl", 0.0, 0.0);
811         edje_object_part_drag_value_set(term->bg_edj, "terminology.tabr", 1.0, 0.0);
812         edje_object_message_signal_process(term->bg_edj);
813      }
814 }
815 
816 static void
_solo_tab_hide(Term_Container * tc)817 _solo_tab_hide(Term_Container *tc)
818 {
819    Solo *solo;
820    Term *term;
821 
822    DBG("title hide tc:%p", tc);
823    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
824    solo = (Solo*) tc;
825    term = solo->term;
826 
827    if (term->tab_spacer)
828      {
829         elm_layout_signal_emit(term->bg, "tabbar,off", "terminology");
830         edje_object_message_signal_process(term->bg_edj);
831         elm_layout_content_unset(term->bg, "terminology.tab");
832         evas_object_del(term->tab_spacer);
833         term->tab_spacer = NULL;
834      }
835 }
836 
837 static void
_solo_update(Term_Container * tc)838 _solo_update(Term_Container *tc)
839 {
840    Solo *solo;
841    Term *term;
842    Term_Container *tc_parent = tc->parent;
843 
844    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
845    solo = (Solo*) tc;
846    term = solo->term;
847 
848    if (tc_parent->type == TERM_CONTAINER_TYPE_SPLIT)
849      {
850         if (term->config->show_tabs)
851           _solo_tab_show(tc);
852         else
853           _solo_tab_hide(tc);
854      }
855 }
856 
857 static Term_Container *
_solo_new(Term * term,Win * wn)858 _solo_new(Term *term, Win *wn)
859 {
860    Term_Container *tc = NULL;
861    Solo *solo = NULL;
862    solo = calloc(1, sizeof(Solo));
863    if (!solo)
864      {
865         free(solo);
866         return NULL;
867      }
868 
869    tc = (Term_Container*)solo;
870    tc->term_next = _solo_term_next;
871    tc->term_prev = _solo_term_prev;
872    tc->term_up = _solo_term_up;
873    tc->term_down = _solo_term_down;
874    tc->term_left = _solo_term_left;
875    tc->term_right = _solo_term_right;
876    tc->term_first = _solo_term_first;
877    tc->term_last = _solo_term_last;
878    tc->focused_term_get = _solo_focused_term_get;
879    tc->get_evas_object = _solo_get_evas_object;
880    tc->split = _solo_split;
881    tc->split_direction = _solo_split_direction;
882    tc->find_term_at_coords = _solo_find_term_at_coords;
883    tc->size_eval = _solo_size_eval;
884    tc->swallow = NULL;
885    tc->focus = _solo_focus;
886    tc->unfocus = _solo_unfocus;
887    tc->set_title = _solo_set_title;
888    tc->bell = _solo_bell;
889    tc->close = _solo_close;
890    tc->update = _solo_update;
891    tc->title = eina_stringshare_add("Terminology");
892    tc->is_visible = _solo_is_visible;
893    tc->detach = NULL;
894    tc->type = TERM_CONTAINER_TYPE_SOLO;
895 
896    tc->parent = NULL;
897    tc->wn = wn;
898 
899    solo->term = term;
900 
901    term->container = tc;
902 
903    return tc;
904 }
905 
906 /* }}} */
907 /* {{{ Win */
908 
909 static void
_cb_win_focus_in(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)910 _cb_win_focus_in(void *data,
911                  Evas_Object *_obj EINA_UNUSED,
912                  void *_event EINA_UNUSED)
913 {
914    Win *wn = data;
915    Term_Container *tc = (Term_Container*) wn;
916    Term *term;
917 
918    DBG("FOCUS_IN tc:%p tc->is_focused:%d",
919        tc, tc->is_focused);
920    if (!tc->is_focused)
921      elm_win_urgent_set(wn->win, EINA_FALSE);
922    tc->is_focused = EINA_TRUE;
923    if (wn->on_popover)
924        return;
925 
926    term = tc->focused_term_get(tc);
927 
928    if (wn->group_input)
929      {
930         Eina_List *l;
931         Term *t;
932 
933         GROUPED_INPUT_TERM_FOREACH(wn, l, t)
934           {
935              elm_layout_signal_emit(t->bg, "focus,in", "terminology");
936              termio_event_feed_mouse_in(t->termio);
937              termio_focus_in(t->termio);
938           }
939      }
940    else if ( wn->config->mouse_over_focus )
941      {
942         Term *term_mouse;
943         Evas_Coord mx, my;
944 
945         evas_pointer_canvas_xy_get(evas_object_evas_get(wn->win), &mx, &my);
946         term_mouse = tc->find_term_at_coords(tc, mx, my);
947         if ((term_mouse) && (term_mouse != term))
948           {
949              if (term)
950                {
951                   if (!term->config->disable_focus_visuals)
952                     {
953                        elm_layout_signal_emit(term->bg, "focus,out", "terminology");
954                        elm_layout_signal_emit(term->core, "focus,out", "terminology");
955                     }
956                }
957              term = term_mouse;
958           }
959      }
960 
961    if (term)
962      {
963         term_focus(term);
964      }
965    else
966      {
967         DBG("focus tc:%p", tc);
968         tc->focus(tc, tc);
969      }
970 }
971 
972 static void
_cb_win_focus_out(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)973 _cb_win_focus_out(void *data,
974                   Evas_Object *_obj EINA_UNUSED,
975                   void *_event EINA_UNUSED)
976 {
977    Win *wn = data;
978    Term_Container *tc = (Term_Container*) wn;
979 
980    if (wn->on_popover)
981      return;
982 
983    DBG("FOCUS OUT tc:%p tc->is_focused:%d",
984        tc, tc->is_focused);
985    tc->unfocus(tc, NULL);
986    if (wn->group_input)
987      {
988         Eina_List *l;
989         Term *term;
990 
991         GROUPED_INPUT_TERM_FOREACH(wn, l, term)
992           {
993              elm_layout_signal_emit(term->bg, "focus,out", "terminology");
994              termio_focus_out(term->termio);
995           }
996      }
997 }
998 
999 static Eina_Bool
_win_is_focused(Win * wn)1000 _win_is_focused(Win *wn)
1001 {
1002    Term_Container *tc;
1003 
1004    if (!wn)
1005      return EINA_FALSE;
1006    tc = (Term_Container*) wn;
1007 
1008    DBG("tc:%p tc->is_focused:%d",
1009        tc, tc->is_focused);
1010    return tc->is_focused;
1011 }
1012 
1013 
win_term_set(Win * wn,Term * term)1014 int win_term_set(Win *wn, Term *term)
1015 {
1016    Term_Container *tc_win = NULL, *tc_child = NULL;
1017 
1018    tc_child = _solo_new(term, wn);
1019    if (!tc_child)
1020      goto bad;
1021 
1022    tc_win = (Term_Container*) wn;
1023 
1024    tc_win->swallow(tc_win, NULL, tc_child);
1025 
1026    _cb_size_hint(term, NULL, term->termio, NULL);
1027 
1028    return 0;
1029 
1030 bad:
1031    free(tc_child);
1032    return -1;
1033 }
1034 
1035 
1036 Config *
win_config_get(const Win * wn)1037 win_config_get(const Win *wn)
1038 {
1039    return wn->config;
1040 }
1041 
1042 Eina_List *
win_terms_get(const Win * wn)1043 win_terms_get(const Win *wn)
1044 {
1045    return wn->terms;
1046 }
1047 
1048 Evas_Object *
win_evas_object_get(const Win * wn)1049 win_evas_object_get(const Win *wn)
1050 {
1051    return wn->win;
1052 }
1053 
1054 static void
_term_trans(Term * term)1055 _term_trans(Term *term)
1056 {
1057    Edje_Message_Int msg;
1058    Evas_Object *edje = elm_layout_edje_get(term->core);
1059    Win *wn = term->wn;
1060 
1061    if (term->config->translucent)
1062      msg.val = term->config->opacity;
1063    else
1064      msg.val = 100;
1065    edje_object_message_send(term->bg_edj, EDJE_MESSAGE_INT, 1, &msg);
1066    edje_object_message_send(edje, EDJE_MESSAGE_INT, 1, &msg);
1067 
1068    if (term->config->translucent != wn->translucent)
1069      {
1070         if (term->config->translucent)
1071           {
1072              elm_win_alpha_set(wn->win, EINA_TRUE);
1073              evas_object_hide(wn->backbg);
1074              wn->translucent = EINA_TRUE;
1075           }
1076         else
1077           {
1078              elm_win_alpha_set(wn->win, EINA_FALSE);
1079              evas_object_show(wn->backbg);
1080              wn->translucent = EINA_FALSE;
1081           }
1082      }
1083 }
1084 
1085 void
main_trans_update(void)1086 main_trans_update(void)
1087 {
1088    Win *wn;
1089    Term *term;
1090    Eina_List *l, *ll;
1091 
1092    EINA_LIST_FOREACH(wins, l, wn)
1093      {
1094         EINA_LIST_FOREACH(wn->terms, ll, term)
1095           {
1096              _term_trans(term);
1097           }
1098      }
1099 }
1100 
1101 
1102 static void
_cb_del(void * data,Evas * _e EINA_UNUSED,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)1103 _cb_del(void *data,
1104         Evas *_e EINA_UNUSED,
1105         Evas_Object *_obj EINA_UNUSED,
1106         void *_event EINA_UNUSED)
1107 {
1108    Win *wn = data;
1109 
1110    // already obj here is deleted - dont do it again
1111    wn->win = NULL;
1112    win_free(wn);
1113 }
1114 
1115 void
win_free(Win * wn)1116 win_free(Win *wn)
1117 {
1118    Term *term;
1119 
1120    wins = eina_list_remove(wins, wn);
1121    if (wn->config_elm_handler)
1122      {
1123         ecore_event_handler_del(wn->config_elm_handler);
1124         wn->config_elm_handler = NULL;
1125      }
1126    EINA_LIST_FREE(wn->terms, term)
1127      {
1128         term_unref(term);
1129      }
1130    if (wn->cmdbox_del_timer)
1131      {
1132         ecore_timer_del(wn->cmdbox_del_timer);
1133         wn->cmdbox_del_timer = NULL;
1134      }
1135    if (wn->cmdbox)
1136      {
1137         evas_object_del(wn->cmdbox);
1138         wn->cmdbox = NULL;
1139      }
1140    if (wn->win)
1141      {
1142         evas_object_smart_callback_del_full(wn->win, "focus,in", _cb_win_focus_in, wn);
1143         evas_object_smart_callback_del_full(wn->win, "focus,out", _cb_win_focus_out, wn);
1144         evas_object_event_callback_del_full(wn->win, EVAS_CALLBACK_DEL, _cb_del, wn);
1145         evas_object_del(wn->win);
1146      }
1147    if (wn->size_job)
1148      ecore_job_del(wn->size_job);
1149    config_del(wn->config);
1150    eina_stringshare_del(wn->preedit_str);
1151    keyin_compose_seq_reset(&wn->khdl);
1152    if (wn->khdl.imf)
1153      {
1154         ecore_imf_context_event_callback_del
1155           (wn->khdl.imf, ECORE_IMF_CALLBACK_COMMIT, _imf_event_commit_cb);
1156         ecore_imf_context_del(wn->khdl.imf);
1157      }
1158    if (wn->hide_cursor_timer)
1159      {
1160         ecore_timer_del(wn->hide_cursor_timer);
1161      }
1162    ecore_imf_shutdown();
1163    free(wn);
1164 }
1165 
1166 Win *
win_evas_object_to_win(const Evas_Object * win)1167 win_evas_object_to_win(const Evas_Object *win)
1168 {
1169    Win *wn;
1170    Eina_List *l;
1171 
1172    EINA_LIST_FOREACH(wins, l, wn)
1173      {
1174         if (wn->win == win) return wn;
1175      }
1176    return NULL;
1177 }
1178 
1179 Eina_List *
terms_from_win_object(Evas_Object * win)1180 terms_from_win_object(Evas_Object *win)
1181 {
1182    Win *wn;
1183 
1184    wn = win_evas_object_to_win(win);
1185    if (!wn) return NULL;
1186 
1187    return wn->terms;
1188 }
1189 
1190 
1191 static Evas_Object *
win_add(const char * name,const char * role,const char * title,const char * icon_name)1192 win_add(const char *name, const char *role,
1193         const char *title, const char *icon_name)
1194 {
1195    Evas_Object *win;
1196 
1197    if (!name) name = "main";
1198    if (!title) title = "Terminology";
1199    if (!icon_name) icon_name = "terminology";
1200 
1201    win = elm_win_add(NULL, name, ELM_WIN_BASIC);
1202    elm_win_title_set(win, title);
1203    elm_win_icon_name_set(win, icon_name);
1204    if (role) elm_win_role_set(win, role);
1205 
1206    elm_win_autodel_set(win, EINA_TRUE);
1207 
1208    return win;
1209 }
1210 
1211 static Evas_Object *
_win_get_evas_object(const Term_Container * tc)1212 _win_get_evas_object(const Term_Container *tc)
1213 {
1214    Win *wn;
1215    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1216 
1217    wn = (Win*) tc;
1218 
1219    return wn->win;
1220 }
1221 
1222 static Term *
_win_term_next(const Term_Container * _tc EINA_UNUSED,const Term_Container * child)1223 _win_term_next(const Term_Container *_tc EINA_UNUSED,
1224                const Term_Container *child)
1225 {
1226    return child->term_first(child);
1227 }
1228 
1229 static Term *
_win_term_prev(const Term_Container * _tc EINA_UNUSED,const Term_Container * child)1230 _win_term_prev(const Term_Container *_tc EINA_UNUSED,
1231                const Term_Container *child)
1232 {
1233    return child->term_last(child);
1234 }
1235 
1236 static Term *
_win_term_up(const Term_Container * _tc EINA_UNUSED,const Term_Container * child EINA_UNUSED)1237 _win_term_up(const Term_Container *_tc EINA_UNUSED,
1238              const Term_Container *child EINA_UNUSED)
1239 {
1240    return NULL;
1241 }
1242 
1243 static Term *
_win_term_down(const Term_Container * _tc EINA_UNUSED,const Term_Container * child EINA_UNUSED)1244 _win_term_down(const Term_Container *_tc EINA_UNUSED,
1245                const Term_Container *child EINA_UNUSED)
1246 {
1247    return NULL;
1248 }
1249 
1250 static Term *
_win_term_left(const Term_Container * _tc EINA_UNUSED,const Term_Container * child EINA_UNUSED)1251 _win_term_left(const Term_Container *_tc EINA_UNUSED,
1252                const Term_Container *child EINA_UNUSED)
1253 {
1254    return NULL;
1255 }
1256 
1257 static Term *
_win_term_right(const Term_Container * _tc EINA_UNUSED,const Term_Container * child EINA_UNUSED)1258 _win_term_right(const Term_Container *_tc EINA_UNUSED,
1259                 const Term_Container *child EINA_UNUSED)
1260 {
1261    return NULL;
1262 }
1263 
1264 static Term *
_win_term_first(const Term_Container * tc)1265 _win_term_first(const Term_Container *tc)
1266 {
1267    Win *wn;
1268    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1269 
1270    wn = (Win*) tc;
1271    return wn->child->term_first(wn->child);
1272 }
1273 
1274 static Term *
_win_term_last(const Term_Container * tc)1275 _win_term_last(const Term_Container *tc)
1276 {
1277    Win *wn;
1278    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1279 
1280    wn = (Win*) tc;
1281    return wn->child->term_last(wn->child);
1282 }
1283 
1284 static Term *
_win_focused_term_get(const Term_Container * tc)1285 _win_focused_term_get(const Term_Container *tc)
1286 {
1287    Win *wn;
1288    Term *term = NULL;
1289 
1290    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1291 
1292    wn = (Win*) tc;
1293    if (tc->is_focused && wn->child)
1294      term = wn->child->focused_term_get(wn->child);
1295    return term;
1296 }
1297 
1298 static Term *
_win_find_term_at_coords(const Term_Container * tc,Evas_Coord mx,Evas_Coord my)1299 _win_find_term_at_coords(const Term_Container *tc,
1300                          Evas_Coord mx, Evas_Coord my)
1301 {
1302    Win *wn;
1303    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1304 
1305    wn = (Win*) tc;
1306 
1307    if (!wn->child)
1308      return NULL;
1309 
1310    return wn->child->find_term_at_coords(wn->child, mx, my);
1311 }
1312 
1313 static void
_win_size_eval(Term_Container * tc,Sizeinfo * info)1314 _win_size_eval(Term_Container *tc, Sizeinfo *info)
1315 {
1316    Win *wn;
1317    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1318 
1319    wn = (Win*) tc;
1320 
1321    assert(wn->child);
1322    wn->child->size_eval(wn->child, info);
1323 }
1324 
1325 static void
_win_swallow(Term_Container * tc,Term_Container * orig,Term_Container * new_child)1326 _win_swallow(Term_Container *tc, Term_Container *orig,
1327              Term_Container *new_child)
1328 {
1329    Win *wn;
1330    Evas_Object *o;
1331 
1332    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1333 
1334    wn = (Win*) tc;
1335 
1336    DBG("orig:%p", orig);
1337 
1338    if (orig)
1339      {
1340         elm_layout_content_unset(wn->base, "terminology.content");
1341      }
1342 
1343    o = new_child->get_evas_object(new_child);
1344    elm_layout_content_set(wn->base, "terminology.content", o);
1345 
1346    if ((new_child->type == TERM_CONTAINER_TYPE_SOLO)
1347        && (wn->config->show_tabs))
1348      {
1349         if (_tab_drag && _tab_drag->term && (_tab_drag->term->wn == wn) &&
1350             _tab_drag->icon)
1351           _solo_tab_show(new_child);
1352         else
1353           _solo_tab_hide(new_child);
1354      }
1355 
1356    evas_object_show(o);
1357    new_child->parent = tc;
1358    wn->child = new_child;
1359 
1360    if (_tab_drag && _tab_drag->icon)
1361      {
1362         evas_object_raise(_tab_drag->icon);
1363      }
1364 }
1365 
1366 static void
_win_close(Term_Container * tc,Term_Container * _child EINA_UNUSED)1367 _win_close(Term_Container *tc,
1368            Term_Container *_child EINA_UNUSED)
1369 {
1370    Win *wn;
1371    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1372    wn = (Win*) tc;
1373 
1374    DBG("win close");
1375    if (_tab_drag && _tab_drag->term && (_tab_drag->term->wn == wn))
1376      {
1377         _tab_drag->parent_type = TERM_CONTAINER_TYPE_WIN;
1378         _tab_drag_free();
1379         return;
1380      }
1381    eina_stringshare_del(tc->title);
1382    win_free(wn);
1383 }
1384 
1385 static void
_win_focus(Term_Container * tc,Term_Container * relative)1386 _win_focus(Term_Container *tc, Term_Container *relative)
1387 {
1388    Win *wn;
1389    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1390 
1391    wn = (Win*) tc;
1392    elm_object_focus_allow_set(wn->base, EINA_TRUE);
1393    DBG("tc:%p tc->is_focused:%d from_child:%d",
1394        tc, tc->is_focused, wn->child == relative);
1395    elm_object_focus_set(wn->base, EINA_TRUE);
1396 
1397    if (!tc->is_focused)
1398      elm_win_urgent_set(wn->win, EINA_FALSE);
1399 
1400    tc->is_focused = EINA_TRUE;
1401    if ((relative != wn->child) || (!wn->focused))
1402      {
1403         DBG("focus tc:%p", tc);
1404         wn->child->focus(wn->child, tc);
1405         elm_win_keyboard_mode_set(wn->win, ELM_WIN_KEYBOARD_TERMINAL);
1406         if (wn->khdl.imf)
1407           {
1408              Term *focused;
1409 
1410              ecore_imf_context_input_panel_show(wn->khdl.imf);
1411              ecore_imf_context_reset(wn->khdl.imf);
1412              ecore_imf_context_focus_in(wn->khdl.imf);
1413 
1414              focused = tc->focused_term_get(tc);
1415              if (focused)
1416                termio_imf_cursor_set(focused->termio, wn->khdl.imf);
1417           }
1418      }
1419 }
1420 
1421 static void
_win_unfocus(Term_Container * tc,Term_Container * relative)1422 _win_unfocus(Term_Container *tc, Term_Container *relative)
1423 {
1424    Win *wn;
1425    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1426 
1427    wn = (Win*) tc;
1428 
1429    elm_object_focus_set(wn->base, EINA_FALSE);
1430    elm_object_focus_allow_set(wn->base, EINA_FALSE);
1431 
1432    DBG("tc:%p tc->is_focused:%d from_child:%d",
1433        tc, tc->is_focused, wn->child == relative);
1434    elm_win_keyboard_mode_set(wn->win, ELM_WIN_KEYBOARD_OFF);
1435    if (relative != wn->child && wn->child)
1436      {
1437         if (wn->khdl.imf)
1438           {
1439              Term *focused;
1440 
1441              ecore_imf_context_reset(wn->khdl.imf);
1442              focused = tc->focused_term_get(tc);
1443              if (focused)
1444                termio_imf_cursor_set(focused->termio, wn->khdl.imf);
1445              ecore_imf_context_focus_out(wn->khdl.imf);
1446              ecore_imf_context_input_panel_hide(wn->khdl.imf);
1447           }
1448         tc->is_focused = EINA_FALSE;
1449         wn->child->unfocus(wn->child, tc);
1450      }
1451 }
1452 
1453 static void
_win_bell(Term_Container * tc,Term_Container * _child EINA_UNUSED)1454 _win_bell(Term_Container *tc,
1455           Term_Container *_child EINA_UNUSED)
1456 {
1457    Win *wn;
1458    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1459 
1460    wn = (Win*) tc;
1461 
1462    if (tc->is_focused) return;
1463 
1464    if (wn->config->urg_bell)
1465      {
1466         elm_win_urgent_set(wn->win, EINA_TRUE);
1467      }
1468 }
1469 
1470 static void
_win_set_title(Term_Container * tc,Term_Container * _child EINA_UNUSED,const char * title)1471 _win_set_title(Term_Container *tc,
1472                Term_Container *_child EINA_UNUSED,
1473                const char *title)
1474 {
1475    Win *wn;
1476    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1477 
1478    wn = (Win*) tc;
1479 
1480    eina_stringshare_del(tc->title);
1481    tc->title =  eina_stringshare_ref(title);
1482 
1483    elm_win_title_set(wn->win, title);
1484 }
1485 
1486 Eina_Bool
_term_container_is_splittable(Term_Container * tc,Eina_Bool is_horizontal)1487 _term_container_is_splittable(Term_Container *tc, Eina_Bool is_horizontal)
1488 {
1489    int w = 0, h = 0, c_w = 0, c_h = 0;
1490    Term *tm;
1491 
1492    if (terminology_starting_up)
1493      return EINA_TRUE;
1494 
1495    tm = tc->term_first(tc);
1496    evas_object_geometry_get(tm->bg, NULL, NULL, &w, &h);
1497    evas_object_textgrid_cell_size_get(termio_textgrid_get(tm->termio),
1498                                       &c_w, &c_h);
1499    if (is_horizontal)
1500      {
1501         if (c_h * 2 > h)
1502            return EINA_FALSE;
1503      }
1504    else
1505      {
1506         if (c_w * 2 > w)
1507            return EINA_FALSE;
1508      }
1509    return EINA_TRUE;
1510 }
1511 
1512 static void
_win_split(Term_Container * tc,Term_Container * child,Term * from,const char * cmd,Eina_Bool is_horizontal)1513 _win_split(Term_Container *tc, Term_Container *child,
1514            Term *from, const char *cmd, Eina_Bool is_horizontal)
1515 {
1516    Win *wn;
1517 
1518    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1519    wn = (Win*) tc;
1520 
1521    if (_term_container_is_splittable(tc, is_horizontal))
1522      {
1523         Term *tm_new, *tm;
1524         Term_Container *tc_split, *tc_solo_new;
1525         char *wdir = NULL;
1526         char buf[PATH_MAX];
1527 
1528         // copy the current path to wdir if we should change the directory,
1529         // passing wdir NULL otherwise:
1530         if (wn->config->changedir_to_current)
1531           {
1532              if (from)
1533                tm = from;
1534              else
1535                tm = tc->focused_term_get(tc);
1536              if (tm && termio_cwd_get(tm->termio, buf, sizeof(buf)))
1537                wdir = buf;
1538           }
1539         tm_new = term_new(wn, wn->config,
1540                           cmd, wn->config->login_shell, wdir,
1541                           80, 24, EINA_FALSE, NULL);
1542         tc_solo_new = _solo_new(tm_new, wn);
1543         evas_object_data_set(tm_new->termio, "sizedone", tm_new->termio);
1544 
1545         elm_layout_content_unset(wn->base, "terminology.content");
1546 
1547         tc_split = _split_new(child, tc_solo_new, 0.5, is_horizontal);
1548         if (wn->config->show_tabs)
1549           {
1550              if (child->type == TERM_CONTAINER_TYPE_SOLO)
1551                {
1552                   _solo_tab_show(child);
1553                }
1554              _solo_tab_show(tc_solo_new);
1555           }
1556 
1557         child->unfocus(child, tc_split);
1558         tc->swallow(tc, NULL, tc_split);
1559         tc_split->is_focused = EINA_TRUE;
1560         tc_split->focus(tc_split, tc_solo_new);
1561         tc_solo_new->focus(tc_solo_new, tc_split);
1562      }
1563    else
1564      {
1565         ERR("term is not splittable");
1566      }
1567 }
1568 
1569 static int
_win_split_direction(Term_Container * tc,Term_Container * child_orig,Term_Container * child_new,Split_Direction direction)1570 _win_split_direction(Term_Container *tc,
1571                      Term_Container *child_orig,
1572                      Term_Container *child_new,
1573                      Split_Direction direction)
1574 {
1575    Term_Container *child1, *child2, *tc_split;
1576    Win *wn;
1577    Eina_Bool is_horizontal =
1578       (direction == SPLIT_DIRECTION_LEFT) || (direction == SPLIT_DIRECTION_RIGHT) ?
1579       EINA_FALSE : EINA_TRUE;
1580 
1581    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1582    wn = (Win*) tc;
1583 
1584    if (!_term_container_is_splittable(tc, is_horizontal))
1585      {
1586         ERR("term is not splittable");
1587         return -1;
1588      }
1589 
1590    if ((direction == SPLIT_DIRECTION_TOP) ||
1591        (direction == SPLIT_DIRECTION_LEFT))
1592      {
1593         child1 = child_new;
1594         child2 = child_orig;
1595      }
1596    else
1597      {
1598         child1 = child_orig;
1599         child2 = child_new;
1600      }
1601 
1602    wn->tc.unfocus(&wn->tc, NULL); /* unfocus from top */
1603 
1604    tc_split = _split_new(child1, child2, 0.5, is_horizontal);
1605 
1606    if (wn->config->show_tabs)
1607      {
1608         if (child_orig->type == TERM_CONTAINER_TYPE_SOLO)
1609           {
1610              _solo_tab_show(child_orig);
1611           }
1612         _solo_tab_show(child_new);
1613      }
1614 
1615    tc_split->is_focused = tc->is_focused;
1616    tc->swallow(tc, NULL, tc_split);
1617 
1618    child_new->focus(child_new, NULL); /* refocus from bottom */
1619 
1620    return 0;
1621 }
1622 
1623 static Eina_Bool
_set_cursor(Term * term,void * data)1624 _set_cursor(Term *term, void *data)
1625 {
1626    const char *cursor = data;
1627 
1628    assert(term->core);
1629    if (cursor)
1630      {
1631         elm_object_cursor_set(term->core, cursor);
1632         term->core_cursor_set = 1;
1633      }
1634    else
1635      {
1636         if (term->core_cursor_set)
1637           elm_object_cursor_unset(term->core);
1638         term->core_cursor_set = 0;
1639      }
1640 
1641    return ECORE_CALLBACK_PASS_ON;
1642 }
1643 
1644 static void
_win_update(Term_Container * tc)1645 _win_update(Term_Container *tc)
1646 {
1647    Win *wn;
1648 
1649    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
1650    wn = (Win*) tc;
1651 
1652    if (wn->config->hide_cursor >= CONFIG_CURSOR_IDLE_TIMEOUT_MAX)
1653      {
1654         ecore_timer_del(wn->hide_cursor_timer);
1655         wn->hide_cursor_timer = NULL;
1656 
1657         for_each_term_do(wn, &_set_cursor, NULL);
1658      }
1659 
1660    wn->child->update(wn->child);
1661 }
1662 
1663 Eina_Bool
win_has_single_child(const Win * wn)1664 win_has_single_child(const Win *wn)
1665 {
1666    const Term_Container *child = wn->child;
1667 
1668    return (child->type == TERM_CONTAINER_TYPE_SOLO);
1669 }
1670 
1671 static void
_cb_win_key_up(void * data,Evas * _e EINA_UNUSED,Evas_Object * _obj EINA_UNUSED,void * event)1672 _cb_win_key_up(void *data,
1673                Evas *_e EINA_UNUSED,
1674                Evas_Object *_obj EINA_UNUSED,
1675                void *event)
1676 {
1677    Win *wn = data;
1678    Evas_Event_Key_Up *ev = event;
1679 
1680    DBG("GROUP key up (%p) (ctrl:%d)",
1681        wn, evas_key_modifier_is_set(ev->modifiers, "Control"));
1682    keyin_handle_up(&wn->khdl, ev);
1683 }
1684 
1685 const char *
term_preedit_str_get(Term * term)1686 term_preedit_str_get(Term *term)
1687 {
1688    Win *wn = term->wn;
1689    Term_Container *tc = (Term_Container*) wn;
1690 
1691    if (wn->on_popover)
1692      return NULL;
1693    tc = (Term_Container*) wn;
1694    term = tc->focused_term_get(tc);
1695    if (term)
1696      {
1697         return wn->preedit_str;
1698      }
1699    return NULL;
1700 }
1701 
1702 Ecore_IMF_Context *
term_imf_context_get(Term * term)1703 term_imf_context_get(Term *term)
1704 {
1705    Win *wn = term->wn;
1706    Term_Container *tc = (Term_Container*) wn;
1707    Term *focused;
1708 
1709    tc = (Term_Container*) wn;
1710    focused = tc->focused_term_get(tc);
1711    if (term == focused)
1712      return wn->khdl.imf;
1713    return NULL;
1714 }
1715 
1716 static void
_imf_event_commit_cb(void * data,Ecore_IMF_Context * _ctx EINA_UNUSED,void * event)1717 _imf_event_commit_cb(void *data,
1718                      Ecore_IMF_Context *_ctx EINA_UNUSED,
1719                      void *event)
1720 {
1721    Eina_List *l;
1722    Term *term;
1723    Win *wn = data;
1724    Termpty *ty;
1725    char *str = event;
1726    int len;
1727    DBG("IMF committed '%s'", str);
1728    if (!str)
1729      return;
1730    len = strlen(str);
1731    if (wn->group_input)
1732      {
1733         GROUPED_INPUT_TERM_FOREACH(wn, l, term)
1734           {
1735              ty = termio_pty_get(term->termio);
1736              if (ty)
1737                termpty_write(ty, str, len);
1738           }
1739      }
1740    else
1741      {
1742         Term_Container *tc = (Term_Container*) wn;
1743 
1744         term = tc->focused_term_get(tc);
1745         if (term)
1746           {
1747              ty = termio_pty_get(term->termio);
1748              if (ty)
1749                termpty_write(ty, str, len);
1750           }
1751      }
1752    eina_stringshare_del(wn->preedit_str);
1753    wn->preedit_str = NULL;
1754 }
1755 
1756 
1757 
1758 static void
_imf_event_delete_surrounding_cb(void * data,Ecore_IMF_Context * _ctx EINA_UNUSED,void * event)1759 _imf_event_delete_surrounding_cb(void *data,
1760                                  Ecore_IMF_Context *_ctx EINA_UNUSED,
1761                                  void *event)
1762 {
1763    Win *wn = data;
1764    Ecore_IMF_Event_Delete_Surrounding *ev = event;
1765    DBG("IMF del surrounding %p %i %i", wn, ev->offset, ev->n_chars);
1766 }
1767 
1768 static void
_imf_event_preedit_changed_cb(void * data,Ecore_IMF_Context * ctx,void * _event EINA_UNUSED)1769 _imf_event_preedit_changed_cb(void *data,
1770                               Ecore_IMF_Context *ctx,
1771                               void *_event EINA_UNUSED)
1772 {
1773    Win *wn = data;
1774    char *preedit_string;
1775    int cursor_pos;
1776 
1777    ecore_imf_context_preedit_string_get(ctx, &preedit_string, &cursor_pos);
1778    if (!preedit_string)
1779      return;
1780    DBG("IMF preedit str '%s'", preedit_string);
1781    eina_stringshare_del(wn->preedit_str);
1782    wn->preedit_str = eina_stringshare_add(preedit_string);
1783    free(preedit_string);
1784 }
1785 
1786 
1787 static void
_cb_win_key_down(void * data,Evas * _e EINA_UNUSED,Evas_Object * _obj EINA_UNUSED,void * event_info)1788 _cb_win_key_down(void *data,
1789                  Evas *_e EINA_UNUSED,
1790                  Evas_Object *_obj EINA_UNUSED,
1791                  void *event_info)
1792 {
1793    Win *wn = data;
1794    Eina_List *l = NULL;
1795    Term *term = NULL;
1796    Termpty *ty = NULL;
1797    Evas_Event_Key_Down *ev = event_info;
1798    Eina_Bool done = EINA_FALSE;
1799    int ctrl, alt, shift, win, meta, hyper;
1800 
1801    DBG("GROUP key down (%p) (ctrl:%d)",
1802        wn, evas_key_modifier_is_set(ev->modifiers, "Control"));
1803    _focus_validator();
1804    if ((wn->on_popover) || (wn->cmdbox_up)) return;
1805 
1806    ctrl = evas_key_modifier_is_set(ev->modifiers, "Control");
1807    alt = evas_key_modifier_is_set(ev->modifiers, "Alt");
1808    shift = evas_key_modifier_is_set(ev->modifiers, "Shift");
1809    win = evas_key_modifier_is_set(ev->modifiers, "Super");
1810    meta = evas_key_modifier_is_set(ev->modifiers, "Meta") ||
1811       evas_key_modifier_is_set(ev->modifiers, "AltGr") ||
1812       evas_key_modifier_is_set(ev->modifiers, "ISO_Level3_Shift");
1813    hyper = evas_key_modifier_is_set(ev->modifiers, "Hyper");
1814 
1815    /* 1st/ Tab selector */
1816      {
1817         Term_Container *tc = (Term_Container*) wn;
1818 
1819         term = tc->focused_term_get(tc);
1820         if (term)
1821           {
1822              Term_Container *tc_parent = tc->parent;
1823 
1824              tc = term->container;
1825              tc_parent = tc->parent;
1826 
1827              if (tc_parent->type == TERM_CONTAINER_TYPE_TABS)
1828                {
1829                   Tabs *tabs = (Tabs*) tc_parent;
1830 
1831                   if (tabs->selector != NULL)
1832                     {
1833                        sel_key_down(tabs->selector, ev);
1834                        return;
1835                     }
1836                }
1837           }
1838      }
1839 
1840    /* 2nd/ Miniview */
1841    if (wn->group_input)
1842      {
1843         GROUPED_INPUT_TERM_FOREACH(wn, l, term)
1844           {
1845              done = miniview_handle_key(term_miniview_get(term), ev);
1846              if (!wn->group_input)
1847                return;
1848           }
1849      }
1850    else
1851      {
1852         Term_Container *tc = (Term_Container*) wn;
1853 
1854         term = tc->focused_term_get(tc);
1855         if (!term)
1856           {
1857              DBG("no focused term");
1858              return;
1859           }
1860         done = miniview_handle_key(term_miniview_get(term), ev);
1861      }
1862    if (done)
1863      {
1864         keyin_compose_seq_reset(&wn->khdl);
1865         goto end;
1866      }
1867 
1868 
1869    /* 3rd/ PopMedia */
1870    done = EINA_FALSE;
1871    if (wn->group_input)
1872      {
1873         GROUPED_INPUT_TERM_FOREACH(wn, l, term)
1874           {
1875              if (term_has_popmedia(term) && !strcmp(ev->key, "Escape"))
1876                {
1877                   term_popmedia_close(term);
1878                   done = EINA_TRUE;
1879                }
1880           }
1881      }
1882    else
1883      {
1884         Term_Container *tc = (Term_Container*) wn;
1885 
1886         term = tc->focused_term_get(tc);
1887         if (!term)
1888           {
1889              DBG("no focused term");
1890              return;
1891           }
1892         if (term_has_popmedia(term) && !strcmp(ev->key, "Escape"))
1893           {
1894              term_popmedia_close(term);
1895              done = EINA_TRUE;
1896           }
1897      }
1898    if (done)
1899      {
1900         keyin_compose_seq_reset(&wn->khdl);
1901         goto end;
1902      }
1903 
1904    /* 4th/ Handle key bindings */
1905    done = EINA_FALSE;
1906    if (wn->group_input)
1907      {
1908         wn->group_once_handled = EINA_FALSE;
1909         GROUPED_INPUT_TERM_FOREACH(wn, l, term)
1910           {
1911              done = keyin_handle_key_binding(term->termio, ev, ctrl, alt,
1912                                              shift, win, meta, hyper);
1913              if (!wn->group_input)
1914                return;
1915           }
1916      }
1917    else
1918      {
1919         Term_Container *tc = (Term_Container*) wn;
1920 
1921         term = tc->focused_term_get(tc);
1922         if (!term)
1923           return;
1924         done = keyin_handle_key_binding(term->termio, ev, ctrl, alt,
1925                                         shift, win, meta, hyper);
1926      }
1927    if (done)
1928      {
1929         keyin_compose_seq_reset(&wn->khdl);
1930         goto end;
1931      }
1932    done = EINA_FALSE;
1933 
1934    /* 5th/ Composing */
1935    if (wn->khdl.imf)
1936      {
1937         // EXCEPTION. Don't filter modifiers alt+shift -> breaks emacs
1938         // and jed (alt+shift+5 for search/replace for example)
1939         // Don't filter modifiers alt, is used by shells
1940         if ((!alt) && (!ctrl))
1941           {
1942              Ecore_IMF_Event_Key_Down imf_ev;
1943 
1944              ecore_imf_evas_event_key_down_wrap(ev, &imf_ev);
1945              if (!wn->khdl.composing)
1946                {
1947                   if (ecore_imf_context_filter_event(wn->khdl.imf,
1948                                                      ECORE_IMF_EVENT_KEY_DOWN,
1949                                                      (Ecore_IMF_Event *)&imf_ev))
1950                     goto end;
1951                }
1952           }
1953      }
1954    if (!wn->khdl.composing)
1955      {
1956         Ecore_Compose_State state;
1957         char *compres = NULL;
1958 
1959         keyin_compose_seq_reset(&wn->khdl);
1960         wn->khdl.seq = eina_list_append(wn->khdl.seq,
1961                                         eina_stringshare_add(ev->key));
1962         state = ecore_compose_get(wn->khdl.seq, &compres);
1963         if (state == ECORE_COMPOSE_MIDDLE)
1964           wn->khdl.composing = EINA_TRUE;
1965         else
1966           wn->khdl.composing = EINA_FALSE;
1967         if (!wn->khdl.composing)
1968           keyin_compose_seq_reset(&wn->khdl);
1969         else
1970           goto end;
1971      }
1972    else
1973      {
1974         Ecore_Compose_State state;
1975         char *compres = NULL;
1976 
1977         if (key_is_modifier(ev->key))
1978           goto end;
1979         wn->khdl.seq = eina_list_append(wn->khdl.seq,
1980                                         eina_stringshare_add(ev->key));
1981         state = ecore_compose_get(wn->khdl.seq, &compres);
1982         if (state == ECORE_COMPOSE_NONE)
1983           keyin_compose_seq_reset(&wn->khdl);
1984         else if (state == ECORE_COMPOSE_DONE)
1985           {
1986              keyin_compose_seq_reset(&wn->khdl);
1987              if (compres)
1988                {
1989                   int len = strlen(compres);
1990                   if (wn->group_input)
1991                     {
1992                        GROUPED_INPUT_TERM_FOREACH(wn, l, term)
1993                          {
1994                             ty = termio_pty_get(term->termio);
1995                             if (ty && termpty_can_handle_key(ty, &wn->khdl, ev))
1996                               termpty_write(ty, compres, len);
1997                          }
1998                     }
1999                  else
2000                     {
2001                        ty = termio_pty_get(term->termio);
2002                        if (ty && termpty_can_handle_key(ty, &wn->khdl, ev))
2003                          termpty_write(ty, compres, len);
2004                     }
2005                   free(compres);
2006                   compres = NULL;
2007                }
2008              goto end;
2009           }
2010         else
2011           goto end;
2012      }
2013 
2014    /* 6th/ send key to pty */
2015    if (wn->group_input)
2016      {
2017         GROUPED_INPUT_TERM_FOREACH(wn, l, term)
2018           {
2019              ty = termio_pty_get(term->termio);
2020              if (ty && termpty_can_handle_key(ty, &wn->khdl, ev))
2021                keyin_handle_key_to_pty(ty, ev, alt, shift, ctrl);
2022           }
2023      }
2024    else
2025      {
2026         ty = termio_pty_get(term->termio);
2027         DBG("ty:%p", ty);
2028         if (ty && termpty_can_handle_key(ty, &wn->khdl, ev))
2029           keyin_handle_key_to_pty(ty, ev, alt, shift, ctrl);
2030      }
2031 
2032    /* 7th: specifics: jump on keypress / flicker on key */
2033 end:
2034    if (wn->group_input)
2035      {
2036         GROUPED_INPUT_TERM_FOREACH(wn, l, term)
2037           {
2038              if (term)
2039                termio_key_down(term->termio, ev, done);
2040           }
2041      }
2042    else
2043      {
2044         if (term)
2045           termio_key_down(term->termio, ev, done);
2046      }
2047 }
2048 
2049 static void
_cb_win_mouse_down(void * data,Evas * _e EINA_UNUSED,Evas_Object * _obj EINA_UNUSED,void * event)2050 _cb_win_mouse_down(void *data,
2051                    Evas *_e EINA_UNUSED,
2052                    Evas_Object *_obj EINA_UNUSED,
2053                    void *event)
2054 {
2055    Win *wn = data;
2056    Evas_Event_Mouse_Down *ev = event;
2057    Term *term, *term_mouse;
2058    Term_Container *tc = (Term_Container*) wn;
2059    Term_Container *tc_child;
2060 
2061    if (wn->on_popover || wn->group_input)
2062      return;
2063 
2064    /* Focus In event will handle that */
2065    if (!tc->is_focused)
2066      return;
2067 
2068    term_mouse = tc->find_term_at_coords(tc, ev->canvas.x, ev->canvas.y);
2069    term = tc->focused_term_get(tc);
2070    if (term_mouse == term)
2071      return;
2072 
2073    if (term)
2074      {
2075         tc_child = term->container;
2076         tc_child->unfocus(tc_child, tc);
2077      }
2078 
2079    tc_child = term_mouse->container;
2080    DBG("focus tc_child:%p", tc_child);
2081    tc_child->focus(tc_child, tc);
2082 }
2083 
2084 static Eina_Bool
_hide_cursor(void * data)2085 _hide_cursor(void *data)
2086 {
2087    Win *wn = data;
2088 
2089    wn->hide_cursor_timer = NULL;
2090    for_each_term_do(wn, &_set_cursor, (void*)"blank");
2091    return ECORE_CALLBACK_CANCEL;
2092 }
2093 
2094 static void
_cb_win_mouse_move(void * data,Evas * _e EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event)2095 _cb_win_mouse_move(void *data,
2096                    Evas *_e EINA_UNUSED,
2097                    Evas_Object *obj EINA_UNUSED,
2098                    void *event)
2099 {
2100    Win *wn = data;
2101    Evas_Event_Mouse_Move *ev = event;
2102    Term *term, *term_mouse;
2103    Term_Container *tc = (Term_Container*) wn;
2104    Term_Container *tc_child = NULL;
2105 
2106    if (wn->on_popover)
2107      return;
2108 
2109    if (wn->config->hide_cursor < CONFIG_CURSOR_IDLE_TIMEOUT_MAX)
2110      {
2111         if (wn->hide_cursor_timer)
2112           {
2113              ecore_timer_interval_set(wn->hide_cursor_timer,
2114                                       wn->config->hide_cursor);
2115           }
2116         else
2117           {
2118              for_each_term_do(wn, &_set_cursor, NULL);
2119              wn->hide_cursor_timer = ecore_timer_add(
2120                 wn->config->hide_cursor, _hide_cursor, wn);
2121           }
2122      }
2123 
2124    if (wn->group_input || !tc->is_focused || !wn->config->mouse_over_focus)
2125      return;
2126 
2127    term_mouse = tc->find_term_at_coords(tc,
2128                                         ev->cur.canvas.x, ev->cur.canvas.y);
2129    term = tc->focused_term_get(tc);
2130    if (term_mouse == term)
2131      return;
2132 
2133    DBG("mouse move");
2134    if (term)
2135      {
2136         tc_child = term->container;
2137         tc_child->unfocus(tc_child, tc);
2138      }
2139 
2140    tc_child = term_mouse->container;
2141    DBG("need to focus");
2142    tc_child->focus(tc_child, tc);
2143 }
2144 
2145 static Eina_Bool
_config_font_size_set(Term * term,void * data EINA_UNUSED)2146 _config_font_size_set(Term *term, void *data EINA_UNUSED)
2147 {
2148    Config *config = termio_config_get(term->termio);
2149 
2150    termio_font_size_set(term->termio, config->font.size);
2151    return ECORE_CALLBACK_PASS_ON;
2152 }
2153 
2154 static Eina_Bool
_cb_elm_config_change(void * data,int event EINA_UNUSED,void * info EINA_UNUSED)2155 _cb_elm_config_change(void *data, int event EINA_UNUSED, void *info EINA_UNUSED)
2156 {
2157    Win *wn = data;
2158 
2159    for_each_term_do(wn, &_config_font_size_set, NULL);
2160    return ECORE_CALLBACK_PASS_ON;
2161 }
2162 
2163 static Eina_Bool
_win_is_visible(const Term_Container * tc,const Term_Container * _child EINA_UNUSED)2164 _win_is_visible(const Term_Container *tc, const Term_Container *_child EINA_UNUSED)
2165 {
2166    assert (tc->type == TERM_CONTAINER_TYPE_WIN);
2167    return EINA_TRUE;
2168 }
2169 
2170 Win *
win_new(const char * name,const char * role,const char * title,const char * icon_name,Config * config,Eina_Bool fullscreen,Eina_Bool iconic,Eina_Bool borderless,Eina_Bool override,Eina_Bool maximized)2171 win_new(const char *name, const char *role, const char *title,
2172         const char *icon_name, Config *config,
2173         Eina_Bool fullscreen, Eina_Bool iconic,
2174         Eina_Bool borderless, Eina_Bool override,
2175         Eina_Bool maximized)
2176 {
2177    Win *wn;
2178    Evas_Object *o;
2179    Term_Container *tc;
2180 
2181    wn = calloc(1, sizeof(Win));
2182    if (!wn) return NULL;
2183 
2184    wn->win = win_add(name, role, title, icon_name);
2185    if (!wn->win)
2186      {
2187         free(wn);
2188         return NULL;
2189      }
2190 
2191    if (_win_log_dom < 0)
2192      {
2193         _win_log_dom = eina_log_domain_register("win", NULL);
2194         if (_win_log_dom < 0)
2195           EINA_LOG_CRIT("Could not create logging domain '%s'", "win");
2196      }
2197 
2198    tc = (Term_Container*) wn;
2199    tc->term_next = _win_term_next;
2200    tc->term_prev = _win_term_prev;
2201    tc->term_up = _win_term_up;
2202    tc->term_down = _win_term_down;
2203    tc->term_left = _win_term_left;
2204    tc->term_right = _win_term_right;
2205    tc->term_first = _win_term_first;
2206    tc->term_last = _win_term_last;
2207    tc->focused_term_get = _win_focused_term_get;
2208    tc->get_evas_object = _win_get_evas_object;
2209    tc->split = _win_split;
2210    tc->split_direction = _win_split_direction;
2211    tc->find_term_at_coords = _win_find_term_at_coords;
2212    tc->size_eval = _win_size_eval;
2213    tc->swallow = _win_swallow;
2214    tc->focus = _win_focus;
2215    tc->unfocus = _win_unfocus;
2216    tc->set_title = _win_set_title;
2217    tc->bell = _win_bell;
2218    tc->close = _win_close;
2219    tc->update = _win_update;
2220    tc->is_visible = _win_is_visible;
2221    tc->detach = NULL;
2222    tc->title = eina_stringshare_add(title? title : "Terminology");
2223    tc->type = TERM_CONTAINER_TYPE_WIN;
2224    tc->wn = wn;
2225 
2226    config_default_font_set(config, evas_object_evas_get(wn->win));
2227 
2228    wn->config = config_fork(config);
2229 
2230    evas_object_event_callback_add(wn->win, EVAS_CALLBACK_DEL, _cb_del, wn);
2231 
2232    if (fullscreen) elm_win_fullscreen_set(wn->win, EINA_TRUE);
2233    if (iconic) elm_win_iconified_set(wn->win, EINA_TRUE);
2234    if (borderless) elm_win_borderless_set(wn->win, EINA_TRUE);
2235    if (override) elm_win_override_set(wn->win, EINA_TRUE);
2236    if (maximized) elm_win_maximized_set(wn->win, EINA_TRUE);
2237 
2238    wn->backbg = o = evas_object_rectangle_add(evas_object_evas_get(wn->win));
2239    evas_object_color_set(o, 0, 0, 0, 255);
2240    evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
2241    evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
2242    elm_win_resize_object_add(wn->win, o);
2243    evas_object_show(o);
2244 
2245    wn->conform = o = elm_conformant_add(wn->win);
2246    evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
2247    evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
2248    elm_win_resize_object_add(wn->win, o);
2249    evas_object_show(o);
2250 
2251    wn->base = o = elm_layout_add(wn->win);
2252    elm_object_focus_allow_set(o, EINA_TRUE);
2253    theme_apply(o, config, "terminology/base", NULL, NULL, EINA_TRUE);
2254    evas_object_propagate_events_set(o, EINA_FALSE);
2255    evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
2256    evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
2257    elm_object_content_set(wn->conform, o);
2258 
2259    evas_object_smart_callback_add(wn->win, "focus,in", _cb_win_focus_in, wn);
2260    evas_object_smart_callback_add(wn->win, "focus,out", _cb_win_focus_out, wn);
2261 
2262    evas_object_event_callback_add(wn->base,
2263                                   EVAS_CALLBACK_KEY_DOWN,
2264                                   _cb_win_key_down,
2265                                   wn);
2266    evas_object_event_callback_add(wn->base,
2267                                   EVAS_CALLBACK_KEY_UP,
2268                                   _cb_win_key_up,
2269                                   wn);
2270    evas_object_event_callback_add(wn->base,
2271                                   EVAS_CALLBACK_MOUSE_DOWN,
2272                                   _cb_win_mouse_down,
2273                                   wn);
2274    evas_object_event_callback_add(wn->base,
2275                                   EVAS_CALLBACK_MOUSE_MOVE,
2276                                   _cb_win_mouse_move,
2277                                   wn);
2278    evas_object_show(o);
2279    elm_object_focus_highlight_style_set(o, "blank");
2280    elm_object_focus_set(o, EINA_TRUE);
2281 
2282    if (ecore_imf_init())
2283      {
2284         const char *imf_id = ecore_imf_context_default_id_get();
2285         Evas *e;
2286 
2287         if (!imf_id)
2288           wn->khdl.imf = NULL;
2289         else
2290           {
2291              const Ecore_IMF_Context_Info *imf_info;
2292 
2293              imf_info = ecore_imf_context_info_by_id_get(imf_id);
2294              if ((!imf_info->canvas_type) ||
2295                  (strcmp(imf_info->canvas_type, "evas") == 0))
2296                wn->khdl.imf = ecore_imf_context_add(imf_id);
2297              else
2298                {
2299                   imf_id = ecore_imf_context_default_id_by_canvas_type_get("evas");
2300                   if (imf_id)
2301                     wn->khdl.imf = ecore_imf_context_add(imf_id);
2302                }
2303           }
2304 
2305         if (!wn->khdl.imf)
2306           goto imf_done;
2307 
2308         e = evas_object_evas_get(o);
2309         ecore_imf_context_client_window_set
2310           (wn->khdl.imf, (void *)ecore_evas_window_get(ecore_evas_ecore_evas_get(e)));
2311         ecore_imf_context_client_canvas_set(wn->khdl.imf, e);
2312 
2313         ecore_imf_context_event_callback_add
2314           (wn->khdl.imf, ECORE_IMF_CALLBACK_COMMIT, _imf_event_commit_cb, wn);
2315         ecore_imf_context_event_callback_add
2316           (wn->khdl.imf, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, _imf_event_delete_surrounding_cb, wn);
2317         ecore_imf_context_event_callback_add
2318           (wn->khdl.imf, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, _imf_event_preedit_changed_cb, wn);
2319         /* make IMF usable by a terminal - no preedit, prediction... */
2320         ecore_imf_context_prediction_allow_set
2321           (wn->khdl.imf, EINA_FALSE);
2322         ecore_imf_context_autocapital_type_set
2323           (wn->khdl.imf, ECORE_IMF_AUTOCAPITAL_TYPE_NONE);
2324         ecore_imf_context_input_panel_layout_set
2325           (wn->khdl.imf, ECORE_IMF_INPUT_PANEL_LAYOUT_TERMINAL);
2326         ecore_imf_context_input_mode_set
2327           (wn->khdl.imf, ECORE_IMF_INPUT_MODE_FULL);
2328         ecore_imf_context_input_panel_language_set
2329           (wn->khdl.imf, ECORE_IMF_INPUT_PANEL_LANG_ALPHABET);
2330         ecore_imf_context_input_panel_return_key_type_set
2331           (wn->khdl.imf, ECORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_DEFAULT);
2332 imf_done:
2333         if (wn->khdl.imf)
2334           DBG("Ecore IMF Setup");
2335         else
2336           WRN(_("Ecore IMF failed"));
2337 
2338      }
2339 
2340    if (wn->config->hide_cursor < CONFIG_CURSOR_IDLE_TIMEOUT_MAX)
2341      {
2342         wn->hide_cursor_timer = ecore_timer_add(
2343            wn->config->hide_cursor, _hide_cursor, wn);
2344      }
2345 
2346    wins = eina_list_append(wins, wn);
2347 
2348    wn->tc.is_focused = EINA_TRUE;
2349 
2350    wn->config_elm_handler = ecore_event_handler_add
2351      (ELM_EVENT_CONFIG_ALL_CHANGED, _cb_elm_config_change, wn);
2352    return wn;
2353 }
2354 
2355 void
term_close(Evas_Object * win,Evas_Object * term,Eina_Bool hold_if_requested)2356 term_close(Evas_Object *win, Evas_Object *term, Eina_Bool hold_if_requested)
2357 {
2358    Term *tm;
2359    Term_Container *tc;
2360    Win *wn = win_evas_object_to_win(win);
2361 
2362    if (!wn)
2363      return;
2364 
2365    tm = evas_object_data_get(term, "term");
2366    if (!tm)
2367      return;
2368 
2369    if (tm->hold && hold_if_requested)
2370      return;
2371 
2372    wn->terms = eina_list_remove(wn->terms, tm);
2373    tc = tm->container;
2374 
2375    tc->close(tc, tc);
2376 
2377    term_unref(tm);
2378 }
2379 
2380 /* Returns True if action is permitted */
2381 Eina_Bool
win_is_group_action_handled(Win * wn)2382 win_is_group_action_handled(Win *wn)
2383 {
2384    DBG("wn->group_input:%d wn->group_once_handled:%d wn:%p",
2385        wn->group_input, wn->group_once_handled, wn);
2386    if (!wn->group_input)
2387      return EINA_FALSE;
2388    if (wn->group_once_handled)
2389      return EINA_TRUE;
2390    wn->group_once_handled = EINA_TRUE;
2391    return EINA_FALSE;
2392 }
2393 
2394 Eina_Bool
win_is_group_input(const Win * wn)2395 win_is_group_input(const Win *wn)
2396 {
2397    return wn->group_input;
2398 }
2399 
2400 
2401 
2402 static void
_win_toggle_group(Win * wn)2403 _win_toggle_group(Win *wn)
2404 {
2405    Eina_List *l;
2406    Term *term;
2407 
2408    DBG("WIN TOGGLE");
2409    if (!wn->group_input)
2410      {
2411         GROUPED_INPUT_TERM_FOREACH(wn, l, term)
2412           {
2413              elm_layout_signal_emit(term->bg, "focus,in", "terminology");
2414              elm_layout_signal_emit(term->bg, "grouped,on", "terminology");
2415              if (term->tab_inactive)
2416                edje_object_signal_emit(term->tab_inactive,
2417                                        "grouped,on", "terminology");
2418              termio_event_feed_mouse_in(term->termio);
2419              termio_focus_in(term->termio);
2420           }
2421         wn->group_input = EINA_TRUE;
2422         DBG("GROUP INPUT is now TRUE");
2423      }
2424    else
2425      {
2426         wn->group_input = EINA_FALSE;
2427         DBG("GROUP INPUT is now FALSE");
2428         /* Better disable it for all of them in case of change of policy
2429          * between only visible or all.
2430          * Using the GROUPED_INPUT_TERM_FOREACH macro would miss some terms */
2431         EINA_LIST_FOREACH(wn->terms, l, term)
2432           {
2433              elm_layout_signal_emit(term->bg, "focus,out", "terminology");
2434              elm_layout_signal_emit(term->bg, "grouped,off", "terminology");
2435              if (term->tab_inactive)
2436                edje_object_signal_emit(term->tab_inactive,
2437                                        "grouped,off", "terminology");
2438              termio_focus_out(term->termio);
2439           }
2440         term = wn->child->term_first(wn->child);
2441         wn->child->focus(wn->child, &wn->tc);
2442      }
2443 }
2444 
2445 
2446 void
win_toggle_all_group(Win * wn)2447 win_toggle_all_group(Win *wn)
2448 {
2449    wn->group_only_visible = EINA_FALSE;
2450    _win_toggle_group(wn);
2451 }
2452 void
win_toggle_visible_group(Win * wn)2453 win_toggle_visible_group(Win *wn)
2454 {
2455    wn->group_only_visible = EINA_TRUE;
2456    _win_toggle_group(wn);
2457 }
2458 
2459 /* }}} */
2460 /* {{{ Splits */
2461 
2462 static Term *
_split_term_next(const Term_Container * tc,const Term_Container * child)2463 _split_term_next(const Term_Container *tc, const Term_Container *child)
2464 {
2465    Split *split;
2466 
2467    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2468    split = (Split*) tc;
2469 
2470    if (child == split->tc1)
2471      return split->tc2->term_first(split->tc2);
2472    else
2473      return tc->parent->term_next(tc->parent, tc);
2474 }
2475 
2476 static Term *
_split_term_prev(const Term_Container * tc,const Term_Container * child)2477 _split_term_prev(const Term_Container *tc, const Term_Container *child)
2478 {
2479    Split *split;
2480 
2481    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2482    split = (Split*) tc;
2483 
2484    if (child == split->tc2)
2485      return split->tc1->term_last(split->tc1);
2486    else
2487      return tc->parent->term_prev(tc->parent, tc);
2488 }
2489 
2490 static Term *
_split_term_up(const Term_Container * tc,const Term_Container * child)2491 _split_term_up(const Term_Container *tc,
2492                const Term_Container *child)
2493 {
2494    Split *split;
2495 
2496    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2497    split = (Split*) tc;
2498 
2499    if (child == split->tc2 && split->is_horizontal)
2500      return split->tc1->term_last(split->tc1);
2501    else
2502      return tc->parent->term_up(tc->parent, tc);
2503 }
2504 
2505 static Term *
_split_term_down(const Term_Container * tc,const Term_Container * child)2506 _split_term_down(const Term_Container *tc,
2507                  const Term_Container *child)
2508 {
2509    Split *split;
2510 
2511    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2512    split = (Split*) tc;
2513 
2514    if (child == split->tc1 && split->is_horizontal)
2515      return split->tc2->term_first(split->tc2);
2516    else
2517      return tc->parent->term_down(tc->parent, tc);
2518 }
2519 
2520 static Term *
_split_term_left(const Term_Container * tc,const Term_Container * child)2521 _split_term_left(const Term_Container *tc,
2522                  const Term_Container *child)
2523 {
2524    Split *split;
2525 
2526    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2527    split = (Split*) tc;
2528 
2529    if (child == split->tc2 && !split->is_horizontal)
2530      return split->tc1->term_last(split->tc1);
2531    else
2532      return tc->parent->term_left(tc->parent, tc);
2533 }
2534 
2535 static Term *
_split_term_right(const Term_Container * tc,const Term_Container * child)2536 _split_term_right(const Term_Container *tc,
2537                   const Term_Container *child)
2538 {
2539    Split *split;
2540 
2541    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2542    split = (Split*) tc;
2543 
2544    if (child == split->tc1 && !split->is_horizontal)
2545      return split->tc2->term_first(split->tc2);
2546    else
2547      return tc->parent->term_right(tc->parent, tc);
2548 }
2549 
2550 static Term *
_split_term_first(const Term_Container * tc)2551 _split_term_first(const Term_Container *tc)
2552 {
2553    Split *split;
2554 
2555    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2556    split = (Split*) tc;
2557 
2558    return split->tc1->term_first(split->tc1);
2559 }
2560 
2561 static Term *
_split_term_last(const Term_Container * tc)2562 _split_term_last(const Term_Container *tc)
2563 {
2564    Split *split;
2565 
2566    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2567    split = (Split*) tc;
2568 
2569    return split->tc2->term_last(split->tc2);
2570 }
2571 
2572 static Evas_Object *
_split_get_evas_object(const Term_Container * tc)2573 _split_get_evas_object(const Term_Container *tc)
2574 {
2575    Split *split;
2576 
2577    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2578    split = (Split*) tc;
2579 
2580    return split->panes;
2581 }
2582 
2583 static void
_split_size_eval(Term_Container * tc,Sizeinfo * info)2584 _split_size_eval(Term_Container *tc, Sizeinfo *info)
2585 {
2586    Evas_Coord mw = 0, mh = 0;
2587    Term_Container *tc1, *tc2;
2588    Sizeinfo inforet = {0, 0, 0, 0, 0, 0, 0, 0, 0};
2589    Split *split;
2590 
2591    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2592    split = (Split*) tc;
2593 
2594    tc1 = split->tc1;
2595    tc2 = split->tc2;
2596 
2597    info->min_w = 0;
2598    info->min_h = 0;
2599    info->req_w = 0;
2600    info->req_h = 0;
2601 
2602    evas_object_size_hint_min_get(split->panes, &mw, &mh);
2603    info->bg_min_w = mw;
2604    info->bg_min_h = mh;
2605 
2606    if (split->is_horizontal)
2607      {
2608         tc1->size_eval(tc1, &inforet);
2609         info->req |= inforet.req;
2610         mh -= inforet.min_h;
2611         if (info->req)
2612           {
2613              info->req_h += inforet.req_h;
2614              info->req_w = inforet.req_w;
2615           }
2616 
2617         tc2->size_eval(tc2, &inforet);
2618         info->req |= inforet.req;
2619         mh -= inforet.min_h;
2620         if (info->req)
2621           {
2622              info->req_h += inforet.req_h;
2623              info->req_w = inforet.req_w;
2624           }
2625         info->req_h += mh;
2626         if (info->req)
2627           info->req_w += mw - inforet.min_w - inforet.step_x;
2628      }
2629    else
2630      {
2631         tc1->size_eval(tc1, &inforet);
2632         info->req |= inforet.req;
2633         mw -= inforet.min_w;
2634         if (info->req)
2635           {
2636              info->req_w += inforet.req_w;
2637              info->req_h = inforet.req_h;
2638           }
2639 
2640         tc2->size_eval(tc2, &inforet);
2641         info->req |= inforet.req;
2642         mw -= inforet.min_w;
2643         if (info->req)
2644           {
2645              info->req_w += inforet.req_w;
2646              info->req_h = inforet.req_h;
2647           }
2648         info->req_w += mw;
2649         if (info->req)
2650           info->req_h += mh - inforet.min_h - inforet.step_y;
2651      }
2652    info->step_x = inforet.step_x;
2653    info->step_y = inforet.step_y;
2654 }
2655 
2656 static void
_split_swallow(Term_Container * tc,Term_Container * orig,Term_Container * new_child)2657 _split_swallow(Term_Container *tc, Term_Container *orig,
2658                Term_Container *new_child)
2659 {
2660    Split *split;
2661    Evas_Object *o;
2662 
2663    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2664    split = (Split*) tc;
2665 
2666    assert (orig && (orig == split->tc1 || orig == split->tc2));
2667 
2668    if ((_tab_drag) && (_tab_drag->parent_type == TERM_CONTAINER_TYPE_SPLIT) &&
2669        (_tab_drag->other == orig))
2670      {
2671         _tab_drag->other = new_child;
2672      }
2673 
2674    if (split->last_focus == orig)
2675      split->last_focus = new_child;
2676 
2677    o = orig->get_evas_object(orig);
2678    evas_object_hide(o);
2679 
2680    o = new_child->get_evas_object(new_child);
2681    if (split->tc1 == orig)
2682      {
2683         elm_object_part_content_unset(split->panes, PANES_TOP);
2684         elm_object_part_content_set(split->panes, PANES_TOP, o);
2685         split->tc1 = new_child;
2686      }
2687    else
2688      {
2689         elm_object_part_content_unset(split->panes, PANES_BOTTOM);
2690         elm_object_part_content_set(split->panes, PANES_BOTTOM, o);
2691         split->tc2 = new_child;
2692      }
2693    new_child->parent = tc;
2694    evas_object_show(o);
2695    evas_object_show(split->panes);
2696 }
2697 
2698 static Term *
_split_focused_term_get(const Term_Container * tc)2699 _split_focused_term_get(const Term_Container *tc)
2700 {
2701    Split *split;
2702    Term *term = NULL;
2703 
2704    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2705    split = (Split*) tc;
2706 
2707    if (tc->is_focused)
2708       term = split->last_focus->focused_term_get(split->last_focus);
2709    return term;
2710 }
2711 
2712 static Term *
_split_find_term_at_coords(const Term_Container * tc,Evas_Coord mx,Evas_Coord my)2713 _split_find_term_at_coords(const Term_Container *tc,
2714                            Evas_Coord mx, Evas_Coord my)
2715 {
2716    Split *split;
2717    Evas_Coord ox, oy, ow, oh;
2718    Evas_Object *o;
2719 
2720    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2721    split = (Split*) tc;
2722 
2723    o = split->tc1->get_evas_object(split->tc1);
2724 
2725    evas_object_geometry_get(o, &ox, &oy, &ow, &oh);
2726    if (ELM_RECTS_INTERSECT(ox, oy, ow, oh, mx, my, 1, 1))
2727      {
2728         tc = split->tc1;
2729      }
2730    else
2731      {
2732         tc = split->tc2;
2733      }
2734 
2735    return tc->find_term_at_coords(tc, mx, my);
2736 }
2737 
2738 static void
_split_close(Term_Container * tc,Term_Container * child)2739 _split_close(Term_Container *tc, Term_Container *child)
2740 {
2741    Split *split;
2742    Term_Container *parent, *other_child;
2743    Evas_Object *top, *bottom;
2744 
2745    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2746    split = (Split*) tc;
2747    assert ((child == split->tc1) || (child == split->tc2));
2748 
2749    DBG("close");
2750 
2751    if ((_tab_drag) && (_tab_drag->parent_type == TERM_CONTAINER_TYPE_SPLIT) &&
2752        (_tab_drag->other == child))
2753      {
2754         _tab_drag->other = tc->parent;
2755      }
2756 
2757    top = elm_object_part_content_unset(split->panes, PANES_TOP);
2758    bottom = elm_object_part_content_unset(split->panes, PANES_BOTTOM);
2759    evas_object_hide(top);
2760    evas_object_hide(bottom);
2761 
2762    parent = tc->parent;
2763    other_child = (child == split->tc1) ? split->tc2 : split->tc1;
2764    parent->swallow(parent, tc, other_child);
2765 
2766    if (tc->is_focused)
2767      {
2768         child->unfocus(child, tc);
2769         other_child->focus(other_child, parent);
2770      }
2771 
2772    evas_object_del(split->panes);
2773 
2774    eina_stringshare_del(tc->title);
2775    free(tc);
2776 }
2777 
2778 static void
_split_update(Term_Container * tc)2779 _split_update(Term_Container *tc)
2780 {
2781    Split *split;
2782 
2783    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2784    split = (Split*) tc;
2785 
2786    split->tc1->update(split->tc1);
2787    split->tc2->update(split->tc2);
2788 }
2789 
2790 static void
_split_focus(Term_Container * tc,Term_Container * relative)2791 _split_focus(Term_Container *tc, Term_Container *relative)
2792 {
2793    Split *split;
2794    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2795    split = (Split*) tc;
2796 
2797    DBG("tc:%p tc->is_focused:%d from_parent:%d",
2798        tc, tc->is_focused, tc->parent == relative);
2799 
2800    if (!tc->parent)
2801      return;
2802 
2803    if (tc->parent == relative)
2804      {
2805         /* top to bottom */
2806         if (!tc->is_focused)
2807           {
2808              Term_Container *last_focus = split->last_focus;
2809              Term_Container *other = (split->tc1 == last_focus) ?
2810                 split->tc2 : split->tc1;
2811 
2812              tc->is_focused = EINA_TRUE;
2813              other->unfocus(other, tc);
2814              last_focus->focus(last_focus, tc);
2815           }
2816      }
2817    else
2818      {
2819         /* bottom to top */
2820         if (split->last_focus != relative)
2821           split->last_focus->unfocus(split->last_focus, tc);
2822         split->last_focus = relative;
2823         if (!tc->is_focused)
2824           {
2825              /* was not focused, bring focus up */
2826              tc->is_focused = EINA_TRUE;
2827              tc->parent->focus(tc->parent, tc);
2828           }
2829      }
2830 }
2831 
2832 static void
_split_unfocus(Term_Container * tc,Term_Container * relative)2833 _split_unfocus(Term_Container *tc, Term_Container *relative)
2834 {
2835    Split *split;
2836    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2837    split = (Split*) tc;
2838 
2839    DBG("tc:%p tc->is_focused:%d from_parent:%d",
2840        tc, tc->is_focused, tc->parent == relative);
2841    if (!tc->is_focused)
2842      return;
2843 
2844    tc->is_focused = EINA_FALSE;
2845    if (tc->parent == relative)
2846      split->last_focus->unfocus(split->last_focus, tc);
2847    else
2848      tc->parent->unfocus(tc->parent, tc);
2849 }
2850 
2851 static void
_split_set_title(Term_Container * tc,Term_Container * child,const char * title)2852 _split_set_title(Term_Container *tc, Term_Container *child,
2853                  const char *title)
2854 {
2855    Split *split;
2856 
2857    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2858    split = (Split*) tc;
2859 
2860    if (child == split->last_focus)
2861      {
2862         eina_stringshare_del(tc->title);
2863         tc->title =  eina_stringshare_ref(title);
2864         tc->parent->set_title(tc->parent, tc, title);
2865      }
2866 }
2867 
2868 static void
_split_bell(Term_Container * tc,Term_Container * _child EINA_UNUSED)2869 _split_bell(Term_Container *tc,
2870             Term_Container *_child EINA_UNUSED)
2871 {
2872    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2873 
2874    if (tc->is_focused)
2875      return;
2876 
2877    tc->parent->bell(tc->parent, tc);
2878 }
2879 
2880 static void
_split_split(Term_Container * tc,Term_Container * child,Term * from,const char * cmd,Eina_Bool is_horizontal)2881 _split_split(Term_Container *tc, Term_Container *child,
2882              Term *from,
2883              const char *cmd, Eina_Bool is_horizontal)
2884 {
2885    Split *split;
2886    Win *wn;
2887    Term *tm_new, *tm;
2888    char *wdir = NULL;
2889    char buf[PATH_MAX];
2890    Term_Container *tc_split, *tc_solo_new;
2891    Evas_Object *obj_split;
2892    Eina_Bool child_is_focused;
2893 
2894    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2895    split = (Split *)tc;
2896    wn = tc->wn;
2897 
2898    if (!_term_container_is_splittable(tc, is_horizontal))
2899      return;
2900 
2901    // copy the current path to wdir if we should change the directory,
2902    // passing wdir NULL otherwise:
2903    if (wn->config->changedir_to_current)
2904      {
2905         if (from)
2906           tm = from;
2907         else
2908           tm = child->focused_term_get(child);
2909         if (tm && termio_cwd_get(tm->termio, buf, sizeof(buf)))
2910           wdir = buf;
2911      }
2912    tm_new = term_new(wn, wn->config,
2913                      cmd, wn->config->login_shell, wdir,
2914                      80, 24, EINA_FALSE, NULL);
2915    tc_solo_new = _solo_new(tm_new, wn);
2916    evas_object_data_set(tm_new->termio, "sizedone", tm_new->termio);
2917 
2918    if (child == split->tc1)
2919      elm_object_part_content_unset(split->panes, PANES_TOP);
2920    else
2921      elm_object_part_content_unset(split->panes, PANES_BOTTOM);
2922 
2923    child_is_focused = child->is_focused;
2924 
2925    /* force unfocus animation */
2926    tc_solo_new->is_focused = EINA_TRUE;
2927    tc_solo_new->unfocus(tc_solo_new, NULL);
2928 
2929    tc_split = _split_new(child, tc_solo_new, 0.5, is_horizontal);
2930 
2931    obj_split = tc_split->get_evas_object(tc_split);
2932 
2933    tc_split->is_focused = child_is_focused;
2934    tc->swallow(tc, child, tc_split);
2935 
2936    if (wn->config->show_tabs)
2937      {
2938         _solo_tab_show(tc_solo_new);
2939      }
2940 
2941    child->unfocus(child, tc_split);
2942    tc_split->focus(tc_split, tc_solo_new);
2943    tc_solo_new->focus(tc_solo_new, tc_split);
2944 
2945    evas_object_show(obj_split);
2946    _focus_validator();
2947 }
2948 
2949 static int
_split_split_direction(Term_Container * tc,Term_Container * child_orig,Term_Container * child_new,Split_Direction direction)2950 _split_split_direction(Term_Container *tc,
2951                        Term_Container *child_orig,
2952                        Term_Container *child_new,
2953                        Split_Direction direction)
2954 {
2955    Split *split;
2956    Win *wn;
2957    Term_Container *child1, *child2, *tc_split;
2958    Eina_Bool is_horizontal =
2959       (direction == SPLIT_DIRECTION_LEFT) || (direction == SPLIT_DIRECTION_RIGHT) ?
2960       EINA_FALSE : EINA_TRUE;
2961 
2962    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
2963    split = (Split *)tc;
2964    wn = tc->wn;
2965 
2966    if (!_term_container_is_splittable(tc, is_horizontal))
2967      {
2968         ERR("term is not splittable");
2969         return -1;
2970      }
2971 
2972    if ((direction == SPLIT_DIRECTION_TOP) ||
2973        (direction == SPLIT_DIRECTION_LEFT))
2974      {
2975         child1 = child_new;
2976         child2 = child_orig;
2977      }
2978    else
2979      {
2980         child1 = child_orig;
2981         child2 = child_new;
2982      }
2983 
2984    if (child_orig == split->tc1)
2985      elm_object_part_content_unset(split->panes, PANES_TOP);
2986    else
2987      elm_object_part_content_unset(split->panes, PANES_BOTTOM);
2988 
2989    wn->tc.unfocus(&wn->tc, NULL); /* unfocus from top */
2990 
2991    tc_split = _split_new(child1, child2, 0.5, is_horizontal);
2992 
2993    if (wn->config->show_tabs)
2994      {
2995         if (child_orig->type == TERM_CONTAINER_TYPE_SOLO)
2996           {
2997              _solo_tab_show(child_orig);
2998           }
2999         _solo_tab_show(child_new);
3000      }
3001 
3002    tc_split->is_focused = tc->is_focused;
3003    tc->swallow(tc, child_orig, tc_split);
3004 
3005    child_new->focus(child_new, NULL); /* refocus from bottom */
3006 
3007    return 0;
3008 }
3009 
3010 static Eina_Bool
_split_is_visible(const Term_Container * tc,const Term_Container * _child EINA_UNUSED)3011 _split_is_visible(const Term_Container *tc,
3012                   const Term_Container *_child EINA_UNUSED)
3013 {
3014    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
3015    /* Could return True with the current design because splits are at a higher
3016     * level than tabs */
3017    return tc->parent->is_visible(tc->parent, tc);
3018 }
3019 
3020 static void
_split_detach(Term_Container * tc,Term_Container * solo_child)3021 _split_detach(Term_Container *tc, Term_Container *solo_child)
3022 {
3023    Evas_Object *o;
3024    assert (tc->type == TERM_CONTAINER_TYPE_SPLIT);
3025    assert (solo_child->type == TERM_CONTAINER_TYPE_SOLO);
3026 
3027    _split_close(tc, solo_child);
3028    solo_child->is_focused = EINA_FALSE;
3029 
3030    o = solo_child->get_evas_object(solo_child);
3031    evas_object_hide(o);
3032    solo_child->parent = (Term_Container*) solo_child->wn;
3033 }
3034 
3035 static Term_Container *
_split_new(Term_Container * tc1,Term_Container * tc2,double left_size,Eina_Bool is_horizontal)3036 _split_new(Term_Container *tc1, Term_Container *tc2,
3037            double left_size,
3038            Eina_Bool is_horizontal)
3039 {
3040    Evas_Object *o;
3041    Term_Container *tc = NULL;
3042    Split *split = NULL;
3043    split = calloc(1, sizeof(Split));
3044    if (!split)
3045      {
3046         free(split);
3047         return NULL;
3048      }
3049 
3050    DBG("split new %p 1:%p 2:%p (1 is %sfocused) (2 is %sfocused)", split, tc1, tc2,
3051        tc1->is_focused ? "" : "not ",
3052        tc2->is_focused ? "" : "not ");
3053 
3054    tc = (Term_Container*)split;
3055    tc->term_next = _split_term_next;
3056    tc->term_prev = _split_term_prev;
3057    tc->term_up = _split_term_up;
3058    tc->term_down = _split_term_down;
3059    tc->term_left = _split_term_left;
3060    tc->term_right = _split_term_right;
3061    tc->term_first = _split_term_first;
3062    tc->term_last = _split_term_last;
3063    tc->focused_term_get = _split_focused_term_get;
3064    tc->get_evas_object = _split_get_evas_object;
3065    tc->split = _split_split;
3066    tc->split_direction = _split_split_direction;
3067    tc->find_term_at_coords = _split_find_term_at_coords;
3068    tc->size_eval = _split_size_eval;
3069    tc->swallow = _split_swallow;
3070    tc->focus = _split_focus;
3071    tc->unfocus = _split_unfocus;
3072    tc->set_title = _split_set_title;
3073    tc->bell = _split_bell;
3074    tc->close = _split_close;
3075    tc->update = _split_update;
3076    tc->is_visible = _split_is_visible;
3077    tc->detach = _split_detach;
3078    tc->title = eina_stringshare_add("Terminology");
3079    tc->type = TERM_CONTAINER_TYPE_SPLIT;
3080 
3081 
3082    tc->parent = NULL;
3083    tc->wn = tc1->wn;
3084 
3085    tc1->parent = tc2->parent = tc;
3086 
3087    split->tc1 = tc1;
3088    split->tc2 = tc2;
3089    split->last_focus = tc2;
3090    if (tc1->is_focused)
3091      split->last_focus = tc1;
3092 
3093    o = split->panes = elm_panes_add(tc1->wn->win);
3094    elm_object_style_set(o, "flush");
3095    evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
3096    evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
3097 
3098    split->is_horizontal = is_horizontal;
3099    elm_panes_horizontal_set(o, split->is_horizontal);
3100 
3101    elm_object_part_content_set(o, PANES_TOP,
3102                                tc1->get_evas_object(tc1));
3103    elm_object_part_content_set(o, PANES_BOTTOM,
3104                                tc2->get_evas_object(tc2));
3105    elm_panes_content_left_size_set(o, left_size);
3106 
3107    tc->is_focused = tc1->is_focused | tc2->is_focused;
3108    assert(!(tc1->is_focused && tc2->is_focused));
3109    return tc;
3110 }
3111 
3112 static void
_size_job(void * data)3113 _size_job(void *data)
3114 {
3115    Win *wn = data;
3116    Sizeinfo info = {0, 0, 0, 0, 0, 0, 0, 0, 0};
3117    Term_Container *tc = (Term_Container*) wn;
3118 
3119    wn->size_job = NULL;
3120    tc->size_eval(tc, &info);
3121 
3122    elm_win_size_step_set(wn->win, info.step_x, info.step_y);
3123    if (info.bg_min_w > 0 && info.bg_min_h > 0)
3124      {
3125         elm_win_size_base_set(wn->win,
3126                               info.min_w, info.min_h);
3127         evas_object_size_hint_min_set(wn->backbg,
3128                                       info.bg_min_w,
3129                                       info.bg_min_h);
3130         if (info.req)
3131           evas_object_resize(wn->win, info.req_w, info.req_h);
3132      }
3133 }
3134 
3135 void
win_sizing_handle(Win * wn)3136 win_sizing_handle(Win *wn)
3137 {
3138    if (wn->size_job)
3139      ecore_job_del(wn->size_job);
3140    _size_job(wn);
3141 }
3142 
3143 static void
_cb_size_hint(void * data,Evas * _e EINA_UNUSED,Evas_Object * obj,void * _event EINA_UNUSED)3144 _cb_size_hint(void *data,
3145               Evas *_e EINA_UNUSED,
3146               Evas_Object *obj,
3147               void *_event EINA_UNUSED)
3148 {
3149    Term *term = data;
3150    Evas_Coord mw, mh, rw, rh, w = 0, h = 0;
3151    Evas_Object *edje_base = elm_layout_edje_get(term->core);
3152 
3153    evas_object_size_hint_min_get(obj, &mw, &mh);
3154    evas_object_size_hint_request_get(obj, &rw, &rh);
3155    edje_object_size_min_calc(edje_base, &w, &h);
3156    evas_object_size_hint_min_set(term->core, w, h);
3157    edje_object_size_min_calc(term->bg_edj, &w, &h);
3158    evas_object_size_hint_min_set(term->bg, w, h);
3159    term->step_x = mw;
3160    term->step_y = mh;
3161    term->min_w = w - mw;
3162    term->min_h = h - mh;
3163    term->req_w = w - mw + rw;
3164    term->req_h = h - mh + rh;
3165 
3166    if (term->wn->size_job)
3167      ecore_job_del(term->wn->size_job);
3168    term->wn->size_job = ecore_job_add(_size_job, term->wn);
3169 }
3170 
3171 void
split_horizontally(Evas_Object * term,const char * cmd)3172 split_horizontally(Evas_Object *term,
3173                    const char *cmd)
3174 {
3175    Term *tm;
3176    Term_Container *tc;
3177 
3178    tm = evas_object_data_get(term, "term");
3179    if (!tm) return;
3180 
3181    tc = tm->container;
3182    tc->split(tc, tc, tm, cmd, EINA_TRUE);
3183 }
3184 
3185 void
split_vertically(Evas_Object * term,const char * cmd)3186 split_vertically(Evas_Object *term,
3187                  const char *cmd)
3188 {
3189    Term *tm;
3190    Term_Container *tc;
3191 
3192    tm = evas_object_data_get(term, "term");
3193    if (!tm) return;
3194 
3195    tc = tm->container;
3196    tc->split(tc, tc, tm, cmd, EINA_FALSE);
3197 }
3198 
3199 /* }}} */
3200 /* {{{ Tabs */
3201 
3202 static Term *
_tab_item_to_term(const Tab_Item * tab_item)3203 _tab_item_to_term(const Tab_Item *tab_item)
3204 {
3205    Solo *solo;
3206 
3207    assert (tab_item->tc->type == TERM_CONTAINER_TYPE_SOLO);
3208    solo = (Solo*)tab_item->tc;
3209    assert (solo->term);
3210    assert (solo->term->tab_item == tab_item);
3211    return solo->term;
3212 }
3213 
3214 static void
_cb_tab_activate(void * data,Evas_Object * _obj EINA_UNUSED,const char * _sig EINA_UNUSED,const char * _src EINA_UNUSED)3215 _cb_tab_activate(void *data,
3216                  Evas_Object *_obj EINA_UNUSED,
3217                  const char *_sig EINA_UNUSED,
3218                  const char *_src EINA_UNUSED)
3219 {
3220    Tab_Item *tab_item = data;
3221    Term *term = _tab_item_to_term(tab_item);
3222 
3223    term_focus(term);
3224 }
3225 
3226 
3227 static void
_tabbar_clear(Term * term)3228 _tabbar_clear(Term *term)
3229 {
3230    if (term->tabbar.l.box)
3231      {
3232         elm_box_unpack_all(term->tabbar.l.box);
3233         evas_object_del(term->tabbar.l.box);
3234         term->tabbar.l.box = NULL;
3235      }
3236    if (term->tabbar.r.box)
3237      {
3238         elm_box_unpack_all(term->tabbar.r.box);
3239         evas_object_del(term->tabbar.r.box);
3240         term->tabbar.r.box = NULL;
3241      }
3242 
3243    if (term->tab_spacer)
3244      {
3245         elm_layout_signal_emit(term->bg, "tabbar,off", "terminology");
3246         edje_object_message_signal_process(term->bg_edj);
3247         elm_layout_content_unset(term->bg, "terminology.tab");
3248         evas_object_del(term->tab_spacer);
3249         term->tab_spacer = NULL;
3250      }
3251    if (term->tab_inactive)
3252      {
3253         evas_object_hide(term->tab_inactive);
3254         edje_object_signal_callback_del(term->tab_inactive,
3255                                         "tab,activate", "terminology",
3256                                         _cb_tab_activate);
3257      }
3258 }
3259 
3260 static void
_tab_item_free(Tab_Item * tab_item)3261 _tab_item_free(Tab_Item *tab_item)
3262 {
3263    Term *term;
3264 
3265    if (!tab_item)
3266      return;
3267 
3268    term = _tab_item_to_term(tab_item);
3269    term->tab_item = NULL;
3270    if (term->tab_inactive)
3271      edje_object_signal_callback_del(term->tab_inactive,
3272                                      "tab,activate", "terminology",
3273                                      _cb_tab_activate);
3274    free(tab_item);
3275 }
3276 
3277 static void
_cb_tab_close(void * data,Evas_Object * _obj EINA_UNUSED,const char * _sig EINA_UNUSED,const char * _src EINA_UNUSED)3278 _cb_tab_close(void *data,
3279               Evas_Object *_obj EINA_UNUSED,
3280               const char *_sig EINA_UNUSED,
3281               const char *_src EINA_UNUSED)
3282 {
3283    Term *term = data;
3284    Win *wn = term->wn;
3285    Evas_Object *win = win_evas_object_get(wn);
3286 
3287    term_close(win, term->termio, EINA_FALSE);
3288 }
3289 
3290 static void
_cb_tab_title(void * data,Evas_Object * _obj EINA_UNUSED,const char * _sig EINA_UNUSED,const char * _src EINA_UNUSED)3291 _cb_tab_title(void *data,
3292            Evas_Object *_obj EINA_UNUSED,
3293            const char *_sig EINA_UNUSED,
3294            const char *_src EINA_UNUSED)
3295 {
3296    Term *term = data;
3297 
3298    term_set_title(term);
3299 }
3300 
3301 
3302 
3303 static void
_tabs_recompute_drag(Tabs * tabs)3304 _tabs_recompute_drag(Tabs *tabs)
3305 {
3306    Term *term = NULL;
3307    int n = eina_list_count(tabs->tabs);
3308    int idx = -1;
3309    Tab_Item *tab_item;
3310    Eina_List *l;
3311    double v1 = 0.0, v2 = 1.0;
3312 
3313    EINA_LIST_FOREACH(tabs->tabs, l, tab_item)
3314      {
3315         idx++;
3316         if (tab_item == tabs->current)
3317           {
3318             term = _tab_item_to_term(tab_item);
3319             break;
3320           }
3321      }
3322    assert(term != NULL);
3323    if (n > 1)
3324      {
3325         v1 = (double)(idx) / (double)n;
3326         v2 = (double)(idx+1) / (double)n;
3327      }
3328    tabs->v1_orig = v1;
3329    tabs->v2_orig = v2;
3330    edje_object_part_drag_value_set(term->bg_edj, "terminology.tabl", v1, 0.0);
3331    edje_object_part_drag_value_set(term->bg_edj, "terminology.tabr", v2, 0.0);
3332 }
3333 
3334 static Eina_Bool
_term_hdrag_on(Term * term,void * data EINA_UNUSED)3335 _term_hdrag_on(Term *term, void *data EINA_UNUSED)
3336 {
3337    elm_layout_signal_emit(term->bg, "hdrag,on", "terminology");
3338 
3339    return ECORE_CALLBACK_PASS_ON;
3340 }
3341 
3342 static Eina_Bool
_term_hdrag_off(Term * term,void * data EINA_UNUSED)3343 _term_hdrag_off(Term *term, void *data EINA_UNUSED)
3344 {
3345    elm_layout_signal_emit(term->bg, "hdrag,off", "terminology");
3346 
3347    return ECORE_CALLBACK_PASS_ON;
3348 }
3349 
3350 static void
_tab_drag_disable_anim_over(void)3351 _tab_drag_disable_anim_over(void)
3352 {
3353    if ((!_tab_drag) || (!_tab_drag->term_over) ||
3354        (_tab_drag->split_direction == SPLIT_DIRECTION_NONE))
3355      return;
3356 
3357    switch (_tab_drag->split_direction)
3358      {
3359       case SPLIT_DIRECTION_LEFT:
3360          elm_layout_signal_emit(_tab_drag->term_over->bg,
3361                                 "drag_left,off", "terminology");
3362          break;
3363       case SPLIT_DIRECTION_RIGHT:
3364          elm_layout_signal_emit(_tab_drag->term_over->bg,
3365                                 "drag_right,off", "terminology");
3366          break;
3367       case SPLIT_DIRECTION_TOP:
3368          elm_layout_signal_emit(_tab_drag->term_over->bg,
3369                                 "drag_top,off", "terminology");
3370          break;
3371       case SPLIT_DIRECTION_BOTTOM:
3372          elm_layout_signal_emit(_tab_drag->term_over->bg,
3373                                 "drag_bottom,off", "terminology");
3374          break;
3375       case SPLIT_DIRECTION_TABS:
3376          elm_layout_signal_emit(_tab_drag->term_over->bg,
3377                                 "drag_over_tabs,off", "terminology");
3378          break;
3379       default:
3380          break;
3381      }
3382    elm_layout_signal_emit(_tab_drag->term_over->bg,
3383                           "hdrag,off", "terminology");
3384 }
3385 
3386 static void
_tab_drag_rollback_split(void)3387 _tab_drag_rollback_split(void)
3388 {
3389    Eina_Bool is_horizontal = _tab_drag->is_horizontal;
3390    Term_Container *tc_split = NULL;
3391    Term_Container *child1 = NULL, *child2 = NULL;
3392    Term_Container *other = _tab_drag->other;
3393    Term_Container *parent = NULL;
3394    Win *wn = _tab_drag->term->wn;
3395    Term_Container *tc_win = (Term_Container*)wn;
3396    Term_Container *tc = _tab_drag->term->container;
3397 
3398    if (!_tab_drag->other)
3399      {
3400         other = wn->child;
3401      }
3402    parent = other->parent;
3403 
3404    if (_tab_drag->is_first_child)
3405      {
3406         child1 = tc;
3407         child2 = other;
3408      }
3409    else
3410      {
3411         child1 = other;
3412         child2 = tc;
3413      }
3414 
3415    assert(!tc->is_focused);
3416    tc_split = _split_new(child1, child2, _tab_drag->left_size, is_horizontal);
3417    parent->swallow(parent, other, tc_split);
3418    /* Ensure the other child is unfocused */
3419    other->unfocus(other, tc_split);
3420    /* Unfocus from the window down to a single term */
3421    tc_win->unfocus(tc_win, NULL);
3422    /* Focus the dragged term, up to the window */
3423    tc->focus(tc, NULL);
3424 }
3425 
3426 static void
_tabs_attach(Term_Container * tc,Term_Container * tc_new)3427 _tabs_attach(Term_Container *tc, Term_Container *tc_new)
3428 {
3429    Tabs *tabs;
3430    Tab_Item *tab_item;
3431    Evas_Object *o;
3432    Evas_Coord x, y, w, h;
3433    Term_Container *tc_old, *tc_parent;
3434 
3435    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
3436    assert (tc_new->type == TERM_CONTAINER_TYPE_SOLO);
3437 
3438    tabs = (Tabs*) tc;
3439 
3440    tc_new->parent = tc;
3441    tab_item = tab_item_new(tabs, tc_new);
3442 
3443    tc_parent = tc->parent;
3444    tc_old = tabs->current->tc;
3445    tc_old->unfocus(tc_old, tc);
3446    o = tc_old->get_evas_object(tc_old);
3447    evas_object_geometry_get(o, &x, &y, &w, &h);
3448    evas_object_hide(o);
3449    o = tc_new->get_evas_object(tc_new);
3450    evas_object_geometry_set(o, x, y, w, h);
3451    evas_object_show(o);
3452 
3453    tc->swallow(tc, tc_old, tc_new);
3454    tabs->current = tab_item;
3455 
3456    /* XXX: need to refresh parent */
3457    tc_parent->swallow(tc_parent, tc, tc);
3458 
3459    if (tc->is_focused)
3460      tc_new->focus(tc_new, tc);
3461    else
3462      tc_new->unfocus(tc_new, tc);
3463 }
3464 
3465 
3466 static void
_solo_attach(Term_Container * tc,Term_Container * tc_to_add)3467 _solo_attach(Term_Container *tc, Term_Container *tc_to_add)
3468 {
3469    assert(tc->type == TERM_CONTAINER_TYPE_SOLO);
3470    assert(tc_to_add->type == TERM_CONTAINER_TYPE_SOLO);
3471 
3472    if (tc->parent->type != TERM_CONTAINER_TYPE_TABS)
3473      _tabs_new(tc, tc->parent);
3474 
3475    _tabs_attach(tc->parent, tc_to_add);
3476    assert(eina_list_count(((Tabs*)(tc->parent))->tabs) > 1);
3477 }
3478 
3479 static void
_tab_drag_reparented(void)3480 _tab_drag_reparented(void)
3481 {
3482    assert(_tab_drag);
3483    _tab_drag->parent_type = TERM_CONTAINER_TYPE_UNKNOWN;
3484 }
3485 
3486 static void
_term_on_horizontal_drag(void * data,Evas_Object * o EINA_UNUSED,const char * emission EINA_UNUSED,const char * source EINA_UNUSED)3487 _term_on_horizontal_drag(void *data,
3488                          Evas_Object *o EINA_UNUSED,
3489                          const char *emission EINA_UNUSED,
3490                          const char *source EINA_UNUSED)
3491 {
3492    Eina_List *l, *next, *prev;
3493    int tab_active_idx;
3494    int n;
3495    Tabs *tabs;
3496    Tab_Item *tab_item;
3497    Tab_Item *item_moved;
3498    Term *term = data;
3499    Term_Container *tc = term->container;
3500    Term_Container *tc_parent = tc->parent;
3501    Term *term_moved;
3502    double v1, v2, m;
3503 
3504    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
3505    if (tc->parent->type != TERM_CONTAINER_TYPE_TABS)
3506      {
3507         edje_object_part_drag_value_set(term->bg_edj, "terminology.tabl",
3508                                         0.0, 0.0);
3509         edje_object_part_drag_value_set(term->bg_edj, "terminology.tabr",
3510                                         1.0, 0.0);
3511         return;
3512      }
3513 
3514    tabs = (Tabs*) tc_parent;
3515    n = eina_list_count(tabs->tabs);
3516    if (n <= 1)
3517      return;
3518 
3519    tab_item = tabs->current;
3520 
3521    _tab_drag_free();
3522 
3523    tab_active_idx = -1;
3524    EINA_LIST_FOREACH(tabs->tabs, l, tab_item)
3525      {
3526         tab_active_idx++;
3527         if (tab_item == tabs->current)
3528           break;
3529      }
3530    tab_item = tabs->current;
3531 
3532    edje_object_part_drag_value_get(term->bg_edj, "terminology.tabl",
3533                                    &v1, NULL);
3534    edje_object_part_drag_value_get(term->bg_edj, "terminology.tabr",
3535                                    &v2, NULL);
3536    m = 1.2 / ((double)(2 * n)); /* 1.2, to have some sense of hysteresis */
3537    while ((tab_active_idx < n - 1) &&
3538        ((v2 > (tabs->v2_orig + m)) ||
3539         ((v1 == v2) && (v2 > tabs->v2_orig))))
3540      {
3541         /* To the right */
3542         l = eina_list_nth_list(tabs->tabs, tab_active_idx);
3543         next = eina_list_next(l);
3544         item_moved = next->data;
3545         term_moved = _tab_item_to_term(item_moved);
3546         elm_box_unpack(term->tabbar.r.box, term_moved->tab_inactive);
3547         elm_box_pack_end(term->tabbar.l.box, term_moved->tab_inactive);
3548 
3549         tabs->tabs = eina_list_remove_list(tabs->tabs, l);
3550         tabs->tabs = eina_list_append_relative_list(tabs->tabs,
3551                                                     eina_list_data_get(l),
3552                                                     next);
3553         _tabs_recompute_drag(tabs);
3554         tab_active_idx++;
3555         if (v2 <= tabs->v2_orig)
3556           return;
3557      }
3558    while ((tab_active_idx > 0) &&
3559        ((v1 < tabs->v1_orig - m) ||
3560         ((v1 == v2) && v1 < tabs->v1_orig)))
3561      {
3562         /* To the left */
3563         l = eina_list_nth_list(tabs->tabs, tab_active_idx);
3564         prev = eina_list_prev(l);
3565         item_moved = prev->data;
3566         term_moved = _tab_item_to_term(item_moved);
3567         elm_box_unpack(term->tabbar.l.box, term_moved->tab_inactive);
3568         elm_box_pack_start(term->tabbar.r.box, term_moved->tab_inactive);
3569 
3570         tabs->tabs = eina_list_remove_list(tabs->tabs, prev);
3571         tabs->tabs = eina_list_append_relative_list(tabs->tabs,
3572                                                eina_list_data_get(prev),
3573                                                l);
3574         _tabs_recompute_drag(tabs);
3575         tab_active_idx--;
3576      }
3577 }
3578 
3579 static void
_tabs_set_main_tab(Term * term,Tab_Item * tab_item)3580 _tabs_set_main_tab(Term *term,
3581                    Tab_Item *tab_item)
3582 {
3583    if (!term->tab_spacer)
3584      {
3585         Evas_Coord w = 0, h = 0;
3586         Evas *canvas = evas_object_evas_get(term->bg);
3587 
3588         term->tab_spacer = evas_object_rectangle_add(canvas);
3589         evas_object_color_set(term->tab_spacer, 0, 0, 0, 0);
3590         elm_coords_finger_size_adjust(1, &w, 1, &h);
3591         evas_object_size_hint_min_set(term->tab_spacer, w, h);
3592         elm_layout_content_set(term->bg, "terminology.tab", term->tab_spacer);
3593      }
3594    evas_object_show(term->tab_spacer);
3595    elm_layout_text_set(term->bg, "terminology.tab.title", tab_item->tc->title);
3596    elm_layout_signal_emit(term->bg, "tabbar,on", "terminology");
3597    elm_layout_signal_emit(term->bg, "tab_btn,on", "terminology");
3598    edje_object_message_signal_process(term->bg_edj);
3599 }
3600 
3601 static Evas_Object*
_tab_inactive_get_or_create(Evas * canvas,Term * term,Tab_Item * tab_item)3602 _tab_inactive_get_or_create(Evas *canvas,
3603                             Term *term,
3604                             Tab_Item *tab_item)
3605 {
3606    Evas_Object *o;
3607    Evas_Coord w, h;
3608 
3609    if (term->tab_inactive)
3610      {
3611         o = term->tab_inactive;
3612         goto created;
3613      }
3614 
3615    term->tab_inactive = o = edje_object_add(canvas);
3616 
3617    theme_apply(o, term->config, "terminology/tabbar_back",
3618                NULL, NULL, EINA_FALSE);
3619    evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
3620    evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
3621    edje_object_size_min_calc(o, &w, &h);
3622    evas_object_size_hint_min_set(o, w, h);
3623    evas_object_data_set(o, "term", term);
3624 
3625 created:
3626    edje_object_signal_callback_add(o, "tab,activate", "terminology",
3627                                    _cb_tab_activate, tab_item);
3628 
3629    if (term->missed_bell)
3630      edje_object_signal_emit(o, "bell", "terminology");
3631    else
3632      edje_object_signal_emit(o, "bell,off", "terminology");
3633    edje_object_part_text_set(o, "terminology.title",
3634                              tab_item->tc->title);
3635    return o;
3636 }
3637 
3638 
3639 static void
_tabbar_fill(Tabs * tabs)3640 _tabbar_fill(Tabs *tabs)
3641 {
3642    Eina_List *l;
3643    Eina_Bool after_current = EINA_FALSE;
3644    Tab_Item *tab_item;
3645    Term *main_term = _tab_item_to_term(tabs->current);
3646    Evas *canvas = evas_object_evas_get(main_term->bg);
3647 
3648    EINA_LIST_FOREACH(tabs->tabs, l, tab_item)
3649      {
3650         Term *term = _tab_item_to_term(tab_item);
3651 
3652         if (tab_item == tabs->current)
3653           {
3654              _tabs_set_main_tab(term, tab_item);
3655              assert(main_term == term);
3656              evas_object_hide(term->tab_inactive);
3657              edje_object_signal_callback_del(term->tab_inactive,
3658                                              "tab,activate", "terminology",
3659                                              _cb_tab_activate);
3660              after_current = EINA_TRUE;
3661           }
3662         else
3663           {
3664              Evas_Object *o;
3665 
3666              _tabbar_clear(term);
3667 
3668              o = _tab_inactive_get_or_create(canvas, term, tab_item);
3669              evas_object_show(o);
3670              if (after_current)
3671                elm_box_pack_end(main_term->tabbar.r.box, o);
3672              else
3673                elm_box_pack_end(main_term->tabbar.l.box, o);
3674           }
3675      }
3676    _tabs_recompute_drag(tabs);
3677 }
3678 
3679 static void
_tabs_get_or_create_boxes(Term * term,Term * src)3680 _tabs_get_or_create_boxes(Term *term, Term *src)
3681 {
3682    Evas_Object *o;
3683 
3684    assert(term->tabbar.l.box == NULL);
3685    assert(term->tabbar.r.box == NULL);
3686 
3687    /* Left */
3688    if (src && src->tabbar.l.box)
3689      {
3690         term->tabbar.l.box = o = src->tabbar.l.box;
3691         elm_box_unpack_all(term->tabbar.l.box);
3692         src->tabbar.l.box = NULL;
3693      }
3694    else
3695      {
3696         term->tabbar.l.box = o = elm_box_add(term->bg);
3697         elm_box_horizontal_set(o, EINA_TRUE);
3698         elm_box_homogeneous_set(o, EINA_TRUE);
3699         evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
3700         evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
3701      }
3702    elm_layout_content_set(term->bg, "terminology.tabl.content", o);
3703    evas_object_show(o);
3704 
3705    /* Right */
3706    if (src && src->tabbar.r.box)
3707      {
3708         term->tabbar.r.box = o = src->tabbar.r.box;
3709         elm_box_unpack_all(term->tabbar.r.box);
3710         src->tabbar.r.box = NULL;
3711      }
3712    else
3713      {
3714         term->tabbar.r.box = o = elm_box_add(term->bg);
3715         elm_box_horizontal_set(o, EINA_TRUE);
3716         elm_box_homogeneous_set(o, EINA_TRUE);
3717         evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
3718         evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
3719      }
3720    elm_layout_content_set(term->bg, "terminology.tabr.content", o);
3721    evas_object_show(o);
3722 }
3723 
3724 static void
_tab_drag_rollback_win(void)3725 _tab_drag_rollback_win(void)
3726 {
3727    Term *term = _tab_drag->term;
3728    Win *wn = term->wn;
3729    Term_Container *tc_win = (Term_Container*)wn;
3730    Term_Container *tc = term->container;
3731 
3732    if (term->unswallowed)
3733      elm_layout_content_set(term->bg, "terminology.content", term->core);
3734    term->unswallowed = EINA_FALSE;
3735 
3736    tc_win->swallow(tc_win, NULL, tc);
3737    tc_win->unfocus(tc_win, NULL);
3738    tc->focus(tc, NULL);
3739 
3740    _tab_drag_reparented();
3741 }
3742 
3743 static void
_tab_drag_rollback_tabs(void)3744 _tab_drag_rollback_tabs(void)
3745 {
3746    Term *term = _tab_drag->term;
3747    Win *wn = term->wn;
3748    Term_Container *tc_win = (Term_Container*)wn;
3749    Term_Container *tc = term->container;
3750    Term_Container *tc_tabs = _tab_drag->tabs_child;
3751    int n;
3752    Tabs *tabs;
3753 
3754    if (tc_tabs->type == TERM_CONTAINER_TYPE_TABS)
3755      {
3756         tabs = (Tabs*) tc_tabs;
3757 
3758         /* reinsert at correct place */
3759         _solo_attach(tabs->current->tc, tc);
3760      }
3761    else
3762      {
3763         assert(tc_tabs->type == TERM_CONTAINER_TYPE_SOLO);
3764 
3765         /* Create tabs with solo */
3766         assert(term->tab_item == NULL);
3767         _solo_attach(_tab_drag->tabs_child, term->container);
3768         tabs = (Tabs *) term->container->parent;
3769      }
3770 
3771    n = eina_list_count(tabs->tabs);
3772    assert (n >= 2);
3773 
3774    /* move tab_item to expected place */
3775    if (_tab_drag->previous_position < n)
3776      {
3777         Tab_Item *tab_item = term->tab_item;
3778 
3779         tabs->tabs = eina_list_remove(tabs->tabs, tab_item);
3780         if (_tab_drag->previous_position == n-1)
3781           {
3782              tabs->tabs = eina_list_append(tabs->tabs, term->tab_item);
3783           }
3784         else
3785           {
3786              int i = 0;
3787              Eina_List *l;
3788 
3789              EINA_LIST_FOREACH(tabs->tabs, l, tab_item)
3790                {
3791                   if (i == _tab_drag->previous_position)
3792                     {
3793                        tabs->tabs = eina_list_prepend_relative_list(tabs->tabs,
3794                                                                     term->tab_item,
3795                                                                     l);
3796                        break;
3797                     }
3798                   i++;
3799                }
3800           }
3801      }
3802 
3803    tc_win->unfocus(tc_win, NULL);
3804    tc->focus(tc, NULL);
3805 
3806    /* Repack in correct boxes */
3807    elm_box_unpack_all(term->tabbar.l.box);
3808    elm_box_unpack_all(term->tabbar.r.box);
3809    _tabbar_fill(tabs);
3810 
3811    _tab_drag_reparented();
3812 }
3813 
3814 static void
_tab_drag_rollback(void)3815 _tab_drag_rollback(void)
3816 {
3817    _focus_validator();
3818    switch (_tab_drag->parent_type)
3819      {
3820       case TERM_CONTAINER_TYPE_TABS:
3821          _tab_drag_rollback_tabs();
3822          break;
3823       case TERM_CONTAINER_TYPE_SPLIT:
3824          _tab_drag_rollback_split();
3825          break;
3826       case TERM_CONTAINER_TYPE_WIN:
3827          _tab_drag_rollback_win();
3828          break;
3829       default:
3830          ERR("invalid parent type:%d", _tab_drag->parent_type);
3831          abort();
3832      }
3833    _focus_validator();
3834 }
3835 
3836 static void
_tab_drag_save_state(Term_Container * tc)3837 _tab_drag_save_state(Term_Container *tc)
3838 {
3839    assert(_tab_drag);
3840 
3841    _tab_drag->parent_type = tc->parent->type;
3842    switch (_tab_drag->parent_type)
3843      {
3844       case TERM_CONTAINER_TYPE_TABS:
3845            {
3846               int position = 0;
3847               Tabs *tabs;
3848               Eina_List *l;
3849               Tab_Item *tab_item;
3850 
3851               tabs = (Tabs*) tc->parent;
3852               EINA_LIST_FOREACH(tabs->tabs, l, tab_item)
3853                 {
3854                    if (tab_item->tc == tc)
3855                      break;
3856                    position++;
3857                 }
3858               _tab_drag->previous_position = position;
3859               _tab_drag->tabs_child = tc->parent;
3860            }
3861          break;
3862       case TERM_CONTAINER_TYPE_SPLIT:
3863            {
3864               Split *split;
3865 
3866               split = (Split*)tc->parent;
3867               _tab_drag->is_horizontal = split->is_horizontal;
3868               if ((_tab_drag->is_first_child = (split->tc1 == tc)))
3869                 _tab_drag->other = split->tc2;
3870               else
3871                 _tab_drag->other = split->tc1;
3872               _tab_drag->left_size = elm_panes_content_left_size_get(
3873                  split->panes);
3874            }
3875          break;
3876       default:
3877          ERR("invalid parent type:%d", tc->parent->type);
3878          abort();
3879      }
3880 }
3881 
3882 static void
_tab_drag_free(void)3883 _tab_drag_free(void)
3884 {
3885    if (!_tab_drag)
3886      return;
3887    if (_tab_drag->term_over && _tab_drag->term_over->has_bg_cursor)
3888      {
3889         elm_object_cursor_unset(_tab_drag->term_over->bg);
3890         _tab_drag->term_over->has_bg_cursor = EINA_FALSE;
3891      }
3892    if (_tab_drag->term->has_bg_cursor)
3893      {
3894         elm_object_cursor_unset(_tab_drag->term->bg);
3895         _tab_drag->term->has_bg_cursor = EINA_FALSE;
3896      }
3897 
3898    /* free _tab_drag->icon to mark we're freeing _tab_drag */
3899    evas_object_del(_tab_drag->icon);
3900    _tab_drag->icon = NULL;
3901 
3902    if (_tab_drag->parent_type != TERM_CONTAINER_TYPE_UNKNOWN)
3903      _tab_drag_rollback();
3904 
3905    _tab_drag_disable_anim_over();
3906    for_each_term_do(_tab_drag->term->wn, &_term_hdrag_on, NULL);
3907 
3908    ecore_timer_del(_tab_drag->timer);
3909    _tab_drag->timer = NULL;
3910 
3911    evas_object_del(_tab_drag->img);
3912    _tab_drag->img = NULL;
3913 
3914    term_unref(_tab_drag->term);
3915    free(_tab_drag);
3916    _tab_drag = NULL;
3917 }
3918 
3919 static void
_tab_drag_reinsert(Term * term,double mid)3920 _tab_drag_reinsert(Term *term, double mid)
3921 {
3922    Term_Container *tc = term->container;
3923    Term_Container *tc_parent = tc->parent;
3924    Tabs *tabs;
3925 
3926    tc = term->container;
3927    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
3928 
3929    if (tc_parent->type != TERM_CONTAINER_TYPE_TABS)
3930      return;
3931 
3932    tabs = (Tabs*) tc_parent;
3933 
3934    edje_object_part_drag_value_set(term->bg_edj, "terminology.tabl", mid, 0.0);
3935    edje_object_part_drag_value_set(term->bg_edj, "terminology.tabr", mid, 0.0);
3936 
3937    _term_on_horizontal_drag(term, NULL, NULL, NULL);
3938    /* In case there is no drag, need to recompute to something valid */
3939    _tabs_recompute_drag(tabs);
3940 }
3941 
3942 static void
_tab_reorg(Term * term,Term * to_term,Evas_Coord mx)3943 _tab_reorg(Term *term, Term *to_term, Evas_Coord mx)
3944 {
3945    Term_Container *tc_orig = term->container;
3946    Term_Container *to_tc = to_term->container;
3947 
3948    assert(tc_orig->type == TERM_CONTAINER_TYPE_SOLO);
3949    assert(to_tc->type == TERM_CONTAINER_TYPE_SOLO);
3950 
3951    if (_tab_drag->split_direction == SPLIT_DIRECTION_TABS)
3952      {
3953         Evas_Coord x = 0, w = 0;
3954         double mid;
3955 
3956         edje_object_part_geometry_get(term->bg_edj, "terminology.tabregion",
3957                                       &x, NULL, &w, NULL);
3958 
3959         mid = (double)(mx - x) / (double)w;
3960 
3961         _solo_attach(to_tc, tc_orig);
3962 
3963         /* reinsert at correct place */
3964         _tab_drag_reparented();
3965         _tab_drag_reinsert(term, mid);
3966         return;
3967      }
3968 
3969    to_tc->split_direction(to_tc, to_tc, tc_orig, _tab_drag->split_direction);
3970    _tab_drag_reparented();
3971 }
3972 
3973 static void
_tab_drag_stop(void)3974 _tab_drag_stop(void)
3975 {
3976    Evas_Coord mx = 0, my = 0;
3977    Win *wn;
3978    Term_Container *tc_wn;
3979    Term *term;
3980    Term *term_at_coords;
3981 
3982    assert(_tab_drag);
3983 
3984    term = _tab_drag->term;
3985    wn = term->wn;
3986    tc_wn = (Term_Container*) wn;
3987 
3988    evas_pointer_canvas_xy_get(_tab_drag->e, &mx, &my);
3989    term_at_coords = tc_wn->find_term_at_coords(tc_wn, mx, my);
3990    if (!term_at_coords)
3991      goto end;
3992 
3993    evas_object_image_source_visible_set(_tab_drag->img, EINA_TRUE);
3994    if (term->has_bg_cursor)
3995      {
3996         elm_object_cursor_unset(term->bg);
3997         term->has_bg_cursor = EINA_FALSE;
3998      }
3999    elm_layout_content_unset(_tab_drag->icon, "terminology.content");
4000    elm_layout_content_set(term->bg, "terminology.content", term->core);
4001    term->unswallowed = EINA_FALSE;
4002    evas_object_show(term->core);
4003    evas_object_show(term->bg);
4004 
4005    if (term_at_coords == term)
4006      {
4007         Evas_Coord x = 0, y = 0, w = 0, h = 0, off_x = 0, off_y = 0;
4008         double mid;
4009 
4010         /* Reinsert in same set of Tabs or same "tab" (could be a split) */
4011         evas_object_geometry_get(term->bg_edj, &off_x, &off_y, NULL, NULL);
4012         edje_object_part_geometry_get(term->bg_edj, "terminology.tabregion",
4013                                       &x, &y, &w, &h);
4014         if (!ELM_RECTS_INTERSECT(x,y,w,h, mx,my,1,1))
4015           goto end;
4016 
4017         mid = (double)(mx - x) / (double)w;
4018         _tab_drag_reparented();
4019         _tab_drag_reinsert(term, mid);
4020      }
4021    else if (_tab_drag->split_direction != SPLIT_DIRECTION_NONE)
4022      {
4023         /* Move to different set of Tabs */
4024         _tab_reorg(term, term_at_coords, mx);
4025      }
4026 
4027 end:
4028    _tab_drag_free();
4029 }
4030 
4031 static void
_term_on_drag_stop(void * data,Evas_Object * o EINA_UNUSED,const char * emission EINA_UNUSED,const char * source EINA_UNUSED)4032 _term_on_drag_stop(void *data,
4033                    Evas_Object *o EINA_UNUSED,
4034                    const char *emission EINA_UNUSED,
4035                    const char *source EINA_UNUSED)
4036 {
4037    Term *term = data;
4038    Term_Container *tc;
4039 
4040    if (_tab_drag && _tab_drag->icon)
4041      {
4042         _tab_drag_stop();
4043         return;
4044      }
4045    _tab_drag_free();
4046 
4047    tc = term->container;
4048    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
4049 
4050    if (tc->parent->type == TERM_CONTAINER_TYPE_TABS)
4051      {
4052         Term_Container *tc_parent = tc->parent;
4053         Tabs *tabs = (Tabs*) tc_parent;
4054 
4055         edje_object_part_drag_value_set(term->bg_edj, "terminology.tabl",
4056                                         tabs->v1_orig, 0.0);
4057         edje_object_part_drag_value_set(term->bg_edj, "terminology.tabr",
4058                                         tabs->v2_orig, 0.0);
4059      }
4060    _focus_validator();
4061 }
4062 
4063 
4064 static void
_tabs_drag_mouse_move(void * data EINA_UNUSED,Evas_Object * obj EINA_UNUSED,const char * emission EINA_UNUSED,const char * source EINA_UNUSED)4065 _tabs_drag_mouse_move(
4066    void *data EINA_UNUSED,
4067    Evas_Object *obj EINA_UNUSED,
4068    const char *emission EINA_UNUSED,
4069    const char *source EINA_UNUSED)
4070 {
4071    Evas_Coord x, y, w, h, off_x, off_y, mx, my;
4072    Win *wn;
4073    Term_Container *tc_wn;
4074    Term *term_at_coords;
4075    Split_Direction split_direction = SPLIT_DIRECTION_NONE;
4076 
4077    if (!_tab_drag || !_tab_drag->icon)
4078      return;
4079 
4080    wn = _tab_drag->term->wn;
4081    tc_wn = (Term_Container*) wn;
4082 
4083    evas_object_geometry_get(_tab_drag->icon, NULL, NULL, &w, &h);
4084    evas_pointer_canvas_xy_get(_tab_drag->e, &mx, &my);
4085    x = (mx - (w/2));
4086    y = (my - (h/2));
4087    evas_object_move(_tab_drag->icon, x, y);
4088 
4089    term_at_coords = tc_wn->find_term_at_coords(tc_wn, mx, my);
4090    if (!term_at_coords)
4091      return;
4092    evas_object_geometry_get(term_at_coords->bg_edj, &off_x, &off_y, NULL, NULL);
4093 
4094    edje_object_part_geometry_get(term_at_coords->bg_edj, "terminology.tabregion",
4095                                  &x, &y, &w, &h);
4096    if (ELM_RECTS_INTERSECT(x+off_x, y+off_y, w, h, mx, my, 1, 1))
4097      {
4098         split_direction = SPLIT_DIRECTION_TABS;
4099         goto found;
4100      }
4101 
4102    edje_object_part_geometry_get(term_at_coords->bg_edj, "drag_left_outline",
4103                                  &x, &y, &w, &h);
4104    if (ELM_RECTS_INTERSECT(x+off_x, y+off_y, w, h, mx, my, 1, 1))
4105      {
4106         split_direction = SPLIT_DIRECTION_LEFT;
4107         goto found;
4108      }
4109 
4110    edje_object_part_geometry_get(term_at_coords->bg_edj, "drag_right_outline",
4111                                  &x, &y, &w, &h);
4112    if (ELM_RECTS_INTERSECT(x+off_x, y+off_y, w, h, mx, my, 1, 1))
4113      {
4114         split_direction = SPLIT_DIRECTION_RIGHT;
4115         goto found;
4116      }
4117 
4118    edje_object_part_geometry_get(term_at_coords->bg_edj, "drag_top_outline",
4119                                  &x, &y, &w, &h);
4120    if (ELM_RECTS_INTERSECT(x+off_x, y+off_y, w, h, mx, my, 1, 1))
4121      {
4122         split_direction = SPLIT_DIRECTION_TOP;
4123         goto found;
4124      }
4125 
4126    edje_object_part_geometry_get(term_at_coords->bg_edj, "drag_bottom_outline",
4127                                  &x, &y, &w, &h);
4128    if (ELM_RECTS_INTERSECT(x+off_x, y+off_y, w, h, mx, my, 1, 1))
4129      {
4130         split_direction = SPLIT_DIRECTION_BOTTOM;
4131         goto found;
4132      }
4133 
4134 found:
4135    if ((_tab_drag->term_over != NULL) &&
4136        ((_tab_drag->term_over != term_at_coords) ||
4137         (_tab_drag->split_direction != split_direction)))
4138      {
4139         _tab_drag_disable_anim_over();
4140      }
4141    if ((split_direction != SPLIT_DIRECTION_NONE) &&
4142        ((_tab_drag->term_over != term_at_coords) ||
4143         (_tab_drag->split_direction != split_direction)))
4144      {
4145         switch (split_direction)
4146           {
4147            case SPLIT_DIRECTION_LEFT:
4148               elm_layout_signal_emit(term_at_coords->bg,
4149                                      "drag_left,on", "terminology");
4150               break;
4151            case SPLIT_DIRECTION_RIGHT:
4152               elm_layout_signal_emit(term_at_coords->bg,
4153                                      "drag_right,on", "terminology");
4154               break;
4155            case SPLIT_DIRECTION_TOP:
4156               elm_layout_signal_emit(term_at_coords->bg,
4157                                      "drag_top,on", "terminology");
4158               break;
4159            case SPLIT_DIRECTION_BOTTOM:
4160               elm_layout_signal_emit(term_at_coords->bg,
4161                                      "drag_bottom,on", "terminology");
4162               break;
4163            case SPLIT_DIRECTION_TABS:
4164               elm_layout_signal_emit(term_at_coords->bg,
4165                                      "drag_over_tabs,on", "terminology");
4166            default:
4167               break;
4168           }
4169      }
4170    _tab_drag->term_over = term_at_coords;
4171    _tab_drag->split_direction = split_direction;
4172 }
4173 
4174 static Eina_Bool
_tab_drag_start(void * data EINA_UNUSED)4175 _tab_drag_start(void *data EINA_UNUSED)
4176 {
4177    /* Start icons animation before actually drag-starts */
4178    Evas_Coord mx, my, w, h, ch_w, ch_h, core_w, core_h;
4179    Term *term = _tab_drag->term;
4180    Evas_Object *o = elm_layout_add(term->bg);
4181    Evas_Object *img;
4182    Term_Container *tc = term->container;
4183    float ratio;
4184 
4185    if (!term->container)
4186      {
4187         _tab_drag_free();
4188         return ECORE_CALLBACK_CANCEL;
4189      }
4190 
4191    for_each_term_do(_tab_drag->term->wn, &_term_hdrag_off, NULL);
4192 
4193    _tab_drag->icon = o;
4194    theme_apply(o, term->config, "terminology/tab_drag_thumb",
4195                NULL, NULL, EINA_TRUE);
4196    elm_layout_text_set(o, "terminology.title",
4197                        term->container->title);
4198    elm_layout_content_unset(term->bg, "terminology.content");
4199    term->unswallowed = EINA_TRUE;
4200    img = evas_object_image_filled_add(evas_object_evas_get(term->core));
4201    evas_object_lower(term->core);
4202    evas_object_move(term->core, -9999, -9999);
4203    evas_object_show(term->core);
4204    evas_object_clip_unset(term->core);
4205    evas_object_image_source_set(img, term->core);
4206    evas_object_geometry_get(term->core, NULL, NULL, &core_w, &core_h);
4207    evas_object_resize(img, core_w, core_h);
4208    _tab_drag->img = img;
4209    elm_layout_content_set(o, "terminology.content", img);
4210    evas_object_size_hint_min_get(term->core, &ch_w, &ch_h);
4211 
4212    evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
4213    evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
4214 
4215    w = ch_w * 10;
4216    h = ch_h * 5;
4217    ratio = (float) core_w / (float) core_h;
4218    if (h * ratio > w)
4219      h = w / ratio;
4220    else
4221      w = h * ratio;
4222    evas_object_resize(o, w, h);
4223    evas_pointer_canvas_xy_get(_tab_drag->e, &mx, &my);
4224    evas_object_move(_tab_drag->icon, mx - w/2, my - h/2);
4225    evas_object_raise(o);
4226    elm_object_cursor_set(term->bg, ELM_CURSOR_HAND2);
4227    term->has_bg_cursor = EINA_TRUE;
4228    evas_object_show(o);
4229 
4230    _tab_drag_save_state(tc);
4231    DBG("detaching %p from %p", tc, tc->parent);
4232    tc->parent->detach(tc->parent, tc);
4233    assert(term->tab_item == NULL);
4234    _focus_validator();
4235    assert(!tc->is_focused);
4236 
4237    _tab_drag->timer = NULL;
4238    return ECORE_CALLBACK_CANCEL;
4239 }
4240 
4241 static void
_tabs_mouse_down(void * data,Evas_Object * obj EINA_UNUSED,const char * emission EINA_UNUSED,const char * source EINA_UNUSED)4242 _tabs_mouse_down(
4243    void *data,
4244    Evas_Object *obj EINA_UNUSED,
4245    const char *emission EINA_UNUSED,
4246    const char *source EINA_UNUSED)
4247 {
4248    /* Launch a timer to start drag animation */
4249    Term *term = data;
4250    Evas_Coord mx = 0, my = 0;
4251 
4252    assert(term->container != NULL);
4253    assert(_tab_drag == NULL);
4254 
4255    _tab_drag = calloc(1, sizeof(*_tab_drag));
4256    if (!_tab_drag)
4257      return;
4258 
4259    _tab_drag->e = evas_object_evas_get(term->bg);
4260    evas_pointer_canvas_xy_get(_tab_drag->e, &mx, &my);
4261 
4262    term_ref(term);
4263 
4264    _tab_drag->mdx = mx;
4265    _tab_drag->mdy = my;
4266    _tab_drag->term = term;
4267    _tab_drag->timer = ecore_timer_add(DRAG_TIMEOUT,
4268                                       _tab_drag_start, NULL);
4269 }
4270 
4271 
4272 Eina_Bool
term_tab_go(Term * term,int tnum)4273 term_tab_go(Term *term, int tnum)
4274 {
4275    Term_Container *tc = term->container,
4276                   *child = tc;
4277 
4278    while (tc)
4279      {
4280         Tabs *tabs;
4281         Tab_Item *tab_item;
4282 
4283         if (tc->type != TERM_CONTAINER_TYPE_TABS)
4284           {
4285              child = tc;
4286              tc = tc->parent;
4287              continue;
4288           }
4289         tabs = (Tabs*) tc;
4290         tab_item = eina_list_nth(tabs->tabs, tnum);
4291         if (!tab_item)
4292           {
4293              child = tc;
4294              tc = tc->parent;
4295              continue;
4296           }
4297         if (tab_item != tabs->current)
4298            tab_item->tc->focus(tab_item->tc, child);
4299         return EINA_TRUE;
4300      }
4301    return EINA_FALSE;
4302 }
4303 
4304 static void
4305 _tabs_selector_cb_selected(void *data,
4306                            Evas_Object *_obj EINA_UNUSED,
4307                            void *info);
4308 static void
4309 _tabs_selector_cb_exit(void *data,
4310                        Evas_Object *_obj EINA_UNUSED,
4311                        void *_info EINA_UNUSED);
4312 
4313 static void
4314 _tabs_selector_cb_ending(void *data,
4315                          Evas_Object *_obj EINA_UNUSED,
4316                          void *_info EINA_UNUSED);
4317 
4318 static void
_tabs_restore(Tabs * tabs)4319 _tabs_restore(Tabs *tabs)
4320 {
4321    Eina_List *l;
4322    Tab_Item *tab_item;
4323    Term_Container *tc = (Term_Container*)tabs;
4324    Term *term;
4325    Solo *solo;
4326    Win *wn = tc->wn;
4327    Evas_Object *selector = tabs->selector;
4328    Evas_Object *selector_bg = tabs->selector_bg;
4329 
4330    if (!tabs->selector)
4331      return;
4332 
4333    EINA_LIST_FOREACH(wn->terms, l, term)
4334      {
4335         if (term->unswallowed)
4336           {
4337              evas_object_image_source_visible_set(term->sel, EINA_TRUE);
4338              elm_layout_content_set(term->bg, "terminology.content", term->core);
4339              term->unswallowed = EINA_FALSE;
4340              evas_object_show(term->core);
4341           }
4342      }
4343 
4344    EINA_LIST_FOREACH(tabs->tabs, l, tab_item)
4345      {
4346         tab_item->selector_entry = NULL;
4347         if (tab_item->tc->is_focused)
4348           tab_item->tc->unfocus(tab_item->tc, tc);
4349      }
4350 
4351    evas_object_smart_callback_del_full(selector, "selected",
4352                                   _tabs_selector_cb_selected, tabs);
4353    evas_object_smart_callback_del_full(selector, "exit",
4354                                   _tabs_selector_cb_exit, tabs);
4355    evas_object_smart_callback_del_full(selector, "ending",
4356                                   _tabs_selector_cb_ending, tabs);
4357 
4358 
4359    tabs->selector = NULL;
4360    tabs->selector_bg = NULL;
4361 
4362    /* XXX: reswallow in parent */
4363    tc->parent->swallow(tc->parent, tc, tc);
4364    solo = (Solo*)tabs->current->tc;
4365    term = solo->term;
4366    _tabbar_clear(term);
4367 
4368    /* Restore -> recreate the whole tabbar */
4369    _tabs_recreate(tabs);
4370    tabs->current->tc->unfocus(tabs->current->tc, tabs->current->tc);
4371    tabs->current->tc->focus(tabs->current->tc, tabs->current->tc);
4372 
4373    elm_object_focus_set(selector, EINA_FALSE);
4374 
4375    evas_object_del(selector);
4376    evas_object_del(selector_bg);
4377 }
4378 
4379 static void
_tabs_selector_cb_ending(void * data,Evas_Object * _obj EINA_UNUSED,void * _info EINA_UNUSED)4380 _tabs_selector_cb_ending(void *data,
4381                          Evas_Object *_obj EINA_UNUSED,
4382                          void *_info EINA_UNUSED)
4383 {
4384    Tabs *tabs = data;
4385 
4386    edje_object_signal_emit(tabs->selector_bg, "end", "terminology");
4387 }
4388 
4389 static void
_tabs_selector_cb_selected(void * data,Evas_Object * _obj EINA_UNUSED,void * info)4390 _tabs_selector_cb_selected(void *data,
4391                            Evas_Object *_obj EINA_UNUSED,
4392                            void *info)
4393 {
4394    Tabs *tabs = data;
4395    Eina_List *l;
4396    Tab_Item *tab_item;
4397 
4398    EINA_LIST_FOREACH(tabs->tabs, l, tab_item)
4399      {
4400         if (tab_item->tc->selector_img == info)
4401           {
4402              tabs->current = tab_item;
4403              _tabs_restore(tabs);
4404              return;
4405           }
4406      }
4407 
4408    ERR("Can't find selected tab item");
4409 }
4410 
4411 static void
_tabs_selector_cb_exit(void * data,Evas_Object * _obj EINA_UNUSED,void * _info EINA_UNUSED)4412 _tabs_selector_cb_exit(void *data,
4413                        Evas_Object *_obj EINA_UNUSED,
4414                        void *_info EINA_UNUSED)
4415 {
4416    Tabs *tabs = data;
4417 
4418    _tabs_restore(tabs);
4419 }
4420 
4421 static void
_cb_tab_selector_show(Tabs * tabs,Tab_Item * to_item)4422 _cb_tab_selector_show(Tabs *tabs, Tab_Item *to_item)
4423 {
4424    Term_Container *tc = (Term_Container *)tabs;
4425    Eina_List *l;
4426    int count;
4427    double z;
4428    Win *wn = tc->wn;
4429    Tab_Item *tab_item;
4430    Evas_Object *o;
4431    Evas_Coord x, y, w, h;
4432    Edje_Message_Int msg;
4433 
4434    if (tabs->selector_bg)
4435      return;
4436 
4437    o = tc->get_evas_object(tc);
4438    evas_object_geometry_get(o, &x, &y, &w, &h);
4439 
4440    tabs->selector_bg = edje_object_add(evas_object_evas_get(tc->wn->win));
4441    theme_apply(tabs->selector_bg, wn->config, "terminology/sel/base",
4442                NULL, NULL, EINA_FALSE);
4443 
4444    evas_object_geometry_set(tabs->selector_bg, x, y, w, h);
4445    evas_object_hide(o);
4446 
4447    if (wn->config->translucent)
4448      msg.val = wn->config->opacity;
4449    else
4450      msg.val = 100;
4451    edje_object_message_send(tabs->selector_bg, EDJE_MESSAGE_INT, 1, &msg);
4452    background_set_shine(wn->config, tabs->selector_bg);
4453    edje_object_signal_emit(tabs->selector_bg, "begin", "terminology");
4454 
4455    tabs->selector = sel_add(wn->win);
4456    EINA_LIST_FOREACH(tabs->tabs, l, tab_item)
4457      {
4458         Evas_Object *img;
4459         Eina_Bool is_selected, missed_bell;
4460         Solo *solo;
4461         Term *term;
4462 
4463         solo = (Solo*)tab_item->tc;
4464         term = solo->term;
4465         _tabbar_clear(term);
4466 
4467         elm_layout_content_unset(term->bg, "terminology.content");
4468         term->unswallowed = EINA_TRUE;
4469         img = evas_object_image_filled_add(evas_object_evas_get(wn->win));
4470         o = term->core;
4471         evas_object_lower(o);
4472         evas_object_move(o, -9999, -9999);
4473         evas_object_show(o);
4474         evas_object_clip_unset(o);
4475         evas_object_image_source_set(img, o);
4476         evas_object_geometry_get(o, NULL, NULL, &w, &h);
4477         evas_object_resize(img, w, h);
4478         evas_object_data_set(img, "tc", tab_item->tc);
4479         tab_item->tc->selector_img = img;
4480 
4481         is_selected = (tab_item == tabs->current);
4482         missed_bell = term->missed_bell;
4483         tab_item->selector_entry = NULL;
4484         tab_item->selector_entry = sel_entry_add(tabs->selector, img,
4485                                                  is_selected,
4486                                                  missed_bell, wn->config);
4487      }
4488    edje_object_part_swallow(tabs->selector_bg, "terminology.content",
4489                             tabs->selector);
4490 
4491    evas_object_show(tabs->selector);
4492 
4493    /* XXX: refresh */
4494    tc->parent->swallow(tc->parent, tc, tc);
4495 
4496    evas_object_show(tabs->selector_bg);
4497    evas_object_smart_callback_add(tabs->selector, "selected",
4498                                   _tabs_selector_cb_selected, tabs);
4499    evas_object_smart_callback_add(tabs->selector, "exit",
4500                                   _tabs_selector_cb_exit, tabs);
4501    evas_object_smart_callback_add(tabs->selector, "ending",
4502                                   _tabs_selector_cb_ending, tabs);
4503    z = 1.0;
4504    sel_go(tabs->selector);
4505    count = eina_list_count(tabs->tabs);
4506    if (count >= 1)
4507      z = 1.0 / (sqrt(count) * 0.8);
4508    if (z > 1.0) z = 1.0;
4509    sel_orig_zoom_set(tabs->selector, z);
4510    sel_zoom(tabs->selector, z);
4511    if (to_item)
4512      {
4513         sel_entry_selected_set(tabs->selector, to_item->tc->selector_img,
4514                                EINA_TRUE);
4515         sel_exit(tabs->selector);
4516      }
4517 }
4518 
4519 static void
_cb_select(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)4520 _cb_select(void *data,
4521            Evas_Object *_obj EINA_UNUSED,
4522            void *_event EINA_UNUSED)
4523 {
4524    Term *term = data;
4525    Term_Container *tc = term->container;
4526 
4527    while (tc)
4528      {
4529         Tabs *tabs;
4530 
4531         if (tc->type != TERM_CONTAINER_TYPE_TABS)
4532           {
4533              tc = tc->parent;
4534              continue;
4535           }
4536         tabs = (Tabs*) tc;
4537         if (eina_list_count(tabs->tabs) < 2)
4538           {
4539              tc = tc->parent;
4540              continue;
4541           }
4542 
4543         _cb_tab_selector_show(tabs, NULL);
4544         return;
4545      }
4546 }
4547 
4548 
4549 
4550 static Evas_Object *
_tabs_get_evas_object(const Term_Container * container)4551 _tabs_get_evas_object(const Term_Container *container)
4552 {
4553    Tabs *tabs;
4554    Term_Container *tc;
4555 
4556    assert (container->type == TERM_CONTAINER_TYPE_TABS);
4557    tabs = (Tabs*)container;
4558 
4559    if (tabs->selector_bg)
4560      return tabs->selector_bg;
4561 
4562    assert(tabs->current != NULL);
4563    tc = tabs->current->tc;
4564    return tc->get_evas_object(tc);
4565 }
4566 
4567 static Term *
_tabs_focused_term_get(const Term_Container * tc)4568 _tabs_focused_term_get(const Term_Container *tc)
4569 {
4570    Tabs *tabs;
4571 
4572    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
4573    tabs = (Tabs*)tc;
4574 
4575    return tc->is_focused ?
4576       tabs->current->tc->focused_term_get(tabs->current->tc)
4577       : NULL;
4578 }
4579 
4580 static Term *
_tabs_find_term_at_coords(const Term_Container * container,Evas_Coord mx,Evas_Coord my)4581 _tabs_find_term_at_coords(const Term_Container *container,
4582                           Evas_Coord mx,
4583                           Evas_Coord my)
4584 {
4585    Tabs *tabs;
4586    Term_Container *tc;
4587 
4588    assert (container->type == TERM_CONTAINER_TYPE_TABS);
4589    tabs = (Tabs*)container;
4590 
4591    tc = tabs->current->tc;
4592 
4593    return tc->find_term_at_coords(tc, mx, my);
4594 }
4595 
4596 static void
_tabs_size_eval(Term_Container * container,Sizeinfo * info)4597 _tabs_size_eval(Term_Container *container, Sizeinfo *info)
4598 {
4599    Tabs *tabs;
4600    Term_Container *tc;
4601    Config *config;
4602 
4603    assert (container->type == TERM_CONTAINER_TYPE_TABS);
4604    tabs = (Tabs*)container;
4605 
4606    tc = tabs->current->tc;
4607    config = tc->wn->config;
4608    tc->size_eval(tc, info);
4609    /* Current sizing code does not take the tab area correctly into account */
4610    if (config->show_tabs)
4611      {
4612         info->step_x = 1;
4613         info->step_y = 1;
4614      }
4615 }
4616 
4617 static Eina_List *
_tab_item_find(const Tabs * tabs,const Term_Container * child,int * pos)4618 _tab_item_find(const Tabs *tabs, const Term_Container *child,
4619                int *pos)
4620 {
4621    Eina_List *l;
4622    Tab_Item *tab_item;
4623    int i = 0;
4624 
4625    EINA_LIST_FOREACH(tabs->tabs, l, tab_item)
4626      {
4627         if (tab_item->tc == child)
4628           {
4629              if (pos)
4630                *pos = i;
4631              return l;
4632           }
4633         i++;
4634      }
4635    return NULL;
4636 }
4637 
4638 static void
_tabs_close(Term_Container * tc,Term_Container * child)4639 _tabs_close(Term_Container *tc, Term_Container *child)
4640 {
4641    int count;
4642    Tabs *tabs;
4643    Eina_List *l;
4644    Tab_Item *item, *next_item;
4645    Eina_List *next;
4646    Term_Container *next_child, *tc_parent;
4647    Term *term;
4648    Solo *solo;
4649    int pos = 0;
4650 
4651    /* TODO: figure out whether to move position if tab_drag */
4652 
4653    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
4654    tabs = (Tabs*)tc;
4655 
4656    tc_parent = tc->parent;
4657 
4658    l = _tab_item_find(tabs, child, &pos);
4659    item = l->data;
4660 
4661    next = eina_list_next(l);
4662    if (!next)
4663      next = tabs->tabs;
4664 
4665    tabs->tabs = eina_list_remove_list(tabs->tabs, l);
4666 
4667    next_item = next->data;
4668    next_child = next_item->tc;
4669    assert (next_child->type == TERM_CONTAINER_TYPE_SOLO);
4670 
4671    assert (child->type == TERM_CONTAINER_TYPE_SOLO);
4672    solo = (Solo*)child;
4673    term = solo->term;
4674    child->unfocus(child, tc);
4675 
4676    elm_layout_signal_emit(term->bg, "tabcount,off", "terminology");
4677    elm_layout_signal_emit(term->bg, "tab_btn,off", "terminology");
4678 
4679    count = eina_list_count(tabs->tabs);
4680    if (count == 1)
4681      {
4682         Term *next_term;
4683         Solo *next_solo;
4684         Config *config;
4685 
4686         assert (next_child->type == TERM_CONTAINER_TYPE_SOLO);
4687         next_solo = (Solo*)next_child;
4688         next_term = next_solo->term;
4689         assert(next_term != term);
4690         assert(tc != next_child);
4691         config = next_term->config;
4692 
4693         _tabbar_clear(term);
4694 
4695         evas_object_del(next_term->tab_spacer);
4696         next_term->tab_spacer = NULL;
4697         if (next_term->tab_inactive)
4698           {
4699              evas_object_hide(next_term->tab_inactive);
4700              edje_object_signal_callback_del(next_term->tab_inactive,
4701                                              "tab,activate", "terminology",
4702                                              _cb_tab_activate);
4703           }
4704         elm_layout_signal_emit(next_term->bg, "tabcount,off", "terminology");
4705         elm_layout_signal_emit(next_term->bg, "tab_btn,off", "terminology");
4706 
4707         if (tabs->selector)
4708           _tabs_restore(tabs);
4709 
4710         if (config->show_tabs)
4711           _solo_tab_show(next_child);
4712 
4713         eina_stringshare_del(tc->title);
4714 
4715         tc_parent->swallow(tc_parent, tc, next_child);
4716         if (tc->is_focused)
4717           next_child->focus(next_child, tc);
4718 
4719         if ((_tab_drag) && (_tab_drag->parent_type == TERM_CONTAINER_TYPE_TABS)
4720             && (_tab_drag->tabs_child == tc))
4721           {
4722              _tab_drag->tabs_child = next_child;
4723              _solo_tab_show(next_child);
4724           }
4725 
4726         _tab_item_free(item);
4727         _tab_item_free(next_item);
4728         EINA_LIST_FREE(tabs->tabs, item) {}
4729         free(tc);
4730 
4731         return;
4732      }
4733 
4734    if ((_tab_drag) && (_tab_drag->parent_type == TERM_CONTAINER_TYPE_TABS)
4735        && (_tab_drag->tabs_child == tc))
4736      {
4737         if (pos < _tab_drag->previous_position)
4738           _tab_drag->previous_position--;
4739      }
4740 
4741      if (item->tc->selector_img)
4742        {
4743           Evas_Object *o;
4744           o = item->tc->selector_img;
4745           item->tc->selector_img = NULL;
4746           evas_object_del(o);
4747        }
4748 
4749      count--;
4750 
4751      if (item == tabs->current)
4752        {
4753           tc->swallow(tc, child, next_child);
4754           if (tc->is_focused)
4755             next_child->focus(next_child, tc);
4756           _tab_item_free(item);
4757           assert(tabs->current != item);
4758           return;
4759        }
4760      _tab_item_free(item);
4761 
4762      _tabs_recompute_drag(tabs);
4763 }
4764 
4765 static void
_tabs_update(Term_Container * tc)4766 _tabs_update(Term_Container *tc)
4767 {
4768    Tabs *tabs;
4769    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
4770    tabs = (Tabs*)tc;
4771 
4772    /* Update -> recreate the whole tabbar */
4773    _tabs_recreate(tabs);
4774 }
4775 
4776 static Term *
_tabs_term_next(const Term_Container * tc,const Term_Container * child)4777 _tabs_term_next(const Term_Container *tc, const Term_Container *child)
4778 {
4779    Tabs *tabs;
4780    Tab_Item *tab_item;
4781    Eina_List *l;
4782 
4783    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
4784    tabs = (Tabs*)tc;
4785    l = _tab_item_find(tabs, child, NULL);
4786    l = eina_list_next(l);
4787    if (l)
4788      {
4789         tab_item = l->data;
4790         tc = tab_item->tc;
4791         return tc->term_first(tc);
4792      }
4793    else
4794      {
4795         return tc->parent->term_next(tc->parent, tc);
4796      }
4797 }
4798 
4799 static Term *
_tabs_term_prev(const Term_Container * tc,const Term_Container * child)4800 _tabs_term_prev(const Term_Container *tc, const Term_Container *child)
4801 {
4802    Tabs *tabs;
4803    Tab_Item *tab_item;
4804    Eina_List *l;
4805 
4806    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
4807    tabs = (Tabs*)tc;
4808    l = _tab_item_find(tabs, child, NULL);
4809    l = eina_list_prev(l);
4810    if (l)
4811      {
4812         tab_item = l->data;
4813         tc = tab_item->tc;
4814         return tc->term_last(tc);
4815      }
4816    else
4817      {
4818         return tc->parent->term_prev(tc->parent, tc);
4819      }
4820 }
4821 
4822 static Term *
_tabs_term_up(const Term_Container * tc,const Term_Container * _child EINA_UNUSED)4823 _tabs_term_up(const Term_Container *tc,
4824               const Term_Container *_child EINA_UNUSED)
4825 {
4826    return tc->parent->term_up(tc->parent, tc);
4827 }
4828 
4829 static Term *
_tabs_term_down(const Term_Container * tc,const Term_Container * _child EINA_UNUSED)4830 _tabs_term_down(const Term_Container *tc,
4831                 const Term_Container *_child EINA_UNUSED)
4832 {
4833    return tc->parent->term_down(tc->parent, tc);
4834 }
4835 
4836 static Term *
_tabs_term_left(const Term_Container * tc,const Term_Container * _child EINA_UNUSED)4837 _tabs_term_left(const Term_Container *tc,
4838                 const Term_Container *_child EINA_UNUSED)
4839 {
4840    return tc->parent->term_left(tc->parent, tc);
4841 }
4842 
4843 static Term *
_tabs_term_right(const Term_Container * tc,const Term_Container * _child EINA_UNUSED)4844 _tabs_term_right(const Term_Container *tc,
4845                  const Term_Container *_child EINA_UNUSED)
4846 {
4847    return tc->parent->term_right(tc->parent, tc);
4848 }
4849 
4850 static Term *
_tabs_term_first(const Term_Container * tc)4851 _tabs_term_first(const Term_Container *tc)
4852 {
4853    Tabs *tabs;
4854    Tab_Item *tab_item;
4855 
4856    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
4857    tabs = (Tabs*)tc;
4858 
4859    tab_item = tabs->tabs->data;
4860    tc = tab_item->tc;
4861 
4862    return tc->term_first(tc);
4863 }
4864 
4865 static Term *
_tabs_term_last(const Term_Container * tc)4866 _tabs_term_last(const Term_Container *tc)
4867 {
4868    Tabs *tabs;
4869    Tab_Item *tab_item;
4870    Eina_List *l;
4871 
4872    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
4873    tabs = (Tabs*)tc;
4874 
4875    l = eina_list_last(tabs->tabs);
4876    tab_item = l->data;
4877    tc = tab_item->tc;
4878 
4879    return tc->term_last(tc);
4880 }
4881 
4882 static void
_tabcount_refresh(Tabs * tabs)4883 _tabcount_refresh(Tabs *tabs)
4884 {
4885    Eina_List *l;
4886    Tab_Item *tab_item;
4887    Term *main_term = _tab_item_to_term(tabs->current);
4888    Evas *canvas = evas_object_evas_get(main_term->bg);
4889    char buf[32], bufmissed[32];
4890    int n = eina_list_count(tabs->tabs);
4891    Evas_Coord w = 0, h = 0;
4892    unsigned int missed = 0;
4893    int i;
4894 
4895    if (n <= 0)
4896      {
4897         ERR("no tab");
4898         return;
4899      }
4900 
4901    buf[0] = '\0';
4902    i = 0;
4903    EINA_LIST_FOREACH(tabs->tabs, l, tab_item)
4904      {
4905         Solo *solo;
4906         Term *term;
4907 
4908         i++;
4909         assert (tab_item->tc->type == TERM_CONTAINER_TYPE_SOLO);
4910         solo = (Solo*) tab_item->tc;
4911         term = solo->term;
4912 
4913         if (term->tab_inactive)
4914           {
4915              evas_object_hide(term->tab_inactive);
4916              edje_object_signal_callback_del(term->tab_inactive,
4917                                              "tab,activate", "terminology",
4918                                              _cb_tab_activate);
4919           }
4920 
4921         if (tabs->current == tab_item)
4922           {
4923              snprintf(buf, sizeof(buf), "%i/%i", i, n);
4924           }
4925         else if (term->missed_bell)
4926           missed++;
4927      }
4928    if (missed > 0)
4929      snprintf(bufmissed, sizeof(bufmissed), "%i", missed);
4930    else
4931      bufmissed[0] = '\0';
4932 
4933    if (!main_term->tab_spacer)
4934      {
4935         main_term->tab_spacer = evas_object_rectangle_add(canvas);
4936         evas_object_color_set(main_term->tab_spacer, 0, 0, 0, 0);
4937      }
4938    elm_coords_finger_size_adjust(1, &w, 1, &h);
4939    evas_object_size_hint_min_set(main_term->tab_spacer, w, h);
4940 
4941    elm_layout_content_set(main_term->bg, "terminology.tabcount.control",
4942                           main_term->tab_spacer);
4943    elm_layout_text_set(main_term->bg, "terminology.tabcount.label", buf);
4944    elm_layout_text_set(main_term->bg, "terminology.tabmissed.label", bufmissed);
4945    elm_layout_signal_emit(main_term->bg, "tabcount,on", "terminology");
4946    _tabbar_clear(main_term);
4947    if (missed > 0)
4948      elm_layout_signal_emit(main_term->bg, "tabmissed,on", "terminology");
4949    else
4950      elm_layout_signal_emit(main_term->bg, "tabmissed,off", "terminology");
4951 }
4952 
4953 static void
_tabs_swallow(Term_Container * tc,Term_Container * orig,Term_Container * new_child)4954 _tabs_swallow(Term_Container *tc, Term_Container *orig,
4955               Term_Container *new_child)
4956 {
4957    Tabs *tabs;
4958    Tab_Item *tab_item;
4959    Eina_List *l;
4960    Evas_Object *o;
4961    Evas_Coord x, y, w, h;
4962    Term *term_orig, *term_new;
4963    Solo *solo_orig, *solo_new;
4964    Term_Container *tc_parent = tc->parent;
4965 
4966    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
4967    tabs = (Tabs*) tc;
4968 
4969    l = _tab_item_find(tabs, new_child, NULL);
4970    tab_item = l->data;
4971 
4972    if (tabs->selector)
4973      {
4974         Evas_Object *img = tab_item->tc->selector_img;
4975         evas_object_image_source_set(img,
4976                                      new_child->get_evas_object(new_child));
4977         evas_object_data_set(img, "tc", new_child);
4978         if (tab_item->selector_entry)
4979           sel_entry_update(tab_item->selector_entry);
4980         return;
4981      }
4982    if (orig == new_child)
4983      {
4984         assert(tabs->current == tab_item);
4985         return;
4986      }
4987 
4988    /* Occurs when closing current tab */
4989    if (tc->is_focused)
4990      tabs->current->tc->unfocus(tabs->current->tc, tc);
4991 
4992    assert (orig->type == TERM_CONTAINER_TYPE_SOLO);
4993    solo_orig = (Solo*)orig;
4994    term_orig = solo_orig->term;
4995    assert (new_child->type == TERM_CONTAINER_TYPE_SOLO);
4996    solo_new = (Solo*)new_child;
4997    term_new = solo_new->term;
4998 
4999    tabs->current = tab_item;
5000    /* Remove tab effects from the previous focused tab */
5001    elm_layout_signal_emit(term_orig->bg, "tabcount,off", "terminology");
5002 
5003    if (term_new->config->show_tabs)
5004      {
5005         _tabs_get_or_create_boxes(term_new, term_orig);
5006         assert(term_new == _tab_item_to_term(tabs->current));
5007         _tabbar_fill(tabs);
5008      }
5009    else
5010      {
5011         _tabcount_refresh(tabs);
5012      }
5013 
5014     o = orig->get_evas_object(orig);
5015     evas_object_geometry_get(o, &x, &y, &w, &h);
5016     evas_object_hide(o);
5017     o = new_child->get_evas_object(new_child);
5018     evas_object_geometry_set(o, x, y, w, h);
5019     evas_object_show(o);
5020     /* XXX: need to refresh */
5021     tc_parent->swallow(tc_parent, tc, tc);
5022 }
5023 
5024 
5025 static void
_tab_new_cb(void * data,Evas_Object * _obj EINA_UNUSED,void * _event_info EINA_UNUSED)5026 _tab_new_cb(void *data,
5027             Evas_Object *_obj EINA_UNUSED,
5028             void *_event_info EINA_UNUSED)
5029 {
5030    Tabs *tabs = data;
5031    Term_Container *tc = (Term_Container*) tabs,
5032                   *tc_new;
5033    Term *tm_new;
5034    Win *wn = tc->wn;
5035    char *wdir = NULL;
5036    char buf[PATH_MAX];
5037 
5038    // copy the current path to wdir if we should change the directory,
5039    // passing wdir NULL otherwise:
5040    if (wn->config->changedir_to_current)
5041      {
5042         Term *tm;
5043         Term_Container *tc_old = tabs->current->tc;
5044         tm = tc_old->term_first(tc_old);
5045 
5046         if (tm && termio_cwd_get(tm->termio, buf, sizeof(buf)))
5047           wdir = buf;
5048      }
5049 
5050    tm_new = term_new(wn, wn->config,
5051                      NULL, wn->config->login_shell, wdir,
5052                      80, 24, EINA_FALSE, NULL);
5053    tc_new = _solo_new(tm_new, wn);
5054    evas_object_data_set(tm_new->termio, "sizedone", tm_new->termio);
5055 
5056    _tabs_attach(tc, tc_new);
5057 }
5058 
5059 static void
_cb_new(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)5060 _cb_new(void *data,
5061         Evas_Object *_obj EINA_UNUSED,
5062         void *_event EINA_UNUSED)
5063 {
5064    Term *term = data;
5065    Term_Container *tc = term->container;
5066 
5067    assert (tc->type == TERM_CONTAINER_TYPE_SOLO);
5068 
5069    _solo_tabs_new(tc);
5070    _focus_validator();
5071 }
5072 
5073 static void
_cb_close(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)5074 _cb_close(void *data,
5075           Evas_Object *_obj EINA_UNUSED,
5076           void *_event EINA_UNUSED)
5077 {
5078    Term *term = data;
5079    Term_Container *tc = term->container;
5080 
5081    term_close(tc->wn->win, term->termio, EINA_FALSE);
5082 }
5083 
5084 void
main_new(Evas_Object * term)5085 main_new(Evas_Object *term)
5086 {
5087    Term *tm;
5088 
5089    tm = evas_object_data_get(term, "term");
5090    if (!tm) return;
5091 
5092    _cb_new(tm, term, NULL);
5093 }
5094 
5095 static void
_tabs_focus(Term_Container * tc,Term_Container * relative)5096 _tabs_focus(Term_Container *tc, Term_Container *relative)
5097 {
5098    Tabs *tabs;
5099 
5100    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
5101    tabs = (Tabs*) tc;
5102 
5103    if (!tc->parent)
5104      return;
5105 
5106    DBG("tc:%p tc->is_focused:%d from_parent:%d",
5107        tc, tc->is_focused, tc->parent == relative);
5108    if (tc->parent == relative)
5109      {
5110         if (!tc->is_focused)
5111           {
5112              tc->is_focused = EINA_TRUE;
5113              tabs->current->tc->focus(tabs->current->tc, tc);
5114           }
5115      }
5116    else
5117      {
5118         Eina_List *l;
5119         Tab_Item *tab_item;
5120 
5121         l = _tab_item_find(tabs, relative, NULL);
5122         if (!l)
5123           return;
5124 
5125         tc->is_focused = EINA_TRUE;
5126 
5127         tab_item = l->data;
5128         if (tab_item != tabs->current)
5129           {
5130              Config *config = tc->wn->config;
5131              tabs->current->tc->unfocus(tabs->current->tc, tc);
5132 
5133              if (config->tab_zoom >= 0.01 && !config->show_tabs)
5134                {
5135                   _cb_tab_selector_show(tabs, tab_item);
5136                   return;
5137                }
5138 
5139              tc->swallow(tc, tabs->current->tc, relative);
5140           }
5141         tc->parent->focus(tc->parent, tc);
5142      }
5143 }
5144 
5145 static void
_tabs_unfocus(Term_Container * tc,Term_Container * relative)5146 _tabs_unfocus(Term_Container *tc, Term_Container *relative)
5147 {
5148    Tabs *tabs;
5149 
5150    DBG("tc:%p tc->is_focused:%d from_parent:%d",
5151        tc, tc->is_focused, tc->parent == relative);
5152    if (!tc->is_focused)
5153      return;
5154    if (!tc->parent)
5155      return;
5156 
5157    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
5158    tabs = (Tabs*) tc;
5159 
5160    if (tc->parent == relative)
5161      {
5162         tabs->current->tc->unfocus(tabs->current->tc, tc);
5163         tc->is_focused = EINA_FALSE;
5164      }
5165    else
5166      {
5167         Tab_Item *tab_item;
5168         Eina_List *l;
5169 
5170         EINA_LIST_FOREACH(tabs->tabs, l, tab_item)
5171           {
5172              if (relative == tab_item->tc) {
5173                   tc->parent->unfocus(tc->parent, tc);
5174                   tc->is_focused = EINA_FALSE;
5175                   return;
5176              }
5177           }
5178      }
5179 }
5180 
5181 static void
_tabs_bell(Term_Container * tc,Term_Container * child)5182 _tabs_bell(Term_Container *tc,
5183            Term_Container *child)
5184 {
5185    Tabs *tabs;
5186    Term *term;
5187    Solo *solo;
5188    char bufmissed[32];
5189    Eina_List *l;
5190    Tab_Item *tab_item;
5191    int missed = 0;
5192 
5193    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
5194    tabs = (Tabs*) tc;
5195 
5196    assert (child->type == TERM_CONTAINER_TYPE_SOLO);
5197    solo = (Solo*)child;
5198    term = solo->term;
5199 
5200    if (tc->is_focused && child->is_focused)
5201      return;
5202 
5203    if (term->tab_inactive && term->missed_bell)
5204      edje_object_signal_emit(term->tab_inactive, "bell", "terminology");
5205 
5206    EINA_LIST_FOREACH(tabs->tabs, l, tab_item)
5207      {
5208         assert (tab_item->tc->type == TERM_CONTAINER_TYPE_SOLO);
5209         solo = (Solo*) tab_item->tc;
5210         term = solo->term;
5211 
5212         if (term->missed_bell)
5213           missed++;
5214      }
5215 
5216    tab_item = tabs->current;
5217    assert (tab_item->tc->type == TERM_CONTAINER_TYPE_SOLO);
5218    solo = (Solo*)tab_item->tc;
5219    term = solo->term;
5220    if (missed > 0)
5221      {
5222         snprintf(bufmissed, sizeof(bufmissed), "%i", missed);
5223         elm_layout_text_set(term->bg, "terminology.tabmissed.label", bufmissed);
5224         elm_layout_signal_emit(term->bg, "tabmissed,on", "terminology");
5225      }
5226    else
5227      elm_layout_signal_emit(term->bg, "tabmissed,off", "terminology");
5228    edje_object_message_signal_process(term->bg_edj);
5229 
5230    tc->parent->bell(tc->parent, tc);
5231 }
5232 
5233 static void
_tabs_set_title(Term_Container * tc,Term_Container * child,const char * title)5234 _tabs_set_title(Term_Container *tc, Term_Container *child,
5235                 const char *title)
5236 {
5237    Tabs *tabs;
5238    Tab_Item *tab_item;
5239    Eina_List *l;
5240    Solo *solo;
5241    Term *term;
5242 
5243    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
5244    tabs = (Tabs*) tc;
5245 
5246    l = _tab_item_find(tabs, child, NULL);
5247    if (!l)
5248      return;
5249    tab_item = l->data;
5250 
5251    if (tabs->selector && tab_item->selector_entry)
5252      {
5253         sel_entry_title_set(tab_item->selector_entry, title);
5254      }
5255 
5256    assert (tab_item->tc->type == TERM_CONTAINER_TYPE_SOLO);
5257    solo = (Solo*)tab_item->tc;
5258    term = solo->term;
5259 
5260    if (tab_item == tabs->current)
5261      {
5262         eina_stringshare_del(tc->title);
5263         tc->title =  eina_stringshare_ref(title);
5264         tc->parent->set_title(tc->parent, tc, title);
5265 
5266         if (term->config->show_tabs)
5267           {
5268              elm_layout_text_set(term->bg, "terminology.tab.title", title);
5269           }
5270      }
5271    else
5272      {
5273         if (term->tab_inactive)
5274           edje_object_part_text_set(term->tab_inactive,
5275                                     "terminology.title",
5276                                     title);
5277      }
5278 }
5279 
5280 static void
_tabs_recreate(Tabs * tabs)5281 _tabs_recreate(Tabs *tabs)
5282 {
5283    Eina_List *l;
5284    Solo *solo;
5285    Term *term;
5286    Tab_Item *tab_item;
5287    Evas_Coord w = 0, h = 0;
5288    int missed = 0;
5289    int n = eina_list_count(tabs->tabs);
5290 
5291    if (n <= 0)
5292      {
5293         ERR("no tab");
5294         return;
5295      }
5296 
5297    EINA_LIST_FOREACH(tabs->tabs, l, tab_item)
5298      {
5299         assert (tab_item->tc->type == TERM_CONTAINER_TYPE_SOLO);
5300         solo = (Solo*) tab_item->tc;
5301         term = solo->term;
5302 
5303         if (term->tab_inactive)
5304           {
5305              evas_object_hide(term->tab_inactive);
5306              edje_object_signal_callback_del(term->tab_inactive,
5307                                              "tab,activate", "terminology",
5308                                              _cb_tab_activate);
5309           }
5310 
5311         if (term->missed_bell)
5312           missed++;
5313      }
5314 
5315    tab_item = tabs->current;
5316    assert (tab_item->tc->type == TERM_CONTAINER_TYPE_SOLO);
5317    solo = (Solo*)tab_item->tc;
5318    term = solo->term;
5319 
5320 
5321    // this is all below just for tab bar at the top
5322    if (term->config->show_tabs)
5323      {
5324         _tabbar_clear(term);
5325 
5326         if (!term->tab_spacer)
5327           {
5328              term->tab_spacer = evas_object_rectangle_add(evas_object_evas_get(term->bg));
5329              evas_object_color_set(term->tab_spacer, 0, 0, 0, 0);
5330           }
5331         elm_coords_finger_size_adjust(1, &w, 1, &h);
5332         evas_object_size_hint_min_set(term->tab_spacer, w, h);
5333         elm_layout_content_set(term->bg, "terminology.tab_btn",
5334                                term->tab_spacer);
5335 
5336         elm_layout_signal_emit(term->bg, "tabcount,off", "terminology");
5337 
5338         elm_layout_signal_emit(term->bg, "tabbar,on", "terminology");
5339         elm_layout_signal_emit(term->bg, "tab_btn,on", "terminology");
5340         assert(term == _tab_item_to_term(tabs->current));
5341         _tabs_get_or_create_boxes(term, NULL);
5342         _tabbar_fill(tabs);
5343         edje_object_message_signal_process(term->bg_edj);
5344      }
5345    else
5346      {
5347         _tabcount_refresh(tabs);
5348      }
5349 }
5350 
5351 static Tab_Item*
tab_item_new(Tabs * tabs,Term_Container * child)5352 tab_item_new(Tabs *tabs, Term_Container *child)
5353 {
5354    Tab_Item *tab_item;
5355    Solo *solo;
5356    Term *term;
5357 
5358    tab_item = calloc(1, sizeof(Tab_Item));
5359    if (!tab_item)
5360      return NULL;
5361    tab_item->tc = child;
5362    assert(child != NULL);
5363    assert(child->type == TERM_CONTAINER_TYPE_SOLO);
5364    solo = (Solo*)child;
5365    term = solo->term;
5366    assert(term->tab_item == NULL);
5367    term->tab_item = tab_item;
5368 
5369    tabs->tabs = eina_list_append(tabs->tabs, tab_item);
5370 
5371    return tab_item;
5372 }
5373 
5374 static void
_tabs_split(Term_Container * tc,Term_Container * _child EINA_UNUSED,Term * from,const char * cmd,Eina_Bool is_horizontal)5375 _tabs_split(Term_Container *tc,
5376             Term_Container *_child EINA_UNUSED,
5377             Term *from,
5378             const char *cmd,
5379             Eina_Bool is_horizontal)
5380 {
5381    tc->parent->split(tc->parent, tc, from, cmd, is_horizontal);
5382 }
5383 
5384 static int
_tabs_split_direction(Term_Container * tc,Term_Container * child_orig EINA_UNUSED,Term_Container * child_new,Split_Direction direction)5385 _tabs_split_direction(Term_Container *tc,
5386                       Term_Container *child_orig EINA_UNUSED,
5387                       Term_Container *child_new,
5388                       Split_Direction direction)
5389 {
5390    return tc->parent->split_direction(tc->parent, tc, child_new, direction);
5391 }
5392 
5393 static Eina_Bool
_tabs_is_visible(const Term_Container * tc,const Term_Container * child)5394 _tabs_is_visible(const Term_Container *tc, const Term_Container *child)
5395 {
5396    Tabs *tabs;
5397    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
5398    tabs = (Tabs*) tc;
5399    return child == tabs->current->tc;
5400 }
5401 
5402 
5403 static void
_tabs_detach(Term_Container * tc,Term_Container * solo_child)5404 _tabs_detach(Term_Container *tc, Term_Container *solo_child)
5405 {
5406    Evas_Object *o;
5407    assert (tc->type == TERM_CONTAINER_TYPE_TABS);
5408    assert (solo_child->type == TERM_CONTAINER_TYPE_SOLO);
5409 
5410    _tabs_close(tc, solo_child);
5411    solo_child->is_focused = EINA_FALSE;
5412 
5413    o = solo_child->get_evas_object(solo_child);
5414    evas_object_hide(o);
5415    solo_child->parent = (Term_Container*) solo_child->wn;
5416 }
5417 
5418 static Term_Container *
_tabs_new(Term_Container * child,Term_Container * parent)5419 _tabs_new(Term_Container *child, Term_Container *parent)
5420 {
5421    Win *wn;
5422    Term_Container *tc;
5423    Tabs *tabs;
5424    Tab_Item *tab_item;
5425 
5426    tabs = calloc(1, sizeof(Tabs));
5427    if (!tabs)
5428      {
5429         return NULL;
5430      }
5431 
5432    wn = child->wn;
5433 
5434    tc = (Term_Container*)tabs;
5435    tc->term_next = _tabs_term_next;
5436    tc->term_prev = _tabs_term_prev;
5437    tc->term_up = _tabs_term_up;
5438    tc->term_down = _tabs_term_down;
5439    tc->term_left = _tabs_term_left;
5440    tc->term_right = _tabs_term_right;
5441    tc->term_first = _tabs_term_first;
5442    tc->term_last = _tabs_term_last;
5443    tc->focused_term_get = _tabs_focused_term_get;
5444    tc->get_evas_object = _tabs_get_evas_object;
5445    tc->split = _tabs_split;
5446    tc->split_direction = _tabs_split_direction;
5447    tc->find_term_at_coords = _tabs_find_term_at_coords;
5448    tc->size_eval = _tabs_size_eval;
5449    tc->swallow = _tabs_swallow;
5450    tc->focus = _tabs_focus;
5451    tc->unfocus = _tabs_unfocus;
5452    tc->set_title = _tabs_set_title;
5453    tc->bell = _tabs_bell;
5454    tc->close = _tabs_close;
5455    tc->update = _tabs_update;
5456    tc->is_visible = _tabs_is_visible;
5457    tc->detach = _tabs_detach;
5458    tc->title = eina_stringshare_add("Terminology");
5459    tc->type = TERM_CONTAINER_TYPE_TABS;
5460 
5461    tc->parent = parent;
5462    tc->wn = wn;
5463 
5464    child->parent = tc;
5465    tab_item = tab_item_new(tabs, child);
5466    tabs->current = tab_item;
5467 
5468    /* XXX: need to refresh */
5469    parent->swallow(parent, child, tc);
5470 
5471    tc->is_focused = child->is_focused;
5472 
5473    return tc;
5474 }
5475 
5476 
5477 /* }}} */
5478 /* {{{ Popup Media */
5479 struct Pop_Media {
5480      const char *src;
5481      Eina_Bool from_user_interaction;
5482 };
5483 
5484 Eina_Bool
term_has_popmedia(const Term * term)5485 term_has_popmedia(const Term *term)
5486 {
5487    return !!term->popmedia;
5488 }
5489 
5490 void
term_popmedia_close(Term * term)5491 term_popmedia_close(Term *term)
5492 {
5493    if (term->popmedia)
5494      elm_layout_signal_emit(term->bg, "popmedia,off", "terminology");
5495 }
5496 
5497 static void
_cb_popmedia_del(void * data,Evas * _e EINA_UNUSED,Evas_Object * _o EINA_UNUSED,void * _event_info EINA_UNUSED)5498 _cb_popmedia_del(void *data,
5499                  Evas *_e EINA_UNUSED,
5500                  Evas_Object *_o EINA_UNUSED,
5501                  void *_event_info EINA_UNUSED)
5502 {
5503    Term *term = data;
5504 
5505    term->popmedia = NULL;
5506    term->popmedia_deleted = EINA_TRUE;
5507    elm_layout_signal_emit(term->bg, "popmedia,off", "terminology");
5508 }
5509 
5510 static void
_cb_popmedia_done(void * data,Evas_Object * _obj EINA_UNUSED,const char * _sig EINA_UNUSED,const char * _src EINA_UNUSED)5511 _cb_popmedia_done(void *data,
5512                   Evas_Object *_obj EINA_UNUSED,
5513                   const char *_sig EINA_UNUSED,
5514                   const char *_src EINA_UNUSED)
5515 {
5516    Term *term = data;
5517 
5518    if (term->popmedia || term->popmedia_deleted)
5519      {
5520         if (term->popmedia)
5521           {
5522              evas_object_event_callback_del(term->popmedia, EVAS_CALLBACK_DEL,
5523                                             _cb_popmedia_del);
5524              evas_object_del(term->popmedia);
5525              term->popmedia = NULL;
5526           }
5527         term->popmedia_deleted = EINA_FALSE;
5528         termio_mouseover_suspend_pushpop(term->termio, -1);
5529         _popmedia_queue_process(term);
5530      }
5531 }
5532 
5533 static void
_cb_media_loop(void * data,Evas_Object * _obj EINA_UNUSED,void * _info EINA_UNUSED)5534 _cb_media_loop(void *data,
5535                Evas_Object *_obj EINA_UNUSED,
5536                void *_info EINA_UNUSED)
5537 {
5538    Term *term = data;
5539 
5540    if (term->popmedia_queue)
5541      {
5542         if (term->popmedia)
5543             media_play_set(term->popmedia, EINA_FALSE);
5544         elm_layout_signal_emit(term->bg, "popmedia,off", "terminology");
5545      }
5546 }
5547 
5548 static void
_popmedia_queue_free(Term * term)5549 _popmedia_queue_free(Term *term)
5550 {
5551    struct Pop_Media *pm;
5552    if (!term->popmedia_queue)
5553      return;
5554 
5555    EINA_LIST_FREE(term->popmedia_queue, pm)
5556      {
5557         eina_stringshare_del(pm->src);
5558         free(pm);
5559      }
5560 }
5561 
5562 static void
_popmedia_queue_add(Term * term,const char * src,Eina_Bool from_user_interaction)5563 _popmedia_queue_add(Term *term, const char *src,
5564                     Eina_Bool from_user_interaction)
5565 {
5566    struct Pop_Media *pm = calloc(1, sizeof(struct Pop_Media));
5567 
5568    if (!pm)
5569      return;
5570 
5571    pm->src = eina_stringshare_add(src);
5572    pm->from_user_interaction = from_user_interaction;
5573 
5574    term->popmedia_queue = eina_list_append(term->popmedia_queue, pm);
5575    if (!term->popmedia)
5576      _popmedia_queue_process(term);
5577 }
5578 
5579 static void
_popmedia_now(Term * term,const char * src,Eina_Bool from_user_interaction)5580 _popmedia_now(Term *term, const char *src,
5581               Eina_Bool from_user_interaction)
5582 {
5583    struct Pop_Media *pm;
5584 
5585    /* Flush queue */
5586    EINA_LIST_FREE(term->popmedia_queue, pm)
5587      {
5588         eina_stringshare_del(pm->src);
5589      }
5590    elm_layout_signal_emit(term->bg, "popmedia,off", "terminology");
5591 
5592    _popmedia_queue_add(term, src, from_user_interaction);
5593 }
5594 
5595 
5596 static void
_popmedia_show(Term * term,const char * src,Media_Type type)5597 _popmedia_show(Term *term, const char *src, Media_Type type)
5598 {
5599    Evas_Object *o;
5600    Config *config = termio_config_get(term->termio);
5601 
5602    assert(!term->popmedia);
5603    EINA_SAFETY_ON_NULL_RETURN(config);
5604    termio_mouseover_suspend_pushpop(term->termio, 1);
5605    term->popmedia = o = media_add(win_evas_object_get(term->wn),
5606                                   src, config, MEDIA_POP, type);
5607    term->popmedia_deleted = EINA_FALSE;
5608    evas_object_smart_callback_add(o, "loop", _cb_media_loop, term);
5609    evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, _cb_popmedia_del, term);
5610    elm_layout_content_set(term->bg, "terminology.popmedia", o);
5611    evas_object_show(o);
5612    term->poptype = type;
5613    switch (type)
5614      {
5615       case MEDIA_TYPE_IMG:
5616          elm_layout_signal_emit(term->bg, "popmedia,image", "terminology");
5617          break;
5618       case MEDIA_TYPE_SCALE:
5619          elm_layout_signal_emit(term->bg, "popmedia,scale", "terminology");
5620          break;
5621       case MEDIA_TYPE_EDJE:
5622          elm_layout_signal_emit(term->bg, "popmedia,edje", "terminology");
5623          break;
5624       case MEDIA_TYPE_MOV:
5625          elm_layout_signal_emit(term->bg, "popmedia,movie", "terminology");
5626          break;
5627       case MEDIA_TYPE_UNKNOWN:
5628       default:
5629          break;
5630      }
5631 }
5632 
5633 #ifdef HAVE_ECORE_CON_URL_HEAD
5634 typedef struct _Ty_Http_Head {
5635      const char *handler;
5636      const char *src;
5637      Ecore_Con_Url *url;
5638      Ecore_Event_Handler *url_complete;
5639      Ecore_Timer *timeout;
5640      Term *term;
5641      Eina_Bool fallback_allowed;
5642 } Ty_Http_Head;
5643 
5644 static void
_ty_http_head_delete(Ty_Http_Head * ty_head)5645 _ty_http_head_delete(Ty_Http_Head *ty_head)
5646 {
5647    eina_stringshare_del(ty_head->handler);
5648    eina_stringshare_del(ty_head->src);
5649    ecore_con_url_free(ty_head->url);
5650    ecore_event_handler_del(ty_head->url_complete);
5651    if (ty_head->term)
5652      {
5653         elm_layout_signal_emit(ty_head->term->bg, "done", "terminology");
5654         term_unref(ty_head->term);
5655      }
5656    ecore_timer_del(ty_head->timeout);
5657 
5658    free(ty_head);
5659 }
5660 
5661 static Eina_Bool
_media_http_head_timeout(void * data)5662 _media_http_head_timeout(void *data)
5663 {
5664    Ty_Http_Head *ty_head = data;
5665 
5666    ty_head->timeout = NULL;
5667    if (ty_head->fallback_allowed)
5668      {
5669         media_unknown_handle(ty_head->handler, ty_head->src);
5670      }
5671    _ty_http_head_delete(ty_head);
5672    return ECORE_CALLBACK_CANCEL;
5673 }
5674 
5675 static Eina_Bool
_media_http_head_complete(void * data,int _kind EINA_UNUSED,void * event_info)5676 _media_http_head_complete(void *data,
5677                           int _kind EINA_UNUSED,
5678                           void *event_info)
5679 {
5680    Ecore_Con_Event_Url_Complete *ev = event_info;
5681    Ty_Http_Head *ty_head = data;
5682    const Eina_List *headers, *l;
5683    Media_Type type = MEDIA_TYPE_UNKNOWN;
5684    char *str;
5685 
5686    if (ev->status != 200)
5687      goto error;
5688    headers = ecore_con_url_response_headers_get(ev->url_con);
5689    EINA_LIST_FOREACH(headers, l, str)
5690      {
5691 #define  _CONTENT_TYPE_HDR "Content-Type: "
5692 #define  _LOCATION_HDR "Location: "
5693         if (!strncmp(str, _LOCATION_HDR, strlen(_LOCATION_HDR)))
5694           {
5695              unsigned int len;
5696 
5697              str += strlen(_LOCATION_HDR);
5698              if (*str != '/')
5699                {
5700                   eina_stringshare_del(ty_head->src);
5701 
5702                   /* skip the crlf */
5703                   len = strlen(str);
5704                   if (len <= 2)
5705                     goto error;
5706 
5707                   ty_head->src = eina_stringshare_add_length(str, len - 2);
5708                   if (!ty_head->src)
5709                     goto error;
5710                }
5711           }
5712         else if (!strncmp(str, _CONTENT_TYPE_HDR, strlen(_CONTENT_TYPE_HDR)))
5713           {
5714              str += strlen(_CONTENT_TYPE_HDR);
5715              if (!strncmp(str, "image/", strlen("image/")))
5716                {
5717                   type = MEDIA_TYPE_IMG;
5718                }
5719              else if (!strncmp(str, "video/", strlen("video/")))
5720                {
5721                   type = MEDIA_TYPE_MOV;
5722                }
5723              if (type != MEDIA_TYPE_UNKNOWN)
5724                {
5725                   _popmedia_show(ty_head->term, ty_head->src, type);
5726                   break;
5727                }
5728           }
5729 #undef _CONTENT_TYPE_HDR
5730 #undef _LOCATION_HDR
5731      }
5732    if (type == MEDIA_TYPE_UNKNOWN)
5733      goto error;
5734 
5735    _ty_http_head_delete(ty_head);
5736    return EINA_TRUE;
5737 error:
5738    if (ty_head->fallback_allowed)
5739      {
5740         media_unknown_handle(ty_head->handler, ty_head->src);
5741      }
5742    _ty_http_head_delete(ty_head);
5743    return EINA_TRUE;
5744 }
5745 #endif
5746 
5747 static void
_popmedia_unknown(Term * term,const char * src,Eina_Bool from_user_interaction)5748 _popmedia_unknown(Term *term, const char *src, Eina_Bool from_user_interaction)
5749 {
5750    Media_Type type;
5751    Config *config = termio_config_get(term->termio);
5752 
5753    type = media_src_type_get(src);
5754    if (type == MEDIA_TYPE_UNKNOWN)
5755      {
5756 #ifdef HAVE_ECORE_CON_URL_HEAD
5757         Ty_Http_Head *ty_head = calloc(1, sizeof(Ty_Http_Head));
5758         if (!ty_head)
5759           return;
5760 
5761         if (config->helper.local.general && config->helper.local.general[0])
5762           {
5763              ty_head->handler = eina_stringshare_add(config->helper.local.general);
5764              if (!ty_head->handler)
5765                goto error;
5766           }
5767         /* If it comes from a user interaction (click on a link), allow
5768          * fallback to "xdg-open"
5769          * Otherwise, it's from terminology's escape code. Thus don't allow
5770          * fallback and only display content "inline".
5771          */
5772         ty_head->fallback_allowed = from_user_interaction;
5773         ty_head->src = eina_stringshare_add(src);
5774         if (!ty_head->src)
5775           goto error;
5776         ty_head->url = ecore_con_url_new(src);
5777         if (!ty_head->url)
5778           goto error;
5779         if (!ecore_con_url_head(ty_head->url))
5780           goto error;
5781         ty_head->url_complete = ecore_event_handler_add
5782           (ECORE_CON_EVENT_URL_COMPLETE, _media_http_head_complete, ty_head);
5783         ty_head->timeout = ecore_timer_add(2.5, _media_http_head_timeout, ty_head);
5784         if (!ty_head->url_complete)
5785           goto error;
5786         ty_head->term = term;
5787         elm_layout_signal_emit(term->bg, "busy", "terminology");
5788         term_ref(term);
5789         return;
5790 
5791 error:
5792         _ty_http_head_delete(ty_head);
5793 #endif
5794         if (from_user_interaction)
5795           {
5796              media_unknown_handle(config->helper.local.general, src);
5797           }
5798      }
5799    else
5800      {
5801         _popmedia_show(term, src, type);
5802      }
5803 }
5804 
5805 static void
_popmedia_queue_process(Term * term)5806 _popmedia_queue_process(Term *term)
5807 {
5808    struct Pop_Media *pm;
5809 
5810    if (!term->popmedia_queue)
5811      return;
5812    pm = term->popmedia_queue->data;
5813    term->popmedia_queue = eina_list_remove_list(term->popmedia_queue,
5814                                                 term->popmedia_queue);
5815    if (!pm)
5816      return;
5817    _popmedia_unknown(term, pm->src, pm->from_user_interaction);
5818    eina_stringshare_del(pm->src);
5819    free(pm);
5820 }
5821 
5822 static void
_cb_popup(void * data,Evas_Object * _obj EINA_UNUSED,void * event)5823 _cb_popup(void *data,
5824           Evas_Object *_obj EINA_UNUSED,
5825           void *event)
5826 {
5827    Term *term = data;
5828    const char *src = event;
5829    Eina_Bool from_user_interaction = EINA_FALSE;
5830 
5831    if (!src)
5832      {
5833         /* Popup a link, there was user interaction on it. */
5834         from_user_interaction = EINA_TRUE;
5835         src = termio_link_get(term->termio, NULL);
5836      }
5837    if (!src)
5838      return;
5839    _popmedia_unknown(term, src, from_user_interaction);
5840    if (!event)
5841      free((void*)src);
5842 }
5843 
5844 static void
_cb_popup_queue(void * data,Evas_Object * _obj EINA_UNUSED,void * event)5845 _cb_popup_queue(void *data,
5846                 Evas_Object *_obj EINA_UNUSED,
5847                 void *event)
5848 {
5849    Term *term = data;
5850    const char *src = event;
5851    Eina_Bool from_user_interaction = EINA_FALSE;
5852 
5853    if (!src)
5854      {
5855         from_user_interaction = EINA_TRUE;
5856         src = termio_link_get(term->termio, NULL);
5857      }
5858    if (!src)
5859      return;
5860    _popmedia_queue_add(term, src, from_user_interaction);
5861    if (!event)
5862      free((void*)src);
5863 }
5864 
5865 /* }}} */
5866 /* {{{ Term */
5867 
5868 Eina_Bool
term_is_visible(const Term * term)5869 term_is_visible(const Term *term)
5870 {
5871    const Term_Container *tc;
5872 
5873    if (!term)
5874      return EINA_FALSE;
5875 
5876    tc = term->container;
5877    if (!tc)
5878      return EINA_FALSE;
5879 
5880    return tc->is_visible(tc, tc);
5881 }
5882 
5883 void
background_set_shine(const Config * config,Evas_Object * bg_edj)5884 background_set_shine(const Config *config, Evas_Object *bg_edj)
5885 {
5886    Edje_Message_Int msg;
5887 
5888    if (config)
5889      msg.val = config->shine;
5890    else
5891      msg.val = 255;
5892 
5893    if (bg_edj)
5894        edje_object_message_send(bg_edj, EDJE_MESSAGE_INT, 2, &msg);
5895 }
5896 
5897 void
term_apply_shine(Term * term,int shine)5898 term_apply_shine(Term *term, int shine)
5899 {
5900    Config *config = term->config;
5901 
5902    if (config->shine != shine)
5903      {
5904         config->shine = shine;
5905         background_set_shine(config, term->bg_edj);
5906         config_save(config);
5907      }
5908 }
5909 
5910 
5911 static void
_term_config_set(Term * term,Config * config)5912 _term_config_set(Term *term, Config *config)
5913 {
5914    Config *old_config = term->config;
5915 
5916    term->config = config;
5917    termio_config_set(term->termio, config);
5918    _term_media_update(term, term->config);
5919    if (old_config != term->wn->config)
5920      config_del(old_config);
5921 }
5922 
5923 Eina_Bool
term_is_focused(const Term * term)5924 term_is_focused(const Term *term)
5925 {
5926    Term_Container *tc;
5927 
5928    if (!term)
5929      return EINA_FALSE;
5930 
5931    tc = term->container;
5932    if (!tc)
5933      return EINA_FALSE;
5934 
5935    return tc->is_focused;
5936 }
5937 
change_theme(Evas_Object * win,Config * config)5938 void change_theme(Evas_Object *win, Config *config)
5939 {
5940    const Eina_List *terms, *l;
5941    Term *term;
5942 
5943    terms = terms_from_win_object(win);
5944    if (!terms) return;
5945 
5946    EINA_LIST_FOREACH(terms, l, term)
5947      {
5948         if (!theme_apply(term->bg, config, "terminology/background",
5949                          NULL, NULL, EINA_TRUE))
5950           ERR("Couldn't find terminology theme!");
5951         colors_term_init(termio_textgrid_get(term->termio),
5952                          config->color_scheme);
5953         termio_config_set(term->termio, config);
5954      }
5955 
5956    l = elm_theme_overlay_list_get(NULL);
5957    if (l) l = eina_list_last(l);
5958    if (l) elm_theme_overlay_del(NULL, l->data);
5959    elm_theme_overlay_add(NULL, config_theme_path_get(config));
5960    main_trans_update();
5961 }
5962 
5963 void
term_focus(Term * term)5964 term_focus(Term *term)
5965 {
5966    Term_Container *tc;
5967 
5968    DBG("is focused? tc:%p", term->container);
5969    if (term_is_focused(term))
5970      return;
5971 
5972    tc = term->container;
5973    DBG("tc:%p", tc);
5974    tc->focus(tc, tc);
5975 }
5976 
5977 void
term_unfocus(Term * term)5978 term_unfocus(Term *term)
5979 {
5980    Term_Container *tc;
5981 
5982    DBG("is focused? tc:%p", term->container);
5983    if (!term_is_focused(term))
5984      return;
5985 
5986    tc = term->container;
5987    DBG("tc:%p", tc);
5988    tc->unfocus(tc, tc);
5989 }
5990 
5991 enum term_to_direction {
5992      TERM_TO_PREV,
5993      TERM_TO_NEXT,
5994      TERM_TO_UP,
5995      TERM_TO_DOWN,
5996      TERM_TO_LEFT,
5997      TERM_TO_RIGHT,
5998 };
5999 
6000 static void
term_go_to(Term * from,enum term_to_direction dir)6001 term_go_to(Term *from, enum term_to_direction dir)
6002 {
6003    Term *new_term, *focused_term;
6004    Win *wn = from->wn;
6005    Term_Container *tc;
6006 
6007    tc = (Term_Container *) wn;
6008 
6009    focused_term = tc->focused_term_get(tc);
6010    if (!focused_term)
6011      focused_term = from;
6012    tc = focused_term->container;
6013 
6014    switch (dir)
6015      {
6016       case TERM_TO_PREV:
6017          new_term = tc->term_prev(tc, tc);
6018          break;
6019       case TERM_TO_NEXT:
6020          new_term = tc->term_next(tc, tc);
6021          break;
6022       case TERM_TO_UP:
6023          new_term = tc->term_up(tc, tc);
6024          break;
6025       case TERM_TO_DOWN:
6026          new_term = tc->term_down(tc, tc);
6027          break;
6028       case TERM_TO_LEFT:
6029          new_term = tc->term_left(tc, tc);
6030          break;
6031       case TERM_TO_RIGHT:
6032          new_term = tc->term_right(tc, tc);
6033          break;
6034      }
6035 
6036    if (new_term && new_term != focused_term)
6037      term_focus(new_term);
6038 
6039    /* TODO: get rid of it? */
6040    _term_miniview_check(from);
6041 }
6042 
6043 void
term_prev(Term * term)6044 term_prev(Term *term)
6045 {
6046    term_go_to(term, TERM_TO_PREV);
6047 }
6048 
6049 void
term_next(Term * term)6050 term_next(Term *term)
6051 {
6052    term_go_to(term, TERM_TO_NEXT);
6053 }
6054 
6055 void
term_up(Term * term)6056 term_up(Term *term)
6057 {
6058    term_go_to(term, TERM_TO_UP);
6059 }
6060 
6061 void
term_down(Term * term)6062 term_down(Term *term)
6063 {
6064    term_go_to(term, TERM_TO_DOWN);
6065 }
6066 
6067 void
term_left(Term * term)6068 term_left(Term *term)
6069 {
6070    term_go_to(term, TERM_TO_LEFT);
6071 }
6072 
6073 void
term_right(Term * term)6074 term_right(Term *term)
6075 {
6076    term_go_to(term, TERM_TO_RIGHT);
6077 }
6078 
6079 Term *
term_prev_get(const Term * term)6080 term_prev_get(const Term *term)
6081 {
6082    Term_Container *tc = term->container;
6083 
6084    return tc->term_prev(tc, tc);
6085 }
6086 
6087 Term *
term_next_get(const Term * term)6088 term_next_get(const Term *term)
6089 {
6090    Term_Container *tc = term->container;
6091 
6092    return tc->term_next(tc, tc);
6093 }
6094 
6095 
6096 static void
_term_miniview_check(Term * term)6097 _term_miniview_check(Term *term)
6098 {
6099    Eina_List *l, *wn_list;
6100 
6101    EINA_SAFETY_ON_NULL_RETURN(term);
6102    EINA_SAFETY_ON_NULL_RETURN(term->miniview);
6103 
6104    wn_list = win_terms_get(term_win_get(term));
6105 
6106    EINA_LIST_FOREACH(wn_list, l, term)
6107      {
6108         if (term->miniview_shown)
6109           {
6110              DBG("is focused? tc:%p", term->container);
6111              if (term_is_focused(term))
6112                elm_layout_signal_emit(term->bg, "miniview,on", "terminology");
6113           }
6114      }
6115 }
6116 
6117 void
term_miniview_hide(Term * term)6118 term_miniview_hide(Term *term)
6119 {
6120    EINA_SAFETY_ON_NULL_RETURN(term);
6121    EINA_SAFETY_ON_NULL_RETURN(term->miniview);
6122 
6123    if (term->miniview_shown)
6124      {
6125         elm_layout_signal_emit(term->bg, "miniview,off", "terminology");
6126         term->miniview_shown = EINA_FALSE;
6127      }
6128 }
6129 
6130 void
term_miniview_toggle(Term * term)6131 term_miniview_toggle(Term *term)
6132 {
6133    EINA_SAFETY_ON_NULL_RETURN(term);
6134    EINA_SAFETY_ON_NULL_RETURN(term->miniview);
6135 
6136    if (term->miniview_shown)
6137      {
6138         elm_layout_signal_emit(term->bg, "miniview,off", "terminology");
6139         term->miniview_shown = EINA_FALSE;
6140      }
6141    else
6142      {
6143         elm_layout_signal_emit(term->bg, "miniview,on", "terminology");
6144         term->miniview_shown = EINA_TRUE;
6145      }
6146 }
6147 
6148 static void
_on_popover_done(Win * wn)6149 _on_popover_done(Win *wn)
6150 {
6151    Term_Container *tc = (Term_Container*) wn;
6152    Eina_List *l;
6153    Term *term;
6154 
6155    wn->on_popover--;
6156    if (wn->on_popover)
6157      return;
6158 
6159    if (!_win_is_focused(wn))
6160      return;
6161    EINA_LIST_FOREACH(wn->terms, l, term)
6162      {
6163         DBG("is focused? tc:%p", term->container);
6164         if (term_is_focused(term))
6165           return;
6166      }
6167    DBG("focus tc:%p", tc);
6168    tc->focus(tc, tc);
6169 }
6170 
6171 static void
_set_title_ok_cb(void * data,Evas_Object * _obj EINA_UNUSED,void * _event_info EINA_UNUSED)6172 _set_title_ok_cb(void *data,
6173                  Evas_Object *_obj EINA_UNUSED,
6174                  void *_event_info EINA_UNUSED)
6175 {
6176    Evas_Object *popup = data;
6177    Term *term = evas_object_data_get(popup, "term");
6178    Evas_Object *entry = elm_object_content_get(popup);
6179    const char *title = elm_entry_entry_get(entry);
6180 
6181    if (!title || !strlen(title))
6182      title = NULL;
6183 
6184    termio_user_title_set(term->termio, title);
6185    elm_object_focus_set(entry, EINA_FALSE);
6186    elm_popup_dismiss(popup);
6187 }
6188 
6189 static void
_set_title_cancel_cb(void * data,Evas_Object * _obj EINA_UNUSED,void * _event_info EINA_UNUSED)6190 _set_title_cancel_cb(void *data,
6191                      Evas_Object *_obj EINA_UNUSED,
6192                      void *_event_info EINA_UNUSED)
6193 {
6194    Evas_Object *popup = data;
6195    Evas_Object *entry = elm_object_content_get(popup);
6196 
6197    elm_object_focus_set(entry, EINA_FALSE);
6198    elm_popup_dismiss(popup);
6199 }
6200 
6201 static void
_cb_title_popup_hide(void * data,Evas * _e EINA_UNUSED,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)6202 _cb_title_popup_hide(void *data,
6203                      Evas *_e EINA_UNUSED,
6204                      Evas_Object *_obj EINA_UNUSED,
6205                      void *_event EINA_UNUSED)
6206 {
6207    Term *term = data;
6208    Win *wn = term->wn;
6209 
6210    _on_popover_done(wn);
6211 
6212    term_unref(term);
6213 }
6214 
6215 void
term_set_title(Term * term)6216 term_set_title(Term *term)
6217 {
6218    Evas_Object *o;
6219    Evas_Object *popup;
6220    Term_Container *tc = term->container;
6221    const char *prev_title;
6222 
6223    EINA_SAFETY_ON_NULL_RETURN(term);
6224    term->wn->on_popover++;
6225 
6226    term_ref(term);
6227    tc->unfocus(tc, NULL);
6228 
6229    popup = elm_popup_add(term->wn->win);
6230    evas_object_data_set(popup, "term", term);
6231    evas_object_event_callback_add(popup, EVAS_CALLBACK_HIDE,
6232                                   _cb_title_popup_hide, term);
6233 
6234    elm_object_part_text_set(popup, "title,text", _("Set title"));
6235 
6236    o = elm_button_add(popup);
6237    evas_object_smart_callback_add(o, "clicked", _set_title_ok_cb, popup);
6238    elm_object_text_set(o, _("Ok"));
6239    elm_object_part_content_set(popup, "button1", o);
6240 
6241    o = elm_button_add(popup);
6242    evas_object_smart_callback_add(o, "clicked", _set_title_cancel_cb, popup);
6243    elm_object_text_set(o, _("Cancel"));
6244    elm_object_part_content_set(popup, "button2", o);
6245 
6246    o = elm_entry_add(popup);
6247    elm_entry_single_line_set(o, EINA_TRUE);
6248    elm_entry_editable_set(o, EINA_TRUE);
6249    prev_title = termio_user_title_get(term->termio);
6250    if (prev_title)
6251      {
6252         elm_entry_entry_set(o, prev_title);
6253         elm_entry_cursor_pos_set(o, strlen(prev_title));
6254      }
6255    evas_object_smart_callback_add(o, "activated", _set_title_ok_cb, popup);
6256    evas_object_smart_callback_add(o, "aborted", _set_title_cancel_cb, popup);
6257    elm_object_content_set(popup, o);
6258 
6259    evas_object_show(o);
6260 
6261    evas_object_show(popup);
6262 
6263    elm_object_focus_set(o, EINA_TRUE);
6264 }
6265 
6266 static void
_set_alpha(Config * config,const char * val,Eina_Bool save)6267 _set_alpha(Config *config, const char *val, Eina_Bool save)
6268 {
6269    int opacity;
6270 
6271    if (!config || !val) return;
6272 
6273    config->temporary = !save;
6274 
6275    if (isdigit(*val))
6276      {
6277         opacity = atoi(val);
6278         if (opacity >= 100)
6279           {
6280              config->translucent = EINA_FALSE;
6281              config->opacity = 100;
6282           }
6283         else if (opacity >= 0)
6284           {
6285              config->translucent = EINA_TRUE;
6286              config->opacity = opacity;
6287           }
6288      }
6289    else if ((!strcasecmp(val, "on")) ||
6290             (!strcasecmp(val, "true")) ||
6291             (!strcasecmp(val, "yes")))
6292      config->translucent = EINA_TRUE;
6293    else
6294      config->translucent = EINA_FALSE;
6295    if (save)
6296      config_save(config);
6297    main_trans_update();
6298 }
6299 
6300 static void
_sendfile_progress_del(void * data EINA_UNUSED,Evas * e EINA_UNUSED,Evas_Object * obj,void * info EINA_UNUSED)6301 _sendfile_progress_del(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *info EINA_UNUSED)
6302 {
6303    Evas_Object *o = obj;
6304    Term *term = evas_object_data_get(o, "sendfile-progress-term");
6305    Ecore_Timer *t;
6306 
6307    evas_object_data_del(o, "sendfile-progress-term");
6308    if (term)
6309      {
6310         term->sendfile_progress = NULL;
6311         term->sendfile_progress_bar = NULL;
6312      }
6313    t = evas_object_data_get(o, "sendfile-progress-timer");
6314    evas_object_data_del(o, "sendfile-progress-term");
6315    if (t) ecore_timer_del(t);
6316 }
6317 
6318 static Eina_Bool
_sendfile_progress_reset(void * data)6319 _sendfile_progress_reset(void *data)
6320 {
6321    Evas_Object *o = data;
6322    Term *term = evas_object_data_get(o, "sendfile-progress-term");
6323 
6324    if (term)
6325      {
6326         term->sendfile_progress = NULL;
6327         term->sendfile_progress_bar = NULL;
6328      }
6329    evas_object_data_del(o, "sendfile-progress-timer");
6330    evas_object_data_del(o, "sendfile-progress-term");
6331    evas_object_del(o);
6332    return EINA_FALSE;
6333 }
6334 
6335 static Eina_Bool
_sendfile_progress_hide_delay(void * data)6336 _sendfile_progress_hide_delay(void *data)
6337 {
6338    Term *term = data;
6339    Ecore_Timer *t;
6340 
6341    term->sendfile_progress_hide_timer = NULL;
6342    if (!term->sendfile_progress_enabled) return EINA_FALSE;
6343    term->sendfile_progress_enabled = EINA_FALSE;
6344    elm_layout_signal_emit(term->bg, "sendfile,progress,off", "terminology");
6345    t = evas_object_data_get(term->sendfile_progress, "sendfile-progress-timer");
6346    if (t) ecore_timer_del(t);
6347    t = ecore_timer_add(10.0, _sendfile_progress_reset, term->sendfile_progress);
6348    evas_object_data_set(term->sendfile_progress, "sendfile-progress-timer", t);
6349    return EINA_FALSE;
6350 }
6351 
6352 static void
_sendfile_progress_hide(Term * term)6353 _sendfile_progress_hide(Term *term)
6354 {
6355    if (!term->sendfile_progress_enabled) return;
6356    if (term->sendfile_progress_hide_timer)
6357      ecore_timer_del(term->sendfile_progress_hide_timer);
6358    term->sendfile_progress_hide_timer =
6359      ecore_timer_add(0.5, _sendfile_progress_hide_delay, term);
6360    if (elm_object_focus_get(term->sendfile_progress))
6361      {
6362         elm_object_focus_set(term->sendfile_progress, EINA_FALSE);
6363         term_focus(term);
6364      }
6365 }
6366 
6367 static void
_sendfile_progress_cancel(void * data,Evas_Object * obj EINA_UNUSED,void * info EINA_UNUSED)6368 _sendfile_progress_cancel(void *data, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED)
6369 {
6370    Term *term = data;
6371 
6372    if (!term->sendfile_progress) return;
6373    termio_file_send_cancel(term->termio);
6374    _sendfile_progress_hide(term);
6375 }
6376 
6377 static void
_sendfile_progress(Term * term)6378 _sendfile_progress(Term *term)
6379 {
6380    Evas_Object *o, *base;
6381 
6382    if (term->sendfile_progress)
6383      {
6384         evas_object_del(term->sendfile_progress);
6385         term->sendfile_progress = NULL;
6386      }
6387    if (!edje_object_part_exists(term->bg_edj, "terminology.sendfile.progress"))
6388      {
6389         return;
6390      }
6391    if (term->sendfile_progress_hide_timer)
6392      {
6393         ecore_timer_del(term->sendfile_progress_hide_timer);
6394         term->sendfile_progress_hide_timer = NULL;
6395      }
6396    o = elm_box_add(term->wn->win);
6397    evas_object_data_set(o, "sendfile-progress-term", term);
6398    base = o;
6399    term->sendfile_progress = o;
6400    evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, _sendfile_progress_del, NULL);
6401    elm_box_horizontal_set(o, EINA_TRUE);
6402 
6403    o = elm_button_add(term->wn->win);
6404    elm_object_text_set(o, "Cancel");
6405    evas_object_smart_callback_add(o, "clicked", _sendfile_progress_cancel, term);
6406    evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
6407    elm_box_pack_end(base, o);
6408    evas_object_show(o);
6409 
6410    o = elm_progressbar_add(term->wn->win);
6411    term->sendfile_progress_bar = o;
6412    elm_progressbar_unit_format_set(o, "%1.0f%%");
6413    evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
6414    evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
6415    elm_box_pack_end(base, o);
6416    evas_object_show(o);
6417 
6418    term->sendfile_progress_enabled = EINA_TRUE;
6419    elm_layout_content_set(term->bg, "terminology.sendfile.progress", base);
6420    evas_object_show(base);
6421    elm_layout_signal_emit(term->bg, "sendfile,progress,on", "terminology");
6422 }
6423 
6424 static void
_sendfile_request_del(void * data EINA_UNUSED,Evas * e EINA_UNUSED,Evas_Object * obj,void * info EINA_UNUSED)6425 _sendfile_request_del(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *info EINA_UNUSED)
6426 {
6427    Evas_Object *o = obj;
6428    Term *term = evas_object_data_get(o, "sendfile-request-term");
6429    Ecore_Timer *t;
6430 
6431    evas_object_data_del(o, "sendfile-request-term");
6432    if (term) term->sendfile_request = NULL;
6433    t = evas_object_data_get(o, "sendfile-request-timer");
6434    evas_object_data_del(o, "sendfile-request-term");
6435    if (t) ecore_timer_del(t);
6436 }
6437 
6438 static Eina_Bool
_sendfile_request_reset(void * data)6439 _sendfile_request_reset(void *data)
6440 {
6441    Evas_Object *o = data;
6442    Term *term = evas_object_data_get(o, "sendfile-request-term");
6443 
6444    if (term) term->sendfile_request = NULL;
6445    evas_object_data_del(o, "sendfile-request-timer");
6446    evas_object_data_del(o, "sendfile-request-term");
6447    evas_object_del(o);
6448    return EINA_FALSE;
6449 }
6450 
6451 static Eina_Bool
_sendfile_request_hide_delay(void * data)6452 _sendfile_request_hide_delay(void *data)
6453 {
6454    Term *term = data;
6455    Ecore_Timer *t;
6456 
6457    term->sendfile_request_hide_timer = NULL;
6458    if (!term->sendfile_request_enabled) return EINA_FALSE;
6459    term->sendfile_request_enabled = EINA_FALSE;
6460    elm_layout_signal_emit(term->bg, "sendfile,request,off", "terminology");
6461    t = evas_object_data_get(term->sendfile_request, "sendfile-request-timer");
6462    if (t) ecore_timer_del(t);
6463    t = ecore_timer_add(10.0, _sendfile_request_reset, term->sendfile_request);
6464    evas_object_data_set(term->sendfile_request, "sendfile-request-timer", t);
6465    if (elm_object_focus_get(term->sendfile_request))
6466      {
6467         elm_object_focus_set(term->sendfile_request, EINA_FALSE);
6468         term_focus(term);
6469      }
6470    return EINA_FALSE;
6471 }
6472 
6473 static void
_sendfile_request_hide(Term * term)6474 _sendfile_request_hide(Term *term)
6475 {
6476    if (!term->sendfile_request_enabled) return;
6477    if (term->sendfile_request_hide_timer)
6478      ecore_timer_del(term->sendfile_request_hide_timer);
6479    term->sendfile_request_hide_timer =
6480      ecore_timer_add(0.2, _sendfile_request_hide_delay, term);
6481 }
6482 
6483 static void
_sendfile_request_done(void * data,Evas_Object * obj EINA_UNUSED,void * info)6484 _sendfile_request_done(void *data, Evas_Object *obj EINA_UNUSED, void *info)
6485 {
6486    Term *term = data;
6487    const char *path, *selpath = info;
6488 
6489    if (!term->sendfile_request) return;
6490 
6491    path = elm_fileselector_path_get(term->sendfile_request);
6492    eina_stringshare_replace(&term->sendfile_dir, path);
6493 
6494    if (selpath)
6495      {
6496         _sendfile_progress(term);
6497         termio_file_send_ok(term->termio, selpath);
6498      }
6499    else  termio_file_send_cancel(term->termio);
6500    _sendfile_request_hide(term);
6501 }
6502 
6503 static void
_sendfile_request(Term * term,const char * path)6504 _sendfile_request(Term *term, const char *path)
6505 {
6506    Evas_Object *o;
6507    const char *p;
6508 
6509    if (term->sendfile_request)
6510      {
6511         evas_object_del(term->sendfile_request);
6512         term->sendfile_request = NULL;
6513      }
6514    if (!edje_object_part_exists(term->bg_edj, "terminology.sendfile.request"))
6515      {
6516         termio_file_send_cancel(term->termio);
6517         return;
6518      }
6519    if (term->sendfile_request_hide_timer)
6520      {
6521         ecore_timer_del(term->sendfile_request_hide_timer);
6522         term->sendfile_request_hide_timer = NULL;
6523      }
6524    o = elm_fileselector_add(term->wn->win);
6525    evas_object_data_set(o, "sendfile-request-term", term);
6526    term->sendfile_request = o;
6527    evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, _sendfile_request_del, NULL);
6528    elm_fileselector_is_save_set(o, EINA_TRUE);
6529    elm_fileselector_expandable_set(o, EINA_FALSE);
6530    if (!term->sendfile_dir)
6531      {
6532         const char *dir = eina_environment_home_get();
6533 
6534         if (dir) term->sendfile_dir = eina_stringshare_add(dir);
6535      }
6536    if (term->sendfile_dir) elm_fileselector_path_set(o, term->sendfile_dir);
6537    p = strrchr(path, '/');
6538    if (p) elm_fileselector_current_name_set(o, p + 1);
6539    else elm_fileselector_current_name_set(o, path);
6540    evas_object_smart_callback_add(o, "done", _sendfile_request_done, term);
6541    term->sendfile_request_enabled = EINA_TRUE;
6542    elm_layout_content_set(term->bg, "terminology.sendfile.request", o);
6543    evas_object_show(o);
6544    elm_layout_signal_emit(term->bg, "sendfile,request,on", "terminology");
6545    elm_object_focus_set(o, EINA_TRUE);
6546 }
6547 
6548 static void
_cb_command(void * data,Evas_Object * _obj EINA_UNUSED,void * event)6549 _cb_command(void *data,
6550             Evas_Object *_obj EINA_UNUSED,
6551             void *event)
6552 {
6553    Term *term = data;
6554    const char *cmd = event;
6555 
6556    if (cmd[0] == 'p') // popmedia
6557      {
6558         if (cmd[1] == 'n') // now
6559           {
6560              _popmedia_now(term, cmd + 2, EINA_FALSE);
6561           }
6562         else if (cmd[1] == 'q') // queue it to display after current one
6563           {
6564              _popmedia_queue_add(term, cmd + 2, EINA_FALSE);
6565           }
6566      }
6567    else if (cmd[0] == 'b') // set background
6568      {
6569         if (cmd[1] == 't') // temporary
6570           {
6571              Config *config = termio_config_get(term->termio);
6572 
6573              if (config)
6574                {
6575                   Config *new_config = config_fork(config);
6576 
6577                   new_config->temporary = EINA_TRUE;
6578                   if (cmd[2])
6579                     eina_stringshare_replace(&(new_config->background), cmd + 2);
6580                   else
6581                     eina_stringshare_replace(&(new_config->background), NULL);
6582                   _term_config_set(term, new_config);
6583                }
6584           }
6585         else if (cmd[1] == 'p') // permanent
6586           {
6587              Config *config = termio_config_get(term->termio);
6588 
6589              if (config)
6590                {
6591                   config->temporary = EINA_FALSE;
6592                   if (cmd[2])
6593                     eina_stringshare_replace(&(config->background), cmd + 2);
6594                   else
6595                     eina_stringshare_replace(&(config->background), NULL);
6596                   main_media_update(config);
6597                   config_save(config);
6598                }
6599           }
6600      }
6601    else if (cmd[0] == 'a') // set alpha
6602      {
6603         if (cmd[1] == 't') // temporary
6604           _set_alpha(termio_config_get(term->termio), cmd + 2, EINA_FALSE);
6605         else if (cmd[1] == 'p') // permanent
6606           _set_alpha(termio_config_get(term->termio), cmd + 2, EINA_TRUE);
6607      }
6608    else if (cmd[0] == 'f') // file...
6609      {
6610         if (cmd[1] == 'r') // receive
6611           {
6612              _sendfile_request(term, cmd + 2);
6613           }
6614         else if (cmd[1] == 'd') // data packet
6615           {
6616           }
6617         else if (cmd[1] == 'x') // exit data stream
6618           {
6619           }
6620      }
6621 }
6622 
6623 static void
_cb_tab_go(void * data,Evas_Object * _obj EINA_UNUSED,const char * _sig EINA_UNUSED,const char * _src EINA_UNUSED)6624 _cb_tab_go(void *data,
6625            Evas_Object *_obj EINA_UNUSED,
6626            const char *_sig EINA_UNUSED,
6627            const char *_src EINA_UNUSED)
6628 {
6629    _cb_select(data, NULL, NULL);
6630 }
6631 
6632 static void
_cb_tab_new(void * data,Evas_Object * _obj EINA_UNUSED,const char * _sig EINA_UNUSED,const char * _src EINA_UNUSED)6633 _cb_tab_new(void *data,
6634            Evas_Object *_obj EINA_UNUSED,
6635            const char *_sig EINA_UNUSED,
6636            const char *_src EINA_UNUSED)
6637 {
6638    Term *term = data;
6639    main_new(term->termio);
6640 }
6641 
6642 static void
_cb_prev(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)6643 _cb_prev(void *data,
6644          Evas_Object *_obj EINA_UNUSED,
6645          void *_event EINA_UNUSED)
6646 {
6647    Term *term = data;
6648 
6649    term_prev(term);
6650 }
6651 
6652 static void
_cb_next(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)6653 _cb_next(void *data,
6654          Evas_Object *_obj EINA_UNUSED,
6655          void *_event EINA_UNUSED)
6656 {
6657    Term *term = data;
6658 
6659    term_next(term);
6660 }
6661 
6662 static void
_cb_split_h(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)6663 _cb_split_h(void *data,
6664             Evas_Object *_obj EINA_UNUSED,
6665             void *_event EINA_UNUSED)
6666 {
6667    Term *term = data;
6668    Term_Container *tc = term->container;
6669 
6670    assert(tc->type == TERM_CONTAINER_TYPE_SOLO);
6671    tc->split(tc, tc, term, NULL, EINA_TRUE);
6672 }
6673 
6674 static void
_cb_split_v(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)6675 _cb_split_v(void *data,
6676             Evas_Object *_obj EINA_UNUSED,
6677             void *_event EINA_UNUSED)
6678 {
6679    Term *term = data;
6680    Term_Container *tc = term->container;
6681 
6682    assert(tc->type == TERM_CONTAINER_TYPE_SOLO);
6683    tc->split(tc, tc, term, NULL, EINA_FALSE);
6684 }
6685 
6686 static void
_cb_title(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)6687 _cb_title(void *data,
6688           Evas_Object *_obj EINA_UNUSED,
6689           void *_event EINA_UNUSED)
6690 {
6691    Term *term = data;
6692    Term_Container *tc = term->container;
6693    const char *title = termio_title_get(term->termio);
6694 
6695    if (title)
6696      tc->set_title(tc, tc, title);
6697 }
6698 
6699 static void
_cb_icon(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)6700 _cb_icon(void *data,
6701          Evas_Object *_obj EINA_UNUSED,
6702          void *_event EINA_UNUSED)
6703 {
6704    Term *term = data;
6705    DBG("is focused? tc:%p", term->container);
6706    if (term_is_focused(term))
6707      elm_win_icon_name_set(term->wn->win, termio_icon_name_get(term->termio));
6708 }
6709 
6710 static void
_cb_send_progress(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)6711 _cb_send_progress(void *data,
6712                   Evas_Object *_obj EINA_UNUSED,
6713                   void *_event EINA_UNUSED)
6714 {
6715    Term *term = data;
6716 
6717    elm_progressbar_value_set(term->sendfile_progress_bar,
6718                              termio_file_send_progress_get(term->termio));
6719 }
6720 
6721 static void
_cb_send_end(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)6722 _cb_send_end(void *data,
6723              Evas_Object *_obj EINA_UNUSED,
6724              void *_event EINA_UNUSED)
6725 {
6726    Term *term = data;
6727    if (!term->sendfile_progress) return;
6728    _sendfile_request_hide(term);
6729    _sendfile_progress_hide(term);
6730 }
6731 
6732 static Eina_Bool
_cb_cmd_del(void * data)6733 _cb_cmd_del(void *data)
6734 {
6735    Win *wn = data;
6736 
6737    wn->cmdbox_del_timer = NULL;
6738    if (wn->cmdbox)
6739      {
6740         evas_object_del(wn->cmdbox);
6741         wn->cmdbox = NULL;
6742      }
6743    return EINA_FALSE;
6744 }
6745 
6746 static void
_cb_cmd_activated(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)6747 _cb_cmd_activated(void *data,
6748                   Evas_Object *_obj EINA_UNUSED,
6749                   void *_event EINA_UNUSED)
6750 {
6751    Win *wn = data;
6752    char *cmd = NULL;
6753    Term *term;
6754    Term_Container *tc;
6755 
6756    elm_layout_signal_emit(wn->base, "cmdbox,hide", "terminology");
6757    tc = (Term_Container *) wn;
6758    term = tc->focused_term_get(tc);
6759    if (wn->cmdbox) cmd = (char *)elm_entry_entry_get(wn->cmdbox);
6760    if (cmd)
6761      {
6762         cmd = elm_entry_markup_to_utf8(cmd);
6763         if (cmd)
6764           {
6765              if (term)
6766                  termcmd_do(term->termio, term->wn->win, term->bg, cmd);
6767              free(cmd);
6768           }
6769      }
6770    elm_object_focus_set(wn->base, EINA_TRUE);
6771    wn->cmdbox_up = EINA_FALSE;
6772    if (wn->cmdbox_del_timer) ecore_timer_del(wn->cmdbox_del_timer);
6773    wn->cmdbox_del_timer = ecore_timer_add(5.0, _cb_cmd_del, wn);
6774 }
6775 
6776 static void
_cb_cmd_aborted(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)6777 _cb_cmd_aborted(void *data,
6778                 Evas_Object *_obj EINA_UNUSED,
6779                 void *_event EINA_UNUSED)
6780 {
6781    Win *wn = data;
6782 
6783    elm_layout_signal_emit(wn->base, "cmdbox,hide", "terminology");
6784    elm_object_focus_set(wn->base, EINA_TRUE);
6785    wn->cmdbox_up = EINA_FALSE;
6786    if (wn->cmdbox_del_timer) ecore_timer_del(wn->cmdbox_del_timer);
6787    wn->cmdbox_del_timer = ecore_timer_add(5.0, _cb_cmd_del, wn);
6788 }
6789 
6790 static void
_cb_cmd_changed(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)6791 _cb_cmd_changed(void *data,
6792                 Evas_Object *_obj EINA_UNUSED,
6793                 void *_event EINA_UNUSED)
6794 {
6795    Win *wn = data;
6796    char *cmd = NULL;
6797    Term *term;
6798    Term_Container *tc;
6799 
6800    tc = (Term_Container *) wn;
6801    term = tc->focused_term_get(tc);
6802    if (!term) return;
6803    if (wn->cmdbox) cmd = (char *)elm_entry_entry_get(wn->cmdbox);
6804    if (cmd)
6805      {
6806         cmd = elm_entry_markup_to_utf8(cmd);
6807         if (cmd)
6808           {
6809              termcmd_watch(term->termio, term->wn->win, term->bg, cmd);
6810              free(cmd);
6811           }
6812      }
6813 }
6814 
6815 static void
_cb_cmd_hints_changed(void * data,Evas * _e EINA_UNUSED,Evas_Object * _obj EINA_UNUSED,void * _event_info EINA_UNUSED)6816 _cb_cmd_hints_changed(void *data,
6817                       Evas *_e EINA_UNUSED,
6818                       Evas_Object *_obj EINA_UNUSED,
6819                       void *_event_info EINA_UNUSED)
6820 {
6821    Win *wn = data;
6822 
6823    if (wn->cmdbox)
6824      {
6825         evas_object_show(wn->cmdbox);
6826         elm_layout_content_set(wn->base, "terminology.cmdbox", wn->cmdbox);
6827      }
6828 }
6829 
6830 static void
_cb_cmdbox(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)6831 _cb_cmdbox(void *data,
6832            Evas_Object *_obj EINA_UNUSED,
6833            void *_event EINA_UNUSED)
6834 {
6835    Term *term = data;
6836 
6837    term->wn->cmdbox_up = EINA_TRUE;
6838    if (!term->wn->cmdbox)
6839      {
6840         Evas_Object *o;
6841         Win *wn = term->wn;
6842 
6843         wn->cmdbox = o = elm_entry_add(wn->win);
6844         elm_entry_single_line_set(o, EINA_TRUE);
6845         elm_entry_scrollable_set(o, EINA_FALSE);
6846         elm_scroller_policy_set(o, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF);
6847         elm_entry_input_panel_layout_set(o, ELM_INPUT_PANEL_LAYOUT_TERMINAL);
6848         elm_entry_autocapital_type_set(o, ELM_AUTOCAPITAL_TYPE_NONE);
6849         elm_entry_input_panel_enabled_set(o, EINA_TRUE);
6850         elm_entry_input_panel_language_set(o, ELM_INPUT_PANEL_LANG_ALPHABET);
6851         elm_entry_input_panel_return_key_type_set(o, ELM_INPUT_PANEL_RETURN_KEY_TYPE_GO);
6852         elm_entry_prediction_allow_set(o, EINA_FALSE);
6853         evas_object_show(o);
6854         evas_object_smart_callback_add(o, "activated", _cb_cmd_activated, wn);
6855         evas_object_smart_callback_add(o, "aborted", _cb_cmd_aborted, wn);
6856         evas_object_smart_callback_add(o, "changed,user", _cb_cmd_changed, wn);
6857         evas_object_event_callback_add(o, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
6858                                        _cb_cmd_hints_changed, wn);
6859         elm_layout_content_set(wn->base, "terminology.cmdbox", o);
6860      }
6861    elm_layout_signal_emit(term->wn->base, "cmdbox,show", "terminology");
6862    elm_entry_entry_set(term->wn->cmdbox, "");
6863    evas_object_show(term->wn->cmdbox);
6864    elm_object_focus_set(term->wn->cmdbox, EINA_TRUE);
6865    if (term->wn->cmdbox_del_timer)
6866      {
6867         ecore_timer_del(term->wn->cmdbox_del_timer);
6868         term->wn->cmdbox_del_timer = NULL;
6869      }
6870 }
6871 
6872 
6873 static void
_cb_media_del(void * data,Evas * _e EINA_UNUSED,Evas_Object * _obj EINA_UNUSED,void * _event_info EINA_UNUSED)6874 _cb_media_del(void *data,
6875               Evas *_e EINA_UNUSED,
6876               Evas_Object *_obj EINA_UNUSED,
6877               void *_event_info EINA_UNUSED)
6878 {
6879    Term *term = data;
6880    Config *config = NULL;
6881 
6882    if (term->termio)
6883      config = termio_config_get(term->termio);
6884    term->media = NULL;
6885    if (term->bg)
6886      {
6887         elm_layout_signal_emit(term->bg, "media,off", "terminology");
6888         elm_layout_signal_emit(term->core, "media,off", "terminology");
6889      }
6890    if (!config) return;
6891    if (config->temporary)
6892      eina_stringshare_replace(&(config->background), NULL);
6893 }
6894 
6895 static void
_term_media_update(Term * term,const Config * config)6896 _term_media_update(Term *term, const Config *config)
6897 {
6898    if ((config->background) && (config->background[0]))
6899      {
6900         Evas_Object *o;
6901         Media_Type type;
6902 
6903         if (term->media)
6904           {
6905              evas_object_event_callback_del(term->media,
6906                                             EVAS_CALLBACK_DEL,
6907                                             _cb_media_del);
6908              evas_object_del(term->media);
6909           }
6910         type = media_src_type_get(config->background);
6911         term->media = o = media_add(term->wn->win,
6912                                     config->background, config,
6913                                     MEDIA_BG, type);
6914         evas_object_event_callback_add(o, EVAS_CALLBACK_DEL,
6915                                        _cb_media_del, term);
6916         elm_layout_content_set(term->core, "terminology.background", o);
6917         evas_object_show(o);
6918         term->mediatype = type;
6919         switch (type)
6920           {
6921            case MEDIA_TYPE_IMG:
6922               elm_layout_signal_emit(term->bg, "media,image", "terminology");
6923               elm_layout_signal_emit(term->core, "media,image", "terminology");
6924               break;
6925            case MEDIA_TYPE_SCALE:
6926               elm_layout_signal_emit(term->bg, "media,scale", "terminology");
6927               elm_layout_signal_emit(term->core, "media,scale", "terminology");
6928               break;
6929            case MEDIA_TYPE_EDJE:
6930               elm_layout_signal_emit(term->bg, "media,edje", "terminology");
6931               elm_layout_signal_emit(term->core, "media,edje", "terminology");
6932               break;
6933            case MEDIA_TYPE_MOV:
6934               elm_layout_signal_emit(term->bg, "media,movie", "terminology");
6935               elm_layout_signal_emit(term->core, "media,movie", "terminology");
6936               break;
6937            case MEDIA_TYPE_UNKNOWN:
6938            default:
6939               break;
6940           }
6941      }
6942    else
6943      {
6944         if (term->media)
6945           {
6946              evas_object_event_callback_del(term->media,
6947                                             EVAS_CALLBACK_DEL,
6948                                             _cb_media_del);
6949              elm_layout_signal_emit(term->bg, "media,off", "terminology");
6950              elm_layout_signal_emit(term->core, "media,off", "terminology");
6951              evas_object_del(term->media);
6952              term->media = NULL;
6953           }
6954      }
6955 }
6956 
6957 void
main_media_update(const Config * config)6958 main_media_update(const Config *config)
6959 {
6960    Win *wn;
6961    Term *term;
6962    Eina_List *l, *ll;
6963 
6964    EINA_LIST_FOREACH(wins, l, wn)
6965      {
6966         EINA_LIST_FOREACH(wn->terms, ll, term)
6967           {
6968              if (term->config != config) continue;
6969              if (!config) continue;
6970              _term_media_update(term, config);
6971           }
6972      }
6973 }
6974 
6975 void
main_media_mute_update(const Config * config)6976 main_media_mute_update(const Config *config)
6977 {
6978    Win *wn;
6979    Term *term;
6980    Eina_List *l, *ll;
6981 
6982    EINA_LIST_FOREACH(wins, l, wn)
6983      {
6984         EINA_LIST_FOREACH(wn->terms, ll, term)
6985           {
6986              if (term->media) media_mute_set(term->media, config->mute);
6987              termio_media_mute_set(term->termio, config->mute);
6988           }
6989      }
6990 }
6991 
6992 void
main_media_visualize_update(const Config * config)6993 main_media_visualize_update(const Config *config)
6994 {
6995    Win *wn;
6996    Term *term;
6997    Eina_List *l, *ll;
6998 
6999    EINA_LIST_FOREACH(wins, l, wn)
7000      {
7001         EINA_LIST_FOREACH(wn->terms, ll, term)
7002           {
7003              if (term->media) media_visualize_set(term->media, config->visualize);
7004              termio_media_visualize_set(term->termio, config->visualize);
7005           }
7006      }
7007 }
7008 
7009 void
main_config_sync(const Config * config)7010 main_config_sync(const Config *config)
7011 {
7012    Win *wn;
7013    Term *term;
7014    Eina_List *l, *ll;
7015    Config *main_config = main_config_get();
7016 
7017    if (config != main_config) config_sync(config, main_config);
7018    EINA_LIST_FOREACH(wins, l, wn)
7019      {
7020         if (wn->config != config) config_sync(config, wn->config);
7021         EINA_LIST_FOREACH(wn->terms, ll, term)
7022           {
7023              if (term->config != config)
7024                {
7025                   Evas_Coord mw = 1, mh = 1, w, h, tsize_w = 0, tsize_h = 0;
7026 
7027                   config_sync(config, term->config);
7028                   evas_object_geometry_get(term->termio, NULL, NULL,
7029                                            &tsize_w, &tsize_h);
7030                   evas_object_data_del(term->termio, "sizedone");
7031                   termio_config_update(term->termio);
7032                   evas_object_size_hint_min_get(term->termio, &mw, &mh);
7033                   if (mw < 1) mw = 1;
7034                   if (mh < 1) mh = 1;
7035                   w = tsize_w / mw;
7036                   h = tsize_h / mh;
7037                   evas_object_data_del(term->termio, "sizedone");
7038                   evas_object_size_hint_request_set(term->termio,
7039                                                     w * mw, h * mh);
7040                }
7041           }
7042      }
7043 }
7044 
7045 static void
_term_free(Term * term)7046 _term_free(Term *term)
7047 {
7048    if (_tab_drag && _tab_drag->term == term)
7049      {
7050         _tab_drag_free();
7051      }
7052    if (_tab_drag && _tab_drag->term_over == term)
7053      {
7054         _tab_drag->term_over = NULL;
7055         _tab_drag->split_direction = SPLIT_DIRECTION_NONE;
7056      }
7057    if (term->sendfile_request)
7058      {
7059         evas_object_del(term->sendfile_request);
7060         term->sendfile_request = NULL;
7061      }
7062    if (term->sendfile_progress)
7063      {
7064         evas_object_del(term->sendfile_progress);
7065         term->sendfile_progress = NULL;
7066      }
7067    if (term->sendfile_request_hide_timer)
7068      {
7069         ecore_timer_del(term->sendfile_request_hide_timer);
7070         term->sendfile_request_hide_timer = NULL;
7071      }
7072    if (term->sendfile_progress_hide_timer)
7073      {
7074         ecore_timer_del(term->sendfile_progress_hide_timer);
7075         term->sendfile_progress_hide_timer = NULL;
7076      }
7077    eina_stringshare_del(term->sendfile_dir);
7078    term->sendfile_dir = NULL;
7079    _popmedia_queue_free(term);
7080    if (term->media)
7081      {
7082         evas_object_event_callback_del(term->media,
7083                                        EVAS_CALLBACK_DEL,
7084                                        _cb_media_del);
7085         evas_object_del(term->media);
7086      }
7087    term->media = NULL;
7088    if (term->popmedia) evas_object_del(term->popmedia);
7089    if (term->miniview)
7090      {
7091         evas_object_del(term->miniview);
7092         term->miniview = NULL;
7093      }
7094    term->popmedia = NULL;
7095    term->popmedia_deleted = EINA_FALSE;
7096 
7097    _term_tabregion_free(term);
7098 
7099    if (term->tabbar.l.box)
7100      {
7101         elm_box_unpack_all(term->tabbar.l.box);
7102         evas_object_del(term->tabbar.l.box);
7103         term->tabbar.l.box = NULL;
7104      }
7105    if (term->tabbar.r.box)
7106      {
7107         elm_box_unpack_all(term->tabbar.r.box);
7108         evas_object_del(term->tabbar.r.box);
7109         term->tabbar.r.box = NULL;
7110      }
7111 
7112    evas_object_del(term->tab_inactive);
7113    term->tab_inactive = NULL;
7114    term->tab_item = NULL;
7115 
7116    evas_object_del(term->termio);
7117    term->termio = NULL;
7118 
7119    evas_object_del(term->core);
7120    term->core = NULL;
7121    evas_object_del(term->bg);
7122    term->bg = NULL;
7123    term->bg_edj = NULL;
7124 
7125    if (term->tab_spacer)
7126      {
7127         evas_object_del(term->tab_spacer);
7128         term->tab_spacer = NULL;
7129      }
7130    free(term);
7131 }
7132 
7133 static void
_cb_tab_prev(void * data,Evas_Object * _obj EINA_UNUSED,const char * _sig EINA_UNUSED,const char * _src EINA_UNUSED)7134 _cb_tab_prev(void *data,
7135                   Evas_Object *_obj EINA_UNUSED,
7136                   const char *_sig EINA_UNUSED,
7137                   const char *_src EINA_UNUSED)
7138 {
7139    _cb_prev(data, NULL, NULL);
7140 }
7141 
7142 static void
_cb_tab_next(void * data,Evas_Object * _obj EINA_UNUSED,const char * _sig EINA_UNUSED,const char * _src EINA_UNUSED)7143 _cb_tab_next(void *data,
7144                   Evas_Object *_obj EINA_UNUSED,
7145                   const char *_sig EINA_UNUSED,
7146                   const char *_src EINA_UNUSED)
7147 {
7148    _cb_next(data, NULL, NULL);
7149 }
7150 
7151 
7152 static void
_term_bg_config(Term * term)7153 _term_bg_config(Term *term)
7154 {
7155    _term_trans(term);
7156    background_set_shine(term->config, term->bg_edj);
7157 
7158    termio_theme_set(term->termio, term->bg_edj);
7159    elm_layout_signal_callback_add(term->bg, "popmedia,done", "terminology",
7160                                    _cb_popmedia_done, term);
7161    elm_layout_signal_callback_add(term->bg, "tab,go", "terminology",
7162                                   _cb_tab_go, term);
7163    elm_layout_signal_callback_add(term->bg, "tab,new", "terminology",
7164                                   _cb_tab_new, term);
7165    elm_layout_signal_callback_add(term->bg, "tab,prev", "terminology",
7166                                   _cb_tab_prev, term);
7167    elm_layout_signal_callback_add(term->bg, "tab,next", "terminology",
7168                                   _cb_tab_next, term);
7169    elm_layout_signal_callback_add(term->bg, "tab,close", "terminology",
7170                                   _cb_tab_close, term);
7171    elm_layout_signal_callback_add(term->bg, "tab,title", "terminology",
7172                                    _cb_tab_title, term);
7173    elm_layout_signal_callback_add(term->bg, "tab,hdrag", "*",
7174                                   _term_on_horizontal_drag, term);
7175    elm_layout_signal_callback_add(term->bg, "tab,drag,move", "*",
7176                                   _tabs_drag_mouse_move, term);
7177    elm_layout_signal_callback_add(term->bg, "tab,drag,stop", "*",
7178                                   _term_on_drag_stop, term);
7179    elm_layout_signal_callback_add(term->bg, "tab,mouse,down", "*",
7180                                   _tabs_mouse_down, term);
7181    elm_layout_content_set(term->core, "terminology.content", term->termio);
7182    elm_layout_content_set(term->bg, "terminology.content", term->core);
7183    elm_layout_content_set(term->bg, "terminology.miniview", term->miniview);
7184    if (term->popmedia)
7185      {
7186         elm_layout_content_set(term->bg, "terminology.popmedia", term->popmedia);
7187         switch (term->poptype)
7188           {
7189            case MEDIA_TYPE_IMG:
7190               elm_layout_signal_emit(term->bg, "popmedia,image", "terminology");
7191               break;
7192            case MEDIA_TYPE_SCALE:
7193               elm_layout_signal_emit(term->bg, "popmedia,scale", "terminology");
7194               break;
7195            case MEDIA_TYPE_EDJE:
7196               elm_layout_signal_emit(term->bg, "popmedia,edje", "terminology");
7197               break;
7198            case MEDIA_TYPE_MOV:
7199               elm_layout_signal_emit(term->bg, "popmedia,movie", "terminology");
7200               break;
7201            default:
7202               break;
7203           }
7204      }
7205    if (term->media)
7206      {
7207         elm_layout_content_set(term->core, "terminology.background", term->media);
7208         switch (term->mediatype)
7209           {
7210            case MEDIA_TYPE_IMG:
7211               elm_layout_signal_emit(term->bg, "media,image", "terminology");
7212               elm_layout_signal_emit(term->core, "media,image", "terminology");
7213               break;
7214            case MEDIA_TYPE_SCALE:
7215               elm_layout_signal_emit(term->bg, "media,scale", "terminology");
7216               elm_layout_signal_emit(term->core, "media,scale", "terminology");
7217               break;
7218            case MEDIA_TYPE_EDJE:
7219              elm_layout_signal_emit(term->bg, "media,edje", "terminology");
7220              elm_layout_signal_emit(term->core, "media,edje", "terminology");
7221              break;
7222            case MEDIA_TYPE_MOV:
7223              elm_layout_signal_emit(term->bg, "media,movie", "terminology");
7224              elm_layout_signal_emit(term->core, "media,movie", "terminology");
7225              break;
7226            case MEDIA_TYPE_UNKNOWN:
7227            default:
7228              break;
7229           }
7230      }
7231 
7232    DBG("is focused? tc:%p", term->container);
7233    if (term_is_focused(term) && (_win_is_focused(term->wn)))
7234      {
7235         if (term->config->disable_focus_visuals)
7236           {
7237              elm_layout_signal_emit(term->bg, "focused,set", "terminology");
7238              elm_layout_signal_emit(term->core, "focused,set", "terminology");
7239           }
7240         else
7241           {
7242              elm_layout_signal_emit(term->bg, "focus,in", "terminology");
7243              elm_layout_signal_emit(term->core, "focus,in", "terminology");
7244           }
7245      }
7246    if (term->miniview_shown)
7247         elm_layout_signal_emit(term->bg, "miniview,on", "terminology");
7248 
7249    _term_media_update(term, term->config);
7250 }
7251 
7252 static void
_cb_tabregion_change(void * data,Evas * _e EINA_UNUSED,Evas_Object * obj,void * _info EINA_UNUSED)7253 _cb_tabregion_change(void *data,
7254                      Evas *_e EINA_UNUSED,
7255                      Evas_Object *obj,
7256                      void *_info EINA_UNUSED)
7257 {
7258    Term *term = data;
7259    Evas_Coord w, h;
7260 
7261    evas_object_geometry_get(obj, NULL, NULL, &w, &h);
7262    evas_object_size_hint_min_set(term->tab_region_base, w, h);
7263    elm_layout_content_set(term->core, "terminology.tabregion",
7264                           term->tab_region_base);
7265 }
7266 
7267 static void
_term_tabregion_setup(Term * term)7268 _term_tabregion_setup(Term *term)
7269 {
7270    Evas_Object *o;
7271 
7272    if (term->tab_region_bg) return;
7273    term->tab_region_bg = o = evas_object_rectangle_add(evas_object_evas_get(term->bg));
7274    evas_object_color_set(o, 0, 0, 0, 0);
7275    evas_object_event_callback_add(o, EVAS_CALLBACK_RESIZE, _cb_tabregion_change, term);
7276    elm_layout_content_set(term->bg, "terminology.tabregion", o);
7277 
7278    term->tab_region_base = o = evas_object_rectangle_add(evas_object_evas_get(term->bg));
7279    evas_object_color_set(o, 0, 0, 0, 0);
7280    elm_layout_content_set(term->core, "terminology.tabregion", o);
7281 }
7282 
7283 static void
_term_tabregion_free(Term * term)7284 _term_tabregion_free(Term *term)
7285 {
7286    evas_object_del(term->tab_region_bg);
7287    term->tab_region_bg = NULL;
7288 
7289    evas_object_del(term->tab_region_base);
7290    term->tab_region_base = NULL;
7291 }
7292 
7293 Eina_Bool
main_term_popup_exists(const Term * term)7294 main_term_popup_exists(const Term *term)
7295 {
7296    return term->popmedia || term->popmedia_queue;
7297 }
7298 
7299 Win *
term_win_get(const Term * term)7300 term_win_get(const Term *term)
7301 {
7302    return term->wn;
7303 }
7304 
7305 
7306 Evas_Object *
term_termio_get(const Term * term)7307 term_termio_get(const Term *term)
7308 {
7309    return term->termio;
7310 }
7311 
7312 Evas_Object *
term_miniview_get(const Term * term)7313 term_miniview_get(const Term *term)
7314 {
7315    if (term)
7316      return term->miniview;
7317    return NULL;
7318 }
7319 
7320 Evas_Object *
term_bg_get(const Term * term)7321 term_bg_get(const Term *term)
7322 {
7323    if (term)
7324      return term->bg_edj;
7325    return NULL;
7326 }
7327 
7328 
7329 static void
_cb_bell(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)7330 _cb_bell(void *data,
7331          Evas_Object *_obj EINA_UNUSED,
7332          void *_event EINA_UNUSED)
7333 {
7334    Term *term = data;
7335    Term_Container *tc;
7336 
7337    tc = term->container;
7338 
7339    tc->bell(tc, tc);
7340 }
7341 
7342 
7343 static void
_cb_options_done(void * data)7344 _cb_options_done(void *data)
7345 {
7346    Term *orig_term = data;
7347    Win *wn = orig_term->wn;
7348 
7349    elm_object_focus_highlight_style_set(wn->base, "blank");
7350    _on_popover_done(wn);
7351 
7352    term_unref(orig_term);
7353 }
7354 
7355 static void
_cb_options(void * data,Evas_Object * _obj EINA_UNUSED,void * _event EINA_UNUSED)7356 _cb_options(void *data,
7357             Evas_Object *_obj EINA_UNUSED,
7358             void *_event EINA_UNUSED)
7359 {
7360    Term *term = data;
7361    Term_Container *tc = term->container;
7362 
7363    term->wn->on_popover++;
7364 
7365    term_ref(term);
7366 
7367    tc->unfocus(tc, NULL);
7368 
7369    elm_object_focus_highlight_style_set(term->wn->base, "default");
7370    controls_show(term->wn->win, term->wn->base, term->bg_edj, term->termio,
7371                  _cb_options_done, term);
7372 }
7373 
7374 void
term_ref(Term * term)7375 term_ref(Term *term)
7376 {
7377    term->refcnt++;
7378 }
7379 
7380 void
term_unref(Term * term)7381 term_unref(Term *term)
7382 {
7383    EINA_SAFETY_ON_NULL_RETURN(term);
7384 
7385    term->refcnt--;
7386    if (term->refcnt <= 0)
7387      {
7388         _term_free(term);
7389      }
7390 }
7391 
7392 
7393 Term *
term_new(Win * wn,Config * config,const char * cmd,Eina_Bool login_shell,const char * cd,int size_w,int size_h,Eina_Bool hold,const char * title)7394 term_new(Win *wn, Config *config, const char *cmd,
7395          Eina_Bool login_shell, const char *cd,
7396          int size_w, int size_h, Eina_Bool hold,
7397          const char *title)
7398 {
7399    Term *term;
7400    Evas_Object *o;
7401 
7402    term = calloc(1, sizeof(Term));
7403    if (!term) return NULL;
7404    term_ref(term);
7405 
7406    if (!config) abort();
7407 
7408    termpty_init();
7409    miniview_init();
7410    gravatar_init();
7411 
7412    term->wn = wn;
7413    term->hold = hold;
7414    term->config = config;
7415 
7416    term->core = o = elm_layout_add(wn->win);
7417    theme_apply(o, term->config, "terminology/core", NULL, NULL, EINA_TRUE);
7418    evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
7419    evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
7420 
7421    theme_auto_reload_enable(o);
7422    evas_object_data_set(o, "theme_reload_func", _term_bg_config);
7423    evas_object_data_set(o, "theme_reload_func_data", term);
7424    evas_object_show(o);
7425 
7426    term->bg = o = elm_layout_add(wn->win);
7427    term->bg_edj = elm_layout_edje_get(o);
7428    evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
7429    evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
7430    if (!theme_apply(o, config, "terminology/background", NULL, NULL, EINA_TRUE))
7431      {
7432         CRITICAL(_("Couldn't find terminology theme! Forgot 'ninja install'?"));
7433         evas_object_del(term->bg);
7434         free(term);
7435         return NULL;
7436      }
7437 
7438    theme_auto_reload_enable(o);
7439    evas_object_data_set(o, "theme_reload_func", _term_bg_config);
7440    evas_object_data_set(o, "theme_reload_func_data", term);
7441    evas_object_show(o);
7442 
7443    _term_tabregion_setup(term);
7444 
7445 
7446    if (term->config->mv_always_show)
7447      term->miniview_shown = EINA_TRUE;
7448 
7449    _term_trans(term);
7450 
7451    background_set_shine(term->config, term->bg_edj);
7452 
7453    term->termio = o = termio_add(wn->win, config, cmd, login_shell, cd,
7454                                  size_w, size_h, term, title);
7455    evas_object_data_set(o, "term", term);
7456    colors_term_init(termio_textgrid_get(term->termio),
7457                     term->config->color_scheme);
7458 
7459    termio_theme_set(o, term->bg_edj);
7460 
7461    term->miniview = o = miniview_add(wn->win, term->termio);
7462    evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
7463    evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
7464 
7465    o = term->termio;
7466    evas_object_size_hint_weight_set(o, 0, EVAS_HINT_EXPAND);
7467    evas_object_size_hint_fill_set(o, 0, EVAS_HINT_FILL);
7468    evas_object_event_callback_add(o, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
7469                                   _cb_size_hint, term);
7470    elm_layout_content_set(term->core, "terminology.content", o);
7471 
7472    elm_layout_content_set(term->bg, "terminology.content", term->core);
7473    elm_layout_content_set(term->bg, "terminology.miniview", term->miniview);
7474 
7475    evas_object_smart_callback_add(o, "options", _cb_options, term);
7476    evas_object_smart_callback_add(o, "bell", _cb_bell, term);
7477    evas_object_smart_callback_add(o, "popup", _cb_popup, term);
7478    evas_object_smart_callback_add(o, "popup,queue", _cb_popup_queue, term);
7479    evas_object_smart_callback_add(o, "cmdbox", _cb_cmdbox, term);
7480    evas_object_smart_callback_add(o, "command", _cb_command, term);
7481    evas_object_smart_callback_add(o, "prev", _cb_prev, term);
7482    evas_object_smart_callback_add(o, "next", _cb_next, term);
7483    evas_object_smart_callback_add(o, "new", _cb_new, term);
7484    evas_object_smart_callback_add(o, "close", _cb_close, term);
7485    evas_object_smart_callback_add(o, "select", _cb_select, term);
7486    evas_object_smart_callback_add(o, "split,h", _cb_split_h, term);
7487    evas_object_smart_callback_add(o, "split,v", _cb_split_v, term);
7488    evas_object_smart_callback_add(o, "title,change", _cb_title, term);
7489    evas_object_smart_callback_add(o, "icon,change", _cb_icon, term);
7490    evas_object_smart_callback_add(o, "send,progress", _cb_send_progress, term);
7491    evas_object_smart_callback_add(o, "send,end", _cb_send_end, term);
7492    evas_object_show(o);
7493 
7494    wn->terms = eina_list_append(wn->terms, term);
7495 
7496    _term_bg_config(term);
7497 
7498    return term;
7499 }
7500 
7501 
7502 /* }}} */
7503 
7504 static Eina_Bool
_font_size_set(Term * term,void * data)7505 _font_size_set(Term *term, void *data)
7506 {
7507    int fontsize = (intptr_t) data;
7508 
7509    termio_font_size_set(term->termio, fontsize);
7510 
7511    return ECORE_CALLBACK_PASS_ON;
7512 }
7513 
7514 void
win_font_size_set(Win * wn,int new_size)7515 win_font_size_set(Win *wn, int new_size)
7516 {
7517    for_each_term_do(wn, &_font_size_set, (void*)(intptr_t)new_size);
7518 }
7519 
7520 static Eina_Bool
_font_update(Term * term,void * _data EINA_UNUSED)7521 _font_update(Term *term, void *_data EINA_UNUSED)
7522 {
7523    termio_font_update(term->termio);
7524 
7525    return ECORE_CALLBACK_PASS_ON;
7526 }
7527 
7528 void
win_font_update(Term * term)7529 win_font_update(Term *term)
7530 {
7531    Win *wn = term->wn;
7532    for_each_term_do(wn, &_font_update, NULL);
7533 }
7534 
7535 void
windows_free(void)7536 windows_free(void)
7537 {
7538    Eina_List *l, *l_next;
7539    Win *wn;
7540 
7541    EINA_LIST_FOREACH_SAFE(wins, l, l_next, wn)
7542      {
7543         win_free(wn);
7544      }
7545 
7546    /* TODO: ugly */
7547    if (_win_log_dom < 0) return;
7548    eina_log_domain_unregister(_win_log_dom);
7549    _win_log_dom = -1;
7550 }
7551 
7552 void
windows_update(void)7553 windows_update(void)
7554 {
7555    Eina_List *l;
7556    Win *wn;
7557 
7558    EINA_LIST_FOREACH(wins, l, wn)
7559      {
7560         Term_Container *tc = (Term_Container *) wn;
7561         tc->update(tc);
7562      }
7563 }
7564 
7565 Eina_Bool
for_each_term_do(Win * wn,For_Each_Term cb,void * data)7566 for_each_term_do(Win *wn, For_Each_Term cb, void *data)
7567 {
7568    Eina_List *l;
7569    Term *term;
7570    Eina_Bool res = ECORE_CALLBACK_DONE;
7571 
7572    EINA_LIST_FOREACH(wn->terms, l, term)
7573      {
7574         res = cb(term, data);
7575         if (res == ECORE_CALLBACK_CANCEL)
7576           return res;
7577      }
7578    return res;
7579 }
7580