xref: /dragonfly/contrib/dialog/arrows.c (revision b2dabe2e739bd72461a68ac543307c2dedfb048c)
1 /*
2  *  $Id: arrows.c,v 1.54 2022/04/03 22:38:16 tom Exp $
3  *
4  *  arrows.c -- draw arrows to indicate end-of-range for lists
5  *
6  *  Copyright 2000-2019,2022  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 <dlg_internals.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
merge_colors(chtype foreground,chtype background)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
dlg_draw_helpline(WINDOW * win,bool decorations)71 dlg_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 
98 void
dlg_draw_arrows2(WINDOW * win,int top_arrow,int bottom_arrow,int x,int top,int bottom,chtype attr,chtype borderattr)99 dlg_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 
156 void
dlg_draw_scrollbar(WINDOW * win,long first_data,long this_data,long next_data,long total_data,int left,int right,int top,int bottom,chtype attr,chtype borderattr)157 dlg_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 
260 void
dlg_draw_arrows(WINDOW * win,int top_arrow,int bottom_arrow,int x,int top,int bottom)261 dlg_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