1 /*
2 WDG -- list widget
3
4 Copyright (C) ALoR
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 */
21
22 #include <wdg.h>
23
24 #include <ncurses.h>
25 #include <menu.h>
26
27 #include <stdarg.h>
28
29 /* GLOBALS */
30
31 struct wdg_list_call {
32 int key;
33 void (*callback)(void *);
34 SLIST_ENTRY(wdg_list_call) next;
35 };
36
37 struct wdg_list_handle {
38 MENU *menu;
39 WINDOW *mwin;
40 WINDOW *win;
41 ITEM *current;
42 ITEM **items;
43 size_t nitems;
44 void (*select_callback)(void *);
45 SLIST_HEAD(, wdg_list_call) callbacks;
46 };
47
48 /* PROTOS */
49
50 void wdg_create_list(struct wdg_object *wo);
51
52 static int wdg_list_destroy(struct wdg_object *wo);
53 static int wdg_list_resize(struct wdg_object *wo);
54 static int wdg_list_redraw(struct wdg_object *wo);
55 static int wdg_list_get_focus(struct wdg_object *wo);
56 static int wdg_list_lost_focus(struct wdg_object *wo);
57 static int wdg_list_get_msg(struct wdg_object *wo, int key, struct wdg_mouse_event *mouse);
58
59 static void wdg_list_borders(struct wdg_object *wo);
60
61 static int wdg_list_virtualize(int key);
62 static int wdg_list_driver(struct wdg_object *wo, int key, struct wdg_mouse_event *mouse);
63 static void wdg_list_menu_create(struct wdg_object *wo);
64 static void wdg_list_menu_destroy(struct wdg_object *wo);
65
66 static int wdg_list_callback(struct wdg_object *wo, int key);
67
68 /*******************************************/
69
70 /*
71 * called to create the menu
72 */
wdg_create_list(struct wdg_object * wo)73 void wdg_create_list(struct wdg_object *wo)
74 {
75 WDG_DEBUG_MSG("wdg_create_list");
76
77 /* set the callbacks */
78 wo->destroy = wdg_list_destroy;
79 wo->resize = wdg_list_resize;
80 wo->redraw = wdg_list_redraw;
81 wo->get_focus = wdg_list_get_focus;
82 wo->lost_focus = wdg_list_lost_focus;
83 wo->get_msg = wdg_list_get_msg;
84
85 WDG_SAFE_CALLOC(wo->extend, 1, sizeof(struct wdg_list_handle));
86 }
87
88 /*
89 * called to destroy the menu
90 */
wdg_list_destroy(struct wdg_object * wo)91 static int wdg_list_destroy(struct wdg_object *wo)
92 {
93 WDG_WO_EXT(struct wdg_list_handle, ww);
94 struct wdg_list_call *c;
95 int i = 0;
96
97 WDG_DEBUG_MSG("wdg_list_destroy");
98
99 /* erase the window */
100 wdg_list_menu_destroy(wo);
101
102 wbkgd(ww->win, COLOR_PAIR(wo->screen_color));
103 werase(ww->win);
104 wnoutrefresh(ww->win);
105
106 /* dealloc the structures */
107 delwin(ww->win);
108
109 /* all the items */
110 while (ww->items && ww->items[i] != NULL)
111 free_item(ww->items[i++]);
112
113 WDG_SAFE_FREE(ww->items);
114
115 /* free the callback list */
116 while (SLIST_FIRST(&ww->callbacks) != NULL) {
117 c = SLIST_FIRST(&ww->callbacks);
118 SLIST_REMOVE_HEAD(&ww->callbacks, next);
119 WDG_SAFE_FREE(c);
120 }
121
122 WDG_SAFE_FREE(wo->extend);
123
124 return WDG_E_SUCCESS;
125 }
126
127 /*
128 * called to resize the menu
129 */
wdg_list_resize(struct wdg_object * wo)130 static int wdg_list_resize(struct wdg_object *wo)
131 {
132 wdg_list_redraw(wo);
133
134 return WDG_E_SUCCESS;
135 }
136
137 /*
138 * called to redraw the menu
139 */
wdg_list_redraw(struct wdg_object * wo)140 static int wdg_list_redraw(struct wdg_object *wo)
141 {
142 WDG_WO_EXT(struct wdg_list_handle, ww);
143 size_t c = wdg_get_ncols(wo);
144 size_t l = wdg_get_nlines(wo);
145 size_t x = wdg_get_begin_x(wo);
146 size_t y = wdg_get_begin_y(wo);
147
148 WDG_DEBUG_MSG("wdg_list_redraw");
149
150 /* the window already exist */
151 if (ww->win) {
152 /* erase the border */
153 wbkgd(ww->win, COLOR_PAIR(wo->screen_color));
154 werase(ww->win);
155 touchwin(ww->win);
156 wnoutrefresh(ww->win);
157
158 /* delete the internal menu */
159 wdg_list_menu_destroy(wo);
160
161 /* resize the window and draw the new border */
162 mvwin(ww->win, y, x);
163 wresize(ww->win, l, c);
164 wdg_list_borders(wo);
165
166 /* redraw the menu */
167 wdg_list_menu_create(wo);
168
169 /* the first time we have to allocate the window */
170 } else {
171
172 /* create the menu window (fixed dimensions) */
173 if ((ww->win = newwin(l, c, y, x)) == NULL)
174 return -WDG_E_FATAL;
175
176 /* draw the titles */
177 wdg_list_borders(wo);
178
179 /* draw the menu */
180 wdg_list_menu_create(wo);
181
182 /* no scrolling for menu */
183 scrollok(ww->win, FALSE);
184
185 }
186
187 /* refresh the window */
188 touchwin(ww->win);
189 wnoutrefresh(ww->win);
190 touchwin(ww->mwin);
191 wnoutrefresh(ww->mwin);
192
193 wo->flags |= WDG_OBJ_VISIBLE;
194
195 return WDG_E_SUCCESS;
196 }
197
198 /*
199 * called when the menu gets the focus
200 */
wdg_list_get_focus(struct wdg_object * wo)201 static int wdg_list_get_focus(struct wdg_object *wo)
202 {
203 /* set the flag */
204 wo->flags |= WDG_OBJ_FOCUSED;
205
206 /* redraw the window */
207 wdg_list_redraw(wo);
208
209 return WDG_E_SUCCESS;
210 }
211
212 /*
213 * called when the menu looses the focus
214 */
wdg_list_lost_focus(struct wdg_object * wo)215 static int wdg_list_lost_focus(struct wdg_object *wo)
216 {
217 /* set the flag */
218 wo->flags &= ~WDG_OBJ_FOCUSED;
219
220 /* redraw the window */
221 wdg_list_redraw(wo);
222
223 return WDG_E_SUCCESS;
224 }
225
226 /*
227 * called by the messages dispatcher when the menu is focused
228 */
wdg_list_get_msg(struct wdg_object * wo,int key,struct wdg_mouse_event * mouse)229 static int wdg_list_get_msg(struct wdg_object *wo, int key, struct wdg_mouse_event *mouse)
230 {
231 WDG_WO_EXT(struct wdg_list_handle, ww);
232
233 /* handle the message */
234 switch (key) {
235
236 case KEY_MOUSE:
237 /* is the mouse event within our edges ? */
238 if (wenclose(ww->win, mouse->y, mouse->x)) {
239 wdg_set_focus(wo);
240 /* pass the event to the menu */
241 wdg_list_driver(wo, key, mouse);
242 } else
243 return -WDG_E_NOTHANDLED;
244 break;
245
246 case KEY_DOWN:
247 case KEY_UP:
248 case KEY_NPAGE:
249 case KEY_PPAGE:
250 /* move only if focused */
251 if (wo->flags & WDG_OBJ_FOCUSED) {
252 /* pass the event to the menu */
253 wdg_list_driver(wo, key, mouse);
254 } else
255 return -WDG_E_NOTHANDLED;
256 break;
257
258 case KEY_RETURN:
259 if (item_userptr(current_item(ww->menu)))
260 WDG_EXECUTE(ww->select_callback, item_userptr(current_item(ww->menu)));
261 break;
262
263 /* message not handled */
264 default:
265 return wdg_list_callback(wo, key);
266 break;
267 }
268
269 return WDG_E_SUCCESS;
270 }
271
272 /*
273 * draw the list borders
274 */
wdg_list_borders(struct wdg_object * wo)275 static void wdg_list_borders(struct wdg_object *wo)
276 {
277 WDG_WO_EXT(struct wdg_list_handle, ww);
278 size_t c = wdg_get_ncols(wo);
279
280 /* the object was focused */
281 if (wo->flags & WDG_OBJ_FOCUSED) {
282 wattron(ww->win, A_BOLD);
283 wbkgdset(ww->win, COLOR_PAIR(wo->focus_color));
284 } else
285 wbkgdset(ww->win, COLOR_PAIR(wo->border_color));
286
287 /* draw the borders */
288 box(ww->win, 0, 0);
289
290 /* set the title color */
291 wbkgdset(ww->win, COLOR_PAIR(wo->title_color));
292
293 /* there is a title: print it */
294 if (wo->title) {
295 switch (wo->align) {
296 case WDG_ALIGN_LEFT:
297 wmove(ww->win, 0, 3);
298 break;
299 case WDG_ALIGN_CENTER:
300 wmove(ww->win, 0, (c - strlen(wo->title)) / 2);
301 break;
302 case WDG_ALIGN_RIGHT:
303 wmove(ww->win, 0, c - strlen(wo->title) - 3);
304 break;
305 }
306 wprintw(ww->win, wo->title);
307 }
308
309 /* restore the attribute */
310 if (wo->flags & WDG_OBJ_FOCUSED)
311 wattroff(ww->win, A_BOLD);
312
313 }
314
315 /*
316 * set the elements for the list
317 */
wdg_list_set_elements(struct wdg_object * wo,struct wdg_list * list)318 void wdg_list_set_elements(struct wdg_object *wo, struct wdg_list *list)
319 {
320 WDG_WO_EXT(struct wdg_list_handle, ww);
321 size_t i = 0;
322
323 wdg_list_menu_destroy(wo);
324
325 /* forget the curren position, we are creating a new menu */
326 ww->current = NULL;
327
328 /* free any previously alloc'd item */
329 while (ww->items && ww->items[i] != NULL)
330 free_item(ww->items[i++]);
331
332 WDG_SAFE_FREE(ww->items);
333 i = 0;
334 ww->nitems = 0;
335
336 /* walk thru the list and set the menu items */
337 while (list[i].desc != NULL) {
338 /* count the items added */
339 ww->nitems++;
340
341 WDG_SAFE_REALLOC(ww->items, ww->nitems * sizeof(ITEM *));
342
343 ww->items[i] = new_item(list[i].desc, "");
344 set_item_userptr(ww->items[i], list[i].value);
345 i++;
346 }
347
348 /* add the null termination to the array */
349 WDG_SAFE_REALLOC(ww->items, (ww->nitems + 1) * sizeof(ITEM *));
350 ww->items[ww->nitems] = NULL;
351
352 wdg_list_menu_create(wo);
353 }
354
355
356 /*
357 * stransform keys into menu commands
358 */
wdg_list_virtualize(int key)359 static int wdg_list_virtualize(int key)
360 {
361 switch (key) {
362 case KEY_NPAGE:
363 return (REQ_SCR_DPAGE);
364 case KEY_PPAGE:
365 return (REQ_SCR_UPAGE);
366 case KEY_DOWN:
367 return (REQ_NEXT_ITEM);
368 case KEY_UP:
369 return (REQ_PREV_ITEM);
370 default:
371 if (key != KEY_MOUSE)
372 beep();
373 return (key);
374 }
375 }
376
377 /*
378 * sends command to the active menu
379 */
wdg_list_driver(struct wdg_object * wo,int key,struct wdg_mouse_event * mouse)380 static int wdg_list_driver(struct wdg_object *wo, int key, struct wdg_mouse_event *mouse)
381 {
382 WDG_WO_EXT(struct wdg_list_handle, ww);
383 int c;
384
385 /* variable currently not used */
386 (void) mouse;
387
388 c = menu_driver(ww->menu, wdg_list_virtualize(key) );
389
390 /* skip non selectable items */
391 if ( !(item_opts(current_item(ww->menu)) & O_SELECTABLE) )
392 c = menu_driver(ww->menu, wdg_list_virtualize(key) );
393
394 /* one item has been selected */
395 if (c == E_UNKNOWN_COMMAND) {
396 if (item_userptr(current_item(ww->menu)))
397 WDG_EXECUTE(ww->select_callback, item_userptr(current_item(ww->menu)));
398 }
399
400 /* tring to go outside edges */
401 if (c == E_REQUEST_DENIED)
402 return -WDG_E_NOTHANDLED;
403
404 wnoutrefresh(ww->mwin);
405
406 return WDG_E_SUCCESS;
407 }
408
409 /*
410 * create the menu
411 */
wdg_list_menu_create(struct wdg_object * wo)412 static void wdg_list_menu_create(struct wdg_object *wo)
413 {
414 WDG_WO_EXT(struct wdg_list_handle, ww);
415 size_t l = wdg_get_nlines(wo);
416 size_t x = wdg_get_begin_x(wo);
417 size_t y = wdg_get_begin_y(wo);
418 int mrows = 0, mcols = 0;
419
420 /* skip the creation if:
421 * - already displayed
422 * - no items are present
423 */
424 if (ww->menu || !ww->items || ww->nitems == 0)
425 return;
426
427 /* create the menu */
428 ww->menu = new_menu(ww->items);
429
430 /* set the dimensions */
431 set_menu_format(ww->menu, l - 4, 1);
432 set_menu_spacing(ww->menu, 2, 0, 0);
433
434 /* get the geometry to make a window */
435 scale_menu(ww->menu, &mrows, &mcols);
436
437 ww->mwin = newwin(mrows + 1, mcols, y + 2, x + 2);
438 /* set the color */
439 wbkgd(ww->mwin, COLOR_PAIR(wo->window_color));
440 keypad(ww->mwin, TRUE);
441
442 /* associate with the menu */
443 set_menu_win(ww->menu, ww->mwin);
444
445 /* the subwin for the menu */
446 set_menu_sub(ww->menu, derwin(ww->mwin, mrows + 1, mcols, 2, 2));
447
448 /* menu attributes */
449 set_menu_mark(ww->menu, "");
450 set_menu_grey(ww->menu, COLOR_PAIR(wo->window_color));
451 set_menu_back(ww->menu, COLOR_PAIR(wo->window_color));
452 set_menu_fore(ww->menu, COLOR_PAIR(wo->window_color) | A_REVERSE | A_BOLD);
453
454 /* repristinate the current position */
455 if (ww->current)
456 set_current_item(ww->menu, ww->current);
457
458 /* display the menu */
459 post_menu(ww->menu);
460
461 wnoutrefresh(ww->mwin);
462 }
463
464 /*
465 * destroy the menu
466 */
wdg_list_menu_destroy(struct wdg_object * wo)467 static void wdg_list_menu_destroy(struct wdg_object *wo)
468 {
469 WDG_WO_EXT(struct wdg_list_handle, ww);
470
471 /* nothing to clear */
472 if (ww->menu == NULL)
473 return;
474
475 /* remember the current position to be repristinated on menu create */
476 ww->current = current_item(ww->menu);
477
478 /* hide the menu */
479 unpost_menu(ww->menu);
480
481 /* erase the menu */
482 wbkgd(ww->mwin, COLOR_PAIR(wo->screen_color));
483 werase(ww->mwin);
484 wnoutrefresh(ww->mwin);
485
486 /* delete the memory */
487 free_menu(ww->menu);
488
489 ww->menu = NULL;
490 }
491
492 /*
493 * set the select callback
494 */
wdg_list_select_callback(wdg_t * wo,void (* callback)(void *))495 void wdg_list_select_callback(wdg_t *wo, void (*callback)(void *))
496 {
497 WDG_WO_EXT(struct wdg_list_handle, ww);
498
499 ww->select_callback = callback;
500 }
501
502 /*
503 * add the user callback(s)
504 */
wdg_list_add_callback(wdg_t * wo,int key,void (* callback)(void *))505 void wdg_list_add_callback(wdg_t *wo, int key, void (*callback)(void *))
506 {
507 WDG_WO_EXT(struct wdg_list_handle, ww);
508 struct wdg_list_call *c;
509
510 WDG_SAFE_CALLOC(c, 1, sizeof(struct wdg_list_call));
511
512 c->key = key;
513 c->callback = callback;
514
515 SLIST_INSERT_HEAD(&ww->callbacks, c, next);
516 }
517
518 /*
519 * search for a callback and execute it
520 */
wdg_list_callback(struct wdg_object * wo,int key)521 static int wdg_list_callback(struct wdg_object *wo, int key)
522 {
523 WDG_WO_EXT(struct wdg_list_handle, ww);
524 struct wdg_list_call *c;
525
526 SLIST_FOREACH(c, &ww->callbacks, next) {
527 if (c->key == key) {
528 void *value;
529
530 WDG_DEBUG_MSG("wdg_list_callback");
531
532 /* retrieve the value from the current item */
533 value = item_userptr(current_item(ww->menu));
534
535 /* execute the callback */
536 WDG_EXECUTE(c->callback, value);
537
538 return WDG_E_SUCCESS;
539 }
540 }
541
542 return -WDG_E_NOTHANDLED;
543 }
544
545 /*
546 * force the repaint of the menu
547 */
wdg_list_refresh(wdg_t * wo)548 void wdg_list_refresh(wdg_t *wo)
549 {
550 WDG_WO_EXT(struct wdg_list_handle, ww);
551
552 WDG_DEBUG_MSG("wdg_list_refresh");
553
554 /* remember the position */
555 ww->current = current_item(ww->menu);
556
557 /* delete the window */
558 unpost_menu(ww->menu);
559
560 /* set the old position */
561 set_current_item(ww->menu, ww->current);
562 /* draw the menu */
563 post_menu(ww->menu);
564
565 wnoutrefresh(ww->mwin);
566 }
567
568 /* EOF */
569
570 // vim:ts=3:expandtab
571
572