1 /*
2 * Copyright (c)2004 Cat's Eye Technologies. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
14 * distribution.
15 *
16 * Neither the name of Cat's Eye Technologies nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 * OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /*
35 * curses_widget.c
36 * $Id: curses_widget.c,v 1.14 2005/08/26 22:44:37 cpressey Exp $
37 */
38
39 #include <ctype.h>
40 #include <ncurses.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #ifdef SYSTEM_AURA
45 #include <aura/mem.h>
46 #else
47 #include "mem.h"
48 #endif
49
50 #include "curses_form.h"
51 #include "curses_widget.h"
52 #include "curses_bar.h"
53 #include "curses_util.h"
54
55 #ifdef DEBUG
56 extern FILE *dfui_debug_file;
57 #endif
58
59 extern struct curses_bar *statusbar;
60
61 /*** WIDGETS ***/
62
63 /*
64 * Create a new curses_widget, outside of the context of any particular
65 * curses_form.
66 */
67 struct curses_widget *
curses_widget_new(unsigned int x,unsigned int y,unsigned int width,widget_t type,const char * text,unsigned int size,unsigned int flags)68 curses_widget_new(unsigned int x, unsigned int y,
69 unsigned int width, widget_t type,
70 const char *text, unsigned int size,
71 unsigned int flags)
72 {
73 struct curses_widget *w;
74
75 AURA_MALLOC(w, curses_widget);
76
77 w->form = NULL;
78
79 if (flags & CURSES_WIDGET_WIDEN) {
80 switch (type) {
81 case CURSES_TEXTBOX:
82 width = strlen(text) + 2;
83 break;
84 case CURSES_BUTTON:
85 width = strlen(text) + 4;
86 break;
87 default:
88 width = strlen(text);
89 break;
90 }
91 }
92
93 w->x = x;
94 w->y = y;
95 w->width = width;
96 w->type = type;
97 w->next = NULL;
98 w->prev = NULL;
99 w->flags = flags;
100 w->accel = '\0';
101
102 if (w->type == CURSES_TEXTBOX) {
103 w->size = size;
104 w->text = aura_malloc(size, "w->text");
105 strcpy(w->text, text);
106 } else {
107 w->text = aura_strdup(text);
108 w->size = strlen(text) + 1;
109 /* size argument is ignored */
110 }
111
112 w->curpos = strlen(w->text);
113 w->editable = 1;
114 w->obscured = 0;
115 w->offset = 0;
116 w->amount = 0;
117 w->spin = 0;
118 w->tooltip = NULL;
119 w->user_id = 0;
120 w->userdata = NULL;
121
122 w->click_cb = NULL;
123
124 return(w);
125 }
126
127 /*
128 * Free the memory allocated for a curses_widget. Note that this does
129 * NOT free any allocated memory at the widget's userdata pointer.
130 */
131 void
curses_widget_free(struct curses_widget * w)132 curses_widget_free(struct curses_widget *w)
133 {
134 if (w->tooltip != NULL)
135 free(w->tooltip);
136 free(w->text);
137 AURA_FREE(w, curses_widget);
138 }
139
140 void
curses_widget_tooltip_set(struct curses_widget * w,const char * tooltip)141 curses_widget_tooltip_set(struct curses_widget *w, const char *tooltip)
142 {
143 if (w->tooltip != NULL)
144 free(w->tooltip);
145 w->tooltip = aura_strdup(tooltip);
146 }
147
148 /*
149 * Draw a curses_widget to the window of its associated curses_form.
150 * Note that this does not refresh the screen.
151 */
152 void
curses_widget_draw(struct curses_widget * w)153 curses_widget_draw(struct curses_widget *w)
154 {
155 unsigned int wx, wy, x, len, i, charpos, cutoff, fchpos;
156 char p[5];
157 char bar_char;
158 struct curses_form *cf = w->form;
159
160 wx = w->x + 1 - cf->x_offset;
161 wy = w->y + 1 - cf->y_offset;
162
163 /*
164 * If the widget is outside the clipping rectangle of the
165 * form, don't draw it.
166 */
167 if (!curses_form_widget_is_visible(w))
168 return;
169
170 wmove(cf->win, wy, wx);
171
172 curses_colors_set(cf->win, w == cf->widget_focus ?
173 CURSES_COLORS_FOCUS : CURSES_COLORS_CONTROL);
174
175 switch (w->type) {
176 case CURSES_LABEL:
177 curses_colors_set(cf->win, CURSES_COLORS_LABEL); /* XXX conditional on... */
178 waddnstr(cf->win, w->text, w->width);
179 curs_set(0);
180 break;
181 case CURSES_TEXTBOX:
182 waddstr(cf->win, "[");
183 curses_colors_set(cf->win, w->editable ?
184 CURSES_COLORS_TEXT : CURSES_COLORS_GHOST); /* XXX focus ? */
185 charpos = w->offset;
186 len = strlen(w->text);
187 for (x = 0; x < w->width - 2; x++) {
188 if (charpos < len) {
189 waddch(cf->win, w->obscured ?
190 '*' : w->text[charpos]);
191 charpos++;
192 } else {
193 waddch(cf->win, ' ');
194 }
195 }
196 curses_colors_set(cf->win, w == cf->widget_focus ?
197 CURSES_COLORS_FOCUS : CURSES_COLORS_CONTROL);
198 waddstr(cf->win, "]");
199 /*
200 * Position the cursor to where it's expected.
201 */
202 if (w->curpos - w->offset < w->width - 2) {
203 wmove(cf->win, w->y + 1 - cf->y_offset,
204 w->x + 2 - cf->x_offset + w->curpos - w->offset);
205 } else {
206 wmove(cf->win, w->y + 1 - cf->y_offset,
207 w->x + 2 - cf->x_offset + w->width - 2);
208 }
209 curs_set(1);
210 break;
211 case CURSES_BUTTON:
212 waddstr(cf->win, "< ");
213 waddnstr(cf->win, w->text, w->width - 4);
214 waddstr(cf->win, " >");
215 if (isprint(w->accel)) {
216 for (i = 0; w->text[i] != '\0'; i++) {
217 if (toupper(w->text[i]) == w->accel) {
218 wmove(cf->win, wy, wx + 2 + i);
219 curses_colors_set(cf->win, w == cf->widget_focus ?
220 CURSES_COLORS_ACCELFOCUS : CURSES_COLORS_ACCEL);
221 waddch(cf->win, w->text[i]);
222 break;
223 }
224 }
225 }
226 curs_set(0);
227 break;
228 case CURSES_PROGRESS:
229 waddstr(cf->win, "[");
230 snprintf(p, 5, "%3d%%", w->amount);
231 cutoff = (w->amount * (w->width - 2)) / 100;
232 fchpos = ((w->width - 2) / 2) - 2;
233 for (x = 0; x < w->width - 2; x++) {
234 if (x < cutoff) {
235 curses_colors_set(cf->win, CURSES_COLORS_FOCUS);/* XXX status ? */
236 bar_char = monochrome ? '=' : ' ';
237 } else {
238 curses_colors_set(cf->win, CURSES_COLORS_CONTROL);
239 bar_char = ' ';
240 }
241
242 if (x >= fchpos && x <= fchpos + 3)
243 waddch(cf->win, p[x - fchpos]);
244 else
245 waddch(cf->win, bar_char);
246 }
247 waddstr(cf->win, "]");
248 curs_set(0);
249 break;
250 case CURSES_CHECKBOX:
251 waddstr(cf->win, "[");
252 waddstr(cf->win, w->amount ? "X" : " ");
253 waddstr(cf->win, "]");
254 curs_set(0);
255 break;
256 }
257 }
258
259 void
curses_widget_draw_tooltip(struct curses_widget * w)260 curses_widget_draw_tooltip(struct curses_widget *w)
261 {
262 if (w->tooltip != NULL)
263 curses_bar_set_text(statusbar, w->tooltip);
264 }
265
266 /*
267 * Returns non-zero if the given widget can take control focus
268 * (i.e. if it is not a label, progress bar, or other inert element.)
269 */
270 int
curses_widget_can_take_focus(struct curses_widget * w)271 curses_widget_can_take_focus(struct curses_widget *w)
272 {
273 if (w->type == CURSES_LABEL || w->type == CURSES_PROGRESS)
274 return(0);
275 return(1);
276 }
277
278 int
curses_widget_set_click_cb(struct curses_widget * w,int (* callback)(struct curses_widget *))279 curses_widget_set_click_cb(struct curses_widget *w,
280 int (*callback)(struct curses_widget *))
281 {
282 w->click_cb = callback;
283 return(1);
284 }
285
286 /*
287 * Returns:
288 * -1 to indicate that the widget is not clickable.
289 * 0 to indicate that the widget clicked.
290 * 1 to indicate that the widget clicked and that its form should close.
291 */
292 int
curses_widget_click(struct curses_widget * w)293 curses_widget_click(struct curses_widget *w)
294 {
295 if ((w->type != CURSES_BUTTON && w->type != CURSES_TEXTBOX) ||
296 w->click_cb == NULL)
297 return(-1);
298
299 return(w->click_cb(w));
300 }
301
302 /*** TEXTBOX WIDGETS ***/
303
304 int
curses_textbox_advance_char(struct curses_widget * w)305 curses_textbox_advance_char(struct curses_widget *w)
306 {
307 if (w->text[w->curpos] != '\0') {
308 w->curpos++;
309 if ((w->curpos - w->offset) >= (w->width - 2))
310 w->offset++;
311 curses_widget_draw(w);
312 curses_form_refresh(w->form);
313 return(1);
314 } else {
315 return(0);
316 }
317 }
318
319 int
curses_textbox_retreat_char(struct curses_widget * w)320 curses_textbox_retreat_char(struct curses_widget *w)
321 {
322 if (w->curpos > 0) {
323 w->curpos--;
324 if (w->curpos < w->offset)
325 w->offset--;
326 curses_widget_draw(w);
327 curses_form_refresh(w->form);
328 return(1);
329 } else {
330 return(0);
331 }
332 }
333
334 int
curses_textbox_home(struct curses_widget * w)335 curses_textbox_home(struct curses_widget *w)
336 {
337 w->curpos = 0;
338 w->offset = 0;
339 curses_widget_draw(w);
340 curses_form_refresh(w->form);
341 return(1);
342 }
343
344 int
curses_textbox_end(struct curses_widget * w)345 curses_textbox_end(struct curses_widget *w)
346 {
347 w->curpos = strlen(w->text);
348 while ((w->curpos - w->offset) >= (w->width - 2)) {
349 w->offset++;
350 }
351 curses_widget_draw(w);
352 curses_form_refresh(w->form);
353 return(1);
354 }
355
356 int
curses_textbox_insert_char(struct curses_widget * w,char key)357 curses_textbox_insert_char(struct curses_widget *w, char key)
358 {
359 unsigned int len, i;
360
361 if (!w->editable)
362 return(0);
363
364 len = strlen(w->text);
365 if (len == (w->size - 1))
366 return(0);
367
368 w->text[len + 1] = '\0';
369 for (i = len; i > w->curpos; i--)
370 w->text[i] = w->text[i - 1];
371 w->text[w->curpos++] = key;
372 if ((w->curpos - w->offset) >= (w->width - 2))
373 w->offset++;
374 curses_widget_draw(w);
375 curses_form_refresh(w->form);
376 return(1);
377 }
378
379 int
curses_textbox_backspace_char(struct curses_widget * w)380 curses_textbox_backspace_char(struct curses_widget *w)
381 {
382 int len, i;
383
384 if (!w->editable)
385 return(0);
386
387 len = strlen(w->text);
388 if (w->curpos == 0)
389 return(0);
390
391 for (i = w->curpos; i <= len; i++)
392 w->text[i - 1] = w->text[i];
393 w->curpos--;
394 if (w->curpos < w->offset)
395 w->offset--;
396 curses_widget_draw(w);
397 curses_form_refresh(w->form);
398 return(1);
399 }
400
401 int
curses_textbox_delete_char(struct curses_widget * w)402 curses_textbox_delete_char(struct curses_widget *w)
403 {
404 unsigned int len, i;
405
406 if (!w->editable)
407 return(0);
408
409 len = strlen(w->text);
410 if (w->curpos == len)
411 return(0);
412
413 for (i = w->curpos + 1; i <= len; i++)
414 w->text[i - 1] = w->text[i];
415 curses_widget_draw(w);
416 curses_form_refresh(w->form);
417 return(1);
418 }
419
420 int
curses_textbox_set_text(struct curses_widget * w,const char * text)421 curses_textbox_set_text(struct curses_widget *w, const char *text)
422 {
423 strlcpy(w->text, text, w->size);
424 w->curpos = strlen(w->text);
425 curses_widget_draw(w);
426 curses_form_refresh(w->form);
427 return(1);
428 }
429
430 /*** CHECKBOX WIDGETS ***/
431
432 int
curses_checkbox_toggle(struct curses_widget * w)433 curses_checkbox_toggle(struct curses_widget *w)
434 {
435 if (!w->editable)
436 return(0);
437 w->amount = !w->amount;
438 curses_widget_draw(w);
439 curses_form_refresh(w->form);
440 return(1);
441 }
442
443 /*** PROGRESS WIDGETS ***/
444
445 char spinny[5] = "/-\\|";
446
447 int
curses_progress_spin(struct curses_widget * w)448 curses_progress_spin(struct curses_widget *w)
449 {
450 struct curses_form *cf = w->form;
451 int wx, wy;
452
453 if (w->type != CURSES_PROGRESS)
454 return(0);
455
456 wx = w->x + 1 - cf->x_offset;
457 wy = w->y + 1 - cf->y_offset;
458
459 w->spin = (w->spin + 1) % 4;
460 curses_colors_set(cf->win, CURSES_COLORS_FOCUS);/* XXX status ? */
461 mvwaddch(cf->win, wy, wx + 1, spinny[w->spin]);
462
463 return(1);
464 }
465