]> git.decadent.org.uk Git - ion3.git/blobdiff - ioncore/region.c
Imported Upstream version 20090110
[ion3.git] / ioncore / region.c
index dede094fae0db007d9732d37cbbef10987260479..47f8f5c3d93fd09feef16123a485929a8a7fd285 100644 (file)
@@ -1,12 +1,9 @@
 /*
  * ion/ioncore/region.c
  *
- * Copyright (c) Tuomo Valkonen 1999-2006
+ * 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 <string.h>
@@ -26,6 +23,8 @@
 #include "extlconv.h"
 #include "activity.h"
 #include "region-iter.h"
+#include "return.h"
+#include "key.h"
 
 
 #define D2(X)
@@ -33,8 +32,8 @@
 
 WHook *region_notify_hook=NULL;
 
-static void region_notify_change_(WRegion *reg, const char *how, 
-                                  Obj *detail);
+
+static void region_notify_change_(WRegion *reg, WRegionNotify how);
 
 
 /*{{{ Init & deinit */
@@ -73,7 +72,7 @@ void region_init(WRegion *reg, WWindow *par, const WFitParams *fp)
         reg->rootwin=((WRegion*)par)->rootwin;
         region_set_parent(reg, par);
     }else{
-        assert(OBJ_IS(reg, WRootWin));/* || OBJ_IS(reg, WScreen));*/
+        assert(OBJ_IS(reg, WRootWin));
     }
 }
 
@@ -103,14 +102,19 @@ static void destroy_children(WRegion *reg)
 
 void region_deinit(WRegion *reg)
 {
+    region_notify_change(reg, ioncore_g.notifies.deinit);
+    
     destroy_children(reg);
 
     if(ioncore_g.focus_next==reg){
         D(warn("Region to be focused next destroyed[1]."));
         ioncore_g.focus_next=NULL;
     }
-
+    
+    assert(reg->submapstat==NULL);
+    /*region_free_submapstat(reg);*/
     region_detach_manager(reg);
+    region_unset_return(reg);
     region_unset_parent(reg);
     region_remove_bindings(reg);
     
@@ -148,12 +152,14 @@ void region_updategr(WRegion *reg)
 void region_map(WRegion *reg)
 {
     CALL_DYN(region_map, reg, (reg));
+    region_notify_change_(reg, ioncore_g.notifies.map);
 }
 
 
 void region_unmap(WRegion *reg)
 {
     CALL_DYN(region_unmap, reg, (reg));
+    region_notify_change_(reg, ioncore_g.notifies.unmap);
 }
 
 
@@ -192,18 +198,6 @@ void region_do_set_focus(WRegion *reg, bool warp)
 /*{{{ Manager region dynfuns */
 
 
-void region_managed_activated(WRegion *mgr, WRegion *reg)
-{
-    CALL_DYN(region_managed_activated, mgr, (mgr, reg));
-}
-
-
-void region_managed_inactivated(WRegion *mgr, WRegion *reg)
-{
-    CALL_DYN(region_managed_inactivated, mgr, (mgr, reg));
-}
-
-
 static bool region_managed_prepare_focus_default(WRegion *mgr, WRegion *reg, 
                                                  int flags, 
                                                  WPrepareFocusResult *res)
@@ -228,7 +222,7 @@ bool region_managed_prepare_focus(WRegion *mgr, WRegion *reg,
 }
 
 
-void region_managed_notify(WRegion *mgr, WRegion *reg, const char *how)
+void region_managed_notify(WRegion *mgr, WRegion *reg, WRegionNotify how)
 {
     CALL_DYN(region_managed_notify, mgr, (mgr, reg, how));
 }
@@ -241,7 +235,7 @@ void region_managed_remove(WRegion *mgr, WRegion *reg)
 
 
 /*EXTL_DOC
- * Return the object, if any, that is considered ''currently active''
+ * Return the object, if any, that is considered ``currently active''
  * within the objects managed by \var{mplex}.
  */
 EXTL_SAFE
@@ -288,14 +282,13 @@ void region_updategr_default(WRegion *reg)
 bool region_prepare_focus(WRegion *reg, int flags, 
                           WPrepareFocusResult *res)
 {
-    WRegion *mgr=REGION_MANAGER(reg);
-    WRegion *par=REGION_PARENT_REG(reg);
 
-    if(REGION_IS_MAPPED(reg) && region_may_control_focus(reg)){
-        res->reg=reg;
-        res->flags=0;
-        return TRUE;
-    }else{
+    if(TRUE /* !REGION_IS_ACTIVE(reg) || 
+       !REGION_IS_MAPPED(reg) ||
+       ioncore_g.focus_next!=NULL*/){
+        WRegion *mgr=REGION_MANAGER(reg);
+        WRegion *par=REGION_PARENT_REG(reg);
+        
         if(mgr!=NULL){
             return region_managed_prepare_focus(mgr, reg, flags, res);
         }else if(par!=NULL){
@@ -304,11 +297,14 @@ bool region_prepare_focus(WRegion *reg, int flags,
             /* Just focus reg, if it has no manager, and parent can be 
              * focused. 
              */
+        }else if(!REGION_IS_MAPPED(reg)){
+            region_map(reg);
         }
-        res->reg=reg;
-        res->flags=flags;
-        return TRUE;
     }
+    
+    res->reg=reg;
+    res->flags=flags;
+    return TRUE;
 }
 
 
@@ -374,31 +370,10 @@ bool region_reparent(WRegion *reg, WWindow *par,
 /*{{{ Close */
 
 
-static bool region_rqclose_default(WRegion *reg, bool relocate)
+static void region_rqclose_default(WRegion *reg, bool relocate)
 {
-    WPHolder *ph;
-    bool refuse=TRUE;
-    
-    if((!relocate && !region_may_destroy(reg)) ||
-       !region_manager_allows_destroying(reg)){
-        return FALSE;
-    }
-    
-    ph=region_get_rescue_pholder(reg);
-    
-    if(ph!=NULL){
-        refuse=!region_rescue_clientwins(reg, ph);
-        destroy_obj((Obj*)ph);
-    }
-
-    if(refuse){
-        warn(TR("Failed to rescue some client windows - not closing."));
-        return FALSE;
-    }
-
-    mainloop_defer_destroy((Obj*)reg);
-    
-    return TRUE;
+    if(relocate || region_may_dispose(reg))
+        region_defer_rqdispose(reg);
 }
 
 
@@ -407,19 +382,16 @@ static bool region_rqclose_default(WRegion *reg, bool relocate)
  * depends on whether the particular type of region in question has
  * implemented the feature and, in case of client windows, whether
  * the client supports the \code{WM_DELETE} protocol (see also
- * \fnref{WClientWin.kill}). If the operation is likely to succeed,
- * \code{true} is returned, otherwise \code{false}. In most cases the
- * region will not have been actually destroyed when this function returns.
- * If \var{relocate} is not set, and \var{reg} manages other regions, it
- * will not be closed. Otherwise the managed regions will be attempted
- * to be relocated.
+ * \fnref{WClientWin.kill}). The region will not be destroyed when
+ * this function returns. To find out if and when it is destroyed,
+ * use the \codestr{deinit} notification. If \var{relocate} is not set, 
+ * and \var{reg} manages other regions, it will not be closed. Otherwise
+ * the managed regions will be attempted to be relocated.
  */
 EXTL_EXPORT_MEMBER
-bool region_rqclose(WRegion *reg, bool relocate)
+void region_rqclose(WRegion *reg, bool relocate)
 {
-    bool ret=FALSE;
-    CALL_DYN_RET(ret, bool, region_rqclose, reg, (reg, relocate));
-    return ret;
+    CALL_DYN(region_rqclose, reg, (reg, relocate));
 }
 
 
@@ -428,9 +400,13 @@ static WRegion *region_rqclose_propagate_default(WRegion *reg,
 {
     if(maybe_sub==NULL)
         maybe_sub=region_current(reg);
-    if(maybe_sub!=NULL)
+    
+    if(maybe_sub!=NULL){
         return region_rqclose_propagate(maybe_sub, NULL);
-    return (region_rqclose(reg, FALSE) ? reg : NULL);
+    }else{
+        region_rqclose(reg, FALSE);
+        return reg;
+    }
 }
 
 
@@ -438,8 +414,8 @@ static WRegion *region_rqclose_propagate_default(WRegion *reg,
  * Recursively attempt to close a region or one of the regions managed by 
  * it. If \var{sub} is set, it will be used as the managed region, otherwise
  * \fnref{WRegion.current}\code{(reg)}. The object to be closed is
- * returned or NULL if nothing can be closed. Also see notes for
- * \fnref{WRegion.rqclose}.
+ * returned, or NULL if nothing can be closed. For further details, see
+ * notes for \fnref{WRegion.rqclose}.
  */
 EXTL_EXPORT_MEMBER
 WRegion *region_rqclose_propagate(WRegion *reg, WRegion *maybe_sub)
@@ -451,30 +427,104 @@ WRegion *region_rqclose_propagate(WRegion *reg, WRegion *maybe_sub)
 }
 
 
-bool region_may_destroy(WRegion *reg)
+bool region_may_dispose_default(WRegion *reg)
+{
+    bool res=region_rescue_needed(reg);
+    
+    if(res){
+        const char *name=region_name(reg);
+        warn(TR("Can not destroy %s: contains client windows."),
+             (name!=NULL ? name : TR("(unknown)")));
+    }
+    
+    return !res;
+}
+
+
+bool region_may_dispose(WRegion *reg)
 {
     bool ret=TRUE;
-    CALL_DYN_RET(ret, bool, region_may_destroy, reg, (reg));
+    CALL_DYN_RET(ret, bool, region_may_dispose, reg, (reg));
     return ret;
 }
 
 
-bool region_managed_may_destroy(WRegion *mgr, WRegion *reg)
+static WRegion *region_managed_disposeroot_default(WRegion *mgr, WRegion *reg)
 {
-    bool ret=TRUE;
-    CALL_DYN_RET(ret, bool, region_managed_may_destroy, mgr, (mgr, reg));
+    return reg;
+}
+
+
+WRegion *region_managed_disposeroot(WRegion *mgr, WRegion *reg)
+{
+    WRegion *ret=NULL;
+    CALL_DYN_RET(ret, WRegion*, region_managed_disposeroot, mgr, (mgr, reg));
     return ret;
 }
 
 
-bool region_manager_allows_destroying(WRegion *reg)
+WRegion *region_disposeroot(WRegion *reg)
 {
     WRegion *mgr=REGION_MANAGER(reg);
     
-    if(mgr==NULL)
-        return TRUE;
+    return (mgr!=NULL
+            ? region_managed_disposeroot(mgr, reg)
+            : reg);
+}
+
+
+bool region_rqdispose(WRegion *reg)
+{
+    WRegion *root;
+    
+    if(!region_may_dispose(reg))
+        return FALSE;
+    
+    root=region_disposeroot(reg);
+    
+    if(root==NULL)
+        return FALSE;
+
+    return region_dispose(root);
+}
+
+
+bool region_dispose_(WRegion *reg, bool not_simple)
+{
+    bool rescue=not_simple;
+    bool was_mcf=(not_simple && region_may_control_focus(reg));
+    WPHolder *ph=NULL;
+    
+    if(rescue){
+        if(!region_rescue(reg, NULL, 0)){
+            warn(TR("Failed to rescue some client windows - not closing."));
+            return FALSE;
+        }
+    }
+
+    if(was_mcf)
+        ph=region_unset_get_return(reg);
+    
+    destroy_obj((Obj*)reg);
+    
+    if(ph!=NULL){
+        pholder_goto(ph);
+        destroy_obj((Obj*)ph);
+    }
     
-    return region_managed_may_destroy(mgr, reg);
+    return TRUE;
+}
+
+
+bool region_dispose(WRegion *reg)
+{
+    return region_dispose_(reg, TRUE);
+}
+
+
+void region_defer_rqdispose(WRegion *reg)
+{
+    mainloop_defer_action((Obj*)reg, (WDeferredAction*)region_rqdispose);
 }
 
 
@@ -487,29 +537,11 @@ bool region_manager_allows_destroying(WRegion *reg)
 /* Routine to call to unmanage a region */
 void region_detach_manager(WRegion *reg)
 {
-    WRegion *mgr;
-    
-    mgr=REGION_MANAGER(reg);
+    WRegion *mgr=REGION_MANAGER(reg);
     
     if(mgr==NULL)
         return;
     
-    /* Restore activity state to non-parent manager */
-    if(region_may_control_focus(reg)){
-        WRegion *par=REGION_PARENT_REG(reg);
-        if(par!=NULL && mgr!=par && REGION_PARENT_REG(mgr)==par){
-            /* REGION_ACTIVE shouldn't be set for windowless regions
-             * but make the parent's active_sub point to it
-             * nevertheless so that region_may_control_focus can
-             * be made to work.
-             */
-            par->active_sub=mgr;
-            /*if(region_xwindow(mgr)!=None){*/
-                region_do_set_focus(mgr, FALSE);
-            /*}*/
-        }
-    }
-
     region_set_activity(reg, SETPARAM_UNSET);
 
     region_managed_remove(mgr, reg);
@@ -518,6 +550,39 @@ void region_detach_manager(WRegion *reg)
 }
 
 
+void region_unset_manager_pseudoactivity(WRegion *reg)
+{
+    WRegion *mgr=reg->manager, *par=REGION_PARENT_REG(reg);
+    
+    if(mgr==NULL || mgr==par || !REGION_IS_PSEUDOACTIVE(mgr))
+        return;
+        
+    mgr->flags&=~REGION_PSEUDOACTIVE;
+    
+    region_notify_change(mgr, ioncore_g.notifies.pseudoinactivated);
+    
+    region_unset_manager_pseudoactivity(mgr);
+}
+
+
+void region_set_manager_pseudoactivity(WRegion *reg)
+{
+    WRegion *mgr=reg->manager, *par=REGION_PARENT_REG(reg);
+    
+    if(!REGION_IS_ACTIVE(reg) && !REGION_IS_PSEUDOACTIVE(reg))
+        return;
+        
+    if(mgr==NULL || mgr==par || REGION_IS_PSEUDOACTIVE(mgr))
+        return;
+    
+    mgr->flags|=REGION_PSEUDOACTIVE;
+    
+    region_notify_change(mgr, ioncore_g.notifies.pseudoactivated);
+    
+    region_set_manager_pseudoactivity(mgr);
+}
+
+
 /* This should only be called within region_managed_remove,
  * _after_ any managed lists and other essential structures
  * of mgr have been broken.
@@ -526,13 +591,20 @@ void region_unset_manager(WRegion *reg, WRegion *mgr)
 {
     if(reg->manager!=mgr)
         return;
+        
+    region_notify_change_(reg, ioncore_g.notifies.unset_manager);
+    
+    region_unset_manager_pseudoactivity(reg);
     
     reg->manager=NULL;
     
+    /* Reset status, as it is set by manager */
+    reg->flags&=~REGION_SKIP_FOCUS;
+
     if(region_is_activity_r(reg))
         region_clear_mgd_activity(mgr);
-
-    region_notify_change_(reg, "unset_manager", (Obj*)mgr);
+    
+    region_unset_return(reg);
 }
 
 
@@ -546,10 +618,12 @@ void region_set_manager(WRegion *reg, WRegion *mgr)
     
     reg->manager=mgr;
     
+    region_set_manager_pseudoactivity(reg);
+    
     if(region_is_activity_r(reg))
         region_mark_mgd_activity(mgr);
     
-    region_notify_change_(reg, "set_manager", (Obj*)mgr);
+    region_notify_change_(reg, ioncore_g.notifies.set_manager);
 }
 
 
@@ -673,7 +747,7 @@ bool region_rqorder(WRegion *reg, WRegionOrder order)
 
 /*EXTL_DOC
  * Request ordering. Currently supported values for \var{ord}
- * are 'front' and 'back'.
+ * are \codestr{front} and \codestr{back}.
  */
 EXTL_EXPORT_AS(WRegion, rqorder)
 bool region_rqorder_extl(WRegion *reg, const char *ord)
@@ -776,54 +850,53 @@ void region_rootpos(WRegion *reg, int *xret, int *yret)
 }
 
 
-static bool mrsh_not(WHookDummy *fn, void *p)
+typedef struct{
+    WRegion *reg;
+    WRegionNotify how;
+} MRSHP;
+
+
+static bool mrsh_notify_change(WHookDummy *fn, void *p_)
 {
-    WRegion *reg=(WRegion*)((void**)p)[0];
-    const char *how=(const char*)((void**)p)[1];
-    Obj *detail=(Obj*)((void**)p)[2];
+    MRSHP *p=(MRSHP*)p_;
     
-    fn(reg, how, detail);
+    fn(p->reg, p->how);
     
     return TRUE;
 }
 
 
-static bool mrshe_not(ExtlFn fn, void *p)
+static bool mrshe_notify_change(ExtlFn fn, void *p_)
 {
-    WRegion *reg=(WRegion*)((void**)p)[0];
-    const char *how=(const char*)((void**)p)[1];
-    Obj *detail=(Obj*)((void**)p)[2];
+    MRSHP *p=(MRSHP*)p_;
     
-    extl_call(fn, "oso", NULL, reg, how, detail);
+    extl_call(fn, "os", NULL, p->reg, stringstore_get(p->how));
     
     return TRUE;
 }
 
 
-static void region_notify_change_(WRegion *reg, const char *how, 
-                                  Obj *detail)
+static void region_notify_change_(WRegion *reg, WRegionNotify how)
 {
-    const void *p[3];
+    MRSHP p;
+    
+    p.reg=reg;
+    p.how=how;
     
-    p[0]=reg;
-    p[1]=how;
-    p[2]=detail;
-
     extl_protect(NULL);
-    hook_call(region_notify_hook, p, mrsh_not, mrshe_not),
+    hook_call(region_notify_hook, &p, mrsh_notify_change, mrshe_notify_change),
     extl_unprotect(NULL);
-    
 }
 
 
-void region_notify_change(WRegion *reg, const char *how)
+void region_notify_change(WRegion *reg, WRegionNotify how)
 {
     WRegion *mgr=REGION_MANAGER(reg);
 
     if(mgr!=NULL)
         region_managed_notify(mgr, reg, how);
     
-    region_notify_change_(reg, how, NULL);
+    region_notify_change_(reg, how);
 }
 
 
@@ -880,6 +953,9 @@ static DynFunTab region_dynfuntab[]={
     
     {(DynFun*)region_rescue_clientwins,
      (DynFun*)region_rescue_child_clientwins},
+     
+    {(DynFun*)region_may_dispose,
+     (DynFun*)region_may_dispose_default},
 
     {(DynFun*)region_prepare_manage,
      (DynFun*)region_prepare_manage_default},
@@ -889,6 +965,9 @@ static DynFunTab region_dynfuntab[]={
 
     {(DynFun*)region_managed_prepare_focus,
      (DynFun*)region_managed_prepare_focus_default},
+     
+    {(DynFun*)region_managed_disposeroot,
+     (DynFun*)region_managed_disposeroot_default},
 
     {(DynFun*)region_rqclose_propagate,
      (DynFun*)region_rqclose_propagate_default},