1/*
2 *  $Id: arrows.c,v 1.53 2019/07/24 23:04:14 tom Exp $
3 *
4 *  arrows.c -- draw arrows to indicate end-of-range for lists
5 *
6 *  Copyright 2000-2018,2019	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
44static chtype
45merge_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 */
70void
71dlg_draw_helpline(WINDOW *win, bool decorations)
72{
73    int bottom;
74
75    if (dialog_vars.help_line != 0
76	&& dialog_vars.help_line[0] != 0
77	&& (bottom = getmaxy(win) - 1) > 0) {
78	chtype attr = A_NORMAL;
79	int cols = dlg_count_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	    int cur_x, cur_y;
86
87	    getyx(win, cur_y, cur_x);
88	    other = decorations ? ON_LEFT : 0;
89	    (void) wmove(win, bottom, other + (avail - limit) / 2);
90	    waddch(win, '[');
91	    dlg_print_text(win, dialog_vars.help_line, cols, &attr);
92	    waddch(win, ']');
93	    wmove(win, cur_y, cur_x);
94	}
95    }
96}
97
98void
99dlg_draw_arrows2(WINDOW *win,
100		 int top_arrow,
101		 int bottom_arrow,
102		 int x,
103		 int top,
104		 int bottom,
105		 chtype attr,
106		 chtype borderattr)
107{
108    chtype save = dlg_get_attrs(win);
109    int cur_x, cur_y;
110    int limit_x = getmaxx(win);
111    bool draw_top = TRUE;
112    bool is_toplevel = (wgetparent(win) == stdscr);
113
114    getyx(win, cur_y, cur_x);
115
116    /*
117     * If we're drawing a centered title, do not overwrite with the arrows.
118     */
119    if (dialog_vars.title && is_toplevel && (top - getbegy(win)) < MARGIN) {
120	int have = (limit_x - dlg_count_columns(dialog_vars.title)) / 2;
121	int need = x + 5;
122	if (need > have)
123	    draw_top = FALSE;
124    }
125
126    if (draw_top) {
127	(void) wmove(win, top, x);
128	if (top_arrow) {
129	    dlg_attrset(win, merge_colors(uarrow_attr, attr));
130	    (void) add_acs(win, ACS_UARROW);
131	    (void) waddstr(win, "(-)");
132	} else {
133	    dlg_attrset(win, attr);
134	    (void) whline(win, dlg_boxchar(ACS_HLINE), ON_LEFT);
135	}
136    }
137    mouse_mkbutton(top, x - 1, 6, KEY_PPAGE);
138
139    (void) wmove(win, bottom, x);
140    if (bottom_arrow) {
141	dlg_attrset(win, merge_colors(darrow_attr, borderattr));
142	(void) add_acs(win, ACS_DARROW);
143	(void) waddstr(win, "(+)");
144    } else {
145	dlg_attrset(win, borderattr);
146	(void) whline(win, dlg_boxchar(ACS_HLINE), ON_LEFT);
147    }
148    mouse_mkbutton(bottom, x - 1, 6, KEY_NPAGE);
149
150    (void) wmove(win, cur_y, cur_x);
151    wrefresh(win);
152
153    dlg_attrset(win, save);
154}
155
156void
157dlg_draw_scrollbar(WINDOW *win,
158		   long first_data,
159		   long this_data,
160		   long next_data,
161		   long total_data,
162		   int left,
163		   int right,
164		   int top,
165		   int bottom,
166		   chtype attr,
167		   chtype borderattr)
168{
169    int oldy, oldx;
170
171    chtype save = dlg_get_attrs(win);
172    int top_arrow = (first_data != 0);
173    int bottom_arrow = (next_data < total_data);
174
175    getyx(win, oldy, oldx);
176
177    dlg_draw_helpline(win, TRUE);
178    if (bottom_arrow || top_arrow || dialog_state.use_scrollbar) {
179	char buffer[80];
180	int percent;
181	int len;
182
183	percent = (!total_data
184		   ? 100
185		   : (int) ((next_data * 100)
186			    / total_data));
187
188	if (percent < 0)
189	    percent = 0;
190	else if (percent > 100)
191	    percent = 100;
192
193	dlg_attrset(win, position_indicator_attr);
194	(void) sprintf(buffer, "%d%%", percent);
195	(void) wmove(win, bottom, right - 7);
196	(void) waddstr(win, buffer);
197	if ((len = dlg_count_columns(buffer)) < 4) {
198	    dlg_attrset(win, border_attr);
199	    whline(win, dlg_boxchar(ACS_HLINE), 4 - len);
200	}
201    }
202#define BARSIZE(num) (int) (0.5 + (double) ((all_high * (int) (num)) / (double) total_data))
203#define ORDSIZE(num) (int) ((double) ((all_high * (int) (num)) / (double) all_diff))
204
205    if (dialog_state.use_scrollbar) {
206	int all_high = (bottom - top - 1);
207
208	this_data = MAX(0, this_data);
209
210	if (total_data > 0 && all_high > 0) {
211	    int all_diff = (int) (total_data + 1);
212	    int bar_diff = (int) (next_data + 1 - this_data);
213	    int bar_high;
214
215	    bar_high = ORDSIZE(bar_diff);
216	    if (bar_high <= 0)
217		bar_high = 1;
218
219	    if (bar_high < all_high) {
220		int bar_last = BARSIZE(next_data);
221		int bar_y;
222
223		wmove(win, top + 1, right);
224
225		dlg_attrset(win, save);
226		wvline(win, ACS_VLINE | A_REVERSE, all_high);
227
228		bar_y = ORDSIZE(this_data);
229		if (bar_y >= bar_last && bar_y > 0)
230		    bar_y = bar_last - 1;
231		if (bar_last - bar_y > bar_high && bar_high > 1)
232		    ++bar_y;
233		bar_last = MIN(bar_last, all_high);
234
235		wmove(win, top + 1 + bar_y, right);
236
237		dlg_attrset(win, position_indicator_attr);
238		dlg_attron(win, A_REVERSE);
239#if defined(WACS_BLOCK) && defined(NCURSES_VERSION) && defined(USE_WIDE_CURSES)
240		wvline_set(win, WACS_BLOCK, bar_last - bar_y);
241#else
242		wvline(win, ACS_BLOCK, bar_last - bar_y);
243#endif
244	    }
245	}
246    }
247    dlg_draw_arrows2(win,
248		     top_arrow,
249		     bottom_arrow,
250		     left + ARROWS_COL,
251		     top,
252		     bottom,
253		     attr,
254		     borderattr);
255
256    dlg_attrset(win, save);
257    wmove(win, oldy, oldx);
258}
259
260void
261dlg_draw_arrows(WINDOW *win,
262		int top_arrow,
263		int bottom_arrow,
264		int x,
265		int top,
266		int bottom)
267{
268    dlg_draw_helpline(win, TRUE);
269    dlg_draw_arrows2(win,
270		     top_arrow,
271		     bottom_arrow,
272		     x,
273		     top,
274		     bottom,
275		     menubox_border2_attr,
276		     menubox_border_attr);
277}
278