]> git.decadent.org.uk Git - ion3.git/blob - ioncore/navi.c
71090386bd9364df7999969e88fe6559b5c168d7
[ion3.git] / ioncore / navi.c
1 /*
2  * ion/ioncore/navi.c
3  *
4  * Copyright (c) Tuomo Valkonen 2006-2007. 
5  *
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.
10  */
11
12 #include <string.h>
13
14 #include <libtu/objp.h>
15
16 #include "common.h"
17 #include "extlconv.h"
18 #include "region.h"
19 #include "navi.h"
20
21
22 WRegion *region_navi_first(WRegion *reg, WRegionNavi nh,
23                            WRegionNaviData *data)
24 {
25     WRegion *ret=NULL;
26     CALL_DYN_RET(ret, WRegion*, region_navi_first, reg, 
27                  (reg, nh, data));
28     return ret;
29 }
30
31
32 WRegion *region_navi_next(WRegion *reg, WRegion *mgd, WRegionNavi nh,
33                           WRegionNaviData *data)
34 {
35     WRegion *ret=NULL;
36     CALL_DYN_RET(ret, WRegion*, region_navi_next, reg, 
37                  (reg, mgd, nh, data));
38     return ret;
39 }
40
41
42 bool ioncore_string_to_navi(const char *str, WRegionNavi *nh)
43 {
44     if(str==NULL){
45         warn(TR("Invalid parameter."));
46         return FALSE;
47     }
48     
49     if(!strcmp(str, "any")){
50         *nh=REGION_NAVI_ANY;
51     }else if (!strcmp(str, "end") || 
52               !strcmp(str, "last") || 
53               !strcmp(str, "next")){
54         *nh=REGION_NAVI_END;
55     }else if (!strcmp(str, "beg") || 
56               !strcmp(str, "first") ||
57               !strcmp(str, "prev")){
58         *nh=REGION_NAVI_BEG;
59     }else if(!strcmp(str, "left")){
60         *nh=REGION_NAVI_LEFT;
61     }else if(!strcmp(str, "right")){
62         *nh=REGION_NAVI_RIGHT;
63     }else if(!strcmp(str, "top") || 
64              !strcmp(str, "above") || 
65              !strcmp(str, "up")){
66         *nh=REGION_NAVI_TOP;
67     }else if(!strcmp(str, "bottom") || 
68              !strcmp(str, "below") ||
69              !strcmp(str, "down")){
70         *nh=REGION_NAVI_BOTTOM;
71     }else{
72         warn(TR("Invalid direction parameter."));
73         return FALSE;
74     }
75     
76     return TRUE;
77 }
78
79
80 WRegionNavi ioncore_navi_reverse(WRegionNavi nh)
81 {
82     if(nh==REGION_NAVI_BEG)
83         return REGION_NAVI_END;
84     else if(nh==REGION_NAVI_END)
85         return REGION_NAVI_BEG;
86     else if(nh==REGION_NAVI_LEFT)
87         return REGION_NAVI_RIGHT;
88     else if(nh==REGION_NAVI_RIGHT)
89         return REGION_NAVI_LEFT;
90     else if(nh==REGION_NAVI_TOP)
91         return REGION_NAVI_BOTTOM;
92     else if(nh==REGION_NAVI_BOTTOM)
93         return REGION_NAVI_TOP;
94     else
95         return REGION_NAVI_ANY;
96 }
97
98
99 DECLSTRUCT(WRegionNaviData){
100     WRegionNavi nh;
101     bool descend;
102     ExtlFn ascend_filter;
103     ExtlFn descend_filter;
104     WRegion *startpoint;
105     bool nowrap;
106     Obj *no_ascend;
107     Obj *no_descend;
108     bool nofront;
109 };
110
111
112 static bool may_ascend(WRegion *to, WRegion *from, WRegionNaviData *data)
113 {
114     if(data->ascend_filter!=extl_fn_none()){
115         bool r, v;
116         extl_protect(NULL);
117         r=extl_call(data->ascend_filter, "oo", "b", to, from, &v);
118         extl_unprotect(NULL);
119         return (r && v);
120     }else if(data->no_ascend!=NULL){
121         return (data->no_ascend!=(Obj*)from);
122     }else{
123         /* Set to TRUE for cycling out of nested workspaces etc. */
124         return !OBJ_IS(from, WMPlex);
125     }
126 }
127
128
129 static bool may_descend(WRegion *to, WRegion *from, WRegionNaviData *data)
130 {
131     if(data->descend_filter!=extl_fn_none()){
132         bool r, v;
133         extl_protect(NULL);
134         r=extl_call(data->descend_filter, "oo", "b", to, from, &v);
135         extl_unprotect(NULL);
136         return (r && v);
137     }else if(data->no_descend!=NULL){
138         return (data->no_descend!=(Obj*)from);
139     }else{
140         /* Set to TRUE for cycling into nested workspaces etc. */
141         return !OBJ_IS(to, WMPlex);
142     }
143 }
144
145
146 static WRegion *region_navi_descend(WRegion *reg, WRegionNaviData *data)
147 {
148     if(data->descend){
149         return region_navi_first(reg, data->nh, data);
150     }else{
151         WRegion *nxt;
152         
153         data->descend=TRUE;
154         data->nh=ioncore_navi_reverse(data->nh);
155
156         nxt=region_navi_first(reg, data->nh, data);
157         
158         data->descend=FALSE;
159         data->nh=ioncore_navi_reverse(data->nh);
160         
161         return nxt;
162     }
163 }
164     
165
166 WRegion *region_navi_cont(WRegion *reg, WRegion *res, WRegionNaviData *data)
167 {
168     if(res==NULL){
169         if(data->descend){
170             return (reg==data->startpoint ? NULL : reg);
171         }else{
172             WRegion *mgr=REGION_MANAGER(reg);
173             WRegion *nxt=NULL;
174
175             if(mgr!=NULL && may_ascend(mgr, reg, data)){
176                 if(data->nowrap){
177                     /* tail-recursive case */
178                     return region_navi_next(mgr, reg, data->nh, data);
179                 }else{
180                     nxt=region_navi_next(mgr, reg, data->nh, data);
181                 }
182             }
183             
184             if(nxt==NULL && !data->nowrap){
185                 /* wrap */
186                 nxt=region_navi_descend(reg, data);
187             }
188             
189             return nxt;
190         }
191     }else{
192         if(may_descend(res, reg, data)){
193             return region_navi_descend(res, data);
194         }else{
195             return res;
196         }
197     }
198 }
199
200
201 static bool get_param(WRegionNaviData *data, const char *dirstr, ExtlTab param)
202 {
203     if(!ioncore_string_to_navi(dirstr, &data->nh))
204         return FALSE;
205     
206     data->ascend_filter=extl_fn_none();
207     data->descend_filter=extl_fn_none();
208     data->no_ascend=NULL;
209     data->no_descend=NULL;
210     
211     extl_table_gets_o(param, "no_ascend", &data->no_ascend);
212     extl_table_gets_o(param, "no_descend", &data->no_descend);
213     extl_table_gets_f(param, "ascend_filter", &data->ascend_filter);
214     extl_table_gets_f(param, "descend_filter", &data->descend_filter);
215     data->nowrap=extl_table_is_bool_set(param, "nowrap");
216     data->nofront=extl_table_is_bool_set(param, "nofront");
217     
218     return TRUE;
219 }
220
221
222 static WRegion *release(WRegionNaviData *data, WRegion *res)
223 {
224     extl_unref_fn(data->ascend_filter);
225     extl_unref_fn(data->descend_filter);
226     
227     return res;
228 }
229
230
231 /*EXTL_DOC
232  * Find region next from \var{reg} in direction \var{dirstr}
233  * (up/down/left/right/next/prev/any). The table \var{param} may
234  * contain the boolean field \var{nowrap}, instructing not to wrap 
235  * around, and the \type{WRegion}s \var{no_ascend} and \var{no_descend},
236  * and boolean functions \var{ascend_filter} and \var{descend_filter} 
237  * on \var{WRegion} pairs (\var{to}, \var{from}), are used to decide when
238  * to descend or ascend into another region.
239  */
240 EXTL_EXPORT
241 WRegion *ioncore_navi_next(WRegion *reg, const char *dirstr, ExtlTab param)
242 {
243     WRegionNaviData data;
244     WRegion *mgr;
245     
246     if(reg==NULL){
247         /* ??? */
248         return NULL;
249     }
250     
251     if(!get_param(&data, dirstr, param))
252         return NULL;
253     
254     mgr=REGION_MANAGER(reg);
255     
256     if(mgr==NULL)
257         return FALSE;
258     
259     data.startpoint=reg;
260     data.descend=FALSE;
261     
262     return release(&data, region_navi_next(mgr, reg, data.nh, &data));
263 }
264
265
266 /*EXTL_DOC
267  * Find first region within \var{reg} in direction \var{dirstr}
268  * (up/down/left/right/beg/end/any). For information on \var{param},
269  * see \fnref{ioncore.navi_next}.
270  */
271 EXTL_EXPORT
272 WRegion *ioncore_navi_first(WRegion *reg, const char *dirstr, ExtlTab param)
273 {
274     WRegionNaviData data;
275     
276     if(reg==NULL)
277         return NULL;
278     
279     if(!get_param(&data, dirstr, param))
280         return NULL;
281     
282     data.startpoint=reg;
283     data.descend=TRUE;
284     
285     return release(&data, region_navi_first(reg, data.nh, &data));
286 }
287
288
289 static WRegion *do_goto(WRegion *res)
290 {
291     if(res!=NULL)
292         region_goto(res);
293     
294     return res;
295 }
296
297
298 /*EXTL_DOC
299  * Go to region next from \var{reg} in direction \var{dirstr}
300  * (up/down/left/right/next/prev/any). For information on \var{param},
301  * see \fnref{ioncore.navi_next}. Additionally this function supports
302  * the boolean \var{nofront} field, for not bringing the object to
303  * front.
304  */
305 EXTL_EXPORT
306 WRegion *ioncore_goto_next(WRegion *reg, const char *dirstr, ExtlTab param)
307 {
308     return do_goto(ioncore_navi_next(reg, dirstr, param));
309 }
310
311
312 /*EXTL_DOC
313  * Go to first region within \var{reg} in direction \var{dirstr}
314  * (up/down/left/right/beg/end/any). For information on \var{param},
315  * see \fnref{ioncore.navi_next}. Additionally this function supports
316  * the boolean \var{nofront} field, for not bringing the object to
317  * front.
318  */
319 EXTL_EXPORT
320 WRegion *ioncore_goto_first(WRegion *reg, const char *dirstr, ExtlTab param)
321 {
322     return do_goto(ioncore_navi_first(reg, dirstr, param));
323 }
324
325