]> git.decadent.org.uk Git - ion3.git/blob - ioncore/navi.c
[svn-inject] Installing original source of ion3
[ion3.git] / ioncore / navi.c
1 /*
2  * ion/ioncore/navi.c
3  *
4  * Copyright (c) Tuomo Valkonen 2006. 
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         return !OBJ_IS(from, WMPlex);
124     }
125 }
126
127
128 static bool may_descend(WRegion *to, WRegion *from, WRegionNaviData *data)
129 {
130     if(data->descend_filter!=extl_fn_none()){
131         bool r, v;
132         extl_protect(NULL);
133         r=extl_call(data->descend_filter, "oo", "b", to, from, &v);
134         extl_unprotect(NULL);
135         return (r && v);
136     }else if(data->no_descend!=NULL){
137         return (data->no_descend!=(Obj*)from);
138     }else{
139         return !OBJ_IS(to, WMPlex);
140     }
141 }
142
143
144 static WRegion *region_navi_descend(WRegion *reg, WRegionNaviData *data)
145 {
146     if(data->descend){
147         return region_navi_first(reg, data->nh, data);
148     }else{
149         WRegion *nxt;
150         
151         data->descend=TRUE;
152         data->nh=ioncore_navi_reverse(data->nh);
153
154         nxt=region_navi_first(reg, data->nh, data);
155         
156         data->descend=FALSE;
157         data->nh=ioncore_navi_reverse(data->nh);
158         
159         return nxt;
160     }
161 }
162     
163
164 WRegion *region_navi_cont(WRegion *reg, WRegion *res, WRegionNaviData *data)
165 {
166     if(res==NULL){
167         if(data->descend){
168             return (reg==data->startpoint ? NULL : reg);
169         }else{
170             WRegion *mgr=REGION_MANAGER(reg);
171             WRegion *nxt=NULL;
172
173             if(mgr!=NULL && may_ascend(mgr, reg, data)){
174                 if(data->nowrap){
175                     /* tail-recursive case */
176                     return region_navi_next(mgr, reg, data->nh, data);
177                 }else{
178                     nxt=region_navi_next(mgr, reg, data->nh, data);
179                 }
180             }
181             
182             if(nxt==NULL && !data->nowrap){
183                 /* wrap */
184                 nxt=region_navi_descend(reg, data);
185             }
186             
187             return nxt;
188         }
189     }else{
190         if(may_descend(res, reg, data)){
191             return region_navi_descend(res, data);
192         }else{
193             return res;
194         }
195     }
196 }
197
198
199 static bool get_param(WRegionNaviData *data, const char *dirstr, ExtlTab param)
200 {
201     if(!ioncore_string_to_navi(dirstr, &data->nh))
202         return FALSE;
203     
204     data->ascend_filter=extl_fn_none();
205     data->descend_filter=extl_fn_none();
206     data->no_ascend=NULL;
207     data->no_descend=NULL;
208     
209     extl_table_gets_o(param, "no_ascend", &data->no_ascend);
210     extl_table_gets_o(param, "no_descend", &data->no_descend);
211     extl_table_gets_f(param, "ascend_filter", &data->ascend_filter);
212     extl_table_gets_f(param, "descend_filter", &data->descend_filter);
213     data->nowrap=extl_table_is_bool_set(param, "nowrap");
214     data->nofront=extl_table_is_bool_set(param, "nofront");
215     
216     return TRUE;
217 }
218
219
220 static WRegion *release(WRegionNaviData *data, WRegion *res)
221 {
222     extl_unref_fn(data->ascend_filter);
223     extl_unref_fn(data->descend_filter);
224     
225     return res;
226 }
227
228
229 /*EXTL_DOC
230  * Find region next from \var{reg} in direction \var{dirstr}
231  * (up/down/left/right/next/prev/any). The table param may
232  * 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 functions \var{ascend_filter} and \var{descend_filter} from
235  * \var{WRegion}s (\var{to}, \var{from}), used to decide when to descend
236  * or ascend into another region. (TODO: more detailed explanation.)
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  * (up/down/left/right/beg/end/any). For information on \var{param},
267  * see \fnref{ioncore.navi_next}.
268  */
269 EXTL_EXPORT
270 WRegion *ioncore_navi_first(WRegion *reg, const char *dirstr, ExtlTab param)
271 {
272     WRegionNaviData data;
273     
274     if(reg==NULL){
275         /* ??? */
276         return NULL;
277     }
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         /* TODO: deep rqorder? */
293         region_rqorder(res, REGION_ORDER_FRONT);
294         region_goto(res);
295     }
296     return res;
297 }
298
299
300 /*EXTL_DOC
301  * Go to region next from \var{reg} in direction \var{dirstr}
302  * (up/down/left/right/next/prev/any). For information on \var{param},
303  * see \fnref{ioncore.navi_next}. Additionally this function supports
304  * the boolean \var{nofront} field, for not bringing the object to
305  * front.
306  */
307 EXTL_EXPORT
308 WRegion *ioncore_goto_next(WRegion *reg, const char *dirstr, ExtlTab param)
309 {
310     return do_goto(ioncore_navi_next(reg, dirstr, param));
311 }
312
313
314 /*EXTL_DOC
315  * Go to first region within \var{reg} in direction \var{dirstr}
316  * (up/down/left/right/beg/end/any). For information on \var{param},
317  * see \fnref{ioncore.navi_next}. Additionally this function supports
318  * the boolean \var{nofront} field, for not bringing the object to
319  * front.
320  */
321 EXTL_EXPORT
322 WRegion *ioncore_goto_first(WRegion *reg, const char *dirstr, ExtlTab param)
323 {
324     return do_goto(ioncore_navi_first(reg, dirstr, param));
325 }
326
327