1 /*
2 	This file is part of Warzone 2100.
3 	Copyright (C) 1999-2004  Eidos Interactive
4 	Copyright (C) 2005-2020  Warzone 2100 Project
5 
6 	Warzone 2100 is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at your option) any later version.
10 
11 	Warzone 2100 is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with Warzone 2100; if not, write to the Free Software
18 	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 /** @file
21  *  Functions for the bar graph widget
22  */
23 
24 #include "widget.h"
25 #include "widgint.h"
26 #include "tip.h"
27 #include "form.h"
28 #include "bar.h"
29 #include "lib/ivis_opengl/pieblitfunc.h"
30 #include "lib/ivis_opengl/piepalette.h"
31 
W_BARINIT()32 W_BARINIT::W_BARINIT()
33 	: orientation(WBAR_LEFT)
34 	, size(0)
35 	, minorSize(0)
36 	, iRange(100)
37 	, denominator(1)
38 	, precision(0)
39 	  //sCol
40 	  //sMinorCol
41 {
42 	sCol.rgba = 0;
43 	sMinorCol.rgba = 0;
44 }
45 
W_BARGRAPH(W_BARINIT const * init)46 W_BARGRAPH::W_BARGRAPH(W_BARINIT const *init)
47 	: WIDGET(init, WIDG_BARGRAPH)
48 	, barPos(init->orientation)
49 	, majorSize(init->size)
50 	, minorSize(init->minorSize)
51 	, iRange(init->iRange)
52 	, iValue(0)
53 	, iOriginal(0)
54 	, denominator(MAX(init->denominator, 1))
55 	, precision(init->precision)
56 	, majorCol(init->sCol)
57 	, minorCol(init->sMinorCol)
58 	, textCol(WZCOL_BLACK)
59 	, pTip(init->pTip)
60 	, backgroundColour(WZCOL_FORM_BACKGROUND)
61 {
62 	/* Set the minor colour if necessary */
63 	// Actually, this sets the major colour to the minor colour. The minor colour used to be left completely uninitialised... Wonder what it was for..?
64 	if (style & WBAR_DOUBLE)
65 	{
66 		majorCol = minorCol;
67 	}
68 
69 	ASSERT((init->style & ~(WBAR_PLAIN | WBAR_TROUGH | WBAR_DOUBLE | WIDG_HIDDEN)) == 0, "Unknown bar graph style");
70 	ASSERT(init->orientation >= WBAR_LEFT || init->orientation <= WBAR_BOTTOM, "Unknown orientation");
71 	ASSERT(init->size <= WBAR_SCALE, "Bar size out of range");
72 	ASSERT((init->style & WBAR_DOUBLE) == 0 || init->minorSize <= WBAR_SCALE, "Minor bar size out of range");
73 }
74 
75 /* Set the current size of a bar graph */
widgSetBarSize(const std::shared_ptr<W_SCREEN> & psScreen,UDWORD id,UDWORD iValue)76 void widgSetBarSize(const std::shared_ptr<W_SCREEN> &psScreen, UDWORD id, UDWORD iValue)
77 {
78 	ASSERT_OR_RETURN(, psScreen != nullptr, "Invalid screen pointer");
79 	W_BARGRAPH *psBGraph = (W_BARGRAPH *)widgGetFromID(psScreen, id);
80 	ASSERT_OR_RETURN(, psBGraph != nullptr, "Could not find widget from ID");
81 	ASSERT_OR_RETURN(, psBGraph->type == WIDG_BARGRAPH, "Wrong widget type");
82 
83 	psBGraph->iOriginal = iValue;
84 	if (iValue < psBGraph->iRange)
85 	{
86 		psBGraph->iValue = iValue;
87 	}
88 	else
89 	{
90 		psBGraph->iValue = psBGraph->iRange;
91 	}
92 
93 	psBGraph->majorSize = WBAR_SCALE * psBGraph->iValue / MAX(psBGraph->iRange, 1);
94 	psBGraph->dirty = true;
95 }
96 
97 
98 /* Set the current size of a minor bar on a double graph */
widgSetMinorBarSize(const std::shared_ptr<W_SCREEN> & psScreen,UDWORD id,UDWORD iValue)99 void widgSetMinorBarSize(const std::shared_ptr<W_SCREEN> &psScreen, UDWORD id, UDWORD iValue)
100 {
101 	ASSERT_OR_RETURN(, psScreen != nullptr, "Invalid screen pointer");
102 	W_BARGRAPH *psBGraph = (W_BARGRAPH *)widgGetFromID(psScreen, id);
103 	ASSERT_OR_RETURN(, psBGraph != nullptr, "Could not find widget from ID");
104 	ASSERT_OR_RETURN(, psBGraph->type == WIDG_BARGRAPH, "Wrong widget type");
105 	psBGraph->minorSize = MIN(WBAR_SCALE * iValue / MAX(psBGraph->iRange, 1), WBAR_SCALE);
106 	psBGraph->dirty = true;
107 }
108 
109 
110 /* Respond to a mouse moving over a barGraph */
highlight(W_CONTEXT * psContext)111 void W_BARGRAPH::highlight(W_CONTEXT *psContext)
112 {
113 	if (!pTip.empty())
114 	{
115 		if (auto lockedScreen = screenPointer.lock())
116 		{
117 			tipStart(this, pTip, lockedScreen->TipFontID, x() + psContext->xOffset, y() + psContext->yOffset, width(), height());
118 		}
119 	}
120 }
121 
122 
123 /* Respond to the mouse moving off a barGraph */
highlightLost()124 void W_BARGRAPH::highlightLost()
125 {
126 	tipStop(this);
127 }
128 
129 
barGraphDisplayText(W_BARGRAPH * barGraph,int x0,int x1,int y1)130 static void barGraphDisplayText(W_BARGRAPH *barGraph, int x0, int x1, int y1)
131 {
132 	if (!barGraph->text.empty())
133 	{
134 		barGraph->wzCachedText.setText(barGraph->text, font_bar);
135 		int textWidth = barGraph->wzCachedText.width();
136 		Vector2i pos((x0 + x1 - textWidth) / 2, y1);
137 		// Add a shadow, to make it visible against any background.
138 		for (int dx = -2; dx <= 2; ++dx)
139 		{
140 			for (int dy = -2; dy <= 2; ++dy)
141 			{
142 				if (dx*dx + dy*dy <= 4)
143 				{
144 					barGraph->wzCachedText.render(pos.x + dx, pos.y + dy, WZCOL_BLACK);
145 				}
146 			}
147 		}
148 		barGraph->wzCachedText.render(pos.x, pos.y, barGraph->textCol);
149 	}
150 }
151 
152 /* The simple bar graph display function */
barGraphDisplay(WIDGET * psWidget,UDWORD xOffset,UDWORD yOffset)153 static void barGraphDisplay(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
154 {
155 	SDWORD		x0 = 0, y0 = 0, x1 = 0, y1 = 0;
156 	W_BARGRAPH	*psBGraph;
157 
158 	psBGraph = (W_BARGRAPH *)psWidget;
159 
160 	/* figure out which way the bar graph fills */
161 	switch (psBGraph->barPos)
162 	{
163 	case WBAR_LEFT:
164 		x0 = xOffset + psWidget->x();
165 		y0 = yOffset + psWidget->y();
166 		x1 = x0 + psWidget->width() * psBGraph->majorSize / WBAR_SCALE;
167 		y1 = y0 + psWidget->height();
168 		break;
169 	case WBAR_RIGHT:
170 		y0 = yOffset + psWidget->y();
171 		x1 = xOffset + psWidget->x() + psWidget->width();
172 		x0 = x1 - psWidget->width() * psBGraph->majorSize / WBAR_SCALE;
173 		y1 = y0 + psWidget->height();
174 		break;
175 	case WBAR_TOP:
176 		x0 = xOffset + psWidget->x();
177 		y0 = yOffset + psWidget->y();
178 		x1 = x0 + psWidget->width();
179 		y1 = y0 + psWidget->height() * psBGraph->majorSize / WBAR_SCALE;
180 		break;
181 	case WBAR_BOTTOM:
182 		x0 = xOffset + psWidget->x();
183 		x1 = x0 + psWidget->width();
184 		y1 = yOffset + psWidget->y() + psWidget->height();
185 		y0 = y1 - psWidget->height() * psBGraph->majorSize / WBAR_SCALE;
186 		break;
187 	}
188 
189 	/* Now draw the graph */
190 	iV_ShadowBox(x0, y0, x1, y1, 0, WZCOL_FORM_LIGHT, WZCOL_FORM_DARK, psBGraph->majorCol);
191 
192 	barGraphDisplayText(psBGraph, x0, x1, y1);
193 }
194 
195 
196 /* The double bar graph display function */
barGraphDisplayDouble(WIDGET * psWidget,UDWORD xOffset,UDWORD yOffset)197 static void barGraphDisplayDouble(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
198 {
199 	SDWORD		x0 = 0, y0 = 0, x1 = 0, y1 = 0, x2 = 0, y2 = 0, x3 = 0, y3 = 0;
200 	W_BARGRAPH	*psBGraph = (W_BARGRAPH *)psWidget;
201 
202 	/* figure out which way the bar graph fills */
203 	switch (psBGraph->barPos)
204 	{
205 	case WBAR_LEFT:
206 		/* Calculate the major bar */
207 		x0 = xOffset + psWidget->x();
208 		y0 = yOffset + psWidget->y();
209 		x1 = x0 + psWidget->width() * psBGraph->majorSize / WBAR_SCALE;
210 		y1 = y0 + 2 * psWidget->height() / 3;
211 
212 		/* Calculate the minor bar */
213 		x2 = x0;
214 		y2 = y0 + psWidget->height() / 3;
215 		x3 = x2 + psWidget->width() * psBGraph->minorSize / WBAR_SCALE;
216 		y3 = y0 + psWidget->height();
217 		break;
218 	case WBAR_RIGHT:
219 		/* Calculate the major bar */
220 		y0 = yOffset + psWidget->y();
221 		x1 = xOffset + psWidget->x() + psWidget->width();
222 		x0 = x1 - psWidget->width() * psBGraph->majorSize / WBAR_SCALE;
223 		y1 = y0 + 2 * psWidget->height() / 3;
224 
225 		/* Calculate the minor bar */
226 		x3 = x1;
227 		y2 = y0 + psWidget->height() / 3;
228 		x2 = x3 - psWidget->width() * psBGraph->minorSize / WBAR_SCALE;
229 		y3 = y0 + psWidget->height();
230 		break;
231 	case WBAR_TOP:
232 		/* Calculate the major bar */
233 		x0 = xOffset + psWidget->x();
234 		y0 = yOffset + psWidget->y();
235 		x1 = x0 + 2 * psWidget->width() / 3;
236 		y1 = y0 + psWidget->height() * psBGraph->majorSize / WBAR_SCALE;
237 
238 		/* Calculate the minor bar */
239 		x2 = x0 + psWidget->width() / 3;
240 		y2 = y0;
241 		x3 = x0 + psWidget->width();
242 		y3 = y2 + psWidget->height() * psBGraph->minorSize / WBAR_SCALE;
243 		break;
244 	case WBAR_BOTTOM:
245 		/* Calculate the major bar */
246 		x0 = xOffset + psWidget->x();
247 		x1 = x0 + 2 * psWidget->width() / 3;
248 		y1 = yOffset + psWidget->y() + psWidget->height();
249 		y0 = y1 - psWidget->height() * psBGraph->majorSize / WBAR_SCALE;
250 
251 		/* Calculate the minor bar */
252 		x2 = x0 + psWidget->width() / 3;
253 		x3 = x0 + psWidget->width();
254 		y3 = y1;
255 		y2 = y3 - psWidget->height() * psBGraph->minorSize / WBAR_SCALE;
256 		break;
257 	}
258 
259 	/* Draw the minor bar graph */
260 	if (psBGraph->minorSize > 0)
261 	{
262 		iV_ShadowBox(x2, y2, x3, y3, 0, WZCOL_FORM_LIGHT, WZCOL_FORM_DARK, psBGraph->minorCol);
263 	}
264 
265 	/* Draw the major bar graph */
266 	iV_ShadowBox(x0, y0, x1, y1, 0, WZCOL_FORM_LIGHT, WZCOL_FORM_DARK, psBGraph->majorCol);
267 
268 	barGraphDisplayText(psBGraph, x0, x1, y1);
269 }
270 
271 
272 /* The trough bar graph display function */
barGraphDisplayTrough(WIDGET * psWidget,UDWORD xOffset,UDWORD yOffset)273 void barGraphDisplayTrough(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
274 {
275 	SDWORD		x0 = 0, y0 = 0, x1 = 0, y1 = 0;		// Position of the bar
276 	SDWORD		tx0 = 0, ty0 = 0, tx1 = 0, ty1 = 0;	// Position of the trough
277 	bool		showBar = true, showTrough = true;
278 	W_BARGRAPH	*psBGraph = (W_BARGRAPH *)psWidget;
279 
280 	/* figure out which way the bar graph fills */
281 	switch (psBGraph->barPos)
282 	{
283 	case WBAR_LEFT:
284 		x0 = xOffset + psWidget->x();
285 		y0 = yOffset + psWidget->y();
286 		x1 = x0 + psWidget->width() * psBGraph->majorSize / WBAR_SCALE;
287 		y1 = y0 + psWidget->height();
288 		if (x0 == x1)
289 		{
290 			showBar = false;
291 		}
292 		tx0 = x1 + 1;
293 		ty0 = y0;
294 		tx1 = x0 + psWidget->width();
295 		ty1 = y1;
296 		if (tx0 >= tx1)
297 		{
298 			showTrough = false;
299 		}
300 		break;
301 	case WBAR_RIGHT:
302 		y0 = yOffset + psWidget->y();
303 		x1 = xOffset + psWidget->x() + psWidget->width();
304 		x0 = x1 - psWidget->width() * psBGraph->majorSize / WBAR_SCALE;
305 		y1 = y0 + psWidget->height();
306 		if (x0 == x1)
307 		{
308 			showBar = false;
309 		}
310 		tx0 = xOffset + psWidget->x();
311 		ty0 = y0;
312 		tx1 = x0 - 1;
313 		ty1 = y1;
314 		if (tx0 >= tx1)
315 		{
316 			showTrough = false;
317 		}
318 		break;
319 	case WBAR_TOP:
320 		x0 = xOffset + psWidget->x();
321 		y0 = yOffset + psWidget->y();
322 		x1 = x0 + psWidget->width();
323 		y1 = y0 + psWidget->height() * psBGraph->majorSize / WBAR_SCALE;
324 		if (y0 == y1)
325 		{
326 			showBar = false;
327 		}
328 		tx0 = x0;
329 		ty0 = y1 + 1;
330 		tx1 = x1;
331 		ty1 = y0 + psWidget->height();
332 		if (ty0 >= ty1)
333 		{
334 			showTrough = false;
335 		}
336 		break;
337 	case WBAR_BOTTOM:
338 		x0 = xOffset + psWidget->x();
339 		x1 = x0 + psWidget->width();
340 		y1 = yOffset + psWidget->y() + psWidget->height();
341 		y0 = y1 - psWidget->height() * psBGraph->majorSize / WBAR_SCALE;
342 		if (y0 == y1)
343 		{
344 			showBar = false;
345 		}
346 		tx0 = x0;
347 		ty0 = yOffset + psWidget->y();
348 		tx1 = x1;
349 		ty1 = y0 - 1;
350 		if (ty0 >= ty1)
351 		{
352 			showTrough = false;
353 		}
354 		break;
355 	}
356 
357 	/* Now draw the graph */
358 	if (showBar)
359 	{
360 		pie_BoxFill(x0, y0, x1, y1, psBGraph->majorCol);
361 	}
362 	if (showTrough)
363 	{
364 		iV_ShadowBox(tx0, ty0, tx1, ty1, 0, WZCOL_FORM_DARK, WZCOL_FORM_LIGHT, psBGraph->backgroundColour);
365 	}
366 
367 	barGraphDisplayText(psBGraph, x0, tx1, ty1);
368 }
369 
display(int xOffset,int yOffset)370 void W_BARGRAPH::display(int xOffset, int yOffset)
371 {
372 	if (style & WBAR_TROUGH)
373 	{
374 		barGraphDisplayTrough(this, xOffset, yOffset);
375 	}
376 	else if (style & WBAR_DOUBLE)
377 	{
378 		barGraphDisplayDouble(this, xOffset, yOffset);
379 	}
380 	else
381 	{
382 		barGraphDisplay(this, xOffset, yOffset);
383 	}
384 }
385 
setTip(std::string string)386 void W_BARGRAPH::setTip(std::string string)
387 {
388 	pTip = string;
389 }
390