1 #include "e_mod_tiling.h"
2 
3 /* There are two major concepts, (un)track and add/remove client.
4  * track - track all windows regardless if we are interested in them or not.
5  *    We need that in order to keep proper track of things as they change.
6  * add/remove: Clients should be tiled/untiled.
7  */
8 
9 /* types {{{ */
10 
11 #define TILING_POPUP_TIMEOUT 0.8
12 #define TILING_POPUP_SIZE 100
13 
14 static Eina_Bool started = EINA_FALSE;
15 
16 typedef struct geom_t
17 {
18    int x, y, w, h;
19 } geom_t;
20 
21 typedef enum {
22    POSITION_TOP = 0,
23    POSITION_RIGHT = 1,
24    POSITION_BOTTOM = 2,
25    POSITION_LEFT = 3
26 } Position_On_Client;
27 
28 typedef struct Client_Extra
29 {
30    E_Client *client;
31    geom_t    expected;
32    struct
33    {
34       Eina_Bool drag;
35       Evas_Object *hint, *ic;
36       Ecore_Event_Handler *move, *up;
37       int x,y; /* start points */
38    } drag;
39    struct
40    {
41       geom_t      geom;
42       E_Maximize  maximized;
43       const char *bordername;
44    } orig;
45    int       last_frame_adjustment; // FIXME: Hack for frame resize bug.
46    Eina_Bool floating E_BITFIELD;
47    Eina_Bool tiled E_BITFIELD;
48    Eina_Bool tracked E_BITFIELD;
49 } Client_Extra;
50 
51 typedef struct _Instance
52 {
53    E_Gadcon_Client  *gcc;
54    Evas_Object      *gadget;
55    Eina_Stringshare *gad_id;
56 
57    E_Menu           *lmenu;
58 } Instance;
59 
60 typedef struct {
61    E_Desk *desk;
62    Tiling_Split_Type type;
63 } Desk_Split_Type;
64 
65 struct tiling_g tiling_g = {
66    .module = NULL,
67    .config = NULL,
68    .log_domain = -1,
69 };
70 
71 static void _client_track(E_Client *ec);
72 static void _client_untrack(E_Client *ec);
73 static Eina_Bool _add_client(E_Client *ec, Tiling_Split_Type type);
74 static void             _remove_client(E_Client *ec);
75 static void             _client_apply_settings(E_Client *ec, Client_Extra *extra);
76 static void             _foreach_desk(void (*func)(E_Desk *desk));
77 static Eina_Bool _toggle_tiling_based_on_state(E_Client *ec, Eina_Bool restore);
78 static void _edje_tiling_icon_set(Evas_Object *o);
79 static void _desk_config_apply(E_Desk *d, int old_nb_stacks, int new_nb_stacks);
80 static void _update_current_desk(E_Desk *new);
81 static void _client_drag_terminate(E_Client *ec);
82 
83 /* Func Proto Requirements for Gadcon */
84 static E_Gadcon_Client *_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style);
85 static void             _gc_shutdown(E_Gadcon_Client *gcc);
86 static void             _gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient);
87 
88 static const char  *_gc_label(const E_Gadcon_Client_Class *client_class EINA_UNUSED);
89 static Evas_Object *_gc_icon(const E_Gadcon_Client_Class *client_class EINA_UNUSED, Evas *evas);
90 static const char  *_gc_id_new(const E_Gadcon_Client_Class *client_class EINA_UNUSED);
91 
92 static void         _gadget_icon_set(Instance *inst);
93 
94 /* }}} */
95 /* Globals {{{ */
96 
97 static struct tiling_mod_main_g
98 {
99    char                 edj_path[PATH_MAX];
100    E_Config_DD         *config_edd, *vdesk_edd;
101    Ecore_Event_Handler *handler_client_resize, *handler_client_move,
102                        *handler_client_iconify, *handler_client_uniconify,
103                        *handler_desk_set, *handler_compositor_resize,
104                        *handler_desk_show;
105    E_Client_Hook       *handler_client_resize_begin, *handler_client_add,
106                        *handler_move_begin, *handler_move_end;
107    E_Client_Menu_Hook  *client_menu_hook;
108 
109    Tiling_Info         *tinfo;
110    Eina_Hash           *info_hash;
111    Eina_Hash           *client_extras;
112    Eina_Hash           *desk_type;
113 
114    E_Action            *act_togglefloat, *act_move_up, *act_move_down, *act_move_left,
115                        *act_move_right, *act_toggle_split_mode, *act_swap_window;
116 
117    Desk_Split_Type     *current_split_type;
118    Ecore_Job           *apply_tree_job;
119 
120    struct {
121         Evas_Object *comp_obj;
122         Evas_Object *obj;
123         Ecore_Timer *timer;
124         E_Desk *desk;
125    } split_popup;
126 } _G =
127 {
128 
129 };
130 
131 /* Define the class and gadcon functions this module provides */
132 static const E_Gadcon_Client_Class _gc_class =
133 {
134    GADCON_CLIENT_CLASS_VERSION, "tiling",
135    { _gc_init, _gc_shutdown, _gc_orient, _gc_label, _gc_icon, _gc_id_new,
136      NULL, NULL },
137    E_GADCON_CLIENT_STYLE_PLAIN
138 };
139 
140 /* }}} */
141 /* Utils {{{ */
142 
143 /* I wonder why no one has implemented the following one yet? */
144 static E_Desk *
get_current_desk(void)145 get_current_desk(void)
146 {
147    E_Zone *z = e_zone_current_get();
148 
149    return e_desk_current_get(z);
150 }
151 
152 static Tiling_Split_Type
_current_tiled_state(Eina_Bool allow_float)153 _current_tiled_state(Eina_Bool allow_float)
154 {
155    //update the current desk in case something has changed it
156    _update_current_desk(get_current_desk());
157 
158    if (!_G.current_split_type)
159      {
160         ERR("Invalid state, the current field can never be NULL");
161         return TILING_SPLIT_HORIZONTAL;
162      }
163 
164    if (!allow_float &&
165        _G.current_split_type->type == TILING_SPLIT_FLOAT)
166      return TILING_SPLIT_HORIZONTAL;
167    return _G.current_split_type->type;
168 }
169 
170 static Tiling_Info *
_initialize_tinfo(const E_Desk * desk)171 _initialize_tinfo(const E_Desk *desk)
172 {
173    Tiling_Info *tinfo;
174 
175    tinfo = E_NEW(Tiling_Info, 1);
176    tinfo->desk = desk;
177    eina_hash_direct_add(_G.info_hash, &tinfo->desk, tinfo);
178 
179    tinfo->conf =
180      get_vdesk(tiling_g.config->vdesks, desk->x, desk->y, desk->zone->num);
181 
182    return tinfo;
183 }
184 
185 static void
check_tinfo(const E_Desk * desk)186 check_tinfo(const E_Desk *desk)
187 {
188    if (!desk) return;
189    if (!_G.tinfo || _G.tinfo->desk != desk)
190      {
191         _G.tinfo = eina_hash_find(_G.info_hash, &desk);
192         if (!_G.tinfo)
193           {
194              /* lazy init */
195              _G.tinfo = _initialize_tinfo(desk);
196           }
197         if (!_G.tinfo->conf)
198           {
199              _G.tinfo->conf =
200                get_vdesk(tiling_g.config->vdesks, desk->x, desk->y,
201                          desk->zone->num);
202           }
203      }
204 }
205 
206 static Eina_Bool
desk_should_tile_check(const E_Desk * desk)207 desk_should_tile_check(const E_Desk *desk)
208 {
209    check_tinfo(desk);
210    return _G.tinfo && _G.tinfo->conf && _G.tinfo->conf->nb_stacks;
211 }
212 
213 static int
is_ignored_window(const Client_Extra * extra)214 is_ignored_window(const Client_Extra *extra)
215 {
216    if (extra->client->sticky || extra->client->maximized ||
217          extra->client->fullscreen || extra->floating)
218      return true;
219 
220    return false;
221 }
222 
223 static int
is_tilable(const E_Client * ec)224 is_tilable(const E_Client *ec)
225 {
226    if (ec->icccm.min_h == ec->icccm.max_h && ec->icccm.max_h > 0)
227      return false;
228 
229    if (ec->e.state.centered || e_win_centered_get(ec->internal_elm_win))
230      return false;
231 
232    if (!tiling_g.config->tile_dialogs && ((ec->icccm.transient_for != 0) ||
233                                           (ec->netwm.type == E_WINDOW_TYPE_DIALOG)))
234      return false;
235 
236    if (ec->fullscreen)
237       return false;
238 
239    if (ec->maximized)
240       return false;
241 
242    if (ec->iconic)
243       return false;
244 
245    if (ec->sticky)
246       return false;
247 
248    if (e_client_util_ignored_get(ec))
249      return false;
250 
251    if (e_object_is_del(E_OBJECT(ec)))
252      return false;
253 
254    return true;
255 }
256 
257 static void
change_window_border(E_Client * ec,const char * bordername)258 change_window_border(E_Client *ec, const char *bordername)
259 {
260    if (ec->mwm.borderless)
261       return;
262 
263    ec->border.changed = 0;
264    if (e_client_border_set(ec, bordername))
265      eina_stringshare_refplace(&ec->bordername, ec->border.name);
266 
267    DBG("%p -> border %s", ec, bordername);
268 }
269 
270 static Eina_Bool
_info_hash_update(const Eina_Hash * hash EINA_UNUSED,const void * key EINA_UNUSED,void * data,void * fdata EINA_UNUSED)271 _info_hash_update(const Eina_Hash *hash EINA_UNUSED,
272                   const void *key EINA_UNUSED, void *data, void *fdata EINA_UNUSED)
273 {
274    Tiling_Info *tinfo = data;
275    int old_nb_stacks = 0, new_nb_stacks = 0;
276 
277    if (tinfo->conf)
278      {
279         old_nb_stacks = tinfo->conf->nb_stacks;
280      }
281 
282    if (tinfo->desk)
283      {
284         tinfo->conf =
285           get_vdesk(tiling_g.config->vdesks, tinfo->desk->x, tinfo->desk->y,
286                     tinfo->desk->zone->num);
287 
288         if (tinfo->conf)
289           {
290              new_nb_stacks = tinfo->conf->nb_stacks;
291           }
292 
293         _desk_config_apply((E_Desk *) tinfo->desk, old_nb_stacks, new_nb_stacks);
294      }
295    else
296      {
297         tinfo->conf = NULL;
298      }
299 
300    return true;
301 }
302 
303 void
e_tiling_update_conf(void)304 e_tiling_update_conf(void)
305 {
306    eina_hash_foreach(_G.info_hash, _info_hash_update, NULL);
307 }
308 
309 static void
_e_client_move_resize(E_Client * ec,int x,int y,int w,int h)310 _e_client_move_resize(E_Client *ec, int x, int y, int w, int h)
311 {
312    Client_Extra *extra;
313 
314    extra = eina_hash_find(_G.client_extras, &ec);
315    if (!extra)
316      {
317         ERR("No extra for %p", ec);
318         return;
319      }
320 
321    extra->last_frame_adjustment =
322      MAX(ec->h - ec->client.h, ec->w - ec->client.w);
323    DBG("%p -> %dx%d+%d+%d", ec, w, h, x, y);
324    evas_object_geometry_set(ec->frame, x, y, w, h);
325 }
326 
327 static void
_e_client_unmaximize(E_Client * ec,E_Maximize max)328 _e_client_unmaximize(E_Client *ec, E_Maximize max)
329 {
330    DBG("%p -> %s", ec,
331        (max & E_MAXIMIZE_DIRECTION) ==
332        E_MAXIMIZE_NONE ? "NONE" : (max & E_MAXIMIZE_DIRECTION) ==
333        E_MAXIMIZE_VERTICAL ? "VERTICAL" : (max & E_MAXIMIZE_DIRECTION) ==
334        E_MAXIMIZE_HORIZONTAL ? "HORIZONTAL" : "BOTH");
335    e_client_unmaximize(ec, max);
336 }
337 
338 static Client_Extra *
_restore_client(E_Client * ec)339 _restore_client(E_Client *ec)
340 {
341    Client_Extra *extra;
342 
343    extra = eina_hash_find(_G.client_extras, &ec);
344    if (!extra)
345      {
346         ERR("No extra for %p", ec);
347         return NULL;
348      }
349 
350    if (!extra->tiled)
351      return NULL;
352 
353    _client_untrack(ec);
354    if (!ec->maximized && !ec->fullscreen)
355      {
356         _e_client_move_resize(ec, extra->orig.geom.x, extra->orig.geom.y,
357               extra->orig.geom.w, extra->orig.geom.h);
358         if (extra->orig.maximized != ec->maximized)
359           {
360              e_client_maximize(ec, extra->orig.maximized);
361              ec->maximized = extra->orig.maximized;
362           }
363      }
364 
365    DBG("Change window border back to %s for %p", extra->orig.bordername, ec);
366    change_window_border(ec,
367                         (extra->orig.bordername) ? extra->orig.bordername : "default");
368 
369    return extra;
370 }
371 
372 static Client_Extra *
_get_or_create_client_extra(E_Client * ec)373 _get_or_create_client_extra(E_Client *ec)
374 {
375    Client_Extra *extra;
376 
377    extra = eina_hash_find(_G.client_extras, &ec);
378    if (!extra)
379      {
380         extra = E_NEW(Client_Extra, 1);
381         *extra = (Client_Extra)
382         {
383            .client = ec, .expected =
384            {
385               .x = ec->x, .y = ec->y, .w = ec->w, .h = ec->h,
386            }
387 
388            , .orig =
389            {
390               .geom =
391               {
392                  .x = ec->x, .y = ec->y, .w = ec->w, .h = ec->h,
393               }
394 
395               , .maximized = ec->maximized, .bordername =
396                 eina_stringshare_add(ec->bordername),
397            }
398 
399            ,
400         };
401         eina_hash_direct_add(_G.client_extras, &extra->client, extra);
402      }
403    else
404      {
405         extra->expected = (geom_t)
406         {
407            .x = ec->x, .y = ec->y, .w = ec->w, .h = ec->h,
408         };
409         extra->orig.geom = extra->expected;
410         extra->orig.maximized = ec->maximized;
411         eina_stringshare_replace(&extra->orig.bordername, ec->bordername);
412      }
413 
414    return extra;
415 }
416 
417 void
tiling_e_client_move_resize_extra(E_Client * ec,int x,int y,int w,int h)418 tiling_e_client_move_resize_extra(E_Client *ec, int x, int y, int w, int h)
419 {
420    Client_Extra *extra = eina_hash_find(_G.client_extras, &ec);
421 
422    if (!extra)
423      {
424         ERR("No extra for %p", ec);
425         return;
426      }
427 
428    extra->expected = (geom_t)
429    {
430       .x = x, .y = y, .w = w, .h = h,
431    };
432 
433    _e_client_move_resize(ec, x, y, w, h);
434 }
435 
436 static Client_Extra *
tiling_entry_no_desk_func(E_Client * ec)437 tiling_entry_no_desk_func(E_Client *ec)
438 {
439    if (!ec)
440      return NULL;
441 
442    Client_Extra *extra = eina_hash_find(_G.client_extras, &ec);
443 
444    if (!extra)
445      ERR("No extra for %p", ec);
446 
447    return extra;
448 }
449 
450 static Client_Extra *
tiling_entry_func(E_Client * ec)451 tiling_entry_func(E_Client *ec)
452 {
453    Client_Extra *extra;
454 
455    if (!is_tilable(ec))
456      return NULL;
457 
458    extra = tiling_entry_no_desk_func(ec);
459 
460    if (!extra)
461       return NULL;
462 
463    if (!desk_should_tile_check(ec->desk))
464      return NULL;
465 
466    return extra;
467 }
468 
469 /* }}} */
470 /* Reorganize Stacks {{{ */
471 
472 static void
_reapply_tree(void)473 _reapply_tree(void)
474 {
475    int zx, zy, zw, zh;
476 
477    if (_G.tinfo->tree)
478      {
479         e_zone_desk_useful_geometry_get(_G.tinfo->desk->zone, _G.tinfo->desk, &zx, &zy, &zw, &zh);
480 
481         if (zw > 0 && zh > 0)
482           tiling_window_tree_apply(_G.tinfo->tree, zx, zy, zw, zh,
483                                    tiling_g.config->window_padding,
484                                    EINA_TRUE);
485         else
486           ERR("The zone desk geometry was not useful at all (%d,%d,%d,%d)", zx, zy, zw, zh);
487      }
488 }
489 
490 void
_restore_free_client(void * _item)491 _restore_free_client(void *_item)
492 {
493    Window_Tree *item = _item;
494 
495    if (item->client)
496      {
497         _restore_client(item->client);
498 
499         Client_Extra *extra = eina_hash_find(_G.client_extras, &item->client);
500 
501         if (extra)
502           {
503              extra->tiled = EINA_FALSE;
504           }
505      }
506    free(item);
507 }
508 
509 void
change_desk_conf(struct _Config_vdesk * newconf)510 change_desk_conf(struct _Config_vdesk *newconf)
511 {
512    E_Zone *z;
513    E_Desk *d;
514    int old_nb_stacks, new_nb_stacks = newconf->nb_stacks;
515 
516    z = e_comp_zone_number_get(newconf->zone_num);
517    if (!z)
518      return;
519    d = e_desk_at_xy_get(z, newconf->x, newconf->y);
520    if (!d)
521      return;
522 
523    check_tinfo(d);
524    old_nb_stacks = _G.tinfo->conf->nb_stacks;
525 
526    _G.tinfo->conf = newconf;
527    _G.tinfo->conf->nb_stacks = new_nb_stacks;
528 
529    _desk_config_apply(d, old_nb_stacks, new_nb_stacks);
530 }
531 
532 static void
_desk_config_apply(E_Desk * d,int old_nb_stacks,int new_nb_stacks)533 _desk_config_apply(E_Desk *d, int old_nb_stacks, int new_nb_stacks)
534 {
535    check_tinfo(d);
536 
537    if (new_nb_stacks == 0)
538      {
539         tiling_window_tree_walk(_G.tinfo->tree, _restore_free_client);
540         _G.tinfo->tree = NULL;
541      }
542    else if (new_nb_stacks == old_nb_stacks)
543      {
544         E_Client *ec;
545 
546         E_CLIENT_FOREACH(ec)
547           {
548              _client_apply_settings(ec, NULL);
549           }
550 
551         _reapply_tree();
552      }
553    else
554      {
555         /* Add all the existing windows. */
556         E_Client *ec;
557 
558         E_CLIENT_FOREACH(ec)
559           {
560              _add_client(ec, _current_tiled_state(EINA_TRUE));
561           }
562 
563         _reapply_tree();
564      }
565 }
566 
567 /* }}} */
568 /* Reorganize windows {{{ */
569 
570 static void
_client_apply_settings(E_Client * ec,Client_Extra * extra)571 _client_apply_settings(E_Client *ec, Client_Extra *extra)
572 {
573    const char *bdname = "pixel";
574 
575    if (!extra) extra = tiling_entry_func(ec);
576 
577    if ((!extra) || (!extra->tiled)) return;
578 
579    if (ec->maximized) _e_client_unmaximize(ec, E_MAXIMIZE_BOTH);
580 
581    if (e_theme_border_find("tiling")) bdname = "tiling";
582 
583    if ((!tiling_g.config->show_titles) &&
584        (!ec->bordername || strcmp(ec->bordername, bdname)))
585      change_window_border(ec, bdname);
586    else if ((tiling_g.config->show_titles) &&
587             (ec->bordername && !strcmp(ec->bordername, bdname)))
588      change_window_border(ec, (extra->orig.bordername) ?
589                           extra->orig.bordername : "default");
590 }
591 
592 static void
_e_client_check_based_on_state_cb(void * data,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)593 _e_client_check_based_on_state_cb(void *data, Evas_Object *obj EINA_UNUSED,
594       void *event_info EINA_UNUSED)
595 {
596    E_Client *ec = data;
597    _toggle_tiling_based_on_state(ec, EINA_TRUE);
598 }
599 
600 /**
601  * Find the next tiled client under the current coordinates
602  */
603 static Window_Tree*
_tilable_client(int x,int y)604 _tilable_client(int x, int y)
605 {
606    E_Client *ec;
607 
608    E_CLIENT_FOREACH(ec)
609      {
610         Eina_Rectangle c;
611         Window_Tree *wt;
612 
613         e_client_geometry_get(ec, &c.x, &c.y, &c.w, &c.h);
614 
615         if (!eina_rectangle_coords_inside(&c, x, y)) continue;
616 
617         if (!(wt = tiling_window_tree_client_find(_G.tinfo->tree, ec))) continue;
618 
619         return wt;
620      }
621   return NULL;
622 }
623 
624 static Position_On_Client
_calculate_position_preference(E_Client * ec)625 _calculate_position_preference(E_Client *ec)
626 {
627    int x,y;
628    float bounded_x, bounded_y;
629    Eina_Rectangle rect;
630    evas_pointer_canvas_xy_get(e_comp->evas, &x, &y);
631 
632    e_client_geometry_get(ec, &rect.x, &rect.y, &rect.w, &rect.h);
633 
634    if (!eina_rectangle_coords_inside(&rect, x, y))
635      {
636         ERR("Coorinates are not in there");
637         return -1;
638      }
639 
640    //for the calculation we think of a X cross in the rectangle
641    bounded_x = ((float)x - rect.x)/((float)rect.w);
642    bounded_y = ((float)y - rect.y)/((float)rect.h);
643 
644    if (bounded_y < bounded_x)
645      {
646         //right upper part
647         if (bounded_y < (1.0 - bounded_x))
648           {
649              //left upper
650              return POSITION_TOP;
651           }
652         else
653           {
654              //right lower
655              return POSITION_RIGHT;
656           }
657      }
658    else
659      {
660         //lower left part
661         if (bounded_y < (1.0 - bounded_x))
662           {
663              //left upper
664              return POSITION_LEFT;
665           }
666         else
667           {
668              //right lower
669              return POSITION_BOTTOM;
670           }
671      }
672 
673 
674 }
675 
676 static void
_insert_client_preferred(E_Client * ec)677 _insert_client_preferred(E_Client *ec)
678 {
679    Window_Tree *parent;
680    Tiling_Split_Type type = TILING_SPLIT_VERTICAL;
681    Eina_Bool before;
682    int x,y;
683 
684    evas_pointer_canvas_xy_get(e_comp->evas, &x, &y);
685    parent = _tilable_client(x,y);
686 
687    if (parent)
688      {
689         //calculate a good position where we would like to stay
690         Position_On_Client c;
691 
692         c = _calculate_position_preference(parent->client);
693         if (c == POSITION_TOP || c == POSITION_BOTTOM)
694           {
695              before = (c == POSITION_TOP);
696              type = TILING_SPLIT_VERTICAL;
697           }
698         else
699           {
700              before = (c == POSITION_LEFT);
701              type = TILING_SPLIT_HORIZONTAL;
702           }
703 
704         _G.tinfo->tree = tiling_window_tree_insert(_G.tinfo->tree, parent, ec, type, before);
705      }
706    else
707      {
708         _G.tinfo->tree = tiling_window_tree_insert(_G.tinfo->tree, NULL, ec, _current_tiled_state(EINA_FALSE), EINA_FALSE);
709      }
710 }
711 
712 static void
_insert_client(E_Client * ec,Tiling_Split_Type type)713 _insert_client(E_Client *ec, Tiling_Split_Type type)
714 {
715    E_Client *ec_focused = e_client_focused_get();
716    Window_Tree *place = NULL;
717 
718    if (ec_focused == ec)
719      {
720         _insert_client_preferred(ec);
721      }
722    else
723      {
724         //otherwise place next to the given client
725         place = tiling_window_tree_client_find(_G.tinfo->tree,
726                                                ec_focused);
727         _G.tinfo->tree = tiling_window_tree_insert(_G.tinfo->tree, place, ec, type, EINA_FALSE);
728 
729      }
730 
731 }
732 
733 static Eina_Bool
_add_client(E_Client * ec,Tiling_Split_Type type)734 _add_client(E_Client *ec, Tiling_Split_Type type)
735 {
736    /* Should I need to check that the client is not already added? */
737    if (!ec)
738      {
739         return EINA_FALSE;
740      }
741 
742    Client_Extra *extra = _get_or_create_client_extra(ec);
743    _client_track(ec);
744 
745    if (!is_tilable(ec))
746      {
747         return EINA_FALSE;
748      }
749 
750    if (!desk_should_tile_check(ec->desk))
751       return EINA_FALSE;
752 
753    if (is_ignored_window(extra))
754       return EINA_FALSE;
755 
756    if (type == TILING_SPLIT_FLOAT)
757      {
758         extra->floating = EINA_TRUE;
759         return EINA_FALSE;
760      }
761 
762    if (extra->tiled)
763       return EINA_FALSE;
764 
765    extra->tiled = EINA_TRUE;
766 
767    DBG("adding %p", ec);
768 
769    _client_apply_settings(ec, extra);
770 
771    /* Window tree updating. */
772    _insert_client(ec, type);
773 
774    if (started)
775      _reapply_tree();
776 
777    return EINA_TRUE;
778 }
779 
780 static Eina_Bool
_client_remove_no_apply(E_Client * ec)781 _client_remove_no_apply(E_Client *ec)
782 {
783    if (!ec)
784       return EINA_FALSE;
785 
786    DBG("removing %p", ec);
787 
788    Client_Extra *extra = eina_hash_find(_G.client_extras, &ec);
789 
790    if (!extra)
791      {
792         if (is_tilable(ec))
793           {
794              ERR("No extra for %p", ec);
795           }
796         return EINA_FALSE;
797      }
798 
799    if (extra->drag.drag)
800      {
801         _client_drag_terminate(ec);
802      }
803 
804    if (!extra->tiled)
805       return EINA_FALSE;
806 
807    extra->tiled = EINA_FALSE;
808 
809    /* Window tree updating. */
810      {
811         /* If focused is NULL, it should return the root. */
812         Window_Tree *item = tiling_window_tree_client_find(_G.tinfo->tree, ec);
813 
814         if (!item)
815           {
816              ERR("Couldn't find tree item for client %p!", ec);
817              return EINA_FALSE;
818           }
819 
820         _G.tinfo->tree = tiling_window_tree_remove(_G.tinfo->tree, item);
821      }
822 
823    return EINA_TRUE;
824 }
825 
826 static void
_remove_client(E_Client * ec)827 _remove_client(E_Client *ec)
828 {
829    if (_client_remove_no_apply(ec))
830      _reapply_tree();
831 }
832 
833 /* }}} */
834 /* Toggle Floating {{{ */
835 
836 static void
toggle_floating(E_Client * ec)837 toggle_floating(E_Client *ec)
838 {
839    Client_Extra *extra = tiling_entry_no_desk_func(ec);
840 
841    if (!extra)
842      {
843         return;
844      }
845 
846    extra->floating = !extra->floating;
847 
848    if (!desk_should_tile_check(ec->desk))
849      return;
850 
851    /* This is the new state, act accordingly. */
852    if (extra->floating)
853      {
854         _restore_client(ec);
855         _remove_client(ec);
856      }
857    else
858      {
859         _add_client(ec, _current_tiled_state(EINA_FALSE));
860      }
861 }
862 
863 static void
_window_tree_apply_delayed(void * data EINA_UNUSED)864 _window_tree_apply_delayed(void *data EINA_UNUSED)
865 {
866    _reapply_tree();
867    ecore_job_del(_G.apply_tree_job);
868    _G.apply_tree_job = NULL;
869 }
870 
871 void
tiling_e_client_does_not_fit(E_Client * ec)872 tiling_e_client_does_not_fit(E_Client *ec)
873 {
874    E_Notification_Notify n;
875    Eina_Strbuf *buf;
876    Client_Extra *extra = tiling_entry_no_desk_func(ec);
877 
878    EINA_SAFETY_ON_NULL_RETURN(extra);
879 
880    buf = eina_strbuf_new();
881    eina_strbuf_append_printf(buf, _("Window %s cannot be tiled\n"),
882                              e_client_util_name_get(ec));
883 
884    memset(&n, 0, sizeof(n));
885    n.app_name = _("Tiling");
886    n.icon.icon = "dialog-error";
887    n.summary = _("Window cannot be tiled");
888    n.body = eina_strbuf_string_get(buf);
889    n.timeout = 8000;
890    e_notification_client_send(&n, NULL, NULL);
891    eina_strbuf_string_free(buf);
892 
893    EINA_SAFETY_ON_TRUE_RETURN(extra->floating);
894 
895    //remove the client here without applying the tree again to break maybe possible recursions
896    {
897       extra->floating = EINA_TRUE;
898       _restore_client(ec);
899       _client_remove_no_apply(ec);
900       _G.apply_tree_job = ecore_job_add(_window_tree_apply_delayed, NULL);
901    }
902 }
903 
904 static void
_e_mod_action_toggle_floating_cb(E_Object * obj EINA_UNUSED,const char * params EINA_UNUSED)905 _e_mod_action_toggle_floating_cb(E_Object *obj EINA_UNUSED,
906                                  const char *params EINA_UNUSED)
907 {
908    toggle_floating(e_client_focused_get());
909 }
910 
911 static E_Client *_go_mouse_client = NULL;
912 
913 static Eina_Bool
_e_mod_action_swap_window_go_mouse(E_Object * obj EINA_UNUSED,const char * params EINA_UNUSED,E_Binding_Event_Mouse_Button * ev EINA_UNUSED)914 _e_mod_action_swap_window_go_mouse(E_Object *obj EINA_UNUSED,
915                                    const char *params EINA_UNUSED,
916                                    E_Binding_Event_Mouse_Button *ev EINA_UNUSED)
917 {
918    E_Client *ec = e_client_under_pointer_get(get_current_desk(), NULL);
919 
920    Client_Extra *extra = tiling_entry_func(ec);
921 
922    if (!extra || !extra->tiled)
923      return EINA_FALSE;
924 
925    _go_mouse_client = ec;
926    return EINA_TRUE;
927 }
928 
929 static Eina_Bool
_e_mod_action_swap_window_end_mouse(E_Object * obj EINA_UNUSED,const char * params EINA_UNUSED,E_Binding_Event_Mouse_Button * ev EINA_UNUSED)930 _e_mod_action_swap_window_end_mouse(E_Object *obj EINA_UNUSED,
931                                     const char *params EINA_UNUSED,
932                                     E_Binding_Event_Mouse_Button *ev EINA_UNUSED)
933 {
934    E_Client *ec = e_client_under_pointer_get(get_current_desk(), NULL);
935    E_Client *first_ec = _go_mouse_client;
936 
937    _go_mouse_client = NULL;
938 
939    if (!first_ec)
940      return EINA_FALSE;
941 
942    Client_Extra *extra = tiling_entry_func(ec);
943 
944    if (!extra || !extra->tiled)
945      return EINA_FALSE;
946 
947    /* XXX: Only support swap on the first desk for now. */
948    if (ec->desk != first_ec->desk)
949      return EINA_FALSE;
950 
951    Window_Tree *item, *first_item;
952 
953    item = tiling_window_tree_client_find(_G.tinfo->tree, ec);
954 
955    if (!item)
956      return EINA_FALSE;
957 
958    first_item = tiling_window_tree_client_find(_G.tinfo->tree, first_ec);
959 
960    if (!first_item)
961      return EINA_FALSE;
962 
963    item->client = first_ec;
964    first_item->client = ec;
965 
966    _reapply_tree();
967    return EINA_TRUE;
968 }
969 
970 static void
_e_mod_menu_border_cb(void * data,E_Menu * m EINA_UNUSED,E_Menu_Item * mi EINA_UNUSED)971 _e_mod_menu_border_cb(void *data, E_Menu *m EINA_UNUSED,
972                       E_Menu_Item *mi EINA_UNUSED)
973 {
974    E_Client *ec = data;
975 
976    toggle_floating(ec);
977 }
978 
979 /* }}} */
980 /* {{{ Move windows */
981 
982 static void
_action_move(int cross_edge)983 _action_move(int cross_edge)
984 {
985    E_Desk *desk;
986    E_Client *focused_ec;
987 
988    desk = get_current_desk();
989    if (!desk)
990      return;
991 
992    focused_ec = e_client_focused_get();
993    if (!focused_ec || focused_ec->desk != desk)
994      return;
995 
996    if (!desk_should_tile_check(desk))
997      return;
998 
999    Window_Tree *item =
1000      tiling_window_tree_client_find(_G.tinfo->tree, focused_ec);
1001 
1002    if (item)
1003      {
1004         tiling_window_tree_node_change_pos(item, cross_edge);
1005 
1006         _reapply_tree();
1007      }
1008 }
1009 
1010 static void
_e_mod_action_move_left_cb(E_Object * obj EINA_UNUSED,const char * params EINA_UNUSED)1011 _e_mod_action_move_left_cb(E_Object *obj EINA_UNUSED,
1012                            const char *params EINA_UNUSED)
1013 {
1014    _action_move(TILING_WINDOW_TREE_EDGE_LEFT);
1015 }
1016 
1017 static void
_e_mod_action_move_right_cb(E_Object * obj EINA_UNUSED,const char * params EINA_UNUSED)1018 _e_mod_action_move_right_cb(E_Object *obj EINA_UNUSED,
1019                             const char *params EINA_UNUSED)
1020 {
1021    _action_move(TILING_WINDOW_TREE_EDGE_RIGHT);
1022 }
1023 
1024 static void
_e_mod_action_move_up_cb(E_Object * obj EINA_UNUSED,const char * params EINA_UNUSED)1025 _e_mod_action_move_up_cb(E_Object *obj EINA_UNUSED,
1026                          const char *params EINA_UNUSED)
1027 {
1028    _action_move(TILING_WINDOW_TREE_EDGE_TOP);
1029 }
1030 
1031 static void
_e_mod_action_move_down_cb(E_Object * obj EINA_UNUSED,const char * params EINA_UNUSED)1032 _e_mod_action_move_down_cb(E_Object *obj EINA_UNUSED,
1033                            const char *params EINA_UNUSED)
1034 {
1035    _action_move(TILING_WINDOW_TREE_EDGE_BOTTOM);
1036 }
1037 
1038 /* }}} */
1039 /* Toggle split mode {{{ */
1040 
1041 static Eina_Bool
_split_type_popup_timer_del_cb(void * data EINA_UNUSED)1042 _split_type_popup_timer_del_cb(void *data EINA_UNUSED)
1043 {
1044    evas_object_hide(_G.split_popup.comp_obj);
1045    evas_object_del(_G.split_popup.comp_obj);
1046    _G.split_popup.comp_obj = NULL;
1047    _G.split_popup.obj = NULL;
1048    _G.split_popup.timer = NULL;
1049    _G.split_popup.desk = NULL;
1050 
1051    return EINA_FALSE;
1052 }
1053 
1054 static void
_tiling_split_type_changed_popup(void)1055 _tiling_split_type_changed_popup(void)
1056 {
1057    Evas_Object *comp_obj = _G.split_popup.comp_obj;
1058    Evas_Object *o = _G.split_popup.obj;
1059    E_Desk *desk = NULL;
1060 
1061    /* If this is not NULL, the rest isn't either. */
1062 
1063    /* check for the current desk we have */
1064    if (e_client_focused_get())
1065      {
1066         E_Client *c;
1067 
1068         c = e_client_focused_get();
1069         desk = c->desk;
1070      }
1071 
1072    if (!o)
1073      {
1074         _G.split_popup.obj = o = edje_object_add(e_comp->evas);
1075         if (!e_theme_edje_object_set(o, "base/theme/modules/tiling",
1076                  "e/modules/tiling/main"))
1077            edje_object_file_set(o, _G.edj_path, "modules/tiling/main");
1078         evas_object_resize(o, TILING_POPUP_SIZE, TILING_POPUP_SIZE);
1079 
1080         _G.split_popup.comp_obj = comp_obj = e_comp_object_util_add(o, E_COMP_OBJECT_TYPE_POPUP);
1081 
1082         if (desk)
1083           e_comp_object_util_center_on_zone(comp_obj, e_zone_current_get());
1084         else
1085           e_comp_object_util_center(comp_obj);
1086         _G.split_popup.desk = desk;
1087         evas_object_layer_set(comp_obj, E_LAYER_POPUP);
1088         evas_object_pass_events_set(comp_obj, EINA_TRUE);
1089 
1090         evas_object_show(comp_obj);
1091 
1092         _G.split_popup.timer = ecore_timer_loop_add(TILING_POPUP_TIMEOUT, _split_type_popup_timer_del_cb, NULL);
1093      }
1094    else
1095      {
1096         if (desk != _G.split_popup.desk)
1097           e_comp_object_util_center_on_zone(comp_obj, e_zone_current_get());
1098         ecore_timer_loop_reset(_G.split_popup.timer);
1099      }
1100 
1101 
1102    _edje_tiling_icon_set(o);
1103 }
1104 
1105 static void
_tiling_gadgets_update(void)1106 _tiling_gadgets_update(void)
1107 {
1108    Instance *inst;
1109    Eina_List *itr;
1110 
1111    EINA_LIST_FOREACH(tiling_g.gadget_instances, itr, inst)
1112      {
1113         _gadget_icon_set(inst);
1114      }
1115 }
1116 
1117 static void
_tiling_split_type_next(void)1118 _tiling_split_type_next(void)
1119 {
1120    //update the current desk in case something has changed it
1121    _update_current_desk(get_current_desk());
1122 
1123    if (!_G.current_split_type)
1124      {
1125         ERR("Invalid state, current split type is NULL");
1126         return;
1127      }
1128 
1129    _G.current_split_type->type = (_G.current_split_type->type + 1) % TILING_SPLIT_LAST;
1130 
1131    /* If we don't allow floating, skip it. */
1132    if (!tiling_g.config->have_floating_mode &&
1133        (_G.current_split_type->type == TILING_SPLIT_FLOAT))
1134      {
1135         _G.current_split_type->type = (_G.current_split_type->type + 1) % TILING_SPLIT_LAST;
1136      }
1137 
1138    _tiling_gadgets_update();
1139    _tiling_split_type_changed_popup();
1140 }
1141 
1142 static void
_e_mod_action_toggle_split_mode(E_Object * obj EINA_UNUSED,const char * params EINA_UNUSED)1143 _e_mod_action_toggle_split_mode(E_Object *obj EINA_UNUSED,
1144                                 const char *params EINA_UNUSED)
1145 {
1146    _tiling_split_type_next();
1147 }
1148 
1149 /* }}} */
1150 /* Hooks {{{ */
1151 
1152 static void
_move_or_resize(E_Client * ec)1153 _move_or_resize(E_Client *ec)
1154 {
1155    Client_Extra *extra = tiling_entry_func(ec);
1156 
1157    if (!extra || !extra->tiled)
1158      {
1159         return;
1160      }
1161 
1162    if ((ec->x == extra->expected.x) && (ec->y == extra->expected.y) &&
1163        (ec->w == extra->expected.w) && (ec->h == extra->expected.h))
1164      {
1165         return;
1166      }
1167 /* This seems to create more issues than it seemingly solves? it stops
1168  * resizes when no borders are there....
1169    if (!extra->last_frame_adjustment)
1170      {
1171         printf
1172           ("This is probably because of the frame adjustment bug. Return\n");
1173         _reapply_tree();
1174         return;
1175      }
1176  */
1177    Window_Tree *item = tiling_window_tree_client_find(_G.tinfo->tree, ec);
1178 
1179    if (!item)
1180      {
1181         ERR("Couldn't find tree item for resized client %p!", ec);
1182         return;
1183      }
1184 
1185    {
1186       int w_dir = 1, h_dir = 1;
1187       double w_diff = 1.0, h_diff = 1.0;
1188 
1189       if (abs(extra->expected.w - ec->w) >= 1)
1190         {
1191            w_diff = ((double)ec->w) / extra->expected.w;
1192         }
1193       if (abs(extra->expected.h - ec->h) >= 1)
1194         {
1195            h_diff = ((double)ec->h) / extra->expected.h;
1196         }
1197       switch (ec->resize_mode)
1198         {
1199          case E_POINTER_RESIZE_L:
1200          case E_POINTER_RESIZE_BL:
1201            w_dir = -1;
1202            break;
1203 
1204          case E_POINTER_RESIZE_T:
1205          case E_POINTER_RESIZE_TR:
1206            h_dir = -1;
1207            break;
1208 
1209          case E_POINTER_RESIZE_TL:
1210            w_dir = -1;
1211            h_dir = -1;
1212            break;
1213 
1214          default:
1215            break;
1216         }
1217       if ((!eina_dbl_exact(w_diff, 1.0)) || (!eina_dbl_exact(h_diff, 1.0)))
1218         {
1219            if (!tiling_window_tree_node_resize(item, w_dir, w_diff, h_dir,
1220                                                h_diff))
1221              {
1222                 /* FIXME: Do something? */
1223              }
1224         }
1225    }
1226 
1227    _reapply_tree();
1228 }
1229 
1230 static void
_resize_begin_hook(void * data EINA_UNUSED,E_Client * ec)1231 _resize_begin_hook(void *data EINA_UNUSED, E_Client *ec)
1232 {
1233    Client_Extra *extra = tiling_entry_func(ec);
1234 
1235    if (!extra || !extra->tiled)
1236      {
1237         return;
1238      }
1239 
1240    Window_Tree *item = tiling_window_tree_client_find(_G.tinfo->tree, ec);
1241 
1242    if (!item)
1243      {
1244         ERR("Couldn't find tree item for resized client %p!", ec);
1245         return;
1246      }
1247 
1248    int edges = tiling_window_tree_edges_get(item);
1249 
1250    if (edges & TILING_WINDOW_TREE_EDGE_LEFT)
1251      {
1252         switch (ec->resize_mode)
1253           {
1254            case E_POINTER_RESIZE_L:
1255              ec->resize_mode = E_POINTER_RESIZE_NONE;
1256              break;
1257 
1258            case E_POINTER_RESIZE_TL:
1259              ec->resize_mode = E_POINTER_RESIZE_T;
1260              break;
1261 
1262            case E_POINTER_RESIZE_BL:
1263              ec->resize_mode = E_POINTER_RESIZE_B;
1264              break;
1265 
1266            default:
1267              break;
1268           }
1269      }
1270    if (edges & TILING_WINDOW_TREE_EDGE_RIGHT)
1271      {
1272         switch (ec->resize_mode)
1273           {
1274            case E_POINTER_RESIZE_R:
1275              ec->resize_mode = E_POINTER_RESIZE_NONE;
1276              break;
1277 
1278            case E_POINTER_RESIZE_TR:
1279              ec->resize_mode = E_POINTER_RESIZE_T;
1280              break;
1281 
1282            case E_POINTER_RESIZE_BR:
1283              ec->resize_mode = E_POINTER_RESIZE_B;
1284              break;
1285 
1286            default:
1287              break;
1288           }
1289      }
1290    if (edges & TILING_WINDOW_TREE_EDGE_TOP)
1291      {
1292         switch (ec->resize_mode)
1293           {
1294            case E_POINTER_RESIZE_T:
1295              ec->resize_mode = E_POINTER_RESIZE_NONE;
1296              break;
1297 
1298            case E_POINTER_RESIZE_TL:
1299              ec->resize_mode = E_POINTER_RESIZE_L;
1300              break;
1301 
1302            case E_POINTER_RESIZE_TR:
1303              ec->resize_mode = E_POINTER_RESIZE_R;
1304              break;
1305 
1306            default:
1307              break;
1308           }
1309      }
1310    if (edges & TILING_WINDOW_TREE_EDGE_BOTTOM)
1311      {
1312         switch (ec->resize_mode)
1313           {
1314            case E_POINTER_RESIZE_B:
1315              ec->resize_mode = E_POINTER_RESIZE_NONE;
1316              break;
1317 
1318            case E_POINTER_RESIZE_BL:
1319              ec->resize_mode = E_POINTER_RESIZE_L;
1320              break;
1321 
1322            case E_POINTER_RESIZE_BR:
1323              ec->resize_mode = E_POINTER_RESIZE_R;
1324              break;
1325 
1326            default:
1327              break;
1328           }
1329      }
1330 
1331    if (!e_client_util_resizing_get(ec))
1332      e_client_resize_cancel();
1333 }
1334 
1335 static Eina_Bool
_resize_hook(void * data EINA_UNUSED,int type EINA_UNUSED,E_Event_Client * event)1336 _resize_hook(void *data EINA_UNUSED, int type EINA_UNUSED,
1337              E_Event_Client *event)
1338 {
1339    E_Client *ec = event->ec;
1340 
1341    _move_or_resize(ec);
1342 
1343    return true;
1344 }
1345 
1346 static Eina_Bool
_move_hook(void * data EINA_UNUSED,int type EINA_UNUSED,E_Event_Client * event)1347 _move_hook(void *data EINA_UNUSED, int type EINA_UNUSED, E_Event_Client *event)
1348 {
1349    E_Client *ec = event->ec;
1350    Client_Extra *extra = tiling_entry_func(ec);
1351 
1352    if (!extra || !extra->tiled)
1353      {
1354         return true;
1355      }
1356 
1357    /* A hack because e doesn't trigger events for all property changes */
1358    if (!is_tilable(ec))
1359      {
1360         toggle_floating(ec);
1361 
1362         return true;
1363      }
1364 
1365    e_client_act_move_end(event->ec, NULL);
1366 
1367    _reapply_tree();
1368 
1369    return true;
1370 }
1371 
1372 static void
_frame_del_cb(void * data,Evas * evas EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)1373 _frame_del_cb(void *data, Evas *evas EINA_UNUSED,
1374       Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
1375 {
1376    E_Client *ec = data;
1377 
1378    if (desk_should_tile_check(ec->desk))
1379      {
1380         _client_remove_no_apply(ec);
1381      }
1382 
1383    _client_untrack(ec);
1384 
1385    eina_hash_del(_G.client_extras, &ec, NULL);
1386 
1387    _reapply_tree();
1388 }
1389 
1390 static void
_e_client_extra_unregister_callbacks(void * _client_extra)1391 _e_client_extra_unregister_callbacks(void *_client_extra)
1392 {
1393    Client_Extra *extra = _client_extra;
1394 
1395    _client_untrack(extra->client);
1396 }
1397 
1398 static void
_client_untrack(E_Client * ec)1399 _client_untrack(E_Client *ec)
1400 {
1401    Client_Extra *extra = eina_hash_find(_G.client_extras, &ec);
1402 
1403    if (!extra->tracked)
1404       return;
1405 
1406    extra->tracked = EINA_FALSE;
1407 
1408    evas_object_event_callback_del_full(ec->frame, EVAS_CALLBACK_DEL,
1409          _frame_del_cb, ec);
1410    evas_object_smart_callback_del_full(ec->frame, "maximize_done",
1411          _e_client_check_based_on_state_cb, ec);
1412    evas_object_smart_callback_del_full(ec->frame, "frame_recalc_done",
1413          _e_client_check_based_on_state_cb, ec);
1414    evas_object_smart_callback_del_full(ec->frame, "stick",
1415          _e_client_check_based_on_state_cb, ec);
1416    evas_object_smart_callback_del_full(ec->frame, "unstick",
1417          _e_client_check_based_on_state_cb, ec);
1418 }
1419 
1420 static void
_client_track(E_Client * ec)1421 _client_track(E_Client *ec)
1422 {
1423    Client_Extra *extra = eina_hash_find(_G.client_extras, &ec);
1424 
1425    if (extra->tracked)
1426       return;
1427 
1428    extra->tracked = EINA_TRUE;
1429 
1430    evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_DEL,
1431          _frame_del_cb, ec);
1432    evas_object_smart_callback_add(ec->frame, "maximize_done",
1433          _e_client_check_based_on_state_cb, ec);
1434    evas_object_smart_callback_add(ec->frame, "frame_recalc_done",
1435          _e_client_check_based_on_state_cb, ec);
1436    evas_object_smart_callback_add(ec->frame, "stick",
1437          _e_client_check_based_on_state_cb, ec);
1438    evas_object_smart_callback_add(ec->frame, "unstick",
1439          _e_client_check_based_on_state_cb, ec);
1440 }
1441 
1442 static void
_add_hook(void * data EINA_UNUSED,E_Client * ec)1443 _add_hook(void *data EINA_UNUSED, E_Client *ec)
1444 {
1445    if (!ec)
1446      return;
1447 
1448    if (!ec->new_client)
1449      return;
1450 
1451    if (e_object_is_del(E_OBJECT(ec)))
1452      return;
1453 
1454    _add_client(ec, _current_tiled_state(EINA_TRUE));
1455 }
1456 
1457 static Eina_Bool
_toggle_tiling_based_on_state(E_Client * ec,Eina_Bool restore)1458 _toggle_tiling_based_on_state(E_Client *ec, Eina_Bool restore)
1459 {
1460    Client_Extra *extra = eina_hash_find(_G.client_extras, &ec);
1461 
1462    if (!extra)
1463      {
1464         return EINA_FALSE;
1465      }
1466 
1467    /* This is the new state, act accordingly. */
1468    if (extra->tiled && !is_tilable(ec))
1469      {
1470         if (restore)
1471           {
1472              _restore_client(ec);
1473           }
1474         if (desk_should_tile_check(ec->desk))
1475           {
1476              _remove_client(ec);
1477           }
1478 
1479         return EINA_TRUE;
1480      }
1481    else if (!extra->tiled && is_tilable(ec))
1482      {
1483         _add_client(ec, _current_tiled_state(EINA_FALSE));
1484 
1485         return EINA_TRUE;
1486      }
1487 
1488    return EINA_FALSE;
1489 }
1490 
1491 static bool
_iconify_hook(void * data EINA_UNUSED,int type EINA_UNUSED,E_Event_Client * event)1492 _iconify_hook(void *data EINA_UNUSED, int type EINA_UNUSED,
1493                 E_Event_Client *event)
1494 {
1495    E_Client *ec = event->ec;
1496 
1497    if (ec->deskshow)
1498      return true;
1499 
1500    _toggle_tiling_based_on_state(ec, EINA_TRUE);
1501 
1502    return true;
1503 }
1504 
1505 static bool
_desk_set_hook(void * data EINA_UNUSED,int type EINA_UNUSED,E_Event_Client_Desk_Set * ev)1506 _desk_set_hook(void *data EINA_UNUSED, int type EINA_UNUSED,
1507                E_Event_Client_Desk_Set *ev)
1508 {
1509    DBG("%p: from (%d,%d) to (%d,%d)", ev->ec, ev->desk->x, ev->desk->y,
1510        ev->ec->desk->x, ev->ec->desk->y);
1511    Client_Extra *extra = eina_hash_find(_G.client_extras, &ev->ec);
1512 
1513    if (!extra)
1514      {
1515         return true;
1516      }
1517 
1518    //check the state of the new desk
1519    if (desk_should_tile_check(ev->ec->desk))
1520      {
1521         if (extra->drag.drag)
1522           {
1523              ev->ec->hidden = EINA_TRUE;
1524              e_client_comp_hidden_set(ev->ec, EINA_TRUE);
1525              evas_object_hide(ev->ec->frame);
1526              return true;
1527           }
1528      }
1529    else
1530      {
1531         if (extra->drag.drag)
1532           {
1533              _client_drag_terminate(ev->ec);
1534              extra->floating = EINA_TRUE;
1535           }
1536      }
1537 
1538    //check if we should remove that here
1539    if (desk_should_tile_check(ev->desk))
1540      {
1541         if (tiling_window_tree_client_find(_G.tinfo->tree, ev->ec))
1542           {
1543              _restore_client(ev->ec);
1544              _remove_client(ev->ec);
1545           }
1546      }
1547    if (desk_should_tile_check(ev->ec->desk))
1548      {
1549         _add_client(ev->ec, _current_tiled_state(EINA_FALSE));
1550      }
1551 
1552    return true;
1553 }
1554 
1555 static void
_compositor_resize_hook_desk_reapply(E_Desk * desk)1556 _compositor_resize_hook_desk_reapply(E_Desk *desk)
1557 {
1558    check_tinfo(desk);
1559    if (!desk_should_tile_check(desk))
1560      return;
1561 
1562    _reapply_tree();
1563 }
1564 
1565 static bool
_compositor_resize_hook(void * data EINA_UNUSED,int type EINA_UNUSED,void * ev EINA_UNUSED)1566 _compositor_resize_hook(void *data EINA_UNUSED, int type EINA_UNUSED,
1567                         void *ev EINA_UNUSED)
1568 {
1569    _foreach_desk(_compositor_resize_hook_desk_reapply);
1570 
1571    return true;
1572 }
1573 
1574 static void
_bd_hook(void * d EINA_UNUSED,E_Client * ec)1575 _bd_hook(void *d EINA_UNUSED, E_Client *ec)
1576 {
1577    E_Menu_Item *mi;
1578    E_Menu *m;
1579    Eina_List *l;
1580 
1581    if (!ec->border_menu)
1582      return;
1583    m = ec->border_menu;
1584 
1585    Client_Extra *extra = eina_hash_find(_G.client_extras, &ec);
1586 
1587    if (!extra)
1588      {
1589         return;
1590      }
1591 
1592    /* position menu item just before the last separator */
1593    EINA_LIST_REVERSE_FOREACH(m->items, l, mi)
1594      if (mi->separator)
1595        break;
1596    if ((!mi) || (!mi->separator))
1597      return;
1598    l = eina_list_prev(l);
1599    mi = eina_list_data_get(l);
1600    if (!mi)
1601      return;
1602 
1603    mi = e_menu_item_new_relative(m, mi);
1604    e_menu_item_label_set(mi, _("Floating"));
1605    e_menu_item_check_set(mi, true);
1606    e_menu_item_toggle_set(mi, (extra->floating) ? true : false);
1607    e_menu_item_callback_set(mi, _e_mod_menu_border_cb, ec);
1608 }
1609 
1610 /* }}} */
1611 /* Module setup {{{ */
1612 
1613 static void
_clear_info_hash(void * data)1614 _clear_info_hash(void *data)
1615 {
1616    Tiling_Info *ti = data;
1617 
1618    tiling_window_tree_free(ti->tree);
1619    ti->tree = NULL;
1620    E_FREE(ti);
1621 }
1622 
1623 static void
_clear_border_extras(void * data)1624 _clear_border_extras(void *data)
1625 {
1626    Client_Extra *extra = data;
1627 
1628    eina_stringshare_del(extra->orig.bordername);
1629 
1630    E_FREE(extra);
1631 }
1632 
1633 static void
_clear_desk_types(void * data)1634 _clear_desk_types(void *data)
1635 {
1636    free(data);
1637 }
1638 
1639 E_API E_Module_Api e_modapi = {
1640    E_MODULE_API_VERSION,
1641    "Tiling"
1642 };
1643 
1644 static unsigned char
_client_drag_mouse_up(void * data,int event EINA_UNUSED,void * event_info EINA_UNUSED)1645 _client_drag_mouse_up(void *data, int event EINA_UNUSED, void *event_info EINA_UNUSED)
1646 {
1647    E_Client *ec = data;
1648    Client_Extra *extra = tiling_entry_func(ec);
1649 
1650    if (!extra) return ECORE_CALLBACK_PASS_ON;
1651 
1652    if (extra->drag.drag)
1653      _client_drag_terminate(data);
1654 
1655    //remove the events
1656    E_FREE_FUNC(extra->drag.move, ecore_event_handler_del);
1657    E_FREE_FUNC(extra->drag.up, ecore_event_handler_del);
1658 
1659    return ECORE_CALLBACK_PASS_ON;
1660 }
1661 
1662 static unsigned char
_client_drag_mouse_move(void * data,int event EINA_UNUSED,void * event_info)1663 _client_drag_mouse_move(void *data, int event EINA_UNUSED, void *event_info)
1664 {
1665    Ecore_Event_Mouse_Move *ev = event_info;
1666    Window_Tree *client;
1667    int x,y;
1668    E_Client *ec = data;
1669    Client_Extra *extra = tiling_entry_no_desk_func(data);
1670 
1671    if (!extra)
1672      {
1673         return ECORE_CALLBACK_PASS_ON;
1674      }
1675 
1676    if (evas_object_visible_get(ec->frame))
1677      {
1678         /*only initiaze the drag when x and y is different */
1679         if (extra->drag.x == ev->x && extra->drag.y == ev->y) return ECORE_CALLBACK_PASS_ON;
1680 
1681         _client_remove_no_apply(ec);
1682 
1683         extra->drag.drag = EINA_TRUE;
1684         e_comp_grab_input(EINA_TRUE, EINA_FALSE);
1685 
1686         ec->hidden = EINA_TRUE;
1687         e_client_comp_hidden_set(ec, EINA_TRUE);
1688         evas_object_hide(ec->frame);
1689 
1690         _reapply_tree();
1691      }
1692 
1693    //now check if we can hint somehow
1694    evas_pointer_canvas_xy_get(e_comp->evas, &x, &y);
1695 
1696 
1697    //create hint if not there
1698    if (!extra->drag.hint)
1699      {
1700         extra->drag.hint = edje_object_add(e_comp->evas);
1701         if (!e_theme_edje_object_set(extra->drag.hint,
1702                                      "base/theme/modules/tiling",
1703                                      "e/modules/tiling/indicator"))
1704           edje_object_file_set(extra->drag.hint, _G.edj_path, "modules/tiling/indicator");
1705         evas_object_layer_set(extra->drag.hint, E_LAYER_CLIENT_DRAG);
1706         evas_object_show(extra->drag.hint);
1707 
1708         extra->drag.ic = e_client_icon_add(ec, evas_object_evas_get(e_comp->evas));
1709         edje_object_part_swallow(extra->drag.hint, "e.client.icon", extra->drag.ic);
1710         evas_object_show(extra->drag.ic);
1711      }
1712 
1713    //if there is nothing below, we cannot hint to anything
1714    client = _tilable_client(x, y);
1715    if (client)
1716      {
1717         Position_On_Client c;
1718 
1719         c = _calculate_position_preference(client->client);
1720 
1721         Eina_Rectangle pos = client->client->client;
1722         if (c == POSITION_LEFT)
1723           evas_object_geometry_set(extra->drag.hint, pos.x, pos.y, pos.w/2, pos.h);
1724         else if (c == POSITION_RIGHT)
1725           evas_object_geometry_set(extra->drag.hint, pos.x+pos.w/2, pos.y, pos.w/2, pos.h);
1726         else if (c == POSITION_BOTTOM)
1727           evas_object_geometry_set(extra->drag.hint, pos.x, pos.y + pos.h/2, pos.w, pos.h/2);
1728         else if (c == POSITION_TOP)
1729           evas_object_geometry_set(extra->drag.hint, pos.x, pos.y, pos.w, pos.h/2);
1730         evas_object_show(extra->drag.hint);
1731      }
1732    else
1733      {
1734         //if there is no client, just highlight the zone
1735         Eina_Rectangle geom;
1736         E_Zone *zone = e_zone_current_get();
1737         e_zone_useful_geometry_get(zone, &geom.x, &geom.y, &geom.w, &geom.h);
1738         evas_object_geometry_set(extra->drag.hint, EINA_RECTANGLE_ARGS(&geom));
1739         evas_object_show(extra->drag.hint);
1740      }
1741 
1742    return ECORE_CALLBACK_PASS_ON;
1743 }
1744 
1745 static void
_client_drag_terminate(E_Client * ec)1746 _client_drag_terminate(E_Client *ec)
1747 {
1748    Client_Extra *extra = tiling_entry_no_desk_func(ec);
1749 
1750    if (!extra)
1751      {
1752         return;
1753      }
1754 
1755    //we grappend the comp when we started the drag
1756    e_comp_ungrab_input(EINA_TRUE, EINA_FALSE);
1757 
1758    //insert the client at the position where the up was
1759    if (desk_should_tile_check(get_current_desk()))
1760      {
1761         _insert_client_preferred(ec);
1762         extra->tiled = EINA_TRUE;
1763      }
1764 
1765    //remove the hint object
1766    E_FREE_FUNC(extra->drag.hint, evas_object_del);
1767    E_FREE_FUNC(extra->drag.ic, evas_object_del);
1768 
1769    //bring up the client again
1770    ec->hidden = EINA_FALSE;
1771    e_client_comp_hidden_set(ec, EINA_FALSE);
1772    evas_object_show(ec->frame);
1773 
1774    //remove the events
1775    E_FREE_FUNC(extra->drag.move, ecore_event_handler_del);
1776    E_FREE_FUNC(extra->drag.up, ecore_event_handler_del);
1777 
1778    _reapply_tree();
1779 
1780    evas_object_focus_set(ec->frame, EINA_TRUE);
1781 
1782    extra->drag.drag = EINA_FALSE;
1783 }
1784 
1785 static void
_client_move_begin(void * data EINA_UNUSED,E_Client * ec)1786 _client_move_begin(void *data EINA_UNUSED, E_Client *ec)
1787 {
1788    Client_Extra *extra = tiling_entry_func(ec);
1789 
1790    if (!extra || !extra->tiled)
1791      {
1792         return;
1793      }
1794 
1795    //listen for mouse moves when the move starts we are starting a drag
1796    evas_pointer_canvas_xy_get(e_comp->evas, &extra->drag.x, &extra->drag.y);
1797    extra->drag.move = ecore_event_handler_add(ECORE_EVENT_MOUSE_MOVE, _client_drag_mouse_move, ec);
1798    extra->drag.up = ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP, _client_drag_mouse_up, ec);
1799 
1800 }
1801 
1802 static void
_update_current_desk(E_Desk * new)1803 _update_current_desk(E_Desk *new)
1804 {
1805    Desk_Split_Type *type;
1806 
1807    type = eina_hash_find(_G.desk_type, &new);
1808 
1809    if (!type)
1810      {
1811         type = calloc(1, sizeof(Desk_Split_Type));
1812         type->desk = new;
1813         type->type = TILING_SPLIT_HORIZONTAL;
1814         eina_hash_add(_G.desk_type, &new, type);
1815      }
1816 
1817    _G.current_split_type = type;
1818 }
1819 
1820 static bool
_desk_shown(void * data EINA_UNUSED,int types EINA_UNUSED,void * event_info)1821 _desk_shown(void *data EINA_UNUSED, int types EINA_UNUSED, void *event_info)
1822 {
1823    E_Event_Desk_Show *ev = event_info;
1824 
1825    if (!ev->desk)
1826      {
1827         ERR("The shown desk can never be NULL!");
1828         return ECORE_CALLBACK_PASS_ON;
1829      }
1830 
1831    _update_current_desk(ev->desk);
1832    _tiling_gadgets_update();
1833 
1834    return ECORE_CALLBACK_PASS_ON;
1835 }
1836 
1837 E_API void *
e_modapi_init(E_Module * m)1838 e_modapi_init(E_Module *m)
1839 {
1840    E_Desk *desk;
1841    Eina_List *l;
1842 
1843    tiling_g.module = m;
1844 
1845    if (tiling_g.log_domain < 0)
1846      {
1847         tiling_g.log_domain = eina_log_domain_register("tiling", NULL);
1848         if (tiling_g.log_domain < 0)
1849           {
1850              EINA_LOG_CRIT("could not register log domain 'tiling'");
1851           }
1852      }
1853 
1854    _G.info_hash = eina_hash_pointer_new(_clear_info_hash);
1855    _G.client_extras = eina_hash_pointer_new(_clear_border_extras);
1856    _G.desk_type = eina_hash_pointer_new(_clear_desk_types);
1857 #define HANDLER(_h, _e, _f) \
1858   _h = ecore_event_handler_add(E_EVENT_##_e, (void *)_f, NULL)
1859 
1860    _G.handler_client_resize_begin =
1861       e_client_hook_add(E_CLIENT_HOOK_RESIZE_BEGIN, _resize_begin_hook, NULL);
1862    _G.handler_move_begin =
1863       e_client_hook_add(E_CLIENT_HOOK_MOVE_BEGIN, _client_move_begin, NULL);
1864 
1865    if (e_comp->comp_type == E_PIXMAP_TYPE_X)
1866      _G.handler_client_add =
1867         e_client_hook_add(E_CLIENT_HOOK_EVAL_PRE_FRAME_ASSIGN, _add_hook, NULL);
1868    else
1869      _G.handler_client_add =
1870         e_client_hook_add(E_CLIENT_HOOK_UNIGNORE, _add_hook, NULL);
1871    HANDLER(_G.handler_client_resize, CLIENT_RESIZE, _resize_hook);
1872    HANDLER(_G.handler_client_move, CLIENT_MOVE, _move_hook);
1873 
1874    HANDLER(_G.handler_client_iconify, CLIENT_ICONIFY, _iconify_hook);
1875    HANDLER(_G.handler_client_uniconify, CLIENT_UNICONIFY, _iconify_hook);
1876 
1877    HANDLER(_G.handler_desk_set, CLIENT_DESK_SET, _desk_set_hook);
1878    HANDLER(_G.handler_compositor_resize, COMPOSITOR_UPDATE,
1879            _compositor_resize_hook);
1880    HANDLER(_G.handler_desk_show, DESK_SHOW, _desk_shown);
1881 #undef HANDLER
1882 
1883 #define ACTION_ADD(_action, _cb, _title, _value, _params, _example, _editable) \
1884   {                                                                            \
1885      const char *_name = _value;                                               \
1886      if ((_action = e_action_add(_name))) {                                    \
1887           _action->func.go = _cb;                                              \
1888           e_action_predef_name_set(N_("Tiling"), _title, _name,                \
1889                                    _params, _example, _editable);              \
1890        }                                                                       \
1891   }
1892 
1893    /* Module's actions */
1894    ACTION_ADD(_G.act_togglefloat, _e_mod_action_toggle_floating_cb,
1895               N_("Toggle floating"), "toggle_floating", NULL, NULL, 0);
1896 
1897    ACTION_ADD(_G.act_move_up, _e_mod_action_move_up_cb,
1898               N_("Move the focused window up"), "move_up", NULL, NULL, 0);
1899    ACTION_ADD(_G.act_move_down, _e_mod_action_move_down_cb,
1900               N_("Move the focused window down"), "move_down", NULL, NULL, 0);
1901    ACTION_ADD(_G.act_move_left, _e_mod_action_move_left_cb,
1902               N_("Move the focused window left"), "move_left", NULL, NULL, 0);
1903    ACTION_ADD(_G.act_move_right, _e_mod_action_move_right_cb,
1904               N_("Move the focused window right"), "move_right", NULL, NULL, 0);
1905 
1906    ACTION_ADD(_G.act_toggle_split_mode, _e_mod_action_toggle_split_mode,
1907               N_("Toggle split mode for new windows."), "toggle_split_mode", NULL, NULL, 0);
1908 
1909    ACTION_ADD(_G.act_swap_window, NULL, N_("Swap window"), "swap_window", NULL,
1910               NULL, 0);
1911    _G.act_swap_window->func.go_mouse = _e_mod_action_swap_window_go_mouse;
1912    _G.act_swap_window->func.end_mouse = _e_mod_action_swap_window_end_mouse;
1913 
1914 #undef ACTION_ADD
1915 
1916    /* Configuration entries */
1917    snprintf(_G.edj_path, sizeof(_G.edj_path), "%s/e-module-tiling.edj",
1918             e_module_dir_get(m));
1919    e_configure_registry_category_add("windows", 50, _("Windows"), NULL,
1920                                      "preferences-system-windows");
1921    e_configure_registry_item_add("windows/tiling", 150, _("Tiling"), NULL,
1922                                  _G.edj_path, e_int_config_tiling_module);
1923 
1924    /* Configuration itself */
1925    _G.config_edd = E_CONFIG_DD_NEW("Tiling_Config", Config);
1926    _G.vdesk_edd = E_CONFIG_DD_NEW("Tiling_Config_VDesk", struct _Config_vdesk);
1927 
1928    E_CONFIG_VAL(_G.config_edd, Config, tile_dialogs, INT);
1929    E_CONFIG_VAL(_G.config_edd, Config, show_titles, INT);
1930    E_CONFIG_VAL(_G.config_edd, Config, have_floating_mode, INT);
1931    E_CONFIG_VAL(_G.config_edd, Config, window_padding, INT);
1932 
1933    E_CONFIG_LIST(_G.config_edd, Config, vdesks, _G.vdesk_edd);
1934    E_CONFIG_VAL(_G.vdesk_edd, struct _Config_vdesk, x, INT);
1935    E_CONFIG_VAL(_G.vdesk_edd, struct _Config_vdesk, y, INT);
1936    E_CONFIG_VAL(_G.vdesk_edd, struct _Config_vdesk, zone_num, INT);
1937    E_CONFIG_VAL(_G.vdesk_edd, struct _Config_vdesk, nb_stacks, INT);
1938 
1939    tiling_g.config = e_config_domain_load("module.tiling", _G.config_edd);
1940    if (!tiling_g.config)
1941      {
1942         tiling_g.config = E_NEW(Config, 1);
1943         tiling_g.config->tile_dialogs = 1;
1944         tiling_g.config->show_titles = 1;
1945         tiling_g.config->have_floating_mode = 1;
1946         tiling_g.config->window_padding = 0;
1947      }
1948 
1949    E_CONFIG_LIMIT(tiling_g.config->tile_dialogs, 0, 1);
1950    E_CONFIG_LIMIT(tiling_g.config->show_titles, 0, 1);
1951    E_CONFIG_LIMIT(tiling_g.config->have_floating_mode, 0, 1);
1952    E_CONFIG_LIMIT(tiling_g.config->window_padding, 0, TILING_MAX_PADDING);
1953 
1954    for (l = tiling_g.config->vdesks; l; l = l->next)
1955      {
1956         struct _Config_vdesk *vd;
1957 
1958         vd = l->data;
1959 
1960         E_CONFIG_LIMIT(vd->nb_stacks, 0, 1);
1961      }
1962 
1963    _G.client_menu_hook = e_int_client_menu_hook_add(_bd_hook, NULL);
1964 
1965    desk = get_current_desk();
1966    _G.tinfo = _initialize_tinfo(desk);
1967 
1968    _update_current_desk(get_current_desk());
1969 
1970    /* Add all the existing windows. */
1971    {
1972       E_Client *ec;
1973 
1974       E_CLIENT_FOREACH(ec)
1975       {
1976          _add_client(ec, _current_tiled_state(EINA_TRUE));
1977       }
1978    }
1979    started = EINA_TRUE;
1980    _reapply_tree();
1981    e_gadcon_provider_register(&_gc_class);
1982 
1983 
1984    return m;
1985 }
1986 
1987 static void
_disable_desk(E_Desk * desk)1988 _disable_desk(E_Desk *desk)
1989 {
1990    check_tinfo(desk);
1991    if (!_G.tinfo->conf)
1992      return;
1993 
1994    tiling_window_tree_walk(_G.tinfo->tree, _restore_free_client);
1995    _G.tinfo->tree = NULL;
1996 }
1997 
1998 static void
_disable_all_tiling(void)1999 _disable_all_tiling(void)
2000 {
2001    _foreach_desk(_disable_desk);
2002 }
2003 
2004 static void
_foreach_desk(void (* func)(E_Desk * desk))2005 _foreach_desk(void (*func)(E_Desk *desk))
2006 {
2007    const Eina_List *l;
2008    E_Zone *zone;
2009    E_Desk *desk;
2010    int x, y;
2011 
2012    EINA_LIST_FOREACH(e_comp->zones, l, zone)
2013      {
2014         for (x = 0; x < zone->desk_x_count; x++)
2015           {
2016              for (y = 0; y < zone->desk_y_count; y++)
2017                {
2018                   desk = zone->desks[x + (y * zone->desk_x_count)];
2019 
2020                   func(desk);
2021                }
2022           }
2023      }
2024 }
2025 
2026 E_API int
e_modapi_shutdown(E_Module * m EINA_UNUSED)2027 e_modapi_shutdown(E_Module *m EINA_UNUSED)
2028 {
2029    e_gadcon_provider_unregister(&_gc_class);
2030    started = EINA_FALSE;
2031    _disable_all_tiling();
2032 
2033    e_int_client_menu_hook_del(_G.client_menu_hook);
2034 
2035    if (tiling_g.log_domain >= 0)
2036      {
2037         eina_log_domain_unregister(tiling_g.log_domain);
2038         tiling_g.log_domain = -1;
2039      }
2040 #define SAFE_FREE(x, freefunc) \
2041    if (x) \
2042      { \
2043         freefunc(x); \
2044         x = NULL; \
2045      }
2046 #define FREE_HANDLER(x)            \
2047    SAFE_FREE(x, ecore_event_handler_del);
2048 
2049    FREE_HANDLER(_G.handler_client_resize);
2050    FREE_HANDLER(_G.handler_client_move);
2051 
2052    FREE_HANDLER(_G.handler_client_iconify);
2053    FREE_HANDLER(_G.handler_client_uniconify);
2054 
2055    FREE_HANDLER(_G.handler_desk_set);
2056 
2057    SAFE_FREE(_G.handler_client_resize_begin, e_client_hook_del);
2058    SAFE_FREE(_G.handler_client_add, e_client_hook_del);
2059 #undef FREE_HANDLER
2060 #undef SAFE_FREE
2061 
2062 #define ACTION_DEL(act, title, value)             \
2063   if (act) {                                      \
2064        e_action_predef_name_del("Tiling", title); \
2065        e_action_del(value);                       \
2066        act = NULL;                                \
2067     }
2068    ACTION_DEL(_G.act_togglefloat, "Toggle floating", "toggle_floating");
2069    ACTION_DEL(_G.act_move_up, "Move the focused window up", "move_up");
2070    ACTION_DEL(_G.act_move_down, "Move the focused window down", "move_down");
2071    ACTION_DEL(_G.act_move_left, "Move the focused window left", "move_left");
2072    ACTION_DEL(_G.act_move_right, "Move the focused window right", "move_right");
2073 
2074    ACTION_DEL(_G.act_toggle_split_mode, "Toggle split mode for new windows.",
2075               "toggle_split_mode");
2076    ACTION_DEL(_G.act_swap_window, "Swap window", "swap_window");
2077 #undef ACTION_DEL
2078 
2079    e_configure_registry_item_del("windows/tiling");
2080    e_configure_registry_category_del("windows");
2081 
2082    E_FREE(tiling_g.config);
2083    E_CONFIG_DD_FREE(_G.config_edd);
2084    E_CONFIG_DD_FREE(_G.vdesk_edd);
2085 
2086    tiling_g.module = NULL;
2087 
2088    eina_hash_free(_G.info_hash);
2089    _G.info_hash = NULL;
2090 
2091    eina_hash_free_cb_set(_G.client_extras, _e_client_extra_unregister_callbacks);
2092    eina_hash_free(_G.client_extras);
2093    _G.client_extras = NULL;
2094 
2095    _G.tinfo = NULL;
2096 
2097    return 1;
2098 }
2099 
2100 E_API int
e_modapi_save(E_Module * m EINA_UNUSED)2101 e_modapi_save(E_Module *m EINA_UNUSED)
2102 {
2103    e_config_domain_save("module.tiling", _G.config_edd, tiling_g.config);
2104 
2105    return true;
2106 }
2107 
2108 /* GADGET STUFF. */
2109 
2110 /* Hack to properly save and free the gadget id. */
2111 static Eina_Stringshare *_current_gad_id = NULL;
2112 
2113 static void
_edje_tiling_icon_set(Evas_Object * o)2114 _edje_tiling_icon_set(Evas_Object *o)
2115 {
2116    switch (_current_tiled_state(EINA_TRUE))
2117      {
2118       case TILING_SPLIT_HORIZONTAL:
2119         edje_object_signal_emit(o, "tiling,mode,horizontal", "e");
2120         break;
2121 
2122       case TILING_SPLIT_VERTICAL:
2123         edje_object_signal_emit(o, "tiling,mode,vertical", "e");
2124         break;
2125 
2126       case TILING_SPLIT_FLOAT:
2127         edje_object_signal_emit(o, "tiling,mode,floating", "e");
2128         break;
2129 
2130       default:
2131         ERR("Unknown split type.");
2132      }
2133 }
2134 
2135 static void
_gadget_icon_set(Instance * inst)2136 _gadget_icon_set(Instance *inst)
2137 {
2138    _edje_tiling_icon_set(inst->gadget);
2139 }
2140 
2141 static void
_tiling_cb_menu_configure(void * data EINA_UNUSED,E_Menu * m EINA_UNUSED,E_Menu_Item * mi EINA_UNUSED)2142 _tiling_cb_menu_configure(void *data EINA_UNUSED, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
2143 {
2144    // FIXME here need to be some checks and return ?
2145    e_int_config_tiling_module(NULL, NULL);
2146 }
2147 
2148 static void
_gadget_mouse_down_cb(void * data,Evas * e,Evas_Object * obj EINA_UNUSED,void * event_info)2149 _gadget_mouse_down_cb(void *data, Evas *e, Evas_Object *obj EINA_UNUSED, void *event_info)
2150 {
2151    Evas_Event_Mouse_Down *ev = event_info;
2152    Instance *inst = data;
2153 
2154    if (ev->button == 1) /* Change on left-click. */
2155      {
2156         _tiling_split_type_next();
2157      }
2158    else if (ev->button == 3)
2159      {
2160         E_Zone *zone;
2161         E_Menu *m;
2162         E_Menu_Item *mi;
2163         int x, y;
2164 
2165         zone = e_zone_current_get();
2166 
2167         m = e_menu_new();
2168         mi = e_menu_item_new(m);
2169         e_menu_item_label_set(mi, _("Settings"));
2170         e_util_menu_item_theme_icon_set(mi, "configure");
2171         e_menu_item_callback_set(mi, _tiling_cb_menu_configure, NULL);
2172 
2173         m = e_gadcon_client_util_menu_items_append(inst->gcc, m, 0);
2174 
2175         e_gadcon_canvas_zone_geometry_get(inst->gcc->gadcon, &x, &y, NULL, NULL);
2176         e_menu_activate_mouse(m, zone, x + ev->output.x, y + ev->output.y,
2177                               1, 1, E_MENU_POP_DIRECTION_AUTO, ev->timestamp);
2178         evas_event_feed_mouse_up(e, ev->button,
2179                                  EVAS_BUTTON_NONE, ev->timestamp, NULL);
2180      }
2181 }
2182 
2183 static E_Gadcon_Client *
_gc_init(E_Gadcon * gc,const char * name,const char * id,const char * style)2184 _gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style)
2185 {
2186    Evas_Object *o;
2187    E_Gadcon_Client *gcc;
2188    Instance *inst;
2189 
2190    inst = E_NEW(Instance, 1);
2191 
2192    o = edje_object_add(gc->evas);
2193    if (!e_theme_edje_object_set(o, "base/theme/modules/tiling",
2194                                 "e/modules/tiling/main"))
2195      edje_object_file_set(o, _G.edj_path, "modules/tiling/main");
2196    evas_object_show(o);
2197 
2198    gcc = e_gadcon_client_new(gc, name, id, style, o);
2199    gcc->data = inst;
2200    inst->gcc = gcc;
2201    inst->gad_id = _current_gad_id;
2202    _current_gad_id = NULL;
2203 
2204    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN,
2205                                   _gadget_mouse_down_cb, inst);
2206 
2207    inst->gadget = o;
2208 
2209    _gadget_icon_set(inst);
2210 
2211    tiling_g.gadget_instances = eina_list_append(tiling_g.gadget_instances, inst);
2212 
2213    return gcc;
2214 }
2215 
2216 static void
_gc_shutdown(E_Gadcon_Client * gcc)2217 _gc_shutdown(E_Gadcon_Client *gcc)
2218 {
2219    Instance *inst;
2220    Evas_Object *o;
2221 
2222    if (!(inst = gcc->data)) return;
2223 
2224    o = inst->gadget;
2225 
2226    evas_object_event_callback_del_full(o, EVAS_CALLBACK_MOUSE_DOWN,
2227                                        _gadget_mouse_down_cb, inst);
2228 
2229    if (inst->gadget)
2230      evas_object_del(inst->gadget);
2231 
2232    tiling_g.gadget_instances = eina_list_remove(tiling_g.gadget_instances, inst);
2233 
2234    eina_stringshare_del(inst->gad_id);
2235 
2236    E_FREE(inst);
2237 }
2238 
2239 static void
_gc_orient(E_Gadcon_Client * gcc,E_Gadcon_Orient orient EINA_UNUSED)2240 _gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient EINA_UNUSED)
2241 {
2242    e_gadcon_client_aspect_set(gcc, 16, 16);
2243    e_gadcon_client_min_size_set(gcc, 16, 16);
2244 }
2245 
2246 static const char *
_gc_label(const E_Gadcon_Client_Class * client_class EINA_UNUSED)2247 _gc_label(const E_Gadcon_Client_Class *client_class EINA_UNUSED)
2248 {
2249    return _("Tiling");
2250 }
2251 
2252 static Evas_Object *
_gc_icon(const E_Gadcon_Client_Class * client_class EINA_UNUSED,Evas * evas)2253 _gc_icon(const E_Gadcon_Client_Class *client_class EINA_UNUSED, Evas *evas)
2254 {
2255    Evas_Object *o;
2256 
2257    o = edje_object_add(evas);
2258    edje_object_file_set(o, _G.edj_path, "icon");
2259    return o;
2260 }
2261 
2262 static const char *
_gc_id_new(const E_Gadcon_Client_Class * client_class EINA_UNUSED)2263 _gc_id_new(const E_Gadcon_Client_Class *client_class EINA_UNUSED)
2264 {
2265    char buf[1024];
2266 
2267    snprintf(buf, sizeof(buf), "%s %d", _("Tiling"), tiling_g.gadget_number);
2268 
2269    tiling_g.gadget_number++;
2270 
2271    return _current_gad_id = eina_stringshare_add(buf);
2272 }
2273 
2274 /* }}} */
2275