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 } 460