1 // Copyright (C) 2006-2012 Michael Zeilfelder
2 // This file uses the licence of the Irrlicht Engine.
3 
4 #include "CGUISpinBox.h"
5 #ifdef _IRR_COMPILE_WITH_GUI_
6 
7 #include "CGUIEditBox.h"
8 #include "CGUIButton.h"
9 #include "IGUIEnvironment.h"
10 #include "IEventReceiver.h"
11 #include "fast_atof.h"
12 #include <wchar.h>
13 
14 
15 namespace irr
16 {
17 namespace gui
18 {
19 
20 //! constructor
CGUISpinBox(const wchar_t * text,bool border,IGUIEnvironment * environment,IGUIElement * parent,s32 id,const core::rect<s32> & rectangle)21 CGUISpinBox::CGUISpinBox(const wchar_t* text, bool border,IGUIEnvironment* environment,
22 			IGUIElement* parent, s32 id, const core::rect<s32>& rectangle)
23 : IGUISpinBox(environment, parent, id, rectangle),
24 	EditBox(0), ButtonSpinUp(0), ButtonSpinDown(0), StepSize(1.f),
25 	RangeMin(-FLT_MAX), RangeMax(FLT_MAX), FormatString(L"%f"),
26 	DecimalPlaces(-1)
27 {
28 	#ifdef _DEBUG
29 	setDebugName("CGUISpinBox");
30 	#endif
31 
32 	CurrentIconColor = video::SColor(255,255,255,255);
33 	s32 ButtonWidth = 16;
34 
35 	ButtonSpinDown = Environment->addButton(
36 		core::rect<s32>(rectangle.getWidth() - ButtonWidth, rectangle.getHeight()/2 +1,
37 						rectangle.getWidth(), rectangle.getHeight()), this);
38 	ButtonSpinDown->grab();
39 	ButtonSpinDown->setSubElement(true);
40 	ButtonSpinDown->setTabStop(false);
41 	ButtonSpinDown->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_CENTER, EGUIA_LOWERRIGHT);
42 
43 	ButtonSpinUp = Environment->addButton(
44 		core::rect<s32>(rectangle.getWidth() - ButtonWidth, 0,
45 						rectangle.getWidth(), rectangle.getHeight()/2), this);
46 	ButtonSpinUp->grab();
47 	ButtonSpinUp->setSubElement(true);
48 	ButtonSpinUp->setTabStop(false);
49 	ButtonSpinUp->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_CENTER);
50 
51 	const core::rect<s32> rectEdit(0, 0, rectangle.getWidth() - ButtonWidth - 1, rectangle.getHeight());
52 	EditBox = Environment->addEditBox(text, rectEdit, border, this, -1);
53 	EditBox->grab();
54 	EditBox->setSubElement(true);
55 	EditBox->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
56 
57 	refreshSprites();
58 }
59 
60 
61 //! destructor
~CGUISpinBox()62 CGUISpinBox::~CGUISpinBox()
63 {
64 	if (ButtonSpinUp)
65 		ButtonSpinUp->drop();
66 	if (ButtonSpinDown)
67 		ButtonSpinDown->drop();
68 	if (EditBox)
69 		EditBox->drop();
70 }
71 
refreshSprites()72 void CGUISpinBox::refreshSprites()
73 {
74 	IGUISpriteBank *sb = 0;
75 	if (Environment && Environment->getSkin())
76 	{
77 		sb = Environment->getSkin()->getSpriteBank();
78 	}
79 
80 	if (sb)
81 	{
82 		IGUISkin * skin = Environment->getSkin();
83 		CurrentIconColor = skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);
84 		ButtonSpinDown->setSpriteBank(sb);
85 		ButtonSpinDown->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_SMALL_CURSOR_DOWN), CurrentIconColor);
86 		ButtonSpinDown->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_SMALL_CURSOR_DOWN), CurrentIconColor);
87 		ButtonSpinUp->setSpriteBank(sb);
88 		ButtonSpinUp->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_SMALL_CURSOR_UP), CurrentIconColor);
89 		ButtonSpinUp->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_SMALL_CURSOR_UP), CurrentIconColor);
90 	}
91 	else
92 	{
93 		ButtonSpinDown->setText(L"-");
94 		ButtonSpinUp->setText(L"+");
95 	}
96 }
97 
getEditBox() const98 IGUIEditBox* CGUISpinBox::getEditBox() const
99 {
100 	return EditBox;
101 }
102 
103 
setValue(f32 val)104 void CGUISpinBox::setValue(f32 val)
105 {
106 	wchar_t str[100];
107 
108 	swprintf(str, 99, FormatString.c_str(), val);
109 	EditBox->setText(str);
110 	verifyValueRange();
111 }
112 
113 
getValue() const114 f32 CGUISpinBox::getValue() const
115 {
116 	const wchar_t* val = EditBox->getText();
117 	if ( !val )
118 		return 0.f;
119 	core::stringc tmp(val);
120 	return core::fast_atof(tmp.c_str());
121 }
122 
123 
setRange(f32 min,f32 max)124 void CGUISpinBox::setRange(f32 min, f32 max)
125 {
126 	if (max<min)
127 		core::swap(min, max);
128 	RangeMin = min;
129 	RangeMax = max;
130 
131 	// we have to round the range - otherwise we can get into an infinte setValue/verifyValueRange cycle.
132 	wchar_t str[100];
133 	swprintf(str, 99, FormatString.c_str(), RangeMin);
134 	RangeMin = core::fast_atof(core::stringc(str).c_str());
135 	swprintf(str, 99, FormatString.c_str(), RangeMax);
136 	RangeMax = core::fast_atof(core::stringc(str).c_str());
137 
138 	verifyValueRange();
139 }
140 
141 
getMin() const142 f32 CGUISpinBox::getMin() const
143 {
144 	return RangeMin;
145 }
146 
147 
getMax() const148 f32 CGUISpinBox::getMax() const
149 {
150 	return RangeMax;
151 }
152 
153 
getStepSize() const154 f32 CGUISpinBox::getStepSize() const
155 {
156 	return StepSize;
157 }
158 
159 
setStepSize(f32 step)160 void CGUISpinBox::setStepSize(f32 step)
161 {
162 	StepSize = step;
163 }
164 
165 
166 //! Sets the number of decimal places to display.
setDecimalPlaces(s32 places)167 void CGUISpinBox::setDecimalPlaces(s32 places)
168 {
169 	DecimalPlaces = places;
170 	if (places == -1)
171 		FormatString = "%f";
172 	else
173 	{
174 		FormatString = "%.";
175 		FormatString += places;
176 		FormatString += "f";
177 	}
178 	setRange( RangeMin, RangeMax );
179 	setValue(getValue());
180 }
181 
182 
OnEvent(const SEvent & event)183 bool CGUISpinBox::OnEvent(const SEvent& event)
184 {
185 	if (IsEnabled)
186 	{
187 		bool changeEvent = false;
188 		switch(event.EventType)
189 		{
190 		case EET_MOUSE_INPUT_EVENT:
191 			switch(event.MouseInput.Event)
192 			{
193 			case EMIE_MOUSE_WHEEL:
194 				{
195 					f32 val = getValue() + (StepSize * (event.MouseInput.Wheel < 0 ? -1.f : 1.f));
196 					setValue(val);
197 					changeEvent = true;
198 				}
199 				break;
200 			default:
201 				break;
202 			}
203 			break;
204 
205 		case EET_GUI_EVENT:
206 			if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED)
207 			{
208 				if (event.GUIEvent.Caller == ButtonSpinUp)
209 				{
210 					f32 val = getValue();
211 					val += StepSize;
212 					setValue(val);
213 					changeEvent = true;
214 				}
215 				else if ( event.GUIEvent.Caller == ButtonSpinDown)
216 				{
217 					f32 val = getValue();
218 					val -= StepSize;
219 					setValue(val);
220 					changeEvent = true;
221 				}
222 			}
223 			if (event.GUIEvent.EventType == EGET_EDITBOX_CHANGED || event.GUIEvent.EventType == EGET_EDITBOX_ENTER)
224 			{
225 				if (event.GUIEvent.Caller == EditBox)
226 				{
227 					verifyValueRange();
228 					changeEvent = true;
229 				}
230 			}
231 			break;
232 		default:
233 		break;
234 		}
235 
236 		if ( changeEvent )
237 		{
238 			SEvent e;
239 			e.EventType = EET_GUI_EVENT;
240 			e.GUIEvent.Caller = this;
241 			e.GUIEvent.Element = 0;
242 
243 			e.GUIEvent.EventType = EGET_SPINBOX_CHANGED;
244 			if ( Parent )
245 				Parent->OnEvent(e);
246 			return true;
247 		}
248 	}
249 
250 	return IGUIElement::OnEvent(event);
251 }
252 
253 
draw()254 void CGUISpinBox::draw()
255 {
256 	if ( !isVisible() )
257 		return;
258 
259 	IGUISkin* skin = Environment->getSkin();
260 	if (!skin)
261 		return;
262 
263 	video::SColor iconColor = skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);
264 	if ( iconColor != CurrentIconColor )
265 	{
266 		refreshSprites();
267 	}
268 
269 	IGUISpinBox::draw();
270 }
271 
verifyValueRange()272 void CGUISpinBox::verifyValueRange()
273 {
274 	f32 val = getValue();
275 	if ( val+core::ROUNDING_ERROR_f32 < RangeMin )
276 		val = RangeMin;
277 	else if ( val-core::ROUNDING_ERROR_f32 > RangeMax )
278 		val = RangeMax;
279 	else
280 		return;
281 
282 	setValue(val);
283 }
284 
285 
286 //! Sets the new caption of the element
setText(const wchar_t * text)287 void CGUISpinBox::setText(const wchar_t* text)
288 {
289 	EditBox->setText(text);
290 	setValue(getValue());
291 	verifyValueRange();
292 }
293 
294 
295 //! Returns caption of this element.
getText() const296 const wchar_t* CGUISpinBox::getText() const
297 {
298 	return EditBox->getText();
299 }
300 
301 
302 //! Writes attributes of the element.
serializeAttributes(io::IAttributes * out,io::SAttributeReadWriteOptions * options) const303 void CGUISpinBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
304 {
305 	IGUIElement::serializeAttributes(out, options);
306 	out->addFloat("Min", getMin());
307 	out->addFloat("Max", getMax());
308 	out->addFloat("Step", getStepSize());
309 	out->addInt("DecimalPlaces", DecimalPlaces);
310 }
311 
312 
313 //! Reads attributes of the element
deserializeAttributes(io::IAttributes * in,io::SAttributeReadWriteOptions * options)314 void CGUISpinBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
315 {
316 	IGUIElement::deserializeAttributes(in, options);
317 	setRange(in->getAttributeAsFloat("Min"), in->getAttributeAsFloat("Max"));
318 	setStepSize(in->getAttributeAsFloat("Step"));
319 	setDecimalPlaces(in->getAttributeAsInt("DecimalPlaces"));
320 }
321 
322 
323 } // end namespace gui
324 } // end namespace irr
325 
326 #endif // _IRR_COMPILE_WITH_GUI_
327 
328