]> git.decadent.org.uk Git - ion3.git/blobdiff - ioncore/mplex.c
[svn-upgrade] Integrating new upstream version, ion3 (20070506)
[ion3.git] / ioncore / mplex.c
index 28dc48a1a58fe92a18ba812c3afb35ea92894fc3..71943a4595ca6829fe6da5ea8b267669eb3fe320 100644 (file)
@@ -3,10 +3,7 @@
  *
  * Copyright (c) Tuomo Valkonen 1999-2007. 
  *
- * 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>
@@ -346,7 +343,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)
@@ -359,7 +356,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)
@@ -514,84 +511,126 @@ static void mplex_managed_rqgeom(WMPlex *mplex, WRegion *sub,
 /*{{{ Focus  */
 
 
-static WStacking *mplex_do_to_focus(WMPlex *mplex, WStacking *to_try)
-{
-    WStacking *stacking=mplex_get_stacking(mplex);
-    WStacking *st=NULL;
-    
-    if(stacking==NULL)
-        return NULL;
+typedef struct{
+    WMPlex *mplex;
+    WStacking *to_try;
+    WStacking *group_st;
+    PtrList **hidelist;
+    bool try_hard;
+} FiltData;
 
-    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);
-    
-    if(st!=NULL)
-        return st;
-    else if(mplex->mx_current!=NULL)
-        return mplex->mx_current->st;
-    else
-        return NULL;
-}
 
-
-static WStacking *maybe_focusable(WRegion *reg)
+static WRegion *manager_within(WMPlex *mplex, WStacking *st)
 {
-    if(reg==NULL || !REGION_IS_MAPPED(reg))
-        return NULL;
-
-    return ioncore_find_stacking(reg);
+    return region_managed_within((WRegion*)mplex, st->reg);
 }
 
 
-static WStacking *stacking_within(WMPlex *mplex, WRegion *reg)
+static WStacking *stacking_within(WMPlex *mplex, WStacking *st)
 {
-    while(reg!=NULL && REGION_MANAGER(reg)!=(WRegion*)mplex)
-        reg=REGION_MANAGER(reg);
+    WRegion *reg=manager_within(mplex, st);
     
-    return maybe_focusable(reg);
+    return (reg==NULL 
+            ? NULL
+            : (reg==st->reg
+               ? st
+               : ioncore_find_stacking(reg)));
 }
 
 
-static WStacking *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_)
 {
-    WStacking *to_try=NULL;
-    WRegion *reg=NULL;
+    FiltData *data=(FiltData*)data_;
+    WStacking *stw;
     
-    to_try=maybe_focusable(REGION_ACTIVE_SUB(mplex));
+    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;
+    }
     
-    if(to_try==NULL){
-        /* Search focus history */
-        for(reg=ioncore_g.focus_current; reg!=NULL; reg=reg->active_next){
-            to_try=stacking_within(mplex, reg);
-            if(to_try!=NULL)
-                break;
-        }
+    /* 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;
+}
+
+
+static bool mgr_pseudomodal_approve_filt(WStacking *st, void *data_)
+{
+    FiltData *data=(FiltData*)data_;
     
-    return mplex_do_to_focus(mplex, to_try);
+    return (data->group_st==NULL || st==data->group_st ||
+            manager_within(data->mplex, st)==data->group_st->reg);
 }
 
 
-static WStacking *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;
     
-    return 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;
+    
+    st=stacking_find_to_focus(stacking, to_try, fi, fa, &data);
+    
+    if(st==NULL && hidelist!=NULL)
+        ptrlist_clear(hidelist);
+    
+    return st;
+}
+
+
+static WStacking *mplex_do_to_focus(WMPlex *mplex, WStacking *to_try,
+                                    PtrList **hidelist)
+{
+    return mplex_find_to_focus(mplex, to_try, NULL, hidelist);
 }
 
 
-static WStacking *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)
 {
     WGroup *grp=OBJ_CAST(node->reg, WGroup);
     WStacking *st;
@@ -599,17 +638,71 @@ static WStacking *mplex_to_focus_on(WMPlex *mplex, WStacking *node,
     if(grp!=NULL){
         if(to_try==NULL)
             to_try=grp->current_managed;
-        st=mplex_do_to_focus_on(mplex, node, to_try);
+        st=mplex_find_to_focus(mplex, to_try, node, hidelist);
         if(st!=NULL || to_try!=NULL)
             return st;
+        if(hidelist!=NULL)
+            ptrlist_clear(hidelist);
         /* 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).
          */
     }
     
-    st=mplex_do_to_focus(mplex, node);
-    return (st==node ? st : NULL);
+    st=mplex_do_to_focus(mplex, node, hidelist);
+    
+    if(st==node)
+        return st;
+        
+    if(hidelist!=NULL)
+        ptrlist_clear(hidelist);
+    
+    return NULL;
+}
+
+
+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);
+}
+
+
+static WStacking *mplex_to_focus(WMPlex *mplex)
+{
+    WStacking *to_try=NULL;
+    WRegion *reg=NULL;
+    WStacking *st;
+    
+    to_try=maybe_focusable(REGION_ACTIVE_SUB(mplex));
+    
+    if(to_try==NULL){
+        /* Search focus history */
+        for(reg=ioncore_g.focus_current; reg!=NULL; reg=reg->active_next){
+            to_try=has_stacking_within(mplex, reg);
+            if(to_try!=NULL)
+                break;
+        }
+    }
+    
+    st=mplex_do_to_focus(mplex, to_try, NULL);
+    
+    return (st!=NULL 
+            ? st 
+            : (mplex->mx_current!=NULL
+               ? mplex->mx_current->st
+               : NULL));
 }
 
 
@@ -726,7 +819,7 @@ static bool mplex_refocus(WMPlex *mplex, WStacking *node, bool warp)
     bool ret=TRUE;
     
     if(node!=NULL){
-        foc=mplex_to_focus_on(mplex, node, NULL);
+        foc=mplex_do_to_focus_on(mplex, node, NULL, NULL);
         ret=(foc!=NULL);
     }
         
@@ -746,24 +839,33 @@ bool mplex_do_prepare_focus(WMPlex *mplex, WStacking *node,
                             WStacking *sub, int flags, 
                             WPrepareFocusResult *res)
 {
+    bool ew=(flags&REGION_GOTO_ENTERWINDOW);
+    PtrList *hidelist=NULL;
+    PtrList **hidelistp=(ew ? NULL : &hidelist);
     WStacking *foc;
     
     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);
+        foc=mplex_do_to_focus_on(mplex, node, sub, hidelistp);
     else
-        foc=mplex_do_to_focus(mplex, sub);
+        foc=mplex_do_to_focus(mplex, sub, hidelistp);
 
     if(foc!=NULL){
+        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){
@@ -883,7 +985,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)
@@ -1108,16 +1211,13 @@ bool mplex_do_attach_final(WMPlex *mplex, WRegion *reg, WMPlexPHolder *ph)
                : 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
@@ -1160,6 +1260,7 @@ bool mplex_do_attach_final(WMPlex *mplex, WRegion *reg, WMPlexPHolder *ph)
     node->hidden=TRUE;
     node->szplcy=szplcy;
     node->level=level;
+    node->pseudomodal=(param->flags&MPLEX_ATTACH_PSEUDOMODAL ? 1 : 0);
     
     if(lnode!=NULL){
         llist_link_after(&(mplex->mx_list), 
@@ -1283,17 +1384,20 @@ static void get_params(WMPlex *mplex, ExtlTab tab, int mask,
 {
     int layer=1;
     int tmp;
+    char *tmpstr;
     int ok=~mask;
     
-    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;
@@ -1303,12 +1407,23 @@ 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, "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;
         }
@@ -1374,7 +1489,11 @@ 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. \\
+ *  \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}
  * 
@@ -1410,33 +1529,32 @@ 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)
@@ -1655,12 +1773,13 @@ 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}
+ *               in: one of \codestr{tl}, \codestr{tr}, \codestr{bl} 
+ *               or \codestr{br}. \\
+ *   \var{action} & If this field is set to \codestr{keep}, \var{corner}
  *                  and \var{orientation} are changed for the existing
- *                  status display. If this field is set to \var{remove},
+ *                  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}
@@ -1891,13 +2010,16 @@ static void save_node(WMPlex *mplex, ExtlTab subs, int *n,
     if(st!=extl_table_none()){
         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);