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