installer: Move the installer from contrib/ to usr.sbin/.
[dragonfly.git] / usr.sbin / installer / dfuife_curses / curses_widget.c
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }