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_util.c 36 * $Id: curses_util.c,v 1.7 2005/02/08 07:49:03 cpressey Exp $ 37 */ 38 39 #include <ctype.h> 40 #include <ncurses.h> 41 #include <panel.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 46 #include "curses_util.h" 47 48 unsigned int ymax, xmax; 49 int monochrome = 1; 50 int allocated_colors = 0; 51 52 struct curses_attr { 53 int pair_no; 54 int bold; 55 }; 56 57 struct curses_attr colors_tab[CURSES_COLORS_MAX]; 58 59 int colors[8] = { 60 COLOR_BLACK, 61 COLOR_RED, 62 COLOR_GREEN, 63 COLOR_YELLOW, 64 COLOR_BLUE, 65 COLOR_MAGENTA, 66 COLOR_CYAN, 67 COLOR_WHITE 68 }; 69 70 /* 71 * If there is an established color pair with the given fg and bg 72 * colors, return it. Else allocate a new pair with these colors 73 * and return that. 74 */ 75 static int 76 curses_colors_find(int fg, int bg) 77 { 78 int pair_no; 79 short fge, bge; 80 81 for (pair_no = 0; 82 pair_no <= allocated_colors && pair_no < COLOR_PAIRS; 83 pair_no++) { 84 pair_content(pair_no, &fge, &bge); 85 if (fg == fge && bg == bge) 86 return(pair_no); 87 } 88 89 /* 90 * No pair was found, allocate a new one. 91 */ 92 if (allocated_colors < (COLOR_PAIRS-1)) { 93 allocated_colors++; 94 init_pair(allocated_colors, fg, bg); 95 return(allocated_colors); 96 } 97 98 /* 99 * No space to allocate a new one, return error. 100 */ 101 return(-1); 102 } 103 104 static void 105 curses_colors_cfg(int role, int fg, int bg, int bold) 106 { 107 int pair_no; 108 109 pair_no = curses_colors_find(fg, bg); 110 if (pair_no != -1) { 111 colors_tab[role].pair_no = pair_no; 112 colors_tab[role].bold = bold; 113 } else { 114 colors_tab[role].pair_no = 0; 115 colors_tab[role].bold = bold; 116 } 117 } 118 119 void 120 curses_colors_init(int force_monochrome) 121 { 122 if (!force_monochrome) { 123 if (has_colors()) { 124 monochrome = 0; 125 start_color(); 126 } 127 } 128 129 /* 130 * By default, make it look kinda like the default libdialog. 131 */ 132 curses_colors_cfg(CURSES_COLORS_NORMAL, COLOR_BLACK, COLOR_GREY, 0); 133 curses_colors_cfg(CURSES_COLORS_BACKDROP, COLOR_WHITE, COLOR_BLUE, 0); 134 curses_colors_cfg(CURSES_COLORS_MENUBAR, COLOR_BLACK, COLOR_GREY, 0); 135 curses_colors_cfg(CURSES_COLORS_STATUSBAR, COLOR_BLACK, COLOR_GREY, 0); 136 curses_colors_cfg(CURSES_COLORS_BORDER, COLOR_WHITE, COLOR_GREY, 1); 137 curses_colors_cfg(CURSES_COLORS_FORMTITLE, COLOR_YELLOW, COLOR_GREY, 1); 138 curses_colors_cfg(CURSES_COLORS_LABEL, COLOR_BLACK, COLOR_GREY, 0); 139 curses_colors_cfg(CURSES_COLORS_CONTROL, COLOR_BLACK, COLOR_GREY, 0); 140 curses_colors_cfg(CURSES_COLORS_TEXT, COLOR_BLACK, COLOR_GREY, 0); 141 curses_colors_cfg(CURSES_COLORS_FOCUS, COLOR_WHITE, COLOR_BLUE, 1); 142 curses_colors_cfg(CURSES_COLORS_SCROLLAREA,COLOR_GREY, COLOR_BLACK, 0); 143 curses_colors_cfg(CURSES_COLORS_SCROLLBAR, COLOR_WHITE, COLOR_BLUE, 1); 144 curses_colors_cfg(CURSES_COLORS_ACCEL, COLOR_WHITE, COLOR_GREY, 1); 145 curses_colors_cfg(CURSES_COLORS_ACCELFOCUS,COLOR_YELLOW, COLOR_BLUE, 1); 146 } 147 148 void 149 curses_colors_set(WINDOW *w, int a) 150 { 151 if (!monochrome) 152 wattrset(w, COLOR_PAIR(colors_tab[a].pair_no)); 153 if (colors_tab[a].bold) 154 wattron(w, A_BOLD); 155 else 156 wattroff(w, A_BOLD); 157 } 158 159 void 160 curses_window_blank(WINDOW *w) 161 { 162 unsigned int i; 163 164 for (i = 0; i <= ymax; i++) { 165 wmove(w, i, 0); 166 whline(w, ' ', xmax); 167 } 168 169 wrefresh(w); 170 } 171 172 void 173 curses_frame_draw(int x, int y, int width, int height) 174 { 175 int i; 176 177 mvaddch(y, x, ACS_ULCORNER); 178 hline(ACS_HLINE, width - 2); 179 mvaddch(y, x + width - 1, ACS_URCORNER); 180 181 mvaddch(y + height - 1, x, ACS_LLCORNER); 182 hline(ACS_HLINE, width - 2); 183 mvaddch(y + height - 1, x + width - 1, ACS_LRCORNER); 184 185 move(y + 1, x); 186 vline(ACS_VLINE, height - 2); 187 188 move(y + 1, x + width - 1); 189 vline(ACS_VLINE, height - 2); 190 191 for (i = y + 1; i < y + height - 1; i++) { 192 move(i, x + 1); 193 hline(' ', width - 2); 194 } 195 } 196 197 void 198 curses_load_backdrop(WINDOW *w, const char *filename) 199 { 200 FILE *f; 201 char line[80]; 202 int row = 1; 203 int my, mx; 204 205 getmaxyx(w, my, mx); 206 wclear(w); 207 curses_colors_set(w, CURSES_COLORS_BACKDROP); 208 curses_window_blank(w); 209 210 if ((f = fopen(filename, "r")) != NULL) { 211 while (fgets(line, 79, f) != NULL) { 212 if (row > my) 213 break; 214 if (line[strlen(line) - 1] == '\n') 215 line[strlen(line) - 1] = '\0'; 216 mvwaddnstr(w, row++, 0, line, mx); 217 } 218 fclose(f); 219 } 220 } 221 222 void 223 curses_debug_str(const char *s) 224 { 225 char b[256]; 226 227 move(1, 0); 228 sprintf(b, "[%77s]", s); 229 addstr(b); 230 refresh(); 231 } 232 233 void 234 curses_debug_int(int i) 235 { 236 char b[256]; 237 238 move(1, 0); 239 sprintf(b, "[%06d]", i); 240 addstr(b); 241 refresh(); 242 } 243 244 void 245 curses_debug_key(int i) 246 { 247 char b[256]; 248 249 move(1, 0); 250 sprintf(b, "[%06d] %s", i, keyname(i)); 251 addstr(b); 252 refresh(); 253 } 254 255 void 256 curses_debug_float(float f) 257 { 258 char b[256]; 259 260 move(1, 0); 261 sprintf(b, "[%09.3f]", f); 262 addstr(b); 263 refresh(); 264 } 265 266 /* 267 * Word wrapping. 268 * 269 * text: The text to word-wrap, as one long string. Spaces will be 270 * compressed, but end-of-line characters will be honoured. 271 * line: A buffer (must be allocated by the caller) to hold a single 272 * line extracted from text. 273 * width: The maximum width of a line. 274 * spos: Pointer to the source position in text. Should be initially 275 * set to zero, and retained between calls to this function. 276 * Returns: A boolean indicating whether the end of text was reached. 277 * Typically this function should be called repeatedly until 278 * it returns true. 279 */ 280 int 281 extract_wrapped_line(const char *text, char *line, int width, int *spos) 282 { 283 int dpos = 0; 284 int saved_spos, saved_dpos; 285 286 for (;;) { 287 /* 288 * Skip over whitespace. If we find a newline or the 289 * end of the text, return a blank line. Leave *spos 290 * at the position of the 1st non-whitespace character. 291 */ 292 while (isspace(text[*spos]) && text[*spos] != '\0') { 293 if (text[*spos] == '\n') { 294 line[dpos] = '\0'; 295 (*spos)++; 296 return(0); 297 } 298 (*spos)++; 299 } 300 301 /* 302 * Save start position and destination position. 303 */ 304 saved_spos = *spos; 305 saved_dpos = dpos; 306 307 /* 308 * Read a word from *spos onward. 309 */ 310 while (!isspace(text[*spos]) && 311 text[*spos] != '\0' && 312 dpos < width) { 313 line[dpos++] = text[(*spos)++]; 314 } 315 316 if (text[*spos] == '\0') { 317 /* 318 * End of string - return this word as the last. 319 */ 320 line[dpos] = '\0'; 321 return(1); 322 } else if (dpos >= width) { 323 /* 324 * Last word is too long to fit on this line. 325 */ 326 if (dpos - saved_dpos >= width) { 327 /* 328 * In fact, it's too long to fit on any line! 329 * Truncate it. 330 */ 331 line[width - 1] = '\0'; 332 *spos = saved_spos + (dpos - saved_dpos); 333 return(0); 334 } else { 335 /* 336 * Save it for the next pass. 337 */ 338 *spos = saved_spos; 339 line[saved_dpos - 1] = '\0'; 340 return(0); 341 } 342 } else { 343 line[dpos++] = ' '; 344 } 345 } 346 } 347