config: update test config files to current aversive config.in
[aversive.git] / modules / ihm / menu / menu.c
1 /*  
2  *  Copyright Droids Corporation, Microb Technology, Eirbot (2005)
3  * 
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  *  Revision : $Id: menu.c,v 1.3.4.2 2007-05-23 17:18:15 zer0 Exp $
19  *
20  */
21
22 /* Olivier MATZ, Droids-corp 2004 - 2006
23  * Implementation of a static menu
24  */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 #include <aversive.h>
30
31 #include "menu.h"
32
33 /** return name of a menu, if the type is correct */
34 char * menu_get_name(struct menu * m) {
35     if (!m)
36         return NULL;
37
38     if ( m->type == MENU_TYPE_MENU || 
39          m->type == MENU_TYPE_ROOT ||
40          m->type == MENU_TYPE_FCT_HDR )
41         return (char *)m->data;
42
43     return NULL;
44 }
45
46 /** return type of a menu */
47 uint8_t menu_get_type(struct menu * m) {
48     if (!m)
49         return MENU_TYPE_UNKNOWN;
50
51     return m->type;
52 }
53
54 static uint8_t menu_is_a_submenu(struct menu * m)
55 {
56     if (!m)
57         return 0;
58     
59     return ( m->type == MENU_TYPE_MENU || m->type == MENU_TYPE_ROOT );
60 }
61
62
63 /** call the function described by the menu, and return 0 on success */
64 uint8_t menu_call_fct(struct menu * m) {
65     void (*f)(void *);
66
67     if (!m)
68         return 1;
69     
70     if ( m->type != MENU_TYPE_FCT_HDR )
71         return 1;
72     
73     f = (void (*)(void *) )(m+1)->data;
74
75     if ( (m+1)->type != MENU_TYPE_FCT_PTR || f == NULL )
76         return 1;
77
78     if ( (m+2)->type != MENU_TYPE_FCT_DATA )
79         return 1;
80
81     f( (m+2)->data );
82     return 0;
83 }
84
85
86 /** get previous menu on same level, return NULL if no one */
87 struct menu * menu_get_previous(struct menu * m) {
88     int8_t level = 0;
89
90     if (!m)
91         return NULL;
92     
93     if(m->type == MENU_TYPE_ROOT)
94         return NULL;
95
96     m --;
97     while ( level >= 0 ) {
98             
99         if ( m->type == MENU_TYPE_ROOT ) {
100             return NULL;
101         }
102         if ( m->type == MENU_TYPE_END ) {
103             level ++;
104         }
105         if ( m->type == MENU_TYPE_MENU ) {
106             level --;
107         }
108         if ( level == 0 ) {
109             if (  m->type == MENU_TYPE_FCT_HDR ||
110                   m->type == MENU_TYPE_MENU ) {
111                 return m;
112             }
113         }
114         m--;
115     }
116     return NULL;
117 }
118
119 /** get next menu on same level, return NULL if no one */
120 struct menu * menu_get_next(struct menu * m) {
121     int8_t level = 0;
122
123     if (!m)
124         return NULL;
125     
126     if(m->type == MENU_TYPE_ROOT)
127         return NULL;
128
129     if ( m->type == MENU_TYPE_MENU ) {
130         level ++;
131     }
132
133     m ++;
134     while ( level >= 0 ) {
135             
136         if ( level == 0 ) {
137             if (  m->type == MENU_TYPE_FCT_HDR ||
138                   m->type == MENU_TYPE_MENU ) {
139                 return m;
140             }
141         }
142         if ( m->type == MENU_TYPE_END ) {
143             level --;
144         }
145         if ( m->type == MENU_TYPE_MENU ) {
146             level ++;
147         }
148         m++;
149     }
150     return NULL;
151 }
152
153 /** get the parent of the menu - return NULL if no parent */
154 struct menu * menu_get_parent(struct menu * m)
155 {
156     struct menu * ret;
157
158     if (!m)
159         return NULL;
160     
161     /* return null if root */
162     if(m->type == MENU_TYPE_ROOT)
163         return NULL;
164         
165     do {
166         ret = m;
167     } while ( (m=menu_get_previous(ret)) ) ;
168
169     /* get the previous one and return it if it is a submenu, else return NULL */
170     ret--;
171     if ( menu_is_a_submenu(ret) )
172         return ret;
173     else
174         return NULL;
175 }
176
177 /** return first son or NULL if there is no son */
178 struct menu * menu_get_first_son(struct menu * m)
179 {
180     if (!m)
181         return NULL;
182     
183     if (menu_is_a_submenu(m)) {
184         return m+1;
185     }
186     else return NULL;
187 }
188
189 /** get the submenu 'num' -> can return NULL if does not exist */
190 struct menu * menu_get_sub(struct menu * m, uint8_t num) {
191     if (!m)
192         return NULL;
193     
194     m = menu_get_first_son(m);
195     while(m && num) {
196         num --;
197         m = menu_get_next(m);
198     }
199     return m;
200 }
201
202 /** return number of submenus in a menu */
203 uint8_t menu_get_sub_howmany(struct menu * m) {
204     uint8_t num=0;
205     
206     if (!m)
207         return 0;
208     
209     m = menu_get_first_son(m);
210     while(m) {
211         num ++;
212         m = menu_get_next(m);
213     }
214     return num;
215 }
216
217
218 /** get the parent of the menu - never return NULL except if m is null */
219 struct menu * menu_left(struct menu * m)
220 {
221     struct menu * ret;
222
223     if (!m)
224         return NULL;
225     
226     if ( (ret = menu_get_parent(m)) )
227         return ret;
228     else
229         return m;
230 }
231
232 /** get the son number 'num' or self if it does not exist,
233     try to call the function if it exists, does not return null
234     except if m is null */
235 struct menu * menu_right(struct menu * m)
236 {
237     struct menu * ret ;
238     if (!m)
239         return NULL;
240     
241     if ( (ret=menu_get_first_son(m)) ) 
242         return ret;
243
244     menu_call_fct(m);
245
246     return m;
247 }
248
249 /** return the next menu on same level (if it is the last, go back to beginning */
250 struct menu * menu_up(struct menu * m)
251 {
252     struct menu * ret;
253         
254     if (!m)
255         return NULL;
256     
257     /* if there is a menu before, return it */
258     if ( (ret=menu_get_previous(m)) )
259         return ret;
260
261     /* if there is no before and no next, return self */
262     if ( ! (ret = menu_get_next(m) ) )
263         return m;
264         
265     /* return the next, next, next, ... until the end of menu */
266     do {
267         ret = m;
268     } while ( (m=menu_get_next(ret)) ) ;
269         
270     return ret;
271 }
272
273 /** return the next menu on same level (if it is the first, go back to the end */
274 struct menu * menu_down(struct menu * m)
275 {
276     struct menu * ret;
277         
278     if (!m)
279         return NULL;
280     
281     /* if there is a menu after, return it */
282     if ( (ret=menu_get_next(m)) )
283         return ret;
284
285     /* if there is no before and no next, return self */
286     if ( ! (ret = menu_get_previous(m) ) )
287         return m;
288         
289     /* return the previous, previous, previous, ... until the end of menu */
290     do {
291         ret = m;
292     } while ( (m=menu_get_previous(ret)) ) ;
293         
294     return ret;
295 }
296
297
298 /** move in the menu, depending on the action */
299 struct menu * menu_default_update(struct menu * m, char c)
300 {
301     struct menu * ret;
302
303     switch(c) {
304     case 'b':
305         return menu_up(m);
306     case 'f':
307         return menu_down(m);
308     case 'n':
309         return menu_right(m);
310     case 'p':
311         return menu_left(m);
312     default:
313         if(c >= '0' && c <= '9') {
314             if ( m->type == MENU_TYPE_ROOT && c == '0' ) {
315                 ret = menu_right(m);
316                 if (ret) return ret;
317             }
318             else if ( (ret = menu_right(menu_get_sub(menu_get_parent(m), c-'0'))) ) {
319                 return ret;
320             }
321         }
322         return m;
323     }
324 }
325
326
327 /** default function to display a menu, you can reimplement it */
328 void menu_default_display(struct menu * m)
329 {
330     struct menu * parent = menu_get_parent(m);
331     struct menu * son ;
332     uint8_t i ;
333
334     /* clear term */
335     printf("\e[H\e[J");
336
337     if(parent) {
338         printf("%s\r\n", menu_get_name(parent));
339
340         for (i=0 ; i<menu_get_sub_howmany(parent) ; i++) {
341             son = menu_get_sub(parent, i);
342             if( son == m )
343                 printf("> %d: %s", i, menu_get_name(son));
344             else
345                 printf("  %d: %s", i, menu_get_name(son));
346             if(menu_is_a_submenu(son))
347                     printf(" -->");
348             printf("\r\n");
349         }
350     }
351     else {
352         printf("| 0: %s -->", menu_get_name(m));
353     }
354 }
355