xref: /freebsd/contrib/dialog/arrows.c (revision 4fd0d10e0fe684211328bc148edf89a792425b39)
1 /*
2  *  $Id: arrows.c,v 1.50 2013/05/24 00:05:21 tom Exp $
3  *
4  *  arrows.c -- draw arrows to indicate end-of-range for lists
5  *
6  *  Copyright 2000-2012,2013	Thomas E. Dickey
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU Lesser General Public License, version 2.1
10  *  as published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful, but
13  *  WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this program; if not, write to
19  *	Free Software Foundation, Inc.
20  *	51 Franklin St., Fifth Floor
21  *	Boston, MA 02110, USA.
22  */
23 
24 #include <dialog.h>
25 
26 #ifdef USE_WIDE_CURSES
27 #if defined(CURSES_WACS_ARRAY) && !defined(CURSES_WACS_SYMBOLS)
28 /* workaround for NetBSD 5.1 curses */
29 #undef WACS_DARROW
30 #undef WACS_UARROW
31 #define WACS_DARROW &(CURSES_WACS_ARRAY['.'])
32 #define WACS_UARROW &(CURSES_WACS_ARRAY['-'])
33 #endif
34 #define add_acs(win, code) wadd_wch(win, W ## code)
35 #else
36 #define add_acs(win, code) waddch(win, dlg_boxchar(code))
37 #endif
38 
39 /* size of decorations */
40 #define ON_LEFT 4
41 #define ON_RIGHT 3
42 
43 #ifdef HAVE_COLOR
44 static chtype
45 merge_colors(chtype foreground, chtype background)
46 {
47     chtype result = foreground;
48     if ((foreground & A_COLOR) != (background & A_COLOR)) {
49 	short fg_f, bg_f;
50 	short fg_b, bg_b;
51 	short fg_pair = (short) PAIR_NUMBER(foreground);
52 	short bg_pair = (short) PAIR_NUMBER(background);
53 
54 	if (pair_content(fg_pair, &fg_f, &bg_f) != ERR
55 	    && pair_content(bg_pair, &fg_b, &bg_b) != ERR) {
56 	    result &= ~A_COLOR;
57 	    result |= dlg_color_pair(fg_f, bg_b);
58 	}
59     }
60     return result;
61 }
62 #else
63 #define merge_colors(f,b) (f)
64 #endif
65 
66 /*
67  * If we have help-line text, e.g., from "--hline", draw it between the other
68  * decorations at the bottom of the dialog window.
69  */
70 void
71 dlg_draw_helpline(WINDOW *win, bool decorations)
72 {
73     int cur_x, cur_y;
74     int bottom;
75 
76     if (dialog_vars.help_line != 0
77 	&& (bottom = getmaxy(win) - 1) > 0) {
78 	chtype attr = A_NORMAL;
79 	const int *cols = dlg_index_columns(dialog_vars.help_line);
80 	int other = decorations ? (ON_LEFT + ON_RIGHT) : 0;
81 	int avail = (getmaxx(win) - other - 2);
82 	int limit = dlg_count_real_columns(dialog_vars.help_line) + 2;
83 
84 	if (limit < avail) {
85 	    getyx(win, cur_y, cur_x);
86 	    other = decorations ? ON_LEFT : 0;
87 	    (void) wmove(win, bottom, other + (avail - limit) / 2);
88 	    waddch(win, '[');
89 	    dlg_print_text(win, dialog_vars.help_line, cols[limit], &attr);
90 	    waddch(win, ']');
91 	    wmove(win, cur_y, cur_x);
92 	}
93     }
94 }
95 
96 void
97 dlg_draw_arrows2(WINDOW *win,
98 		 int top_arrow,
99 		 int bottom_arrow,
100 		 int x,
101 		 int top,
102 		 int bottom,
103 		 chtype attr,
104 		 chtype borderattr)
105 {
106     chtype save = dlg_get_attrs(win);
107     int cur_x, cur_y;
108     int limit_x = getmaxx(win);
109     bool draw_top = TRUE;
110     bool is_toplevel = (wgetparent(win) == stdscr);
111 
112     getyx(win, cur_y, cur_x);
113 
114     /*
115      * If we're drawing a centered title, do not overwrite with the arrows.
116      */
117     if (dialog_vars.title && is_toplevel && (top - getbegy(win)) < MARGIN) {
118 	int have = (limit_x - dlg_count_columns(dialog_vars.title)) / 2;
119 	int need = x + 5;
120 	if (need > have)
121 	    draw_top = FALSE;
122     }
123 
124     if (draw_top) {
125 	(void) wmove(win, top, x);
126 	if (top_arrow) {
127 	    (void) wattrset(win, merge_colors(uarrow_attr, attr));
128 	    (void) add_acs(win, ACS_UARROW);
129 	    (void) waddstr(win, "(-)");
130 	} else {
131 	    (void) wattrset(win, attr);
132 	    (void) whline(win, dlg_boxchar(ACS_HLINE), ON_LEFT);
133 	}
134     }
135     mouse_mkbutton(top, x - 1, 6, KEY_PPAGE);
136 
137     (void) wmove(win, bottom, x);
138     if (bottom_arrow) {
139 	(void) wattrset(win, merge_colors(darrow_attr, borderattr));
140 	(void) add_acs(win, ACS_DARROW);
141 	(void) waddstr(win, "(+)");
142     } else {
143 	(void) wattrset(win, borderattr);
144 	(void) whline(win, dlg_boxchar(ACS_HLINE), ON_LEFT);
145     }
146     mouse_mkbutton(bottom, x - 1, 6, KEY_NPAGE);
147 
148     (void) wmove(win, cur_y, cur_x);
149     wrefresh(win);
150 
151     (void) wattrset(win, save);
152 }
153 
154 void
155 dlg_draw_scrollbar(WINDOW *win,
156 		   long first_data,
157 		   long this_data,
158 		   long next_data,
159 		   long total_data,
160 		   int left,
161 		   int right,
162 		   int top,
163 		   int bottom,
164 		   chtype attr,
165 		   chtype borderattr)
166 {
167     char buffer[80];
168     int percent;
169     int len;
170     int oldy, oldx;
171 
172     chtype save = dlg_get_attrs(win);
173     int top_arrow = (first_data != 0);
174     int bottom_arrow = (next_data < total_data);
175 
176     getyx(win, oldy, oldx);
177 
178     dlg_draw_helpline(win, TRUE);
179     if (bottom_arrow || top_arrow || dialog_state.use_scrollbar) {
180 	percent = (!total_data
181 		   ? 100
182 		   : (int) ((next_data * 100)
183 			    / total_data));
184 
185 	if (percent < 0)
186 	    percent = 0;
187 	else if (percent > 100)
188 	    percent = 100;
189 
190 	(void) wattrset(win, position_indicator_attr);
191 	(void) sprintf(buffer, "%d%%", percent);
192 	(void) wmove(win, bottom, right - 7);
193 	(void) waddstr(win, buffer);
194 	if ((len = dlg_count_columns(buffer)) < 4) {
195 	    (void) wattrset(win, border_attr);
196 	    whline(win, dlg_boxchar(ACS_HLINE), 4 - len);
197 	}
198     }
199 #define BARSIZE(num) (int) (0.5 + (double) ((all_high * (int) (num)) / (double) total_data))
200 #define ORDSIZE(num) (int) ((double) ((all_high * (int) (num)) / (double) all_diff))
201 
202     if (dialog_state.use_scrollbar) {
203 	int all_high = (bottom - top - 1);
204 
205 	this_data = MAX(0, this_data);
206 
207 	if (total_data > 0 && all_high > 0) {
208 	    int all_diff = (int) (total_data + 1);
209 	    int bar_diff = (int) (next_data + 1 - this_data);
210 	    int bar_high;
211 	    int bar_y;
212 
213 	    bar_high = ORDSIZE(bar_diff);
214 	    if (bar_high <= 0)
215 		bar_high = 1;
216 
217 	    if (bar_high < all_high) {
218 		int bar_last = BARSIZE(next_data);
219 
220 		wmove(win, top + 1, right);
221 
222 		(void) wattrset(win, save);
223 		wvline(win, ACS_VLINE | A_REVERSE, all_high);
224 
225 		bar_y = ORDSIZE(this_data);
226 		if (bar_y >= bar_last && bar_y > 0)
227 		    bar_y = bar_last - 1;
228 		if (bar_last - bar_y > bar_high && bar_high > 1)
229 		    ++bar_y;
230 		bar_last = MIN(bar_last, all_high);
231 
232 		wmove(win, top + 1 + bar_y, right);
233 
234 		(void) wattrset(win, position_indicator_attr);
235 		wattron(win, A_REVERSE);
236 #if defined(WACS_BLOCK) && defined(NCURSES_VERSION) && defined(USE_WIDE_CURSES)
237 		wvline_set(win, WACS_BLOCK, bar_last - bar_y);
238 #else
239 		wvline(win, ACS_BLOCK, bar_last - bar_y);
240 #endif
241 	    }
242 	}
243     }
244     dlg_draw_arrows2(win,
245 		     top_arrow,
246 		     bottom_arrow,
247 		     left + ARROWS_COL,
248 		     top,
249 		     bottom,
250 		     attr,
251 		     borderattr);
252 
253     (void) wattrset(win, save);
254     wmove(win, oldy, oldx);
255 }
256 
257 void
258 dlg_draw_arrows(WINDOW *win,
259 		int top_arrow,
260 		int bottom_arrow,
261 		int x,
262 		int top,
263 		int bottom)
264 {
265     dlg_draw_helpline(win, TRUE);
266     dlg_draw_arrows2(win,
267 		     top_arrow,
268 		     bottom_arrow,
269 		     x,
270 		     top,
271 		     bottom,
272 		     menubox_border2_attr,
273 		     menubox_border_attr);
274 }
275