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