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
curses_colors_find(int fg,int bg)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
curses_colors_cfg(int role,int fg,int bg,int bold)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
curses_colors_init(int force_monochrome)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
curses_colors_set(WINDOW * w,int a)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
curses_window_blank(WINDOW * w)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
curses_frame_draw(int x,int y,int width,int height)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
curses_load_backdrop(WINDOW * w,const char * filename)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
curses_debug_str(const char * s)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
curses_debug_int(int i)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
curses_debug_key(int i)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
curses_debug_float(float f)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
extract_wrapped_line(const char * text,char * line,int width,int * spos)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