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.12 2005/02/08 07:49:03 cpressey Exp $
37 */
38
39 #include <ctype.h>
40 #include <ncurses.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "libaura/mem.h"
45
46 #include "curses_form.h"
47 #include "curses_widget.h"
48 #include "curses_bar.h"
49 #include "curses_util.h"
50
51 #ifdef DEBUG
52 extern FILE *dfui_debug_file;
53 #endif
54
55 extern struct curses_bar *statusbar;
56
57 /*** WIDGETS ***/
58
59 /*
60 * Create a new curses_widget, outside of the context of any particular
61 * curses_form.
62 */
63 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)64 curses_widget_new(unsigned int x, unsigned int y,
65 unsigned int width, widget_t type,
66 const char *text, unsigned int size,
67 unsigned int flags)
68 {
69 struct curses_widget *w;
70
71 AURA_MALLOC(w, curses_widget);
72
73 w->form = NULL;
74
75 if (flags & CURSES_WIDGET_WIDEN) {
76 switch (type) {
77 case CURSES_TEXTBOX:
78 width = strlen(text) + 2;
79 break;
80 case CURSES_BUTTON:
81 width = strlen(text) + 4;
82 break;
83 default:
84 width = strlen(text);
85 break;
86 }
87 }
88
89 w->x = x;
90 w->y = y;
91 w->width = width;
92 w->type = type;
93 w->next = NULL;
94 w->prev = NULL;
95 w->flags = flags;
96 w->accel = '\0';
97
98 if (w->type == CURSES_TEXTBOX) {
99 w->size = size;
100 w->text = aura_malloc(size, "w->text");
101 strcpy(w->text, text);
102 } else {
103 w->text = aura_strdup(text);
104 w->size = strlen(text) + 1;
105 /* size argument is ignored */
106 }
107
108 w->curpos = strlen(w->text);
109 w->editable = 1;
110 w->obscured = 0;
111 w->offset = 0;
112 w->amount = 0;
113 w->spin = 0;
114 w->tooltip = NULL;
115 w->user_id = 0;
116 w->userdata = NULL;
117
118 w->click_cb = NULL;
119
120 return(w);
121 }
122
123 /*
124 * Free the memory allocated for a curses_widget. Note that this does
125 * NOT free any allocated memory at the widget's userdata pointer.
126 */
127 void
curses_widget_free(struct curses_widget * w)128 curses_widget_free(struct curses_widget *w)
129 {
130 if (w->tooltip != NULL)
131 free(w->tooltip);
132 free(w->text);
133 AURA_FREE(w, curses_widget);
134 }
135
136 void
curses_widget_tooltip_set(struct curses_widget * w,const char * tooltip)137 curses_widget_tooltip_set(struct curses_widget *w, const char *tooltip)
138 {
139 if (w->tooltip != NULL)
140 free(w->tooltip);
141 w->tooltip = aura_strdup(tooltip);
142 }
143
144 /*
145 * Draw a curses_widget to the window of its associated curses_form.
146 * Note that this does not refresh the screen.
147 */
148 void
curses_widget_draw(struct curses_widget * w)149 curses_widget_draw(struct curses_widget *w)
150 {
151 unsigned int wx, wy, x, len, i, charpos, cutoff, fchpos;
152 char p[5];
153 char bar_char;
154 struct curses_form *cf = w->form;
155
156 wx = w->x + 1 - cf->x_offset;
157 wy = w->y + 1 - cf->y_offset;
158
159 /*
160 * If the widget is outside the clipping rectangle of the
161 * form, don't draw it.
162 */
163 if (!curses_form_widget_is_visible(w))
164 return;
165
166 wmove(cf->win, wy, wx);
167
168 curses_colors_set(cf->win, w == cf->widget_focus ?
169 CURSES_COLORS_FOCUS : CURSES_COLORS_CONTROL);
170
171 switch (w->type) {
172 case CURSES_LABEL:
173 curses_colors_set(cf->win, CURSES_COLORS_LABEL); /* XXX conditional on... */
174 waddnstr(cf->win, w->text, w->width);
175 curs_set(0);
176 break;
177 case CURSES_TEXTBOX:
178 waddstr(cf->win, "[");
179 curses_colors_set(cf->win, CURSES_COLORS_TEXT); /* XXX focus ? */
180 charpos = w->offset;
181 len = strlen(w->text);
182 for (x = 0; x < w->width - 2; x++) {
183 if (charpos < len) {
184 waddch(cf->win, w->obscured ?
185 '*' : w->text[charpos]);
186 charpos++;
187 } else {
188 waddch(cf->win, ' ');
189 }
190 }
191 curses_colors_set(cf->win, w == cf->widget_focus ?
192 CURSES_COLORS_FOCUS : CURSES_COLORS_CONTROL);
193 waddstr(cf->win, "]");
194 /*
195 * Position the cursor to where it's expected.
196 */
197 if (w->curpos - w->offset < w->width - 2) {
198 wmove(cf->win, w->y + 1 - cf->y_offset,
199 w->x + 2 - cf->x_offset + w->curpos - w->offset);
200 } else {
201 wmove(cf->win, w->y + 1 - cf->y_offset,
202 w->x + 2 - cf->x_offset + w->width - 2);
203 }
204 curs_set(1);
205 break;
206 case CURSES_BUTTON:
207 waddstr(cf->win, "< ");
208 waddnstr(cf->win, w->text, w->width - 4);
209 waddstr(cf->win, " >");
210 if (isprint(w->accel)) {
211 for (i = 0; w->text[i] != '\0'; i++) {
212 if (toupper(w->text[i]) == w->accel) {
213 wmove(cf->win, wy, wx + 2 + i);
214 curses_colors_set(cf->win, w == cf->widget_focus ?
215 CURSES_COLORS_ACCELFOCUS : CURSES_COLORS_ACCEL);
216 waddch(cf->win, w->text[i]);
217 break;
218 }
219 }
220 }
221 curs_set(0);
222 break;
223 case CURSES_PROGRESS:
224 waddstr(cf->win, "[");
225 snprintf(p, 5, "%3d%%", w->amount);
226 cutoff = (w->amount * (w->width - 2)) / 100;
227 fchpos = ((w->width - 2) / 2) - 2;
228 for (x = 0; x < w->width - 2; x++) {
229 if (x < cutoff) {
230 curses_colors_set(cf->win, CURSES_COLORS_FOCUS);/* XXX status ? */
231 bar_char = monochrome ? '=' : ' ';
232 } else {
233 curses_colors_set(cf->win, CURSES_COLORS_CONTROL);
234 bar_char = ' ';
235 }
236
237 if (x >= fchpos && x <= fchpos + 3)
238 waddch(cf->win, p[x - fchpos]);
239 else
240 waddch(cf->win, bar_char);
241 }
242 waddstr(cf->win, "]");
243 curs_set(0);
244 break;
245 case CURSES_CHECKBOX:
246 waddstr(cf->win, "[");
247 waddstr(cf->win, w->amount ? "X" : " ");
248 waddstr(cf->win, "]");
249 curs_set(0);
250 break;
251 }
252 }
253
254 void
curses_widget_draw_tooltip(struct curses_widget * w)255 curses_widget_draw_tooltip(struct curses_widget *w)
256 {
257 if (w->tooltip != NULL)
258 curses_bar_set_text(statusbar, w->tooltip);
259 }
260
261 /*
262 * Returns non-zero if the given widget can take control focus
263 * (i.e. if it is not a label, progress bar, or other inert element.)
264 */
265 int
curses_widget_can_take_focus(struct curses_widget * w)266 curses_widget_can_take_focus(struct curses_widget *w)
267 {
268 if (w->type == CURSES_LABEL || w->type == CURSES_PROGRESS)
269 return(0);
270 return(1);
271 }
272
273 int
curses_widget_set_click_cb(struct curses_widget * w,int (* callback)(struct curses_widget *))274 curses_widget_set_click_cb(struct curses_widget *w,
275 int (*callback)(struct curses_widget *))
276 {
277 w->click_cb = callback;
278 return(1);
279 }
280
281 /*
282 * Returns:
283 * -1 to indicate that the widget is not clickable.
284 * 0 to indicate that the widget clicked.
285 * 1 to indicate that the widget clicked and that its form should close.
286 */
287 int
curses_widget_click(struct curses_widget * w)288 curses_widget_click(struct curses_widget *w)
289 {
290 if ((w->type != CURSES_BUTTON && w->type != CURSES_TEXTBOX) ||
291 w->click_cb == NULL)
292 return(-1);
293
294 return(w->click_cb(w));
295 }
296
297 /*** TEXTBOX WIDGETS ***/
298
299 int
curses_textbox_advance_char(struct curses_widget * w)300 curses_textbox_advance_char(struct curses_widget *w)
301 {
302 if (w->text[w->curpos] != '\0') {
303 w->curpos++;
304 if ((w->curpos - w->offset) >= (w->width - 2))
305 w->offset++;
306 curses_widget_draw(w);
307 curses_form_refresh(w->form);
308 return(1);
309 } else {
310 return(0);
311 }
312 }
313
314 int
curses_textbox_retreat_char(struct curses_widget * w)315 curses_textbox_retreat_char(struct curses_widget *w)
316 {
317 if (w->curpos > 0) {
318 w->curpos--;
319 if (w->curpos < w->offset)
320 w->offset--;
321 curses_widget_draw(w);
322 curses_form_refresh(w->form);
323 return(1);
324 } else {
325 return(0);
326 }
327 }
328
329 int
curses_textbox_home(struct curses_widget * w)330 curses_textbox_home(struct curses_widget *w)
331 {
332 w->curpos = 0;
333 w->offset = 0;
334 curses_widget_draw(w);
335 curses_form_refresh(w->form);
336 return(1);
337 }
338
339 int
curses_textbox_end(struct curses_widget * w)340 curses_textbox_end(struct curses_widget *w)
341 {
342 w->curpos = strlen(w->text);
343 while ((w->curpos - w->offset) >= (w->width - 2)) {
344 w->offset++;
345 }
346 curses_widget_draw(w);
347 curses_form_refresh(w->form);
348 return(1);
349 }
350
351 int
curses_textbox_insert_char(struct curses_widget * w,char key)352 curses_textbox_insert_char(struct curses_widget *w, char key)
353 {
354 unsigned int len, i;
355
356 if (!w->editable)
357 return(0);
358
359 len = strlen(w->text);
360 if (len == (w->size - 1))
361 return(0);
362
363 w->text[len + 1] = '\0';
364 for (i = len; i > w->curpos; i--)
365 w->text[i] = w->text[i - 1];
366 w->text[w->curpos++] = key;
367 if ((w->curpos - w->offset) >= (w->width - 2))
368 w->offset++;
369 curses_widget_draw(w);
370 curses_form_refresh(w->form);
371 return(1);
372 }
373
374 int
curses_textbox_backspace_char(struct curses_widget * w)375 curses_textbox_backspace_char(struct curses_widget *w)
376 {
377 int len, i;
378
379 if (!w->editable)
380 return(0);
381
382 len = strlen(w->text);
383 if (w->curpos == 0)
384 return(0);
385
386 for (i = w->curpos; i <= len; i++)
387 w->text[i - 1] = w->text[i];
388 w->curpos--;
389 if (w->curpos < w->offset)
390 w->offset--;
391 curses_widget_draw(w);
392 curses_form_refresh(w->form);
393 return(1);
394 }
395
396 int
curses_textbox_delete_char(struct curses_widget * w)397 curses_textbox_delete_char(struct curses_widget *w)
398 {
399 unsigned int len, i;
400
401 if (!w->editable)
402 return(0);
403
404 len = strlen(w->text);
405 if (w->curpos == len)
406 return(0);
407
408 for (i = w->curpos + 1; i <= len; i++)
409 w->text[i - 1] = w->text[i];
410 curses_widget_draw(w);
411 curses_form_refresh(w->form);
412 return(1);
413 }
414
415 int
curses_textbox_set_text(struct curses_widget * w,const char * text)416 curses_textbox_set_text(struct curses_widget *w, const char *text)
417 {
418 strlcpy(w->text, text, w->size);
419 w->curpos = strlen(w->text);
420 curses_widget_draw(w);
421 curses_form_refresh(w->form);
422 return(1);
423 }
424
425 /*** CHECKBOX WIDGETS ***/
426
427 int
curses_checkbox_toggle(struct curses_widget * w)428 curses_checkbox_toggle(struct curses_widget *w)
429 {
430 if (!w->editable)
431 return(0);
432 w->amount = !w->amount;
433 curses_widget_draw(w);
434 curses_form_refresh(w->form);
435 return(1);
436 }
437
438 /*** PROGRESS WIDGETS ***/
439
440 char spinny[5] = "/-\\|";
441
442 int
curses_progress_spin(struct curses_widget * w)443 curses_progress_spin(struct curses_widget *w)
444 {
445 struct curses_form *cf = w->form;
446 int wx, wy;
447
448 if (w->type != CURSES_PROGRESS)
449 return(0);
450
451 wx = w->x + 1 - cf->x_offset;
452 wy = w->y + 1 - cf->y_offset;
453
454 w->spin = (w->spin + 1) % 4;
455 curses_colors_set(cf->win, CURSES_COLORS_FOCUS);/* XXX status ? */
456 mvwaddch(cf->win, wy, wx + 1, spinny[w->spin]);
457
458 return(1);
459 }
460