]> git.decadent.org.uk Git - ion3.git/blobdiff - ioncore/mplex.c
Update cfg_kludge_flash for Flash 10
[ion3.git] / ioncore / mplex.c
index a202e7c51672b0565baacfb03f95539d230d3eb8..5f0a87d3a5a8bd2cfe6786c42bd2ca32b977758c 100644 (file)
@@ -1,12 +1,9 @@
 /*
  * ion/ioncore/mplex.c
  *
- * Copyright (c) Tuomo Valkonen 1999-2007
+ * Copyright (c) Tuomo Valkonen 1999-2009
  *
- * Ion is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
+ * See the included file LICENSE for details.
  */
 
 #include <limits.h>
 #include "saveload.h"
 #include "xwindow.h"
 #include "mplexpholder.h"
+#include "grouppholder.h"
 #include "llist.h"
 #include "names.h"
 #include "sizepolicy.h"
 #include "stacking.h"
 #include "group.h"
 #include "navi.h"
-#include "groupedpholder.h"
 
 
 #define SUBS_MAY_BE_MAPPED(MPLEX) \
@@ -101,7 +98,7 @@ bool mplex_do_init(WMPlex *mplex, WWindow *parent,
     
     mplex->mx_list=NULL;
     mplex->mx_current=NULL;
-    mplex->mx_phs=NULL;
+    mplex->misc_phs=NULL;
     mplex->mx_count=0;
     
     mplex->mgd=NULL;
@@ -150,25 +147,14 @@ void mplex_deinit(WMPlex *mplex)
     assert(mplex->mgd==NULL);
     assert(mplex->mx_list==NULL);
 
-    while(mplex->mx_phs!=NULL){
-        assert(mplexpholder_move(mplex->mx_phs, NULL, NULL, NULL));
+    while(mplex->misc_phs!=NULL){
+        assert(mplexpholder_move(mplex->misc_phs, NULL, NULL, NULL));
     }
     
     window_deinit((WWindow*)mplex);
 }
 
 
-bool mplex_may_destroy(WMPlex *mplex)
-{
-    if(mplex->mgd!=NULL){
-        warn(TR("Refusing to destroy - not empty."));
-        return FALSE;
-    }
-    
-    return TRUE;
-}
-
-
 /*}}}*/
 
 
@@ -245,8 +231,8 @@ WRegion *mplex_mx_current(WMPlex *mplex)
 
 
 /*EXTL_DOC
- * Returns the \var{n}:th object managed by \var{mplex} on the
- * \var{l}:th layer.
+ * Returns the \var{n}:th object on the mutually exclusive
+ * list of \var{mplex}.
  */
 EXTL_SAFE
 EXTL_EXPORT_MEMBER
@@ -260,7 +246,8 @@ WRegion *mplex_mx_nth(WMPlex *mplex, uint n)
 /*EXTL_DOC
  * Iterate over numbered/mutually exclusive region list of \var{mplex} 
  * until \var{iterfn} returns \code{false}.
- * The function itself returns \code{true} if it reaches the end of list
+ * The function is called in protected mode.
+ * This routine returns \code{true} if it reaches the end of list
  * without this happening.
  */
 EXTL_SAFE
@@ -277,7 +264,8 @@ bool mplex_mx_i(WMPlex *mplex, ExtlFn iterfn)
 /*EXTL_DOC
  * Iterate over managed regions of \var{mplex} until \var{iterfn} returns
  * \code{false}.
- * The function itself returns \code{true} if it reaches the end of list
+ * The function is called in protected mode.
+ * This routine returns \code{true} if it reaches the end of list
  * without this happening.
  */
 EXTL_SAFE
@@ -292,8 +280,8 @@ bool mplex_managed_i(WMPlex *mplex, ExtlFn iterfn)
 
 
 /*EXTL_DOC
- * Set index of \var{reg} within the multiplexer to \var{index} within 
- * the mutually exclusive list. Special values for \var{index} are:
+ * Set index of \var{reg} to \var{index} within the mutually exclusive 
+ * list of \var{mplex}. Special values for \var{index} are:
  * \begin{tabularx}{\linewidth}{lX}
  *   $-1$ & Last. \\
  *   $-2$ & After \fnref{WMPlex.mx_current}. \\
@@ -327,8 +315,6 @@ void mplex_set_index(WMPlex *mplex, WRegion *reg, int index)
         llist_unlink(&(mplex->mx_list), lnode);
     }
     
-    /* TODO: Support remove? */
-    
     after=llist_index_to_after(mplex->mx_list, mplex->mx_current, index);
     llist_link_after(&(mplex->mx_list), after, lnode);
     mplex_managed_changed(mplex, MPLEX_CHANGE_REORDER, FALSE, reg);
@@ -336,9 +322,9 @@ void mplex_set_index(WMPlex *mplex, WRegion *reg, int index)
 
 
 /*EXTL_DOC
- * Get index of \var{reg} within the multiplexer on list 1. The first region 
- * managed by \var{mplex} has index zero. If \var{reg} is not managed by 
- * \var{mplex}, -1 is returned.
+ * Get index of \var{reg} on the mutually exclusive list of \var{mplex}.
+ * The indices begin from zero.. If \var{reg} is not on the list,
+ * -1 is returned.
  */
 EXTL_SAFE
 EXTL_EXPORT_MEMBER
@@ -359,7 +345,7 @@ int mplex_get_index(WMPlex *mplex, WRegion *reg)
 
 
 /*EXTL_DOC
- * Move \var{r} ''right'' within objects managed by \var{mplex} on list 1.
+ * Move \var{r} ``right'' within objects managed by \var{mplex} on list 1.
  */
 EXTL_EXPORT_MEMBER
 void mplex_inc_index(WMPlex *mplex, WRegion *r)
@@ -372,7 +358,7 @@ void mplex_inc_index(WMPlex *mplex, WRegion *r)
 
 
 /*EXTL_DOC
- * Move \var{r} ''right'' within objects managed by \var{mplex} on list 1.
+ * Move \var{r} ``left'' within objects managed by \var{mplex} on list 1.
  */
 EXTL_EXPORT_MEMBER
 void mplex_dec_index(WMPlex *mplex, WRegion *r)
@@ -448,7 +434,8 @@ bool mplex_fitrep(WMPlex *mplex, WWindow *par, const WFitParams *fp)
     bool wchg=(REGION_GEOM(mplex).w!=fp->g.w);
     bool hchg=(REGION_GEOM(mplex).h!=fp->g.h);
     
-    window_do_fitrep(&(mplex->win), par, &(fp->g));
+    if(!window_fitrep(&(mplex->win), par, fp))
+        return FALSE;
     
     if(wchg || hchg){
         mplex_fit_managed(mplex);
@@ -508,9 +495,10 @@ static void mplex_managed_rqgeom(WMPlex *mplex, WRegion *sub,
     node=mplex_find_stacking(mplex, sub);
     
     assert(node!=NULL);
-
+    
+    fp.mode=0;
     mplex_managed_geom(mplex, &fp.g);
-
+    
     sizepolicy(&node->szplcy, sub, &rq->geom, rq->flags, &fp);
     
     if(geomret!=NULL)
@@ -521,94 +509,238 @@ static void mplex_managed_rqgeom(WMPlex *mplex, WRegion *sub,
 }
 
 
+void mplex_set_szplcy(WMPlex *mplex, WRegion *sub, WSizePolicy szplcy)
+{
+    WStacking *node;
+
+    node=mplex_find_stacking(mplex, sub);
+    
+    if(node!=NULL)
+        node->szplcy=szplcy;
+}
+
+
+WSizePolicy mplex_get_szplcy(WMPlex *mplex, WRegion *sub)
+{
+    WStacking *node;
+
+    node=mplex_find_stacking(mplex, sub);
+    
+    return (node==NULL ? SIZEPOLICY_DEFAULT : node->szplcy);
+}
+
+
 /*}}}*/
 
 
 /*{{{ Focus  */
 
 
-static WRegion *mplex_do_to_focus(WMPlex *mplex, WStacking *to_try)
+typedef struct{
+    WMPlex *mplex;
+    WStacking *to_try;
+    WStacking *group_st;
+    PtrList **hidelist;
+    bool try_hard;
+} FiltData;
+
+
+static WRegion *manager_within(WMPlex *mplex, WStacking *st)
 {
-    WStacking *stacking=mplex_get_stacking(mplex);
-    WStacking *st=NULL;
-    
-    if(stacking==NULL)
-        return NULL;
+    return region_managed_within((WRegion*)mplex, st->reg);
+}
 
-    if(to_try!=NULL && (to_try->reg==NULL || !REGION_IS_MAPPED(to_try->reg)))
-        to_try=NULL;
 
-    st=stacking_find_to_focus_mapped(stacking, to_try, NULL);
+static WStacking *stacking_within(WMPlex *mplex, WStacking *st)
+{
+    WRegion *reg=manager_within(mplex, st);
     
-    if(st!=NULL)
-        return st->reg;
-    else if(mplex->mx_current!=NULL)
-        return mplex->mx_current->st->reg;
-    else
-        return NULL;
+    return (reg==NULL 
+            ? NULL
+            : (reg==st->reg
+               ? st
+               : ioncore_find_stacking(reg)));
 }
 
 
-WRegion *mplex_to_focus(WMPlex *mplex)
+/* Mutually exclusive regions can't be pseudomodal */
+#define IS_PSEUDOMODAL(ST) ((ST)->lnode==NULL && (ST)->pseudomodal)
+
+
+static bool mapped_pseudomodal_include_filt(WStacking *st, void *data_)
 {
-    WRegion *reg=REGION_ACTIVE_SUB(mplex);
-    WStacking *to_try=NULL;
+    FiltData *data=(FiltData*)data_;
+    WStacking *stw;
     
-    if(reg!=NULL)
-        to_try=ioncore_find_stacking(reg);
+    if(st->reg==NULL || !REGION_IS_MAPPED(st->reg))
+        return FALSE;
+        
+    if(!data->hidelist
+       || (data->to_try==NULL && data->group_st==NULL) 
+       || st->level<STACKING_LEVEL_MODAL1){
+        return TRUE;
+    }
+    
+    /* Ok, modal node in the way. Let's see if it is pseudomodal
+     * and can be hidden.
+     */
+    
+    stw=stacking_within(data->mplex, st);
+    
+    /* This should not happen */
+    if(stw==NULL || stw->reg==NULL)
+        return FALSE;
+    
+    /* The node is within the same group, so it can not be hidden. 
+     * Latter case should not happen.
+     */
+    if(stw==data->group_st || stw==data->to_try)
+        return TRUE;
+    
+    if(IS_PSEUDOMODAL(stw)){
+        /* Don't insert multiple times. */
+        return !ptrlist_reinsert_first(data->hidelist, stw);
+    }
+        
+    return TRUE;
+}
+
 
-    return mplex_do_to_focus(mplex, to_try);
+static bool mgr_pseudomodal_approve_filt(WStacking *st, void *data_)
+{
+    FiltData *data=(FiltData*)data_;
+    
+    return (data->group_st==NULL || st==data->group_st ||
+            manager_within(data->mplex, st)==data->group_st->reg);
 }
 
 
-static WRegion *mplex_do_to_focus_on(WMPlex *mplex, WStacking *node,
-                                     WStacking *to_try)
+WStacking *mplex_find_to_focus(WMPlex *mplex,
+                               WStacking *to_try,
+                               WStacking *group_st,
+                               PtrList **hidelist)
 {
+    WStackingFilter *fi=mapped_pseudomodal_include_filt;
+    WStackingFilter *fa=mgr_pseudomodal_approve_filt;
     WStacking *stacking=mplex_get_stacking(mplex);
-    WStacking *st=NULL;
+    FiltData data;
+    WStacking *st;
     
     if(stacking==NULL)
         return NULL;
-
+    
     if(to_try!=NULL && (to_try->reg==NULL || !REGION_IS_MAPPED(to_try->reg)))
         to_try=NULL;
     
-    st=stacking_find_to_focus_mapped(stacking, to_try, node->reg);
+    data.mplex=mplex;
+    data.to_try=to_try;
+    data.group_st=group_st;
+    data.hidelist=hidelist;
     
-    return (st!=NULL ? st->reg : NULL);
+    st=stacking_find_to_focus(stacking, to_try, fi, fa, &data);
+    
+    if(st==NULL && hidelist!=NULL)
+        ptrlist_clear(hidelist);
+    
+    return st;
 }
 
 
-static WRegion *mplex_to_focus_on(WMPlex *mplex, WStacking *node,
-                                  WStacking *to_try)
+static WStacking *mplex_do_to_focus_on(WMPlex *mplex, WStacking *node,
+                                       WStacking *to_try, 
+                                       PtrList **hidelist, bool *within)
 {
-    WRegion *reg;
     WGroup *grp=OBJ_CAST(node->reg, WGroup);
+    WStacking *st;
     
     if(grp!=NULL){
         if(to_try==NULL)
             to_try=grp->current_managed;
-        reg=mplex_do_to_focus_on(mplex, node, to_try);
-        if(reg!=NULL || to_try!=NULL)
-            return reg;
-        /* We don't know whether something is blocking focus here,
-         * or if there was nothing to focus (as node->reg itself
-         * isn't on the stacking list).
-         */
+        /* Only will return stuff within 'node' */
+        st=mplex_find_to_focus(mplex, to_try, node, hidelist);
+        if(st!=NULL){
+            if(within!=NULL)
+                *within=TRUE;
+            return st;
+        }
+    }
+    
+    st=mplex_find_to_focus(mplex, node, NULL, hidelist);
+    
+    /* If 'node' points to a group, it isn't actually on the stacking list. 
+     * Give it the focus, if there's nothing "proper" that could be focussed.
+     */
+    if(st==NULL && grp!=NULL && REGION_IS_MAPPED(grp))
+        st=node;
+    
+    if(st==node && within!=NULL)
+        *within=TRUE;
+        
+    return st;
+}
+
+
+static WStacking *maybe_focusable(WRegion *reg)
+{
+    if(reg==NULL || !REGION_IS_MAPPED(reg))
+        return NULL;
+
+    return ioncore_find_stacking(reg);
+}
+
+
+static WStacking *has_stacking_within(WMPlex *mplex, WRegion *reg)
+{
+    while(reg!=NULL && REGION_MANAGER(reg)!=(WRegion*)mplex)
+        reg=REGION_MANAGER(reg);
+                
+    return maybe_focusable(reg);
+}
+
+
+/* 1. Try keep focus in REGION_ACTIVE_SUB.
+ * 2. Choose something else, attempting previous in focus history.
+ */
+static WStacking *mplex_to_focus(WMPlex *mplex)
+{
+    WStacking *foc=NULL, *fallback=NULL;
+    WRegion *reg=NULL;
+    
+    foc=maybe_focusable(REGION_ACTIVE_SUB(mplex));
+    
+    if(foc==NULL){
+        /* Search focus history if no specific attempt set.*/
+        for(reg=ioncore_g.focus_current; reg!=NULL; reg=reg->active_next){
+            foc=has_stacking_within(mplex, reg);
+            if(foc!=NULL)
+                break;
+        }
     }
     
-    reg=mplex_do_to_focus(mplex, node);
-    return (reg==node->reg ? reg : NULL);
+    if(foc!=NULL){
+        /* In the history search case, 'foc' might point to a group,
+         * since we don't properly try to find a stacking within it...
+         */
+        return mplex_do_to_focus_on(mplex, foc, NULL, NULL, NULL);
+    }else{
+        return mplex_find_to_focus(mplex, NULL, NULL, NULL);
+    }
 }
 
 
 void mplex_do_set_focus(WMPlex *mplex, bool warp)
 {
     if(!MPLEX_MGD_UNVIEWABLE(mplex)){
-        WRegion *reg=mplex_to_focus(mplex);
+        WStacking *st=mplex_to_focus(mplex);
         
-        if(reg!=NULL){
-            region_do_set_focus(reg, warp);
+        if(st==NULL){
+            st=(mplex->mx_current!=NULL
+                ? mplex->mx_current->st
+                : NULL);
+        }
+        
+        if(st!=NULL){
+            region_do_set_focus(st->reg, warp);
             return;
         }
     }
@@ -617,6 +749,22 @@ void mplex_do_set_focus(WMPlex *mplex, bool warp)
 }
 
 
+static void mplex_refocus(WMPlex *mplex, WStacking *node, bool warp)
+{
+    bool within=FALSE;
+    WStacking *foc=NULL;
+    
+    if(node!=NULL)
+        foc=mplex_do_to_focus_on(mplex, node, NULL, NULL, &within);
+        
+    if(foc==NULL || !within)
+        foc=mplex_to_focus(mplex);
+    
+    if(foc!=NULL)
+        region_maybewarp(foc->reg, warp);
+}
+
+
 /*}}}*/
 
 
@@ -630,25 +778,19 @@ static void mplex_do_remanage_stdisp(WMPlex *mplex, WRegion *sub)
     /* Move stdisp */
     if(sub!=NULL && CAN_MANAGE_STDISP(sub)){
         if(stdisp!=NULL){
-            WRegion *mgrw=region_managed_within((WRegion*)mplex, stdisp);
-            if(mgrw!=sub){
-                WRegion *mgr=REGION_MANAGER(stdisp);
-                if(mgr!=NULL){
-                    if(CAN_MANAGE_STDISP(mgr))
-                        region_unmanage_stdisp(mgr, FALSE, FALSE);
-                    region_detach_manager(stdisp);
-                }
-                
-                region_manage_stdisp(sub, stdisp, 
-                                     &(mplex->stdispinfo));
+            WRegion *omgr=REGION_MANAGER(stdisp);
+            if(omgr!=sub && omgr!=NULL){
+                if(CAN_MANAGE_STDISP(omgr))
+                    region_unmanage_stdisp(omgr, FALSE, FALSE);
+                region_detach_manager(stdisp);
             }
+            
+            region_manage_stdisp(sub, stdisp, 
+                                 &(mplex->stdispinfo));
         }else{
             region_unmanage_stdisp(sub, TRUE, FALSE);
         }
-    }/*else if(stdisp!=NULL){
-        region_detach_manager(stdisp);
-        region_unmap(stdisp);
-    }*/
+    }
 }
 
 
@@ -699,11 +841,13 @@ static void mplex_do_node_display(WMPlex *mplex, WStacking *node,
          * no visible netscape windows.
          */
         {
-            #warning "TODO: less ugly hack"
             WGroup *grp=(WGroup*)OBJ_CAST(sub, WGroupCW);
-            if(grp!=NULL && grp->bottom!=NULL){
-                region_managed_rqorder((WRegion*)grp, grp->bottom->reg, 
-                                       REGION_ORDER_BACK);
+            if(grp!=NULL){
+                WRegion *bottom=group_bottom(grp);
+                if(bottom!=NULL){
+                    region_managed_rqorder((WRegion*)grp, bottom,
+                                           REGION_ORDER_BACK);
+                }
             }
         }
         
@@ -713,57 +857,46 @@ static void mplex_do_node_display(WMPlex *mplex, WStacking *node,
 }
 
 
-static bool mplex_refocus(WMPlex *mplex, WStacking *node, bool warp)
-{
-    WRegion *foc=NULL;
-    bool ret=TRUE;
-    
-    if(node!=NULL){
-        foc=mplex_to_focus_on(mplex, node, NULL);
-        ret=(foc!=NULL);
-    }
-        
-    if(foc==NULL){
-        ret=FALSE;
-        foc=mplex_to_focus(mplex);
-    }
-    
-    if(foc!=NULL /* && !REGION_IS_ACTIVE(foc) */ )
-        region_maybewarp(foc, warp);
-    
-    return ret;
-}
-
-
 bool mplex_do_prepare_focus(WMPlex *mplex, WStacking *node,
                             WStacking *sub, int flags, 
                             WPrepareFocusResult *res)
 {
-    WRegion *foc;
+    bool ew=(flags&REGION_GOTO_ENTERWINDOW);
+    PtrList *hidelist=NULL;
+    PtrList **hidelistp=(ew ? NULL : &hidelist);
+    WStacking *foc;
+    /*bool within=FALSE;*/
     
     if(sub==NULL && node==NULL)
         return FALSE;
     
     /* Display the node in any case */
-    if(node!=NULL && !(flags&REGION_GOTO_ENTERWINDOW))
+    if(node!=NULL && !ew)
         mplex_do_node_display(mplex, node, TRUE);
     
     if(!region_prepare_focus((WRegion*)mplex, flags, res))
         return FALSE;
 
-    if(node!=NULL)
-        foc=mplex_to_focus_on(mplex, node, sub);
-    else
-        foc=mplex_do_to_focus(mplex, sub);
+    foc=mplex_do_to_focus_on(mplex, node, sub, hidelistp, NULL /*&within*/);
 
     if(foc!=NULL){
-        res->reg=foc;
+        while(hidelist!=NULL){
+            WStacking *st=(WStacking*)ptrlist_take_first(&hidelist);
+            st->hidden=TRUE;
+            region_unmap(st->reg);
+        }
+        
+        if(ioncore_g.autoraise && 
+           !(flags&REGION_GOTO_ENTERWINDOW) &&
+           foc->level>STACKING_LEVEL_BOTTOM){
+            WStacking **stackingp=mplex_get_stackingp(mplex);
+            stacking_restack(stackingp, foc, None, NULL, NULL, FALSE);
+        }
+        
+        res->reg=foc->reg;
         res->flags=flags;
         
-        if(sub==NULL)
-            return (foc==node->reg);
-        else
-            return (foc==sub->reg);
+        return (foc==sub || (sub==NULL && foc==node));
     }else{
         return FALSE;
     }
@@ -869,7 +1002,8 @@ bool mplex_set_hidden(WMPlex *mplex, WRegion *reg, int sp)
 
 /*EXTL_DOC
  * Set the visibility of the region \var{reg} on \var{mplex}
- * as specified with the parameter \var{how} (set/unset/toggle).
+ * as specified with the parameter \var{how} 
+ * (one of \codestr{set}, \codestr{unset}, or \codestr{toggle}).
  * The resulting state is returned.
  */
 EXTL_EXPORT_AS(WMPlex, set_hidden)
@@ -940,13 +1074,8 @@ static WRegion *do_navi(WMPlex *mplex, WStacking *sti,
             if(OBJ_IS(st->reg, WGroup)){
                 /* WGroup navigation code should respect modal stuff. */
                 WRegion *res=region_navi_cont((WRegion*)mplex, st->reg, data);
-                if(res!=NULL){
-                    if(res!=st->reg){
-                        return res;
-                    }else{
-                        #warning "TODO: What to do?"
-                    }
-                }
+                if(res!=NULL && res!=st->reg)
+                    return res;
             }else{
                 if(st->level>=min_level && !PASSIVE(st))
                     return region_navi_cont((WRegion*)mplex, st->reg, data);
@@ -1050,7 +1179,6 @@ bool mplex_managed_rqorder(WMPlex *mplex, WRegion *reg, WRegionOrder order)
 static bool mplex_stack(WMPlex *mplex, WStacking *st)
 {
     WStacking *tmp=NULL;
-    Window bottom=None, top=None;
     WStacking **stackingp=mplex_get_stackingp(mplex);
     
     if(stackingp==NULL)
@@ -1084,23 +1212,28 @@ bool mplex_do_attach_final(WMPlex *mplex, WRegion *reg, WMPlexPHolder *ph)
     WLListNode *lnode=NULL;
     WMPlexAttachParams *param=&ph->param;
     bool mx_was_empty, sw, modal, mcf, hidden;
+    WSizePolicy szplcy;
     uint level;
     
     mcf=region_may_control_focus((WRegion*)mplex);
     
     mx_was_empty=(mplex->mx_list==NULL);
     
+    szplcy=((param->flags&MPLEX_ATTACH_SIZEPOLICY &&
+             param->szplcy!=SIZEPOLICY_DEFAULT)
+            ? param->szplcy
+            : (param->flags&MPLEX_ATTACH_UNNUMBERED
+               ? SIZEPOLICY_FULL_BOUNDS
+               : SIZEPOLICY_FULL_EXACT));
+
     modal=(param->flags&MPLEX_ATTACH_LEVEL
-           ? param->level>=STACKING_LEVEL_MODAL1
-           : param->flags&MPLEX_ATTACH_MODAL);
+           && param->level>=STACKING_LEVEL_MODAL1);
     
     level=(param->flags&MPLEX_ATTACH_LEVEL
            ? param->level
-           : (param->flags&MPLEX_ATTACH_MODAL
-              ? STACKING_LEVEL_MODAL1
-              : (param->flags&MPLEX_ATTACH_UNNUMBERED
-                 ? STACKING_LEVEL_NORMAL
-                 : STACKING_LEVEL_BOTTOM)));
+           : (param->flags&MPLEX_ATTACH_UNNUMBERED
+              ? STACKING_LEVEL_NORMAL
+              : STACKING_LEVEL_BOTTOM));
     
     hidden=(param->flags&MPLEX_ATTACH_HIDDEN
             && (param->flags&MPLEX_ATTACH_UNNUMBERED
@@ -1108,12 +1241,11 @@ bool mplex_do_attach_final(WMPlex *mplex, WRegion *reg, WMPlexPHolder *ph)
     
     sw=(!hidden && (param->flags&MPLEX_ATTACH_SWITCHTO 
                     || (param->flags&MPLEX_ATTACH_UNNUMBERED
-                        ? modal
+                        ? FALSE
                         : (mplex_current_node(mplex)==NULL))));
     
     hidden=(hidden || (!sw && !(param->flags&MPLEX_ATTACH_UNNUMBERED)));
     
-    
     node=create_stacking();
     
     if(node==NULL)
@@ -1142,18 +1274,24 @@ bool mplex_do_attach_final(WMPlex *mplex, WRegion *reg, WMPlexPHolder *ph)
     }
 
     node->hidden=TRUE;
-    node->szplcy=param->szplcy;
+    node->szplcy=szplcy;
     node->level=level;
+    node->pseudomodal=(param->flags&MPLEX_ATTACH_PSEUDOMODAL ? 1 : 0);
     
     if(lnode!=NULL){
+        WMPlexPHolder *ph2, *phn, *php;
+        
         llist_link_after(&(mplex->mx_list), 
                          (ph!=NULL ? ph->after : NULL), 
                          lnode);
         mplex->mx_count++;
         
-        /* Move following placeholders after new node */
-        while(ph->next!=NULL)
-            mplexpholder_move(ph->next, mplex, NULL, lnode);
+        
+        /* Move placeholders after new node */
+        for(php=NULL, ph2=ph; ph2!=NULL; php=ph2, ph2=phn){
+            phn=ph2->next;
+            mplexpholder_move(ph2, mplex, php, lnode);
+        }
     }
     
     LINK_ITEM(mplex->mgd, node, mgr_next, mgr_prev);
@@ -1163,13 +1301,45 @@ bool mplex_do_attach_final(WMPlex *mplex, WRegion *reg, WMPlexPHolder *ph)
     
     region_set_manager(reg, (WRegion*)mplex);
     
+    if(param->flags&MPLEX_ATTACH_PASSIVE)
+        reg->flags|=REGION_SKIP_FOCUS;
+    
+    if(!(param->flags&MPLEX_ATTACH_WHATEVER)){
+        WFitParams fp;
+        
+        fp.mode=0;
+        mplex_managed_geom(mplex, &(fp.g));
+        
+        sizepolicy(&node->szplcy, reg, 
+                   (param->flags&MPLEX_ATTACH_GEOM ? &(param->geom) : NULL),
+                   0, &fp);
+        
+        if(rectangle_compare(&fp.g, &REGION_GEOM(reg))!=RECTANGLE_SAME)
+            region_fitrep(reg, NULL, &fp);
+    }
+    
     if(!hidden)
         mplex_do_node_display(mplex, node, FALSE);
     else
         region_unmap(reg);
-    
-    if(sw && mcf)
-        mplex_refocus(mplex, node, FALSE);
+        
+    if(mcf){
+        if(sw){
+            mplex_refocus(mplex, node, FALSE);
+        }else if(!hidden && 
+                 (level>=STACKING_LEVEL_MODAL1 || OBJ_IS(reg, WGroup))){
+            /* New modal regions may require focusing, so try to
+             * give focus back to currently active object.
+             * (There seems to be some problem with uncontained
+             * client windows still..)
+             */
+            mplex_refocus(mplex, NULL, FALSE);
+        }else if(!hidden){
+            region_pointer_focus_hack(reg);
+        }
+    }else if(!hidden){
+        region_pointer_focus_hack(reg);
+    }
     
     if(lnode!=NULL)
         mplex_managed_changed(mplex, MPLEX_CHANGE_ADD, sw, reg);
@@ -1178,41 +1348,29 @@ bool mplex_do_attach_final(WMPlex *mplex, WRegion *reg, WMPlexPHolder *ph)
 }
 
 
+static void mplex_attach_fp(WMPlex *mplex, const WMPlexAttachParams *param,
+                            WFitParams *fp)
+{
+    if(param->flags&MPLEX_ATTACH_GEOM)
+        fp->g=param->geom;
+    else
+        mplex_managed_geom(mplex, &(fp->g));
+    
+    fp->mode=REGION_FIT_WHATEVER|REGION_FIT_BOUNDS;
+}
+
+
 WRegion *mplex_do_attach_pholder(WMPlex *mplex, WMPlexPHolder *ph,
                                  WRegionAttachData *data)
 {
-    WMPlexAttachParams *param=&(ph->param);
-    WSizePolicy szplcy=param->szplcy;
     WFitParams fp;
-    WRegion *reg;
     
-    param->szplcy=(param->flags&MPLEX_ATTACH_SIZEPOLICY &&
-                   param->szplcy!=SIZEPOLICY_DEFAULT
-                   ? param->szplcy
-                   : (param->flags&MPLEX_ATTACH_UNNUMBERED
-                      ? SIZEPOLICY_FULL_BOUNDS
-                      : SIZEPOLICY_FULL_EXACT));
+    mplex_attach_fp(mplex, &ph->param, &fp);
     
-    mplex_managed_geom(mplex, &(fp.g));
-    
-    sizepolicy(&param->szplcy, NULL, 
-               (param->flags&MPLEX_ATTACH_GEOM 
-                ? &(param->geom)
-                : NULL),
-               0, &fp);
-    
-    if(param->flags&MPLEX_ATTACH_WHATEVER)
-        fp.mode|=REGION_FIT_WHATEVER;
-    
-    reg=region_attach_helper((WRegion*)mplex, 
-                             (WWindow*)mplex, &fp,
-                             (WRegionDoAttachFn*)mplex_do_attach_final,
-                             (void*)ph, data);
-    
-    /* restore */
-    ph->param.szplcy=szplcy;
-    
-    return reg;
+    return region_attach_helper((WRegion*)mplex, 
+                                (WWindow*)mplex, &fp,
+                                (WRegionDoAttachFn*)mplex_do_attach_final,
+                                (void*)ph, data);
 }
 
 
@@ -1270,28 +1428,21 @@ WRegion *mplex_attach_simple(WMPlex *mplex, WRegion *reg, int flags)
 static void get_params(WMPlex *mplex, ExtlTab tab, int mask,
                        WMPlexAttachParams *par)
 {
-    int layer=1;
     int tmp;
+    char *tmpstr;
     int ok=~mask;
     
-    if(mask==0 && extl_table_gets_i(tab, "layer", &tmp)){
-        /* backwards compatibility. No need to consider masked cases */
-        if(tmp==2){
-            par->flags|=MPLEX_ATTACH_UNNUMBERED;
-            if(!extl_table_is_bool_set(tab, "passive"))
-                par->flags|=MPLEX_ATTACH_MODAL;
-        }
-    }
-
-    if(extl_table_gets_i(tab, "level", &tmp)){
-        if(tmp>=0 && ok&MPLEX_ATTACH_LEVEL){
-            par->flags|=MPLEX_ATTACH_LEVEL;
-            par->level=tmp;
+    if(ok&MPLEX_ATTACH_LEVEL){
+        if(extl_table_gets_i(tab, "level", &tmp)){
+            if(tmp>=0){
+                par->flags|=MPLEX_ATTACH_LEVEL;
+                par->level=tmp;
+            }
         }
-    }
     
-    if(extl_table_is_bool_set(tab, "modal"))
-        par->flags|=MPLEX_ATTACH_MODAL&ok;
+        if(extl_table_is_bool_set(tab, "modal"))
+            par->level=maxof(par->level, STACKING_LEVEL_MODAL1);
+    }
 
     if(extl_table_is_bool_set(tab, "unnumbered"))
         par->flags|=MPLEX_ATTACH_UNNUMBERED&ok;
@@ -1301,12 +1452,26 @@ static void get_params(WMPlex *mplex, ExtlTab tab, int mask,
 
     if(extl_table_is_bool_set(tab, "hidden"))
         par->flags|=MPLEX_ATTACH_HIDDEN&ok;
+        
+    if(extl_table_is_bool_set(tab, "passive"))
+        par->flags|=MPLEX_ATTACH_PASSIVE&ok;
+        
+    if(extl_table_is_bool_set(tab, "pseudomodal"))
+        par->flags|=MPLEX_ATTACH_PSEUDOMODAL&ok;
 
     if(extl_table_gets_i(tab, "index", &(par->index)))
         par->flags|=MPLEX_ATTACH_INDEX&ok;
 
-    if(extl_table_gets_i(tab, "sizepolicy", &tmp)){
-        if(ok&MPLEX_ATTACH_SIZEPOLICY){
+    if(ok&MPLEX_ATTACH_SIZEPOLICY){
+        if(extl_table_gets_s(tab, "sizepolicy", &tmpstr)){
+            WSizePolicy tmpp;
+            if(string2sizepolicy(tmpstr, &tmpp)){
+                par->flags|=MPLEX_ATTACH_SIZEPOLICY;
+                par->szplcy=tmpp;
+            }
+            free(tmpstr);
+        }else if(extl_table_gets_i(tab, "sizepolicy", &tmp)){
+            /* Backwards compat. numeric version */
             par->flags|=MPLEX_ATTACH_SIZEPOLICY;
             par->szplcy=tmp;
         }
@@ -1372,8 +1537,12 @@ WRegion *mplex_attach_new_(WMPlex *mplex, WMPlexAttachParams *par,
  *  \var{hidden} & (boolean) Attach hidden, if not prevented
  *                  by e.g. the mutually exclusive list being empty.
  *                  This option overrides \var{switchto}. \\
- *  \var{sizepolicy} & (integer) Size policy.
- *                     (TODO: document them somewhere.) \\
+ *  \var{passive} & (boolean) Skip in certain focusing operations. \\
+ *  \var{pseudomodal} & (boolean) The attached region is ``pseudomodal''
+ *                      if the stacking level dictates it to be modal.
+ *                      This means that the region may be hidden to display
+ *                      regions with lesser stacking levels. \\
+ *  \var{sizepolicy} & (string) Size policy; see Section \ref{sec:sizepolicies}. \\
  *  \var{geom} & (table) Geometry specification. \\
  * \end{tabularx}
  * 
@@ -1389,22 +1558,6 @@ WRegion *mplex_attach_new(WMPlex *mplex, ExtlTab param)
 }
 
 
-/*EXTL_DOC
- * Attach all tagged regions to \var{mplex}.
- */
-EXTL_EXPORT_MEMBER
-void mplex_attach_tagged(WMPlex *mplex)
-{
-    WRegion *reg;
-    int flags=MPLEX_ATTACH_SWITCHTO;
-    
-    while((reg=ioncore_tagged_take_first())!=NULL){
-        mplex_attach_simple(mplex, reg, flags);
-        /*flags=0;*/
-    }
-}
-
-
 static bool mplex_handle_drop(WMPlex *mplex, int x, int y,
                               WRegion *dropped)
 {
@@ -1425,45 +1578,54 @@ static bool mplex_handle_drop(WMPlex *mplex, int x, int y,
 
 
 WPHolder *mplex_prepare_manage(WMPlex *mplex, const WClientWin *cwin,
-                               const WManageParams *param, int redir)
+                               const WManageParams *param, int priority)
 {
+    int cpriority=MANAGE_PRIORITY_SUB(priority, MANAGE_PRIORITY_NORMAL);
     WMPlexAttachParams ap;
     WPHolder *ph=NULL;
     WMPlexPHolder *mph;
     WLListNode *after;
     
-    if(redir==MANAGE_REDIR_STRICT_YES || redir==MANAGE_REDIR_PREFER_YES){
+    /* Check current */ {
         WStacking *cur=mplex_current_node(mplex);
         
         if(cur!=NULL){
-            ph=region_prepare_manage(cur->reg, cwin, param,
-                                     MANAGE_REDIR_PREFER_YES);
+            ph=region_prepare_manage(cur->reg, cwin, param, cpriority);
             if(ph!=NULL)
                 return ph;
         }
         
         if(mplex->mx_current!=NULL && mplex->mx_current->st!=cur){
             ph=region_prepare_manage(mplex->mx_current->st->reg, 
-                                     cwin, param, 
-                                     MANAGE_REDIR_PREFER_YES);
+                                     cwin, param, cpriority);
             if(ph!=NULL)
                 return ph;
         }
     }
     
-    if(redir==MANAGE_REDIR_STRICT_YES)
+    if(!MANAGE_PRIORITY_OK(priority, MANAGE_PRIORITY_NORMAL))
         return NULL;
     
     ap.flags=((param->switchto ? MPLEX_ATTACH_SWITCHTO : 0)
               |MPLEX_ATTACH_SIZEPOLICY);
-    ap.szplcy=SIZEPOLICY_FULL_BOUNDS;
+    ap.szplcy=SIZEPOLICY_FULL_EXACT;
     
     mph=create_mplexpholder(mplex, NULL, &ap);
     
     if(mph!=NULL){
-        WGroupedPHolder *gph=create_groupedpholder((WPHolder*)mph);
-        if(gph!=NULL)
+        WGroupPHolder *gph;
+        WGroupAttachParams gp=GROUPATTACHPARAMS_INIT;
+        
+        gp.switchto_set=1;
+        gp.switchto=1;
+        gp.bottom=1;
+        
+        gph=create_grouppholder(NULL, NULL, &gp);
+        
+        if(gph!=NULL){
+            gph->recreate_pholder=(WPHolder*)mph;
             return (WPHolder*)gph;
+        }
     }
     
     return (WPHolder*)mph;
@@ -1546,56 +1708,56 @@ void mplex_managed_remove(WMPlex *mplex, WRegion *sub)
 }
 
 
-bool mplex_rescue_clientwins(WMPlex *mplex, WPHolder *ph)
+void mplex_child_removed(WMPlex *mplex, WRegion *sub)
+{
+    if(sub!=NULL && sub==(WRegion*)(mplex->stdispwatch.obj)){
+        watch_reset(&(mplex->stdispwatch));
+        mplex_set_stdisp(mplex, NULL, NULL);
+    }
+}
+
+
+/*}}}*/
+
+
+/*{{{ Rescue */
+
+
+bool mplex_rescue_clientwins(WMPlex *mplex, WRescueInfo *info)
 {
     bool ret1, ret2;
     WMPlexIterTmp tmp;
+    WLListIterTmp ltmp;
+    WLListNode *lnode, *was_current=mplex->mx_current;
+
+     
+    /* First all mx stuff to move them nicely to another mplex (when that 
+     * is the case), switching to the current region in the target if 
+     * allowed by ph_flags_mask region_rescue.
+     */
+    FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, ltmp){
+        int sw=(lnode==was_current ? PHOLDER_ATTACH_SWITCHTO : 0);
+        region_do_rescue_this(lnode->st->reg, info, sw);
+    }
     
+    /* Then the rest (possibly retrying failed mx stuff).
+     */
     mplex_iter_init(&tmp, mplex);
-    ret1=region_rescue_some_clientwins((WRegion*)mplex, ph,
+    ret1=region_rescue_some_clientwins((WRegion*)mplex, info,
                                        (WRegionIterator*)mplex_iter,
                                        &tmp);
     
-    ret2=region_rescue_child_clientwins((WRegion*)mplex, ph);
+    ret2=region_rescue_child_clientwins((WRegion*)mplex, info);
     
     return (ret1 && ret2);
 }
 
 
-
-void mplex_child_removed(WMPlex *mplex, WRegion *sub)
-{
-    if(sub!=NULL && sub==(WRegion*)(mplex->stdispwatch.obj)){
-        watch_reset(&(mplex->stdispwatch));
-        mplex_set_stdisp(mplex, NULL, NULL);
-    }
-}
-
-
 /*}}}*/
 
 
 /*{{{ Status display support */
 
-#ifndef offsetof
-# define offsetof(T,F) ((size_t)((char*)&((T*)0L)->F-(char*)0L))
-#endif
-
-#define STRUCTOF(T, F, FADDR) \
-        ((T*)((char*)(FADDR)-offsetof(T, F)))
-
-
-static void stdisp_watch_handler(Watch *watch, Obj *obj)
-{
-    /*WMPlex *mplex=STRUCTOF(WMPlex, stdispinfo, 
-     STRUCTOF(WMPlexSTDispInfo, regwatch, watch));
-     WMPlexSTDispInfo *di=&(mplex->stdispinfo);
-     WGenWS *ws=OBJ_CAST(REGION_MANAGER(obj), WGenWS);
-     * 
-     if(ioncore_g.opmode!=IONCORE_OPMODE_DEINIT && ws!=NULL)
-     genws_unmanage_stdisp(ws, TRUE, FALSE);*/
-}
-
 
 bool mplex_set_stdisp(WMPlex *mplex, WRegion *reg, 
                       const WMPlexSTDispInfo *din)
@@ -1626,13 +1788,13 @@ bool mplex_set_stdisp(WMPlex *mplex, WRegion *reg,
                 region_detach_manager(oldstdisp);
         }
     }else{
-        watch_setup(&(mplex->stdispwatch), (Obj*)reg, stdisp_watch_handler);
+        watch_setup(&(mplex->stdispwatch), (Obj*)reg, NULL);
         
         mplex_remanage_stdisp(mplex);
     }
     
     if(oldstdisp!=NULL && oldstdisp!=reg)
-        region_dispose((WRegion*)oldstdisp, FALSE);
+        mainloop_defer_destroy((Obj*)oldstdisp);
     
     return TRUE;
 }
@@ -1669,13 +1831,15 @@ static bool do_attach_stdisp(WRegion *mplex, WRegion *reg, void *unused)
  * 
  * \begin{tabularx}{\linewidth}{lX}
  *   \tabhead{Field & Description}
- *   \var{pos} & The corner of the screen to place the status display
- *               in. One of \code{tl}, \code{tr}, \var{bl} or \var{br}. \\
- *   \var{action} & If this field is set to \code{keep}, \var{corner}
- *                  and \var{orientation} are changed for the existing
- *                  status display. If this field is set to \var{remove},
+ *   \var{pos} & (string) The corner of the screen to place the status 
+ *               display in: one of \codestr{tl}, \codestr{tr}, \codestr{bl} 
+ *               or \codestr{br}. \\
+ *   \var{fullsize} & (boolean) Waste all available space. \\
+ *   \var{action} & (string) If this field is set to \codestr{keep}, 
+ *                  \var{pos} and \var{fullsize} are changed for the existing
+ *                  status display. If this field is set to \codestr{remove},
  *                  the existing status display is removed. If this
- *                  field is not set or is set to \code{replace}, a 
+ *                  field is not set or is set to \codestr{replace}, a 
  *                  new status display is created and the old, if any,
  *                  removed. \\
  * \end{tabularx}
@@ -1718,7 +1882,7 @@ WRegion *mplex_set_stdisp_extl(WMPlex *mplex, ExtlTab t)
         
         data.type=REGION_ATTACH_LOAD;
         data.u.tab=t;
-            
+        
         stdisp=region_attach_helper((WRegion*)mplex, 
                                     (WWindow*)mplex, &fp,
                                     do_attach_stdisp, NULL,
@@ -1904,16 +2068,18 @@ static void save_node(WMPlex *mplex, ExtlTab subs, int *n,
     st=region_get_configuration(node->reg);
     
     if(st!=extl_table_none()){
-        /*"TODO: better switchto saving? */
         if(mplex->mx_current!=NULL && node==mplex->mx_current->st)
             extl_table_sets_b(st, "switchto", TRUE);
-        extl_table_sets_i(st, "sizepolicy", node->szplcy);
+        extl_table_sets_s(st, "sizepolicy", 
+                          sizepolicy2string(node->szplcy));
         extl_table_sets_i(st, "level", node->level);
         g=extl_table_from_rectangle(&REGION_GEOM(node->reg));
         extl_table_sets_t(st, "geom", g);
         extl_unref_table(g);
         if(STACKING_IS_HIDDEN(node))
             extl_table_sets_b(st, "hidden", TRUE);
+        if(STACKING_IS_PSEUDOMODAL(node))
+            extl_table_sets_b(st, "pseudomodal", TRUE);
         if(unnumbered)
             extl_table_sets_b(st, "unnumbered", TRUE);
         
@@ -1959,16 +2125,6 @@ ExtlTab mplex_get_configuration(WMPlex *mplex)
 }
 
 
-static WMPlex *tmp_mplex=NULL;
-static WMPlexAttachParams *tmp_par=NULL;
-
-static WPHolder *pholder_callback()
-{
-    assert(tmp_mplex!=NULL);
-    return (WPHolder*)create_mplexpholder(tmp_mplex, NULL, tmp_par);
-}
-
-
 void mplex_load_contents(WMPlex *mplex, ExtlTab tab)
 {
     ExtlTab substab, subtab;
@@ -1984,27 +2140,26 @@ void mplex_load_contents(WMPlex *mplex, ExtlTab tab)
         n=extl_table_get_n(substab);
         for(i=1; i<=n; i++){
             if(extl_table_geti_t(substab, i, &subtab)){
-                /*mplex_attach_new(mplex, subtab);*/
                 WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT;
-                WRegionAttachData data;
-                char *tmp=NULL;
+                WFitParams fp;
+                WPHolder *ph;
                 
                 get_params(mplex, subtab, 0, &par);
+                mplex_attach_fp(mplex, &par, &fp);
                 
                 par.flags|=MPLEX_ATTACH_INDEX;
                 par.index=LLIST_INDEX_LAST;
                 
-                tmp_par=&par;
-                tmp_mplex=mplex;
+                ph=(WPHolder*)create_mplexpholder(mplex, NULL, &par);
                 
-                data.type=REGION_ATTACH_LOAD;
-                data.u.tab=subtab;
-                
-                ioncore_set_sm_pholder_callback(pholder_callback);
-    
-                mplex_do_attach(mplex, &par, &data);
-
-                tmp_mplex=NULL;
+                if(ph!=NULL){
+                    region_attach_load_helper((WRegion*)mplex, (WWindow*)mplex, &fp,
+                                              (WRegionDoAttachFn*)mplex_do_attach_final,
+                                              (void*)ph, subtab, &ph);
+                                              
+                    if(ph!=NULL)
+                        destroy_obj((Obj*)ph);
+                }
                 
                 extl_unref_table(subtab);
             }
@@ -2075,9 +2230,6 @@ static DynFunTab mplex_dynfuntab[]={
     {(DynFun*)region_get_rescue_pholder_for,
      (DynFun*)mplex_get_rescue_pholder_for},
 
-    {(DynFun*)region_may_destroy,
-     (DynFun*)mplex_may_destroy},
-    
     {(DynFun*)region_navi_first,
      (DynFun*)mplex_navi_first},