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  *  Slide bar widget definitions.
22  */
23 
24 #include "widget.h"
25 #include "widgint.h"
26 #include "slider.h"
27 #include "lib/ivis_opengl/pieblitfunc.h"
28 
29 static bool DragEnabled = true;
30 
31 enum SliderState
32 {
33 	// Slider is being dragged
34 	SLD_DRAG = 1 << 0,
35 
36 	// Slider is hilited
37 	SLD_HILITE = 1 << 1,
38 
39 	// Slider is disabled
40 	SLD_DISABLED = 1 << 2
41 };
42 
sliderEnableDrag(bool Enable)43 void sliderEnableDrag(bool Enable)
44 {
45 	DragEnabled = Enable;
46 }
47 
W_SLDINIT()48 W_SLDINIT::W_SLDINIT()
49 	: orientation(WSLD_LEFT)
50 	, numStops(0)
51 	, barSize(0)
52 	, pos(0)
53 {}
54 
W_SLIDER(W_SLDINIT const * init)55 W_SLIDER::W_SLIDER(W_SLDINIT const *init)
56 	: WIDGET(init, WIDG_SLIDER)
57 	, orientation(init->orientation)
58 	, numStops(init->numStops)
59 	, barSize(init->barSize)
60 	, pos(init->pos)
61 	, state(0)
62 	, pTip(init->pTip)
63 {
64 	ASSERT((init->style & ~(WBAR_PLAIN | WIDG_HIDDEN)) == 0, "Unknown style");
65 	ASSERT(init->orientation >= WSLD_LEFT || init->orientation <= WSLD_BOTTOM, "Unknown orientation");
66 	bool horizontal = init->orientation == WSLD_LEFT || init->orientation == WSLD_RIGHT;
67 	bool vertical = init->orientation == WSLD_TOP || init->orientation == WSLD_BOTTOM;
68 	ASSERT((!horizontal || init->numStops <= init->width  - init->barSize) &&
69 	       (!vertical   || init->numStops <= init->height - init->barSize), "Too many stops for slider length");
70 	ASSERT(init->pos <= init->numStops, "Slider position greater than stops (%d/%d)", init->pos, init->numStops);
71 	ASSERT((!horizontal || init->barSize <= init->width) &&
72 	       (!vertical   || init->barSize <= init->height), "Slider bar is larger than slider width");
73 }
74 
75 /* Get the current position of a slider bar */
widgGetSliderPos(const std::shared_ptr<W_SCREEN> & psScreen,UDWORD id)76 UDWORD widgGetSliderPos(const std::shared_ptr<W_SCREEN> &psScreen, UDWORD id)
77 {
78 	ASSERT_OR_RETURN(0, psScreen != nullptr, "Invalid screen pointer");
79 	WIDGET	*psWidget;
80 
81 	psWidget = widgGetFromID(psScreen, id);
82 	ASSERT(psWidget != nullptr, "Could not find widget from id %u", id);
83 	if (psWidget)
84 	{
85 		return ((W_SLIDER *)psWidget)->pos;
86 	}
87 	return 0;
88 }
89 
90 /* Run a slider widget */
run(W_CONTEXT * psContext)91 void W_SLIDER::run(W_CONTEXT *psContext)
92 {
93 	if ((state & SLD_DRAG) && !mouseDown(MOUSE_LMB))
94 	{
95 		state &= ~SLD_DRAG;
96 		if (auto lockedScreen = screenPointer.lock())
97 		{
98 			lockedScreen->setReturn(shared_from_this());
99 		}
100 		dirty = true;
101 	}
102 	else if (!(state & SLD_DRAG) && mouseDown(MOUSE_LMB))
103 	{
104 		clicked(psContext, WKEY_NONE);
105 	}
106 	if (state & SLD_DRAG)
107 	{
108 		/* Figure out where the drag box should be */
109 		int mx = psContext->mx - x();
110 		int my = psContext->my - y();
111 		int stopSize;
112 		switch (orientation)
113 		{
114 		case WSLD_LEFT:
115 			if (mx <= barSize / 2)
116 			{
117 				pos = 0;
118 			}
119 			else if (mx >= width() - barSize / 2)
120 			{
121 				pos = numStops;
122 			}
123 			else
124 			{
125 				/* Mouse is in the middle of the slider, calculate which stop */
126 				stopSize = (width() - barSize) / numStops;
127 				pos = (mx + stopSize / 2 - barSize / 2)
128 				      * numStops
129 				      / (width() - barSize);
130 			}
131 			break;
132 		case WSLD_RIGHT:
133 			if (mx <= barSize / 2)
134 			{
135 				pos = numStops;
136 			}
137 			else if (mx >= width() - barSize / 2)
138 			{
139 				pos = 0;
140 			}
141 			else
142 			{
143 				/* Mouse is in the middle of the slider, calculate which stop */
144 				stopSize = (width() - barSize) / numStops;
145 				pos = numStops
146 				      - (mx + stopSize / 2 - barSize / 2)
147 				      * numStops
148 				      / (width() - barSize);
149 			}
150 			break;
151 		case WSLD_TOP:
152 			if (my <= barSize / 2)
153 			{
154 				pos = 0;
155 			}
156 			else if (my >= height() - barSize / 2)
157 			{
158 				pos = numStops;
159 			}
160 			else
161 			{
162 				/* Mouse is in the middle of the slider, calculate which stop */
163 				stopSize = (height() - barSize) / numStops;
164 				pos = (my + stopSize / 2 - barSize / 2)
165 				      * numStops
166 				      / (height() - barSize);
167 			}
168 			break;
169 		case WSLD_BOTTOM:
170 			if (my <= barSize / 2)
171 			{
172 				pos = numStops;
173 			}
174 			else if (my >= height() - barSize / 2)
175 			{
176 				pos = 0;
177 			}
178 			else
179 			{
180 				/* Mouse is in the middle of the slider, calculate which stop */
181 				stopSize = (height() - barSize) / numStops;
182 				pos = numStops
183 				      - (my + stopSize / 2 - barSize / 2)
184 				      * numStops
185 				      / (height() - barSize);
186 			}
187 			break;
188 		}
189 	}
190 }
191 
clicked(W_CONTEXT * psContext,WIDGET_KEY)192 void W_SLIDER::clicked(W_CONTEXT *psContext, WIDGET_KEY)
193 {
194 	if (isEnabled() && DragEnabled && geometry().contains(psContext->mx, psContext->my))
195 	{
196 		dirty = true;
197 		state |= SLD_DRAG;
198 	}
199 }
200 
highlight(W_CONTEXT *)201 void W_SLIDER::highlight(W_CONTEXT *)
202 {
203 	state |= SLD_HILITE;
204 	dirty = true;
205 }
206 
207 
208 /* Respond to the mouse moving off a slider */
highlightLost()209 void W_SLIDER::highlightLost()
210 {
211 	state &= ~SLD_HILITE;
212 	dirty = true;
213 }
214 
setTip(std::string string)215 void W_SLIDER::setTip(std::string string)
216 {
217 	pTip = string;
218 }
219 
display(int xOffset,int yOffset)220 void W_SLIDER::display(int xOffset, int yOffset)
221 {
222 	ASSERT(false, "No default implementation exists for sliders");
223 }
224 
enable()225 void W_SLIDER::enable()
226 {
227 	state &= ~SLD_DISABLED;
228 }
229 
disable()230 void W_SLIDER::disable()
231 {
232 	state |= SLD_DISABLED;
233 }
234 
isHighlighted() const235 bool W_SLIDER::isHighlighted() const
236 {
237 	return (state & SLD_HILITE) != 0;
238 }
239 
isEnabled() const240 bool W_SLIDER::isEnabled() const
241 {
242 	return (state & SLD_DISABLED) == 0;
243 }
244