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