Added SILC Thread Queue API
[crypto.git] / apps / irssi / src / fe-text / cuix-api.c
1 #include "module.h"
2 #include "settings.h"
3 #include "term.h"
4 #include "gui-windows.h"
5 #include <stdarg.h>
6 #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
7 #  include <ncurses.h>
8 #else
9 #  include <curses.h>
10 #endif
11 #include <form.h>
12 #include <panel.h>
13 #include <menu.h>
14
15
16 #include "cuix-api.h"
17
18 #define INIT_ENTRIES 8
19 #define X0_OFFSET 4
20 #define Y0_OFFSET 2
21 #define Y_OFFSET 1
22 #define CUIX_FIELD_WIDTH 16
23
24 object *create_object (char *title, int type, void **entries)
25 {
26     object *obj;
27     void **new_entries;
28     int i;
29
30     obj = g_malloc (sizeof(object));
31     if (!obj) {
32         return NULL;
33     }
34     obj->type = type;
35     obj->title = title;
36     if (!entries) {
37         new_entries = g_new0 (void *, INIT_ENTRIES);
38         obj->entries = new_entries;
39         obj->alloced = INIT_ENTRIES;
40         obj->last = 0;
41     } else {
42         for (i = 0; ((entry **)entries)[i]; i++);
43         obj->alloced = i;
44         obj->last = i;
45         obj->entries = entries;
46     }
47     return obj;
48 }
49
50
51 object *create_menu (char *title)
52 {
53     return create_object (title, CUIX_MENU, NULL);
54 }
55
56
57 object *create_form (char *title)
58 {
59     return create_object (title, CUIX_FORM, NULL);
60 }
61
62
63 /* entries must be NULL terminated */
64 object *create_list (char *title, entry **entries)
65 {
66     return create_object (title, CUIX_LIST, (void **)entries);
67 }
68
69 entry *create_entry (char *label, int type, action_fn_type action)
70 {
71     entry *entry;
72     
73     entry = g_malloc (sizeof(object));
74     if (!entry) {
75         return NULL;
76     }
77     entry->type = type;
78     entry->data = label;
79     entry->action = action;
80     return entry;
81 }
82
83 entry *create_menuentry (char *label, action_fn_type action)
84 {
85     return create_entry (label, CUIX_MENUENTRY, action);
86 }
87
88 entry *create_label (char *label)
89 {
90     return create_entry (label, CUIX_LABEL, NULL);
91 }
92
93
94 entry *create_field (char *label, action_fn_type action)
95 {
96     return create_entry (label, CUIX_FIELD, action);
97 }
98
99
100
101 /* Adds child at the last position of father->entries */
102 void attach_entry (object *father, void *child)
103 {
104     void **entries;
105     int i;
106
107     /* Check that we have enough space in father->entries, otherwise alloc
108      * twice more than previously */
109     if (father->last >= father->alloced) {
110         entries = g_new0 (void *,2 * father->alloced);
111         if (!entries) {
112             fprintf (stderr, "Problem with memory allocation, quitting now...\n");
113             exit (1);
114         }
115         for (i = 0; i < father->alloced; i++) {
116             entries[i] = father->entries[i];
117         }
118         g_free (father->entries);
119         father->entries = entries;
120         father->alloced *= 2;
121     }
122     father->entries[father->last++] = child;
123 }
124
125
126 /* Adds a submenu to father */
127 void attach_submenu (object *father, object *child)
128
129
130     /* Check that both are really menus */
131     if (father->type != CUIX_MENU || child->type != CUIX_MENU) {
132         fprintf (stderr, "Typing error, trying to add %p (%d) as child of"
133                 "%p (%d)\n", father, father->type, child, child->type);
134         exit (1);
135     }
136     attach_entry (father, (void *)child);
137 }
138
139
140 /* Returns the maximum width occupied by labels */
141 int get_labels_width (object *obj)
142 {
143     int i;
144     unsigned int w = 0;
145     entry *e;
146     object *o;
147
148     for (i = 0; i < obj->last; i++) {
149         e = (entry *)obj->entries[i];
150         if (e->type == CUIX_LABEL || e->type == CUIX_MENUENTRY) {
151             w = (w > strlen (e->data)) ? w : strlen (e->data);
152         }
153         if (e->type == CUIX_MENU) {
154             o = (object *)obj->entries[i];
155             w = (w > strlen (o->title)) ? w : strlen (o->title);
156         }
157
158     }
159     w += 2 * X0_OFFSET;
160     return (int)w;
161 }
162
163
164 /* Puts in x and y the coordinates to center an object of size objw and objh
165  * in the window win */
166 void get_center (WINDOW *win, int objh, int objw, int *y, int *x)
167 {
168     int begx, begy, maxx, maxy, w, h;
169     getbegyx (win, begy, begx);
170     getmaxyx (win, maxy, maxx);
171     w = maxx - begx;
172     h = maxy - begy;
173     *x = (w - objw) / 2 + begx;
174     *y = (h - objh) / 2 + begy;
175     if (*x < 0)
176         *x = 0;
177     if (*y < 0)
178         *y = 0;
179 }
180
181
182
183 void display_object (object *obj)
184 {
185     WINDOW *subwin;
186     FORM *form;
187     MENU *menu;
188     FIELD **fields;
189     ITEM **items, *cur_item;
190     object *o;
191     entry *e;
192     char *result;
193     int i, x, y, w, h;
194     int ch;
195     p_main = new_panel(root_window->win);
196
197     if (obj->type >= CUIX_LABEL) {
198         fprintf (stderr, "Trying to display an entry %p (%d), terminating...\n",
199                 obj, obj->type);
200         exit (1);
201     }
202
203     switch (obj->type) {
204         case CUIX_LIST:
205             w = get_labels_width (obj);
206             h = Y_OFFSET * obj->last + 2 * Y0_OFFSET;
207             get_center (root_window->win, h, w, &y, &x);
208             cuix_win = newwin (h, w, y, x);
209             box (cuix_win, 0, 0);
210             p_cuix = new_panel(cuix_win);
211             x = X0_OFFSET;
212             y = Y0_OFFSET;
213
214             for (i = 0; i < obj->last; i++) {
215                 e = (entry *)obj->entries[i];
216                 if (e->type != CUIX_LABEL) {
217                     fprintf (stderr, "Non-label entry in a list.\n");
218                     exit (1);
219                 } 
220                 wmove (cuix_win,y,x);
221                 waddstr (cuix_win,e->data);
222                 y += Y_OFFSET;
223                 x = X0_OFFSET;
224             }
225             top_panel (p_cuix);
226             update_panels();
227             doupdate();
228             wgetch(cuix_win);
229             /* refresh (); */
230             /* wrefresh (cuix_win); */
231             break;
232
233         case CUIX_FORM:
234             w = get_labels_width (obj);
235             w = (w > CUIX_FIELD_WIDTH + 2 * X0_OFFSET) ?
236                 w : CUIX_FIELD_WIDTH + 2 * X0_OFFSET;
237             h = Y_OFFSET * obj->last + 2 * Y0_OFFSET;
238             fields = g_new0 (FIELD *, obj->last + 1);
239             for (i = 0; i < obj->last; i++) {
240                 e = (entry *)obj->entries[i];
241                 fields[i] = new_field (1, w,
242                         Y0_OFFSET + i * Y_OFFSET, X0_OFFSET, 0, 0);
243                 if (e->type == CUIX_LABEL) {
244                     field_opts_off (fields[i], O_ACTIVE);
245                     field_opts_off (fields[i], O_EDIT);
246                     set_field_back  (fields[i], A_BOLD);
247                 }
248                 set_field_buffer (fields[i], 0, e->data);
249             }
250             fields[obj->last] = NULL;
251             form = new_form (fields);
252             scale_form (form, &h, &w);
253             h += Y0_OFFSET;
254             w += 2 * X0_OFFSET;
255             get_center (root_window->win, h, w, &y, &x);
256             cuix_win = newwin (h, w, y, x);
257             keypad (cuix_win, TRUE);
258             nonl ();
259             set_form_win (form, cuix_win);
260             set_form_sub (form, derwin(cuix_win, w, h, X0_OFFSET, Y0_OFFSET));
261             post_form (form);
262             box (cuix_win, 0, 0);
263             p_cuix = new_panel (cuix_win);
264             top_panel (p_cuix);
265             while((ch = wgetch(cuix_win)) != '\n' && ch != '\r' && ch != 27 /* ESC */) {       
266                 switch(ch) {       
267                     case KEY_DOWN:
268                         /* Go to next field */
269                         form_driver(form, REQ_NEXT_FIELD);
270                         /* Go to the end of the present buffer */
271                         /* Leaves nicely at the last character */
272                         form_driver(form, REQ_END_LINE);
273                         break;
274                     case KEY_UP:
275                         /* Go to previous field */
276                         form_driver(form, REQ_PREV_FIELD);
277                         form_driver(form, REQ_END_LINE);
278                         break;
279                     case KEY_BACKSPACE:
280                         form_driver(form, REQ_PREV_CHAR);
281                         form_driver(form, REQ_DEL_CHAR);
282                         break;
283                     case KEY_LEFT:
284                         form_driver(form, REQ_PREV_CHAR);
285                         break;
286                     case KEY_RIGHT:
287                         form_driver(form, REQ_NEXT_CHAR);
288                         break;
289                     default:
290                         /* If this is a normal character, it gets */
291                         /* Printed                                */    
292                         form_driver(form, ch);
293                         break;
294                 }
295             }
296             form_driver (form, REQ_VALIDATION);
297             if (ch != 27) {
298                 for (i = 0; i < obj->last; i++) {
299                     e = (entry *)obj->entries[i];
300                     if (e->type == CUIX_FIELD) {
301                         result = field_buffer(fields[i],0);
302                         e->action (result);
303                     }
304                 }
305             }
306             for (i = 0; i < obj->last; i++) {
307                 free_field (fields[i]);
308             }
309             g_free (fields);
310             unpost_form (form);
311
312             break;
313
314         case CUIX_MENU:
315             w = get_labels_width (obj);
316             w = (w > CUIX_FIELD_WIDTH + 2 * X0_OFFSET) ?
317                 w : CUIX_FIELD_WIDTH + 2 * X0_OFFSET;
318             h = Y_OFFSET * obj->last + 2 * Y0_OFFSET;
319             items = g_new0 (ITEM *, obj->last + 1);
320             for (i = 0; i < obj->last; i++) {
321                 e = (entry *)obj->entries[i];
322                 o = (object *)obj->entries[i];
323                 if (e->type == CUIX_MENUENTRY) {
324                     items[i] = new_item (e->data, "");
325                     set_item_userptr (items[i], (void*)e);
326                 } else {
327                     if (e->type == CUIX_LABEL) {
328                         items[i] = new_item (e->data, "");
329                         item_opts_off (items[i], O_SELECTABLE);
330                     } else {
331                         items[i] = new_item (o->title, " (SUB) ");
332                         set_item_userptr (items[i], (void*)o);
333                     }
334                 }
335             }
336             items[obj->last] = NULL;
337             menu = new_menu (items);
338             set_menu_mark (menu, " * ");
339             scale_menu (menu, &h, &w);
340             h += 4 * Y0_OFFSET;
341             w += 4 * X0_OFFSET;
342             get_center (root_window->win, h, w, &y, &x);
343              cuix_win = newwin (h, w, y, x);
344             keypad (cuix_win, TRUE);
345             nonl ();
346             set_menu_win (menu, cuix_win);
347             subwin = derwin (cuix_win,
348                     h - 2 * Y0_OFFSET, w - 2 * X0_OFFSET, Y0_OFFSET, X0_OFFSET);
349             set_menu_sub (menu, subwin);
350             box (cuix_win, 0, 0);
351             post_menu (menu);
352             p_cuix = new_panel (cuix_win);
353             top_panel (p_cuix);
354             while((ch = wgetch(cuix_win)) != 27 /* ESC */) {       
355                 switch(ch) {       
356                     case KEY_DOWN:
357                         menu_driver(menu, REQ_DOWN_ITEM);
358                         break;
359                     case KEY_UP:
360                         menu_driver(menu, REQ_UP_ITEM);
361                         break;
362                     case '\n':
363                     case '\r':
364                         cur_item = current_item(menu);
365                         e = (entry *)item_userptr(cur_item);
366                         o = (object *)item_userptr(cur_item);
367                         if (e->type == CUIX_MENUENTRY)
368                         {
369                             e->action ("");
370                         } else {
371                             display_object (o);
372                         }
373                         goto end;
374                         break;
375                     default:
376                         break;
377                 }
378             }
379 end:
380             for (i = 0; i < obj->last; i++) {
381                 free_item (items[i]);
382             }
383             g_free (items);
384             unpost_menu (menu);
385     }
386 }