A very rough start on 'make test' regression/integration tests
[notion/jeffpc.git] / ioncore / navi.c
blob7dfd19bb9c682c25f202ba042b82c30848dadf17
1 /*
2 * ion/ioncore/navi.c
4 * Copyright (c) Tuomo Valkonen 2006-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <string.h>
11 #include <libtu/objp.h>
13 #include "common.h"
14 #include "extlconv.h"
15 #include "region.h"
16 #include "navi.h"
19 WRegion *region_navi_first(WRegion *reg, WRegionNavi nh,
20 WRegionNaviData *data)
22 WRegion *ret=NULL;
23 CALL_DYN_RET(ret, WRegion*, region_navi_first, reg,
24 (reg, nh, data));
25 return ret;
29 WRegion *region_navi_next(WRegion *reg, WRegion *mgd, WRegionNavi nh,
30 WRegionNaviData *data)
32 WRegion *ret=NULL;
33 CALL_DYN_RET(ret, WRegion*, region_navi_next, reg,
34 (reg, mgd, nh, data));
35 return ret;
39 bool ioncore_string_to_navi(const char *str, WRegionNavi *nh)
41 if(str==NULL){
42 warn(TR("Invalid parameter."));
43 return FALSE;
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;
73 return TRUE;
77 WRegionNavi ioncore_navi_reverse(WRegionNavi nh)
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;
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;
109 static bool may_ascend(WRegion *to, WRegion *from, WRegionNaviData *data)
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);
126 static bool may_descend(WRegion *to, WRegion *from, WRegionNaviData *data)
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);
143 static WRegion *region_navi_descend(WRegion *reg, WRegionNaviData *data)
145 if(data->descend){
146 return region_navi_first(reg, data->nh, data);
147 }else{
148 WRegion *nxt;
150 data->descend=TRUE;
151 data->nh=ioncore_navi_reverse(data->nh);
153 nxt=region_navi_first(reg, data->nh, data);
155 data->descend=FALSE;
156 data->nh=ioncore_navi_reverse(data->nh);
158 return nxt;
163 WRegion *region_navi_cont(WRegion *reg, WRegion *res, WRegionNaviData *data)
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;
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);
181 if(nxt==NULL && !data->nowrap){
182 /* wrap */
183 nxt=region_navi_descend(reg, data);
186 return nxt;
188 }else{
189 if(may_descend(res, reg, data)){
190 return region_navi_descend(res, data);
191 }else{
192 return res;
198 static bool get_param(WRegionNaviData *data, const char *dirstr, ExtlTab param)
200 if(!ioncore_string_to_navi(dirstr, &data->nh))
201 return FALSE;
203 data->ascend_filter=extl_fn_none();
204 data->descend_filter=extl_fn_none();
205 data->no_ascend=NULL;
206 data->no_descend=NULL;
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");
215 return TRUE;
219 static WRegion *release(WRegionNaviData *data, WRegion *res)
221 extl_unref_fn(data->ascend_filter);
222 extl_unref_fn(data->descend_filter);
224 return res;
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.
238 EXTL_EXPORT
239 WRegion *ioncore_navi_next(WRegion *reg, const char *dirstr, ExtlTab param)
241 WRegionNaviData data;
242 WRegion *mgr;
244 if(reg==NULL){
245 /* ??? */
246 return NULL;
249 if(!get_param(&data, dirstr, param))
250 return NULL;
252 mgr=REGION_MANAGER(reg);
254 if(mgr==NULL)
255 return FALSE;
257 data.startpoint=reg;
258 data.descend=FALSE;
260 return release(&data, region_navi_next(mgr, reg, data.nh, &data));
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}.
268 EXTL_EXPORT
269 WRegion *ioncore_navi_first(WRegion *reg, const char *dirstr, ExtlTab param)
271 WRegionNaviData data;
273 if(reg==NULL)
274 return NULL;
276 if(!get_param(&data, dirstr, param))
277 return NULL;
279 data.startpoint=reg;
280 data.descend=TRUE;
282 return release(&data, region_navi_first(reg, data.nh, &data));
286 static WRegion *do_goto(WRegion *res)
288 if(res!=NULL)
289 region_goto(res);
291 return res;
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.
301 EXTL_EXPORT
302 WRegion *ioncore_goto_next(WRegion *reg, const char *dirstr, ExtlTab param)
304 return do_goto(ioncore_navi_next(reg, dirstr, param));
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.
314 EXTL_EXPORT
315 WRegion *ioncore_goto_first(WRegion *reg, const char *dirstr, ExtlTab param)
317 return do_goto(ioncore_navi_first(reg, dirstr, param));