4 * Copyright (c) Tuomo Valkonen 1999-2005.
6 * Ion is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
15 #include <libtu/objp.h>
16 #include <libtu/minmax.h>
18 #include <libextl/extl.h>
19 #include <libmainloop/defer.h>
34 #include "frame-pointer.h"
37 #include "region-iter.h"
42 #define MPLEX_WIN(MPLEX) ((MPLEX)->win.win)
43 #define MPLEX_MGD_UNVIEWABLE(MPLEX) \
44 ((MPLEX)->flags&MPLEX_MANAGED_UNVIEWABLE)
47 /*{{{ Destroy/create mplex */
50 bool mplex_do_init(WMPlex
*mplex
, WWindow
*parent
, Window win
,
51 const WFitParams
*fp
, bool create
)
56 mplex
->l1_current
=NULL
;
59 mplex
->l2_current
=NULL
;
60 watch_init(&(mplex
->stdispinfo
.regwatch
));
61 mplex
->stdispinfo
.pos
=MPLEX_STDISP_BL
;
64 if(!window_init((WWindow
*)mplex
, parent
, fp
))
67 if(!window_do_init((WWindow
*)mplex
, parent
, win
, fp
))
71 mplex
->win
.region
.flags
|=REGION_BINDINGS_ARE_GRABBED
;
73 XSelectInput(ioncore_g
.dpy
, MPLEX_WIN(mplex
), IONCORE_EVENTMASK_CWINMGR
);
75 region_add_bindmap((WRegion
*)mplex
, ioncore_mplex_bindmap
);
77 /* Call this to set MPLEX_MANAGED_UNVIEWABLE if necessary. */
78 mplex_fit_managed(mplex
);
84 bool mplex_init(WMPlex
*mplex
, WWindow
*parent
, const WFitParams
*fp
)
86 return mplex_do_init(mplex
, parent
, None
, fp
, TRUE
);
90 WMPlex
*create_mplex(WWindow
*parent
, const WFitParams
*fp
)
92 CREATEOBJ_IMPL(WMPlex
, mplex
, (p
, parent
, fp
));
96 void mplex_deinit(WMPlex
*mplex
)
100 FOR_ALL_MANAGED_ON_LIST_W_NEXT(mplex
->l1_list
, reg
, next
){
101 destroy_obj((Obj
*)reg
);
104 FOR_ALL_MANAGED_ON_LIST_W_NEXT(mplex
->l2_list
, reg
, next
){
105 destroy_obj((Obj
*)reg
);
108 window_deinit((WWindow
*)mplex
);
115 /*{{{ Hidden L2 objects RB-tree */
117 #define MGD_L2_HIDDEN 0x0001
118 #define MGD_L2_PASSIVE 0x0002
120 static Rb_node mgd_flag_rb
=NULL
;
123 static bool mgd_set_flags(WRegion
*reg
, int flag
)
127 if(mgd_flag_rb
==NULL
){
128 mgd_flag_rb
=make_rb();
129 if(mgd_flag_rb
==NULL
)
133 nd
=rb_find_pkey_n(mgd_flag_rb
, reg
, &found
);
140 nd
=rb_insertp(mgd_flag_rb
, reg
, NULL
);
148 static void mgd_unset_flags(WRegion
*reg
, int flag
)
153 if(mgd_flag_rb
==NULL
)
156 nd
=rb_find_pkey_n(mgd_flag_rb
, reg
, &found
);
167 static int mgd_flags(WRegion
*reg
)
172 if(mgd_flag_rb
==NULL
)
175 nd
=rb_find_pkey_n(mgd_flag_rb
, reg
, &found
);
177 return (found
? nd
->v
.ival
: 0);
181 static bool l2_is_hidden(WRegion
*reg
)
183 return mgd_flags(reg
)&MGD_L2_HIDDEN
;
187 static bool l2_mark_hidden(WRegion
*reg
)
189 return mgd_set_flags(reg
, MGD_L2_HIDDEN
);
193 static void l2_unmark_hidden(WRegion
*reg
)
195 mgd_unset_flags(reg
, MGD_L2_HIDDEN
);
202 /*{{{ Managed list management */
205 static bool on_list(WRegion
*list
, WRegion
*reg
)
209 FOR_ALL_MANAGED_ON_LIST(list
, reg2
){
218 static bool on_l1_list(WMPlex
*mplex
, WRegion
*reg
)
220 return on_list(mplex
->l1_list
, reg
);
224 static bool on_l2_list(WMPlex
*mplex
, WRegion
*reg
)
226 return on_list(mplex
->l2_list
, reg
);
230 static WRegion
*nth_on_list(WRegion
*list
, uint n
)
232 WRegion
*reg
=REGION_FIRST_MANAGED(list
);
234 while(n
-->0 && reg
!=NULL
)
235 reg
=REGION_NEXT_MANAGED(list
, reg
);
241 WRegion
*mplex_current(WMPlex
*mplex
)
243 return (mplex
->l2_current
!=NULL
? mplex
->l2_current
: mplex
->l1_current
);
248 * Returns the layer \var{reg} is on \var{mplex} or $-1$ if \var{reg}
249 * is not managed by \var{mplex}.
252 int mplex_layer(WMPlex
*mplex
, WRegion
*reg
)
254 if(on_l1_list(mplex
, reg
))
256 if(on_l2_list(mplex
, reg
))
263 * Return the managed object currently active within layer \var{l} of
267 WRegion
*mplex_lcurrent(WMPlex
*mplex
, uint l
)
278 * Returns the \var{n}:th object managed by \var{mplex} on the the
282 WRegion
*mplex_lnth(WMPlex
*mplex
, uint l
, uint n
)
285 ? nth_on_list(mplex
->l1_list
, n
)
287 ? nth_on_list(mplex
->l2_list
, n
)
293 * Returns a list of regions managed by \var{mplex} on layer \var{l}.
296 ExtlTab
mplex_llist(WMPlex
*mplex
, uint l
)
299 ? managed_list_to_table(mplex
->l1_list
, NULL
)
301 ? managed_list_to_table(mplex
->l2_list
, NULL
)
302 : extl_table_none()));
307 * Returns the number of regions managed by \var{mplex} on layer \var{l}.
310 int mplex_lcount(WMPlex
*mplex
, uint l
)
320 static void link_at(WMPlex
*mplex
, WRegion
*reg
, int index
)
325 after
=mplex_lnth(mplex
, 1, index
-1);
327 if(!(mplex
->flags
&MPLEX_ADD_TO_END
) &&
328 ioncore_g
.opmode
!=IONCORE_OPMODE_INIT
){
329 after
=mplex
->l1_current
;
337 LINK_ITEM_AFTER(mplex
->l1_list
, after
, reg
, mgr_next
, mgr_prev
);
339 LINK_ITEM_FIRST(mplex
->l1_list
, reg
, mgr_next
, mgr_prev
);
341 LINK_ITEM(mplex
->l1_list
, reg
, mgr_next
, mgr_prev
);
347 * Set index of \var{reg} within the multiplexer to \var{index}.
350 void mplex_set_index(WMPlex
*mplex
, WRegion
*reg
, int index
)
352 if(index
<0 || reg
==NULL
)
355 if(!on_l1_list(mplex
, reg
))
358 UNLINK_ITEM(mplex
->l1_list
, reg
, mgr_next
, mgr_prev
);
359 link_at(mplex
, reg
, index
);
360 mplex_managed_changed(mplex
, MPLEX_CHANGE_REORDER
, FALSE
, reg
);
365 * Get index of \var{reg} within the multiplexer. The first region managed
366 * by \var{mplex} has index zero. If \var{reg} is not managed by \var{mplex},
370 int mplex_get_index(WMPlex
*mplex
, WRegion
*reg
)
375 FOR_ALL_MANAGED_ON_LIST(mplex
->l1_list
, other
){
386 * Move \var{r} ''right'' within objects managed by \var{mplex}.
389 void mplex_inc_index(WMPlex
*mplex
, WRegion
*r
)
392 r
=mplex_lcurrent(mplex
, 1);
394 mplex_set_index(mplex
, r
, mplex_get_index(mplex
, r
)+1);
399 * Move \var{r} ''right'' within objects managed by \var{mplex}.
402 void mplex_dec_index(WMPlex
*mplex
, WRegion
*r
)
405 r
=mplex_lcurrent(mplex
, 1);
407 mplex_set_index(mplex
, r
, mplex_get_index(mplex
, r
)-1);
417 static void mplex_map_mgd(WMPlex
*mplex
)
421 if(mplex
->l1_current
!=NULL
)
422 region_map(mplex
->l1_current
);
424 FOR_ALL_MANAGED_ON_LIST(mplex
->l2_list
, reg
){
425 if(!l2_is_hidden(reg
))
431 static void mplex_unmap_mgd(WMPlex
*mplex
)
435 if(mplex
->l1_current
!=NULL
)
436 region_unmap(mplex
->l1_current
);
438 FOR_ALL_MANAGED_ON_LIST(mplex
->l2_list
, reg
){
445 void mplex_map(WMPlex
*mplex
)
447 window_map((WWindow
*)mplex
);
448 /* A lame requirement of the ICCCM is that client windows should be
449 * unmapped if the parent is unmapped.
451 if(!MPLEX_MGD_UNVIEWABLE(mplex
))
452 mplex_map_mgd(mplex
);
456 void mplex_unmap(WMPlex
*mplex
)
458 window_unmap((WWindow
*)mplex
);
459 /* A lame requirement of the ICCCM is that client windows should be
460 * unmapped if the parent is unmapped.
462 if(!MPLEX_MGD_UNVIEWABLE(mplex
))
463 mplex_unmap_mgd(mplex
);
470 /*{{{ Resize and reparent */
473 bool mplex_fitrep(WMPlex
*mplex
, WWindow
*par
, const WFitParams
*fp
)
475 bool wchg
=(REGION_GEOM(mplex
).w
!=fp
->g
.w
);
476 bool hchg
=(REGION_GEOM(mplex
).h
!=fp
->g
.h
);
478 window_do_fitrep(&(mplex
->win
), par
, &(fp
->g
));
481 mplex_fit_managed(mplex
);
482 mplex_size_changed(mplex
, wchg
, hchg
);
489 void mplex_fit_managed(WMPlex
*mplex
)
495 mplex_managed_geom(mplex
, &(fp
.g
));
497 if(!MPLEX_MGD_UNVIEWABLE(mplex
) && (fp
.g
.w
<=1 || fp
.g
.h
<=1)){
498 mplex
->flags
|=MPLEX_MANAGED_UNVIEWABLE
;
499 if(REGION_IS_MAPPED(mplex
))
500 mplex_unmap_mgd(mplex
);
501 }else if(MPLEX_MGD_UNVIEWABLE(mplex
) && !(fp
.g
.w
<=1 || fp
.g
.h
<=1)){
502 mplex
->flags
&=~MPLEX_MANAGED_UNVIEWABLE
;
503 if(REGION_IS_MAPPED(mplex
))
504 mplex_map_mgd(mplex
);
507 if(!MPLEX_MGD_UNVIEWABLE(mplex
)){
508 fp
.mode
=REGION_FIT_EXACT
;
509 FOR_ALL_MANAGED_ON_LIST(mplex
->l1_list
, sub
){
510 region_fitrep(sub
, NULL
, &fp
);
513 fp
.mode
=REGION_FIT_BOUNDS
;
514 FOR_ALL_MANAGED_ON_LIST(mplex
->l2_list
, sub
){
515 region_fitrep(sub
, NULL
, &fp
);
521 static void mplex_managed_rqgeom(WMPlex
*mplex
, WRegion
*sub
,
522 int flags
, const WRectangle
*geom
,
527 mplex_managed_geom(mplex
, &mg
);
529 if(on_l2_list(mplex
, sub
)){
530 /* allow changes but constrain with managed area */
532 rectangle_constrain(&rg
, &mg
);
540 if(!(flags
®ION_RQGEOM_TRYONLY
))
541 region_fit(sub
, &rg
, REGION_FIT_EXACT
);
551 void mplex_do_set_focus(WMPlex
*mplex
, bool warp
)
556 if(!MPLEX_MGD_UNVIEWABLE(mplex
)){
557 if(mplex
->l2_current
!=NULL
){
558 region_do_set_focus(mplex
->l2_current
, warp
);
560 }else if(mplex
->l1_current
!=NULL
){
561 region_do_set_focus(mplex
->l1_current
, warp
);
566 window_do_set_focus((WWindow
*)mplex
, warp
);
573 /*{{{ Managed region switching */
576 void mplex_managed_activated(WMPlex
*mplex
, WRegion
*reg
)
578 if(on_l2_list(mplex
, reg
)){
579 mplex
->l2_current
=reg
;
580 }else if(mplex
->l2_current
!=NULL
&&
581 mgd_flags(mplex
->l2_current
)&MGD_L2_PASSIVE
){
582 mplex
->l2_current
=NULL
;
588 * Is \var{reg} on the layer2 of \var{mplex}, but hidden?
591 bool mplex_l2_hidden(WMPlex
*mplex
, WRegion
*reg
)
596 return (REGION_MANAGER(reg
)==(WRegion
*)mplex
597 && l2_is_hidden(reg
));
602 * If var \var{reg} is on the l2 list of \var{mplex} and currently shown,
603 * hide it. if \var{reg} is nil, hide all objects on the l2 list.
606 bool mplex_l2_hide(WMPlex
*mplex
, WRegion
*reg
)
608 WRegion
*reg2
, *toact
=NULL
;
609 bool mcf
=region_may_control_focus((WRegion
*)mplex
);
611 if(REGION_MANAGER(reg
)!=(WRegion
*)mplex
)
614 if(l2_is_hidden(reg
))
617 if(!l2_mark_hidden(reg
))
620 if(REGION_IS_MAPPED(mplex
) && !MPLEX_MGD_UNVIEWABLE(mplex
))
623 if(mplex
->l2_current
==reg
){
624 mplex
->l2_current
=NULL
;
625 FOR_ALL_MANAGED_ON_LIST(mplex
->l2_list
, reg2
){
626 /*if(!l2_is_hidden(reg2))*/
627 if((mgd_flags(reg2
)&(MGD_L2_HIDDEN
|MGD_L2_PASSIVE
))==0)
628 mplex
->l2_current
=reg2
;
633 region_warp((WRegion
*)mplex
);
640 * If var \var{reg} is on the l2 list of \var{mplex} and currently hidden,
641 * display it. if \var{reg} is nil, display all objects on the l2 list.
644 bool mplex_l2_show(WMPlex
*mplex
, WRegion
*reg
)
646 WRegion
*reg2
, *toact
=NULL
;
647 bool mcf
=region_may_control_focus((WRegion
*)mplex
);
649 if(REGION_MANAGER(reg
)!=(WRegion
*)mplex
)
652 if(!l2_is_hidden(reg
))
655 return mplex_managed_goto(mplex
, reg
, (mcf
? REGION_GOTO_FOCUS
: 0));
659 static bool mplex_do_managed_display(WMPlex
*mplex
, WRegion
*sub
,
665 if(sub
==mplex
->l1_current
)
666 return mplex
->l2_current
==NULL
;
668 if(sub
==mplex
->l2_current
)
671 if(on_l2_list(mplex
, sub
))
673 else if(!on_l1_list(mplex
, sub
))
676 stdisp
=(WRegion
*)(mplex
->stdispinfo
.regwatch
.obj
);
678 if(!l2
&& OBJ_IS(sub
, WGenWS
)){
680 WRegion
*mgr
=REGION_MANAGER(stdisp
);
682 if(OBJ_IS(mgr
, WGenWS
)){
683 genws_unmanage_stdisp((WGenWS
*)mgr
, FALSE
, FALSE
);
684 region_detach_manager(stdisp
);
687 genws_manage_stdisp((WGenWS
*)sub
, stdisp
,
688 mplex
->stdispinfo
.pos
);
691 genws_unmanage_stdisp((WGenWS
*)sub
, TRUE
, FALSE
);
695 if(REGION_IS_MAPPED(mplex
) && !MPLEX_MGD_UNVIEWABLE(mplex
))
701 if(mplex
->l1_current
!=NULL
&& REGION_IS_MAPPED(mplex
))
702 region_unmap(mplex
->l1_current
);
704 mplex
->l1_current
=sub
;
706 /* Many programs will get upset if the visible, although only
707 * such, client window is not the lowest window in the mplex.
708 * xprop/xwininfo will return the information for the lowest
709 * window. 'netscape -remote' will not work at all if there are
710 * no visible netscape windows.
712 if(OBJ_IS(sub
, WClientWin
))
715 /* This call should be unnecessary... */
716 mplex_managed_activated(mplex
, sub
);
718 int flags
=mgd_flags(sub
);
719 UNLINK_ITEM(mplex
->l2_list
, sub
, mgr_next
, mgr_prev
);
721 LINK_ITEM(mplex
->l2_list
, sub
, mgr_next
, mgr_prev
);
722 if(flags
&MGD_L2_HIDDEN
)
723 l2_unmark_hidden(sub
);
724 if(!(flags
&MGD_L2_PASSIVE
))
725 mplex
->l2_current
=sub
;
729 mplex_managed_changed(mplex
, MPLEX_CHANGE_SWITCHONLY
, TRUE
, sub
);
730 return mplex
->l2_current
==NULL
;
732 return mplex
->l2_current
==sub
;
737 static bool mplex_do_managed_goto(WMPlex
*mplex
, WRegion
*sub
,
738 bool call_changed
, int flags
)
740 if(!mplex_do_managed_display(mplex
, sub
, call_changed
))
743 if(flags
®ION_GOTO_FOCUS
)
744 region_maybewarp((WRegion
*)mplex
, !(flags
®ION_GOTO_NOWARP
));
750 static bool mplex_do_managed_goto_sw(WMPlex
*mplex
, WRegion
*sub
,
753 bool mcf
=region_may_control_focus((WRegion
*)mplex
);
754 return mplex_do_managed_goto(mplex
, sub
, FALSE
,
755 (mcf
? REGION_GOTO_FOCUS
: 0)
756 |REGION_GOTO_NOWARP
);
760 bool mplex_managed_goto(WMPlex
*mplex
, WRegion
*sub
, int flags
)
762 return mplex_do_managed_goto(mplex
, sub
, TRUE
, flags
);
766 static void do_switch(WMPlex
*mplex
, WRegion
*sub
)
769 bool mcf
=region_may_control_focus((WRegion
*)mplex
);
771 ioncore_set_previous_of(sub
);
772 region_managed_goto((WRegion
*)mplex
, sub
,
773 (mcf
? REGION_GOTO_FOCUS
: 0));
779 * Have \var{mplex} display the \var{n}:th object managed by it.
782 void mplex_switch_nth(WMPlex
*mplex
, uint n
)
784 do_switch(mplex
, mplex_lnth(mplex
, 1, n
));
789 * Have \var{mplex} display next (wrt. currently selected) object managed
793 void mplex_switch_next(WMPlex
*mplex
)
795 do_switch(mplex
, REGION_NEXT_MANAGED_WRAP(mplex
->l1_list
,
801 * Have \var{mplex} display previous (wrt. currently selected) object
805 void mplex_switch_prev(WMPlex
*mplex
)
807 do_switch(mplex
, REGION_PREV_MANAGED_WRAP(mplex
->l1_list
,
824 static WRegion
*mplex_do_attach(WMPlex
*mplex
, WRegionAttachHandler
*hnd
,
825 void *hnd_param
, MPlexAttachParams
*param
)
829 bool sw
=param
->flags
&MPLEX_ATTACH_SWITCHTO
;
830 bool l2
=param
->flags
&MPLEX_ATTACH_L2
;
832 mplex_managed_geom(mplex
, &(fp
.g
));
833 fp
.mode
=(l2
? REGION_FIT_BOUNDS
: REGION_FIT_EXACT
);
835 reg
=hnd((WWindow
*)mplex
, &fp
, hnd_param
);
841 LINK_ITEM(mplex
->l2_list
, reg
, mgr_next
, mgr_prev
);
844 link_at(mplex
, reg
, param
->index
);
848 region_set_manager(reg
, (WRegion
*)mplex
, NULL
);
852 sw
=!l2_mark_hidden(reg
);
853 if(param
->flags
&MPLEX_ATTACH_L2_PASSIVE
)
854 mgd_set_flags(reg
, MGD_L2_PASSIVE
);
857 if(!l2
&& mplex
->l1_count
==1)
861 mplex_do_managed_goto_sw(mplex
, reg
, FALSE
);
866 mplex_managed_changed(mplex
, MPLEX_CHANGE_ADD
, sw
, reg
);
872 WRegion
*mplex_attach_simple(WMPlex
*mplex
, WRegion
*reg
, int flags
)
874 MPlexAttachParams par
;
876 if(reg
==(WRegion
*)mplex
)
882 return region__attach_reparent((WRegion
*)mplex
, reg
,
883 (WRegionDoAttachFn
*)mplex_do_attach
,
888 WRegion
*mplex_attach_hnd(WMPlex
*mplex
, WRegionAttachHandler
*hnd
,
889 void *hnd_param
, int flags
)
891 MPlexAttachParams par
;
896 return mplex_do_attach(mplex
, hnd
, hnd_param
, &par
);
900 static void get_params(ExtlTab tab
, MPlexAttachParams
*par
)
907 extl_table_gets_i(tab
, "layer", &layer
);
909 par
->flags
|=MPLEX_ATTACH_L2
;
911 if(extl_table_is_bool_set(tab
, "switchto"))
912 par
->flags
|=MPLEX_ATTACH_SWITCHTO
;
915 if(extl_table_is_bool_set(tab
, "passive"))
916 par
->flags
|=MPLEX_ATTACH_L2_PASSIVE
;
918 extl_table_gets_i(tab
, "index", &(par
->index
));
923 * Attach and reparent existing region \var{reg} to \var{mplex}.
924 * The table \var{param} may contain the fields \var{index} and
925 * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
928 WRegion
*mplex_attach(WMPlex
*mplex
, WRegion
*reg
, ExtlTab param
)
930 MPlexAttachParams par
;
931 get_params(param
, &par
);
933 /* region__attach_reparent should do better checks. */
934 if(reg
==NULL
|| reg
==(WRegion
*)mplex
)
937 return region__attach_reparent((WRegion
*)mplex
, reg
,
938 (WRegionDoAttachFn
*)mplex_do_attach
,
944 * Create a new region to be managed by \var{mplex}. At least the following
945 * fields in \var{param} are understood:
947 * \begin{tabularx}{\linewidth}{lX}
948 * \tabhead{Field & Description}
949 * \var{type} & Class name (a string) of the object to be created. Mandatory. \\
950 * \var{name} & Name of the object to be created (a string). Optional. \\
951 * \var{switchto} & Should the region be switched to (boolean)? Optional. \\
952 * \var{index} & Index of the new region in \var{mplex}'s list of
953 * managed objects (integer, 0 = first). Optional. \\
954 * \var{layer} & Layer to attach on; 1 (default) or 2. \\
955 * \var{passive} & Is a layer 2 object passive/skipped when deciding
956 * object to gives focus to (boolean)? Optional.
959 * In addition parameters to the region to be created are passed in this
963 WRegion
*mplex_attach_new(WMPlex
*mplex
, ExtlTab param
)
965 MPlexAttachParams par
;
966 get_params(param
, &par
);
968 return region__attach_load((WRegion
*)mplex
, param
,
969 (WRegionDoAttachFn
*)mplex_do_attach
,
975 * Attach all tagged regions to \var{mplex}.
978 void mplex_attach_tagged(WMPlex
*mplex
)
982 while((reg
=ioncore_tags_take_first())!=NULL
)
983 mplex_attach_simple(mplex
, reg
, 0);
987 static bool mplex_handle_drop(WMPlex
*mplex
, int x
, int y
,
990 WRegion
*curr
=mplex_lcurrent(mplex
, 1);
992 /* This code should handle dropping tabs on floating workspaces. */
993 if(curr
&& HAS_DYN(curr
, region_handle_drop
)){
995 region_rootpos(curr
, &rx
, &ry
);
996 if(rectangle_contains(®ION_GEOM(curr
), x
-rx
, y
-ry
)){
997 if(region_handle_drop(curr
, x
, y
, dropped
))
1002 return (NULL
!=mplex_attach_simple(mplex
, dropped
, MPLEX_ATTACH_SWITCHTO
));
1006 bool mplex_manage_clientwin(WMPlex
*mplex
, WClientWin
*cwin
,
1007 const WManageParams
*param
, int redir
)
1009 int swf
=(param
->switchto
? MPLEX_ATTACH_SWITCHTO
: 0);
1011 if(redir
==MANAGE_REDIR_STRICT_YES
|| redir
==MANAGE_REDIR_PREFER_YES
){
1012 if(mplex
->l2_current
!=NULL
){
1013 if(region_manage_clientwin(mplex
->l2_current
, cwin
, param
,
1014 MANAGE_REDIR_PREFER_YES
))
1017 if(mplex
->l1_current
!=NULL
){
1018 if(region_manage_clientwin(mplex
->l1_current
, cwin
, param
,
1019 MANAGE_REDIR_PREFER_YES
))
1024 if(redir
==MANAGE_REDIR_STRICT_YES
)
1027 return (NULL
!=mplex_attach_simple(mplex
, (WRegion
*)cwin
, swf
));
1031 bool mplex_manage_rescue(WMPlex
*mplex
, WClientWin
*cwin
, WRegion
*from
)
1033 return (NULL
!=mplex_attach_simple(mplex
, (WRegion
*)cwin
, 0));
1043 void mplex_managed_remove(WMPlex
*mplex
, WRegion
*sub
)
1048 WRegion
*stdisp
=(WRegion
*)(mplex
->stdispinfo
.regwatch
.obj
);
1050 if(OBJ_IS(sub
, WGenWS
) && stdisp
!=NULL
&& REGION_MANAGER(stdisp
)==sub
){
1051 genws_unmanage_stdisp((WGenWS
*)sub
, TRUE
, TRUE
);
1052 region_detach_manager(stdisp
);
1055 if(mplex
->l1_current
==sub
){
1056 next
=REGION_PREV_MANAGED(mplex
->l1_list
, sub
);
1058 next
=REGION_NEXT_MANAGED(mplex
->l1_list
, sub
);
1059 mplex
->l1_current
=NULL
;
1061 }else if(mplex
->l2_current
==sub
){
1064 mplex
->l2_current
=NULL
;
1065 FOR_ALL_MANAGED_ON_LIST(mplex
->l2_list
, next2
){
1067 (mgd_flags(next2
)&(MGD_L2_HIDDEN
|MGD_L2_PASSIVE
))==0){
1068 mplex
->l2_current
=next2
;
1072 l2
=on_l2_list(mplex
, sub
);
1075 mgd_unset_flags(sub
, ~0);
1078 region_unset_manager(sub
, (WRegion
*)mplex
, &(mplex
->l2_list
));
1081 region_unset_manager(sub
, (WRegion
*)mplex
, &(mplex
->l1_list
));
1085 if(OBJ_IS_BEING_DESTROYED(mplex
))
1088 if(next
!=NULL
&& sw
)
1089 mplex_do_managed_goto_sw(mplex
, next
, FALSE
);
1090 else if(l2
&& region_may_control_focus((WRegion
*)mplex
))
1091 region_warp((WRegion
*)mplex
);
1094 mplex_managed_changed(mplex
, MPLEX_CHANGE_REMOVE
, sw
, sub
);
1098 bool mplex_rescue_clientwins(WMPlex
*mplex
)
1100 bool ret1
, ret2
, ret3
;
1102 ret1
=region_rescue_managed_clientwins((WRegion
*)mplex
, mplex
->l1_list
);
1103 ret2
=region_rescue_managed_clientwins((WRegion
*)mplex
, mplex
->l2_list
);
1104 ret3
=region_rescue_child_clientwins((WRegion
*)mplex
);
1106 return (ret1
&& ret2
&& ret3
);
1110 void mplex_child_removed(WMPlex
*mplex
, WRegion
*sub
)
1112 WMPlexSTDispInfo
*di
=&(mplex
->stdispinfo
);
1114 if(sub
==(WRegion
*)(di
->regwatch
.obj
)){
1115 watch_reset(&(di
->regwatch
));
1116 mplex_set_stdisp(mplex
, NULL
, di
->pos
);
1124 /*{{{ Status display support */
1127 # define offsetof(T,F) ((size_t)((char*)&((T*)0L)->F-(char*)0L))
1130 #define STRUCTOF(T, F, FADDR) \
1131 ((T*)((char*)(FADDR)-offsetof(T, F)))
1134 static void stdisp_watch_handler(Watch
*watch
, Obj
*obj
)
1136 /*WMPlex *mplex=STRUCTOF(WMPlex, stdispinfo,
1137 STRUCTOF(WMPlexSTDispInfo, regwatch, watch));
1138 WMPlexSTDispInfo *di=&(mplex->stdispinfo);
1139 WGenWS *ws=OBJ_CAST(REGION_MANAGER(obj), WGenWS);
1141 if(ioncore_g.opmode!=IONCORE_OPMODE_DEINIT && ws!=NULL)
1142 genws_unmanage_stdisp(ws, TRUE, FALSE);*/
1146 bool mplex_set_stdisp(WMPlex
*mplex
, WRegion
*reg
, int pos
)
1148 WMPlexSTDispInfo
*di
=&(mplex
->stdispinfo
);
1149 WRegion
*oldstdisp
=(WRegion
*)(di
->regwatch
.obj
);
1152 assert(reg
==NULL
|| (Obj
*)reg
==di
->regwatch
.obj
||
1153 (REGION_MANAGER(reg
)==NULL
&&
1154 REGION_PARENT(reg
)==(WWindow
*)mplex
));
1156 if(oldstdisp
!=NULL
){
1157 mgr
=OBJ_CAST(REGION_MANAGER(oldstdisp
), WGenWS
);
1160 mainloop_defer_destroy(di
->regwatch
.obj
);
1161 watch_reset(&(di
->regwatch
));
1169 genws_unmanage_stdisp((WGenWS
*)mgr
, TRUE
, FALSE
);
1170 region_detach_manager(oldstdisp
);
1173 watch_setup(&(di
->regwatch
), (Obj
*)reg
, stdisp_watch_handler
);
1175 if(mplex
->l1_current
!=NULL
&& OBJ_IS(mplex
->l1_current
, WGenWS
) &&
1176 mgr
!=(WGenWS
*)(mplex
->l1_current
)){
1178 genws_unmanage_stdisp(mgr
, FALSE
, TRUE
);
1179 region_detach_manager(oldstdisp
);
1181 mgr
=(WGenWS
*)(mplex
->l1_current
);
1184 genws_manage_stdisp(mgr
, reg
, di
->pos
);
1193 void mplex_get_stdisp(WMPlex
*mplex
, WRegion
**reg
, int *pos
)
1195 WMPlexSTDispInfo
*di
=&(mplex
->stdispinfo
);
1197 *reg
=(WRegion
*)di
->regwatch
.obj
;
1202 static StringIntMap pos_map
[]={
1203 {"tl", MPLEX_STDISP_TL
},
1204 {"tr", MPLEX_STDISP_TR
},
1205 {"bl", MPLEX_STDISP_BL
},
1206 {"br", MPLEX_STDISP_BR
},
1211 static WRegion
*do_attach_stdisp(WMPlex
*mplex
, WRegionAttachHandler
*handler
,
1212 void *handlerparams
, const WFitParams
*fp
)
1214 return handler((WWindow
*)mplex
, fp
, handlerparams
);
1215 /* We do not manage the stdisp, so manager is not set in this
1216 * function unlike a true "attach" function.
1222 * Set/create status display for \var{mplex}. Table is a standard
1223 * description of the object to be created (as passed to e.g.
1224 * \fnref{WMPlex.attach_new}). In addition, the following fields are
1227 * \begin{tabularx}{\linewidth}{lX}
1228 * \tabhead{Field & Description}
1229 * \var{pos} & The corner of the screen to place the status display
1230 * in. One of \code{tl}, \code{tr}, \var{bl} or \var{br}. \\
1231 * \var{action} & If this field is set to \code{keep}, \var{corner}
1232 * and \var{orientation} are changed for the existing
1233 * status display. If this field is set to \var{remove},
1234 * the existing status display is removed. If this
1235 * field is not set or is set to \code{replace}, a
1236 * new status display is created and the old, if any,
1240 EXTL_EXPORT_AS(WMPlex
, set_stdisp
)
1241 WRegion
*mplex_set_stdisp_extl(WMPlex
*mplex
, ExtlTab t
)
1243 WRegion
*stdisp
=NULL
;
1244 int p
=mplex
->stdispinfo
.pos
;
1247 if(extl_table_gets_s(t
, "pos", &s
)){
1248 p
=stringintmap_value(pos_map
, s
, -1);
1250 warn(TR("Invalid position setting."));
1256 extl_table_gets_s(t
, "action", &s
);
1258 if(s
==NULL
|| strcmp(s
, "replace")==0){
1264 fp
.g
.w
=REGION_GEOM(mplex
).w
;
1265 fp
.g
.h
=REGION_GEOM(mplex
).h
;
1266 fp
.mode
=REGION_FIT_BOUNDS
;
1268 /* Full mplex size is stupid so use saved geometry initially
1271 extl_table_gets_rectangle(t
, "geom", &(fp
.g
));
1273 stdisp
=region__attach_load((WRegion
*)mplex
, t
,
1274 (WRegionDoAttachFn
*)do_attach_stdisp
,
1280 }else if(strcmp(s
, "keep")==0){
1281 stdisp
=(WRegion
*)(mplex
->stdispinfo
.regwatch
.obj
);
1282 }else if(strcmp(s
, "remove")!=0){
1283 warn(TR("Invalid action setting."));
1287 if(!mplex_set_stdisp(mplex
, stdisp
, p
)){
1288 destroy_obj((Obj
*)stdisp
);
1296 static ExtlTab
mplex_do_get_stdisp_extl(WMPlex
*mplex
, bool fullconfig
)
1298 WMPlexSTDispInfo
*di
=&(mplex
->stdispinfo
);
1301 if(di
->regwatch
.obj
==NULL
)
1302 return extl_table_none();
1305 t
=region_get_configuration((WRegion
*)di
->regwatch
.obj
);
1306 extl_table_sets_rectangle(t
, "geom", ®ION_GEOM(di
->regwatch
.obj
));
1308 t
=extl_create_table();
1309 extl_table_sets_o(t
, "reg", di
->regwatch
.obj
);
1312 if(t
!=extl_table_none())
1313 extl_table_sets_s(t
, "pos", stringintmap_key(pos_map
, di
->pos
, NULL
));
1320 * Get status display information. See \fnref{WMPlex.get_stdisp} for
1321 * information on the fields.
1323 EXTL_EXPORT_AS(WMPlex
, get_stdisp
)
1324 ExtlTab
mplex_get_stdisp_extl(WMPlex
*mplex
)
1326 return mplex_do_get_stdisp_extl(mplex
, FALSE
);
1336 void mplex_managed_geom_default(const WMPlex
*mplex
, WRectangle
*geom
)
1340 geom
->w
=REGION_GEOM(mplex
).w
;
1341 geom
->h
=REGION_GEOM(mplex
).h
;
1345 void mplex_managed_geom(const WMPlex
*mplex
, WRectangle
*geom
)
1347 CALL_DYN(mplex_managed_geom
, mplex
, (mplex
, geom
));
1351 void mplex_size_changed(WMPlex
*mplex
, bool wchg
, bool hchg
)
1353 CALL_DYN(mplex_size_changed
, mplex
, (mplex
, wchg
, hchg
));
1357 void mplex_managed_changed(WMPlex
*mplex
, int mode
, bool sw
, WRegion
*mgd
)
1359 CALL_DYN(mplex_managed_changed
, mplex
, (mplex
, mode
, sw
, mgd
));
1366 /*{{{ Changed hook helper */
1369 static const char *mode2str(int mode
)
1371 if(mode
==MPLEX_CHANGE_SWITCHONLY
)
1372 return "switchonly";
1373 else if(mode
==MPLEX_CHANGE_REORDER
)
1375 else if(mode
==MPLEX_CHANGE_ADD
)
1377 else if(mode
==MPLEX_CHANGE_REMOVE
)
1383 static bool mrsh_chg(ExtlFn fn
, WMPlexChangedParams
*p
)
1385 ExtlTab t
=extl_create_table();
1388 extl_table_sets_o(t
, "reg", (Obj
*)p
->reg
);
1389 extl_table_sets_s(t
, "mode", mode2str(p
->mode
));
1390 extl_table_sets_b(t
, "sw", p
->sw
);
1391 extl_table_sets_o(t
, "sub", (Obj
*)p
->sub
);
1393 ret
=extl_call(fn
, "t", NULL
, t
);
1395 extl_unref_table(t
);
1401 void mplex_call_changed_hook(WMPlex
*mplex
, WHook
*hook
,
1402 int mode
, bool sw
, WRegion
*reg
)
1404 WMPlexChangedParams p
;
1411 hook_call_p(hook
, &p
, (WHookMarshallExtl
*)mrsh_chg
);
1421 ExtlTab
mplex_get_configuration(WMPlex
*mplex
)
1425 ExtlTab tab
, subs
, stdisptab
;
1427 tab
=region_get_base_configuration((WRegion
*)mplex
);
1429 subs
=extl_create_table();
1430 extl_table_sets_t(tab
, "subs", subs
);
1432 FOR_ALL_MANAGED_ON_LIST(mplex
->l1_list
, sub
){
1433 ExtlTab st
=region_get_configuration(sub
);
1434 if(st
!=extl_table_none()){
1435 if(sub
==mplex
->l1_current
)
1436 extl_table_sets_b(st
, "switchto", TRUE
);
1437 extl_table_seti_t(subs
, ++n
, st
);
1438 extl_unref_table(st
);
1442 FOR_ALL_MANAGED_ON_LIST(mplex
->l2_list
, sub
){
1443 ExtlTab st
=region_get_configuration(sub
);
1444 if(st
!=extl_table_none()){
1445 int flags
=mgd_flags(sub
);
1446 extl_table_sets_i(st
, "layer", 2);
1447 extl_table_sets_b(st
, "switchto", !(flags
&MGD_L2_HIDDEN
));
1448 extl_table_sets_b(st
, "passive", flags
&MGD_L2_PASSIVE
);
1449 extl_table_seti_t(subs
, ++n
, st
);
1450 extl_unref_table(st
);
1454 extl_unref_table(subs
);
1456 /*stdisptab=mplex_do_get_stdisp_extl(mplex, TRUE);
1457 if(stdisptab!=extl_table_none()){
1458 extl_table_sets_t(tab, "stdisp", stdisptab);
1459 extl_unref_table(stdisptab);
1466 void mplex_load_contents(WMPlex
*mplex
, ExtlTab tab
)
1468 ExtlTab substab
, subtab
;
1471 /*if(extl_table_gets_t(tab, "stdisp", &subtab)){
1472 mplex_set_stdisp_extl(mplex, subtab);
1473 extl_unref_table(subtab);
1476 if(extl_table_gets_t(tab
, "subs", &substab
)){
1477 n
=extl_table_get_n(substab
);
1478 for(i
=1; i
<=n
; i
++){
1479 if(extl_table_geti_t(substab
, i
, &subtab
)){
1480 mplex_attach_new(mplex
, subtab
);
1481 extl_unref_table(subtab
);
1484 extl_unref_table(substab
);
1489 WRegion
*mplex_load(WWindow
*par
, const WFitParams
*fp
, ExtlTab tab
)
1491 WMPlex
*mplex
=create_mplex(par
, fp
);
1493 mplex_load_contents(mplex
, tab
);
1494 return (WRegion
*)mplex
;
1501 /*{{{ Dynfuntab and class info */
1504 static DynFunTab mplex_dynfuntab
[]={
1505 {region_do_set_focus
,
1506 mplex_do_set_focus
},
1508 {region_managed_remove
,
1509 mplex_managed_remove
},
1511 {region_managed_rqgeom
,
1512 mplex_managed_rqgeom
},
1514 {(DynFun
*)region_managed_goto
,
1515 (DynFun
*)mplex_managed_goto
},
1517 {(DynFun
*)region_handle_drop
,
1518 (DynFun
*)mplex_handle_drop
},
1520 {region_map
, mplex_map
},
1521 {region_unmap
, mplex_unmap
},
1523 {(DynFun
*)region_manage_clientwin
,
1524 (DynFun
*)mplex_manage_clientwin
},
1526 {(DynFun
*)region_current
,
1527 (DynFun
*)mplex_current
},
1529 {(DynFun
*)region_rescue_clientwins
,
1530 (DynFun
*)mplex_rescue_clientwins
},
1532 {(DynFun
*)region_manage_rescue
,
1533 (DynFun
*)mplex_manage_rescue
},
1535 {(DynFun
*)region_get_configuration
,
1536 (DynFun
*)mplex_get_configuration
},
1538 {mplex_managed_geom
,
1539 mplex_managed_geom_default
},
1541 {(DynFun
*)region_fitrep
,
1542 (DynFun
*)mplex_fitrep
},
1544 {region_child_removed
,
1545 mplex_child_removed
},
1547 {region_managed_activated
,
1548 mplex_managed_activated
},
1554 IMPLCLASS(WMPlex
, WWindow
, mplex_deinit
, mplex_dynfuntab
);