1 /*
2 Widgets for the Midnight Commander
3
4 Copyright (C) 1994-2026
5 Free Software Foundation, Inc.
6
7 Authors:
8 Radek Doulik, 1994, 1995
9 Miguel de Icaza, 1994, 1995
10 Jakub Jelinek, 1995
11 Andrej Borsenkow, 1996
12 Norbert Warmuth, 1997
13 Andrew Borodin <aborodin@vmail.ru>, 2009-2022
14
15 This file is part of the Midnight Commander.
16
17 The Midnight Commander is free software: you can redistribute it
18 and/or modify it under the terms of the GNU General Public License as
19 published by the Free Software Foundation, either version 3 of the License,
20 or (at your option) any later version.
21
22 The Midnight Commander is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
26
27 You should have received a copy of the GNU General Public License
28 along with this program. If not, see <https://www.gnu.org/licenses/>.
29 */
30
31 /** \file gauge.c
32 * \brief Source: WGauge widget (progress indicator)
33 */
34
35 #include <config.h>
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #include "lib/global.h"
42
43 #include "lib/tty/tty.h"
44 #include "lib/tty/color.h"
45 #include "lib/skin.h"
46 #include "lib/widget.h"
47
48 /*** global variables ****************************************************************************/
49
50 /*** file scope macro definitions ****************************************************************/
51
52 /*** file scope type declarations ****************************************************************/
53
54 /*** forward declarations (file scope functions) *************************************************/
55
56 /*** file scope variables ************************************************************************/
57
58 /* --------------------------------------------------------------------------------------------- */
59 /*** file scope functions ************************************************************************/
60 /* --------------------------------------------------------------------------------------------- */
61 /**
62 * Show the progress in the terminal's tab, title bar or taskbar, using the ConEmu
63 * "OSC 9 ; 4" sequence supported by Windows Terminal, GNOME Terminal (VTE), iTerm2 and others.
64 *
65 * @param g gauge
66 * @param percentage progress to report, -1 to remove the progress indicator
67 */
68
69 static void
70 gauge_report_to_terminal (WGauge *g, int percentage)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
71 {
72 if (!g->terminal_progress || g->terminal_percentage == percentage)
73 return;
74
75 g->terminal_percentage = percentage;
76
77 if (percentage < 0)
78 fprintf (stdout, ESC_STR "]9;4;0" ESC_STR "\\");
79 else
80 fprintf (stdout, ESC_STR "]9;4;1;%d" ESC_STR "\\", percentage);
81 (void) fflush (stdout);
82 }
83
84 /* --------------------------------------------------------------------------------------------- */
85
86 static cb_ret_t
87 gauge_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
88 {
89 WGauge *g = GAUGE (w);
90 const int *colors;
91
92 switch (msg)
93 {
94 case MSG_DRAW:
95 colors = widget_get_colors (w);
96 widget_gotoyx (w, 0, 0);
97 if (!g->shown)
98 {
99 tty_setcolor (colors[DLG_COLOR_NORMAL]);
100 tty_printf ("%*s", w->rect.cols, "");
101 gauge_report_to_terminal (g, -1);
102 }
103 else
104 {
105 int gauge_len;
106 int percentage, columns;
107 int total = g->max;
108 int done = g->current;
109
110 if (total <= 0 || done < 0)
111 {
112 done = 0;
113 total = 100;
114 }
115 if (done > total)
116 done = total;
117 while (total > 65535)
118 {
119 total /= 256;
120 done /= 256;
121 }
122
123 gauge_len = w->rect.cols - 7; // 7 positions for percentage
124
125 percentage = (200 * done / total + 1) / 2;
126 columns = (2 * gauge_len * done / total + 1) / 2;
127 tty_print_char ('[');
128 if (g->from_left_to_right)
129 {
130 tty_setcolor (CORE_GAUGE_COLOR);
131 tty_printf ("%*s", columns, "");
132 tty_setcolor (colors[DLG_COLOR_NORMAL]);
133 tty_printf ("%*s] %3d%%", gauge_len - columns, "", percentage);
134 }
135 else
136 {
137 tty_setcolor (colors[DLG_COLOR_NORMAL]);
138 tty_printf ("%*s", gauge_len - columns, "");
139 tty_setcolor (CORE_GAUGE_COLOR);
140 tty_printf ("%*s", columns, "");
141 tty_setcolor (colors[DLG_COLOR_NORMAL]);
142 tty_printf ("] %3d%%", percentage);
143 }
144
145 gauge_report_to_terminal (g, percentage);
146 }
147 return MSG_HANDLED;
148
149 case MSG_DESTROY:
150 gauge_report_to_terminal (g, -1);
151 return widget_default_callback (w, sender, msg, parm, data);
152
153 default:
154 return widget_default_callback (w, sender, msg, parm, data);
155 }
156 }
157
158 /* --------------------------------------------------------------------------------------------- */
159 /*** public functions ****************************************************************************/
160 /* --------------------------------------------------------------------------------------------- */
161
162 WGauge *
163 gauge_new (int y, int x, int cols, gboolean shown, int max, int current,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
164 gboolean from_left_to_right, gboolean terminal_progress)
165 {
166 WRect r = { y, x, 1, cols };
167 WGauge *g;
168 Widget *w;
169
170 g = g_new (WGauge, 1);
171 w = WIDGET (g);
172 widget_init (w, &r, gauge_callback, NULL);
173
174 g->shown = shown;
175 if (max == 0)
176 max = 1; // I do not like division by zero :)
177 g->max = max;
178 g->current = current;
179 g->from_left_to_right = from_left_to_right;
180 g->terminal_progress = terminal_progress;
181 g->terminal_percentage = -1;
182
183 return g;
184 }
185
186 /* --------------------------------------------------------------------------------------------- */
187
188 void
189 gauge_set_value (WGauge *g, int max, int current)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
190 {
191 if (g->current == current && g->max == max)
192 return; // Do not flicker
193
194 if (max == 0)
195 max = 1; // I do not like division by zero :)
196 g->current = current;
197 g->max = max;
198 widget_draw (WIDGET (g));
199 }
200
201 /* --------------------------------------------------------------------------------------------- */
202
203 void
204 gauge_show (WGauge *g, gboolean shown)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
205 {
206 if (g->shown != shown)
207 {
208 g->shown = shown;
209 widget_draw (WIDGET (g));
210 }
211 }
212
213 /* --------------------------------------------------------------------------------------------- */