1 /*=============================================================================
2 Blobby Volley 2
3 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de)
4 Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de)
5 
6 This program 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 This program 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 this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 =============================================================================*/
20 
21 /* header include */
22 #include "IMGUI.h"
23 
24 /* includes */
25 #include <queue>
26 #include <cassert>
27 
28 #include <SDL2/SDL.h>
29 
30 #include "InputManager.h"
31 
32 /* implementation */
33 
34 enum ObjectType
35 {
36 	IMAGE,
37 	OVERLAY,
38 	TEXT,
39 	BUTTON, // Unused!
40 	SCROLLBAR,
41 	ACTIVESCROLLBAR,
42 	EDITBOX,
43 	ACTIVEEDITBOX,
44 	SELECTBOX,
45 	ACTIVESELECTBOX,
46 	BLOB,
47 	CHAT
48 };
49 
50 struct QueueObject
51 {
52 	ObjectType type;
53 	int id;
54 	Vector2 pos1;
55 	Vector2 pos2;
56 	Color col;
57 	float alpha;
58 	std::string text;
59 	std::vector<std::string> entries;
60 	int selected;
61 	int length;
62 	unsigned int flags;
63 };
64 
65 typedef std::queue<QueueObject> RenderQueue;
66 
67 IMGUI* IMGUI::mSingleton = 0;
68 RenderQueue *mQueue;
69 
IMGUI()70 IMGUI::IMGUI()
71 {
72 	mQueue = new RenderQueue;
73 	mActiveButton = -1;
74 	mHeldWidget = 0;
75 	mLastKeyAction = NONE;
76 	mLastWidget = 0;
77 	mButtonReset = false;
78 	mInactive = false;
79 }
80 
~IMGUI()81 IMGUI::~IMGUI()
82 {
83 	delete mQueue;
84 }
85 
getSingleton()86 IMGUI& IMGUI::getSingleton()
87 {
88 	if (!mSingleton)
89 		mSingleton = new IMGUI;
90 	return *mSingleton;
91 }
92 
begin()93 void IMGUI::begin()
94 {
95 	mUsingCursor = false;
96 	mButtonReset = false;
97 
98 	while (!mQueue->empty())
99 		mQueue->pop();
100 
101 
102 	mLastKeyAction = NONE;
103 
104 	if (InputManager::getSingleton()->up())
105 		mLastKeyAction = UP;
106 
107 	if (InputManager::getSingleton()->down())
108 		mLastKeyAction = DOWN;
109 
110 	if (InputManager::getSingleton()->left())
111 		mLastKeyAction = LEFT;
112 
113 	if (InputManager::getSingleton()->right())
114 		mLastKeyAction = RIGHT;
115 
116 	if (InputManager::getSingleton()->select())
117 		mLastKeyAction = SELECT;
118 
119 	if (InputManager::getSingleton()->exit())
120 		mLastKeyAction = BACK;
121 }
122 
end()123 void IMGUI::end()
124 {
125 	int FontSize;
126 	RenderManager& rmanager = RenderManager::getSingleton();
127 
128 	while (!mQueue->empty())
129 	{
130 		QueueObject& obj = mQueue->front();
131 		switch (obj.type)
132 		{
133 			case IMAGE:
134 				rmanager.drawImage(obj.text, obj.pos1, obj.pos2);
135 				break;
136 
137 			case OVERLAY:
138 				rmanager.drawOverlay(obj.alpha, obj.pos1, obj.pos2, obj.col);
139 				break;
140 
141 			case TEXT:
142 				rmanager.drawText(obj.text, obj.pos1, obj.flags);
143 				break;
144 
145 			case SCROLLBAR:
146 				rmanager.drawOverlay(0.5, obj.pos1, obj.pos1 + Vector2(210.0, 26.0));
147 				rmanager.drawImage("gfx/scrollbar.bmp",obj.pos1 + Vector2(obj.pos2.x * 200.0 + 5 , 13));
148 				break;
149 
150 			case ACTIVESCROLLBAR:
151 				rmanager.drawOverlay(0.4, obj.pos1, obj.pos1 + Vector2(210.0, 26.0));
152 				rmanager.drawImage("gfx/scrollbar.bmp",obj.pos1 + Vector2(obj.pos2.x * 200.0 + 5 , 13));
153 				break;
154 
155 			case EDITBOX:
156 				FontSize = (obj.flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL);
157 				rmanager.drawOverlay(0.5, obj.pos1, obj.pos1 + Vector2(10+obj.length*FontSize, 10+FontSize));
158 				rmanager.drawText(obj.text, obj.pos1+Vector2(5, 5), obj.flags);
159 				break;
160 
161 			case ACTIVEEDITBOX:
162 				FontSize = (obj.flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL);
163 				rmanager.drawOverlay(0.3, obj.pos1, obj.pos1 + Vector2(10+obj.length*FontSize, 10+FontSize));
164 				rmanager.drawText(obj.text, obj.pos1+Vector2(5, 5), obj.flags);
165 				if (obj.pos2.x >= 0)
166 					rmanager.drawOverlay(1.0, Vector2((obj.pos2.x)*FontSize+obj.pos1.x+5, obj.pos1.y+5), Vector2((obj.pos2.x)*FontSize+obj.pos1.x+5+3, obj.pos1.y+5+FontSize), Color(255,255,255));
167 				break;
168 
169 			case SELECTBOX:
170 				FontSize = (obj.flags & TF_SMALL_FONT ? (FONT_WIDTH_SMALL+LINE_SPACER_SMALL) : (FONT_WIDTH_NORMAL+LINE_SPACER_NORMAL));
171 				rmanager.drawOverlay(0.5, obj.pos1, obj.pos2);
172 				for (unsigned int c = 0; c < obj.entries.size(); c++)
173 				{
174 					if( c == static_cast<unsigned int>(obj.selected) )
175 						rmanager.drawText(obj.entries[c], Vector2(obj.pos1.x+5, obj.pos1.y+(c*FontSize)+5), obj.flags | TF_HIGHLIGHT);
176 					else
177 						rmanager.drawText(obj.entries[c], Vector2(obj.pos1.x+5, obj.pos1.y+(c*FontSize)+5), obj.flags);
178 				}
179 				break;
180 
181 			case ACTIVESELECTBOX:
182 				FontSize = (obj.flags & TF_SMALL_FONT ? (FONT_WIDTH_SMALL+LINE_SPACER_SMALL) : (FONT_WIDTH_NORMAL+LINE_SPACER_NORMAL));
183 				rmanager.drawOverlay(0.3, obj.pos1, obj.pos2);
184 				for (unsigned int c = 0; c < obj.entries.size(); c++)
185 				{
186 					if( c == static_cast<unsigned int>(obj.selected) )
187 						rmanager.drawText(obj.entries[c], Vector2(obj.pos1.x+5, obj.pos1.y+(c*FontSize)+5), obj.flags | TF_HIGHLIGHT);
188 					else
189 						rmanager.drawText(obj.entries[c], Vector2(obj.pos1.x+5, obj.pos1.y+(c*FontSize)+5), obj.flags);
190 				}
191 				break;
192 
193 			case CHAT:
194 				FontSize = (obj.flags & TF_SMALL_FONT ? (FONT_WIDTH_SMALL+LINE_SPACER_SMALL) : (FONT_WIDTH_NORMAL+LINE_SPACER_NORMAL));
195 				rmanager.drawOverlay(0.5, obj.pos1, obj.pos2);
196 				for (unsigned int c = 0; c < obj.entries.size(); c++)
197 				{
198 					if (obj.text[c] == 'R' )
199 						rmanager.drawText(obj.entries[c], Vector2(obj.pos1.x+5, obj.pos1.y+(c*FontSize)+5), obj.flags | TF_HIGHLIGHT);
200 					else
201 						rmanager.drawText(obj.entries[c], Vector2(obj.pos1.x+5, obj.pos1.y+(c*FontSize)+5), obj.flags);
202 				}
203 				break;
204 
205 			case BLOB:
206 				rmanager.drawBlob(obj.pos1, obj.col);
207 				break;
208 
209 			default:
210 				break;
211 		}
212 		mQueue->pop();
213 	}
214 #if __DESKTOP__
215 	if (mDrawCursor)
216 	{
217 		rmanager.drawImage("gfx/cursor.bmp", InputManager::getSingleton()->position() + Vector2(24.0, 24.0));
218 		mDrawCursor = false;
219 	}
220 #endif
221 }
222 
doImage(int id,const Vector2 & position,const std::string & name,const Vector2 & size)223 void IMGUI::doImage(int id, const Vector2& position, const std::string& name, const Vector2& size)
224 {
225 	QueueObject obj;
226 	obj.type = IMAGE;
227 	obj.id = id;
228 	obj.pos1 = position;
229 	obj.pos2 = size;
230 	obj.text = name;
231 	mQueue->push(obj);
232 }
233 
doText(int id,const Vector2 & position,const std::string & text,unsigned int flags)234 void IMGUI::doText(int id, const Vector2& position, const std::string& text, unsigned int flags)
235 {
236 	QueueObject obj;
237 	obj.type = TEXT;
238 	obj.id = id;
239 	obj.pos1 = position;
240 
241 	int fontSize = flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL;
242 	// update position depending on alignment
243 	if( flags & TF_ALIGN_CENTER )
244 	{
245 		obj.pos1.x -= text.size() * fontSize / 2;
246 	}
247 
248 	if( flags & TF_ALIGN_RIGHT )
249 	{
250 		obj.pos1.x -= text.size() * fontSize;
251 	}
252 
253 
254 	obj.text = text;
255 	obj.flags = flags;
256 	mQueue->push(obj);
257 }
258 
doText(int id,const Vector2 & position,TextManager::STRING text,unsigned int flags)259 void IMGUI::doText(int id, const Vector2& position, TextManager::STRING text, unsigned int flags)
260 {
261 	doText(id, position, TextManager::getSingleton()->getString(text), flags);
262 }
263 
doOverlay(int id,const Vector2 & pos1,const Vector2 & pos2,const Color & col,float alpha)264 void IMGUI::doOverlay(int id, const Vector2& pos1, const Vector2& pos2, const Color& col, float alpha)
265 {
266 	QueueObject obj;
267 	obj.type = OVERLAY;
268 	obj.id = id;
269 	obj.pos1 = pos1;
270 	obj.pos2 = pos2;
271 	obj.col = col;
272 	obj.alpha = alpha;
273 	mQueue->push(obj);
274 	RenderManager::getSingleton().redraw();
275 }
276 
doButton(int id,const Vector2 & position,TextManager::STRING text,unsigned int flags)277 bool IMGUI::doButton(int id, const Vector2& position, TextManager::STRING text, unsigned int flags)
278 {
279 	return doButton(id, position, TextManager::getSingleton()->getString(text), flags);
280 }
281 
doButton(int id,const Vector2 & position,const std::string & text,unsigned int flags)282 bool IMGUI::doButton(int id, const Vector2& position, const std::string& text, unsigned int flags)
283 {
284 	bool clicked = false;
285 	QueueObject obj;
286 	obj.id = id;
287 	obj.pos1 = position;
288 	obj.text = text;
289 	obj.type = TEXT;
290 	obj.flags = flags;
291 
292 	int fontSize = flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL;
293 	// update position depending on alignment
294 	if( flags & TF_ALIGN_CENTER )
295 	{
296 		obj.pos1.x -= text.size() * fontSize / 2;
297 	}
298 
299 	if( flags & TF_ALIGN_RIGHT )
300 	{
301 		obj.pos1.x -= text.size() * fontSize;
302 	}
303 
304 	if (!mInactive)
305 	{
306 		// M.W. : Activate cursorless object-highlighting once the up or down key is pressed.
307 		if (mActiveButton == -1)
308 		{
309 			switch (mLastKeyAction)
310 			{
311 				case DOWN:
312 					mActiveButton = 0;
313 					mLastKeyAction = NONE;
314 					break;
315 
316 				case UP:
317 					mActiveButton = mLastWidget;
318 					mLastKeyAction = NONE;
319 					break;
320 
321 				default:
322 					break;
323 			}
324 		}
325 
326 		// Highlight first menu object for arrow key navigation.
327 		if (mActiveButton == 0 && !mButtonReset)
328 			mActiveButton = id;
329 
330 		// React to keyboard input.
331 		if (id == mActiveButton)
332 		{
333 			obj.flags = obj.flags | TF_HIGHLIGHT;
334 			switch (mLastKeyAction)
335 			{
336 				case DOWN:
337 					mActiveButton = 0;
338 					mLastKeyAction = NONE;
339 					break;
340 
341 				case UP:
342 					mActiveButton = mLastWidget;
343 					mLastKeyAction = NONE;
344 					break;
345 
346 				case SELECT:
347 					clicked = true;
348 					mLastKeyAction = NONE;
349 					break;
350 				default:
351 					break;
352 			}
353 		}
354 
355 		// React to back button
356 		if (mLastKeyAction == BACK)
357 		{
358 			if ((text == (TextManager::getSingleton())->getString(TextManager::LBL_CANCEL)) ||
359 			    (text == (TextManager::getSingleton())->getString(TextManager::MNU_LABEL_EXIT)))
360 			{
361 				//todo: Workarround to catch backkey
362 				clicked = true;
363 				mActiveButton = id;
364 			}
365 		}
366 
367 		#if __MOBILE__
368 			const int tolerance = 3;
369 		#else
370 			const int tolerance = 0;
371 		#endif
372 		// React to mouse input.
373 		Vector2 mousepos = InputManager::getSingleton()->position();
374 		if (mousepos.x + tolerance >= position.x &&
375 			mousepos.y + tolerance * 2 >= position.y &&
376 			mousepos.x - tolerance <= position.x + text.length() * (flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL) &&
377 			mousepos.y - tolerance * 2 <= position.y + (flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL))
378 		{
379 			obj.flags = obj.flags
380 			#if __DESKTOP__
381 				| TF_HIGHLIGHT
382 			#endif
383 			;
384 			if (InputManager::getSingleton()->click())
385 			{
386 				clicked = true;
387 				mActiveButton = id;
388 			}
389 		}
390 	}
391 
392 	mLastWidget = id;
393 	mQueue->push(obj);
394 	return clicked;
395 }
396 
doImageButton(int id,const Vector2 & position,const Vector2 & size,const std::string & image)397 bool IMGUI::doImageButton(int id, const Vector2& position, const Vector2& size, const std::string& image)
398 {
399 	doImage(id, position, image);
400 
401 	// React to mouse input.
402 	if (InputManager::getSingleton()->click())
403 	{
404 		Vector2 mousepos = InputManager::getSingleton()->position();
405 		Vector2 btnpos = position - size * 0.5;
406 		if (mousepos.x > btnpos.x && mousepos.y > btnpos.y &&
407 				mousepos.x < btnpos.x + size.x &&	mousepos.y < btnpos.y + size.y)
408 		{
409 			return true;
410 		}
411 	}
412 
413 	return false;
414 }
415 
doScrollbar(int id,const Vector2 & position,float & value)416 bool IMGUI::doScrollbar(int id, const Vector2& position, float& value)
417 {
418 	QueueObject obj;
419 	obj.id = id;
420 	obj.pos1 = position;
421 	obj.type = SCROLLBAR;
422 
423 	bool deselected = false;
424 
425 	if (InputManager::getSingleton()->unclick())
426 	{
427 		if (id == mHeldWidget)
428 			deselected = true;
429 
430 		mHeldWidget = 0;
431 	}
432 
433 	if (!mInactive)
434 	{
435 		// M.W. : Activate cursorless object-highlighting once the up or down key is pressed.
436 		if (mActiveButton == -1)
437 		{
438 			switch (mLastKeyAction)
439 			{
440 				case DOWN:
441 					mActiveButton = 0;
442 					mLastKeyAction = NONE;
443 					break;
444 
445 				case UP:
446 					mActiveButton = mLastWidget;
447 					mLastKeyAction = NONE;
448 					break;
449 
450 				default:
451 					break;
452 			}
453 		}
454 
455 		// Highlight first menu object for arrow key navigation.
456 		if (mActiveButton == 0 && !mButtonReset)
457 			mActiveButton = id;
458 
459 		// React to keyboard input.
460 		if (id == mActiveButton)
461 		{
462 			obj.type = ACTIVESCROLLBAR;
463 			switch (mLastKeyAction)
464 			{
465 				case DOWN:
466 					mActiveButton = 0;
467 					mLastKeyAction = NONE;
468 					break;
469 
470 				case UP:
471 					mActiveButton = mLastWidget;
472 					mLastKeyAction = NONE;
473 					break;
474 
475 				case LEFT:
476 					value -= 0.1;
477 					mLastKeyAction = NONE;
478 					break;
479 
480 				case RIGHT:
481 					value += 0.1;
482 					mLastKeyAction = NONE;
483 					break;
484 
485 				default:
486 					break;
487 			}
488 		}
489 
490 		#if __MOBILE__
491 			const int tolerance = 3;
492 		#else
493 			const int tolerance = 0;
494 		#endif
495 
496 		// React to mouse input.
497 		Vector2 mousepos = InputManager::getSingleton()->position();
498 		if (mousepos.x + 5 > position.x &&
499 			mousepos.y + tolerance * 2 > position.y &&
500 			mousepos.x < position.x + 205 &&
501 			mousepos.y - tolerance < position.y + 24.0)
502 		{
503 			obj.type = ACTIVESCROLLBAR;
504 
505 			if (InputManager::getSingleton()->click())
506 			{
507 				mHeldWidget = id;
508 			}
509 
510 			if (mHeldWidget == id)
511 			{
512 				value = (mousepos.x - position.x) / 200.0;
513 				mActiveButton = id;
514 			}
515 
516 			if(InputManager::getSingleton()->mouseWheelUp())
517 				value += 0.1;
518 
519 			if(InputManager::getSingleton()->mouseWheelDown())
520 				value -= 0.1;
521 		}
522 	}
523 
524 	value = value > 0.0 ? (value < 1.0 ? value : 1.0) : 0.0;
525 	obj.pos2.x = value;
526 
527 	mLastWidget = id;
528 	mQueue->push(obj);
529 
530 	return deselected;
531 }
532 
resetSelection()533 void IMGUI::resetSelection()
534 {
535 	mInactive = false;
536 	mActiveButton = -1;
537 	mButtonReset = true;
538 }
539 
doEditbox(int id,const Vector2 & position,unsigned int length,std::string & text,unsigned & cpos,unsigned int flags,bool force_active)540 bool IMGUI::doEditbox(int id, const Vector2& position, unsigned int length, std::string& text, unsigned& cpos, unsigned int flags, bool force_active)
541 {
542 	int FontSize = (flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL);
543 	bool changed = false;
544 	QueueObject obj;
545 	obj.id = id;
546 	obj.pos1 = position;
547 	obj.type = EDITBOX;
548 	obj.length = length; // lenght does not actually work!
549 	obj.flags = flags;
550 
551 	// React to mouse input.
552 	Vector2 mousepos = InputManager::getSingleton()->position();
553 	if (mousepos.x > position.x &&
554 		mousepos.y > position.y &&
555 		mousepos.x < position.x + length * FontSize + 10 &&
556 		mousepos.y < position.y + FontSize + 10)
557 	{
558 		obj.flags = obj.flags | TF_HIGHLIGHT;
559 		if (InputManager::getSingleton()->click())
560 		{
561 			// Handle click on the text.
562 			if (mousepos.x < position.x + text.length() * FontSize)
563 				cpos = (int) ((mousepos.x-position.x-5+(FontSize/2)) / FontSize);
564 			// Handle click behind the text.
565 			else if (mousepos.x < position.x + length * FontSize + 10)
566 				cpos = (int) text.length();
567 
568 			mActiveButton = id;
569 
570 			// Show keyboard
571 			SDL_StartTextInput();
572 		}
573 	}
574 
575 	if (!mInactive)
576 	{
577 		// M.W. : Activate cursorless object-highlighting once the up or down key is pressed.
578 		if (mActiveButton == -1)
579 		{
580 			switch (mLastKeyAction)
581 			{
582 				case DOWN:
583 					mActiveButton = 0;
584 					mLastKeyAction = NONE;
585 					break;
586 
587 				case UP:
588 					mActiveButton = mLastWidget;
589 					mLastKeyAction = NONE;
590 					break;
591 
592 				default:
593 					break;
594 			}
595 
596 			// M.W. : Initialize the cursor position at the end of the string.
597 			// IMPORTANT: If you make changes to EditBox text that alter the length
598 			//				of the text, either call resetSelection() to come back
599 			//				to this area of code or update cpos manually to prevent
600 			//				crashes due to a misplaced cursor.
601 			cpos = text.length();
602 		}
603 
604 		// Highlight first menu object for arrow key navigation.
605 		if (mActiveButton == 0 && !mButtonReset)
606 			mActiveButton = id;
607 
608 		// React to keyboard input.
609 		if (id == mActiveButton || force_active)
610 		{
611 			obj.type = ACTIVEEDITBOX;
612 			switch (mLastKeyAction)
613 			{
614 				case DOWN:
615 					mActiveButton = 0;
616 					mLastKeyAction = NONE;
617 					break;
618 
619 				case UP:
620 					mActiveButton = mLastWidget;
621 					mLastKeyAction = NONE;
622 					break;
623 
624 				case LEFT:
625 					if (cpos > 0)
626 						cpos--;
627 					mLastKeyAction = NONE;
628 					break;
629 
630 				case RIGHT:
631 					if (cpos < text.length())
632 						cpos++;
633 					mLastKeyAction = NONE;
634 					break;
635 
636 				default:
637 					break;
638 			}
639 			std::string input = InputManager::getSingleton()->getLastTextKey();
640 			if (input == "backspace" && text.length() > 0 && cpos > 0)
641 			{
642 				text.erase(cpos - 1, 1);
643 				cpos--;
644 			}
645 			 else if (input == "del" && text.length() > cpos)
646 			{
647 				text.erase(cpos, 1);
648 			}
649 			 else if (input == "return")
650 			{
651 				// Workarround for chatwindow! Delete this after GUI-Rework
652 				changed = true;
653 			}
654 			// This is a temporary solution until the new
655 			// UTF-8 class can tell the real length!!!
656 			 else if (text.length() < length)
657 			{
658 				if (input == "space")
659 				{
660 					text.insert(cpos, " ");
661 					cpos++;
662 					changed = true;
663 				}
664 				 else if (input == "keypad0")
665 				{
666 					text.insert(cpos, "0");
667 					cpos++;
668 					changed = true;
669 				}
670 				 else if (input == "keypad1")
671 				{
672 					text.insert(cpos, "1");
673 					cpos++;
674 					changed = true;
675 				}
676 				 else if (input == "keypad2")
677 				{
678 					text.insert(cpos, "2");
679 					cpos++;
680 					changed = true;
681 				}
682 				 else if (input == "keypad3")
683 				{
684 					text.insert(cpos, "3");
685 					cpos++;
686 					changed = true;
687 				}
688 				 else if (input == "keypad4")
689 				{
690 					text.insert(cpos, "4");
691 					cpos++;
692 					changed = true;
693 				}
694 				 else if (input == "keypad5")
695 				{
696 					text.insert(cpos, "5");
697 					cpos++;
698 					changed = true;
699 				}
700 				 else if (input == "keypad6")
701 				{
702 					text.insert(cpos, "6");
703 					cpos++;
704 					changed = true;
705 				}
706 				 else if (input == "keypad7")
707 				{
708 					text.insert(cpos, "7");
709 					cpos++;
710 					changed = true;
711 				}
712 				 else if (input == "keypad8")
713 				{
714 					text.insert(cpos, "8");
715 					cpos++;
716 					changed = true;
717 				}
718 				 else if (input == "keypad9")
719 				{
720 					text.insert(cpos, "9");
721 					cpos++;
722 					changed = true;
723 				}
724 				 else if (input.length() == 1)
725 				{
726 					text.insert(cpos, input);
727 					cpos++;
728 					changed = true;
729 				}
730 			}
731 		}
732 	}
733 
734 	obj.pos2.x = SDL_GetTicks() % 1000 >= 500 ? cpos : -1.0;
735 	obj.text = text;
736 
737 	mLastWidget = id;
738 	mQueue->push(obj);
739 
740 	// when content changed, it is active
741 	// part of chat window hack
742 	if( changed && force_active )
743 		mActiveButton = id;
744 
745 	return changed;
746 }
747 
doSelectbox(int id,const Vector2 & pos1,const Vector2 & pos2,const std::vector<std::string> & entries,unsigned int & selected,unsigned int flags)748 SelectBoxAction IMGUI::doSelectbox(int id, const Vector2& pos1, const Vector2& pos2, const std::vector<std::string>& entries, unsigned int& selected, unsigned int flags)
749 {
750 	int FontSize = (flags & TF_SMALL_FONT ? (FONT_WIDTH_SMALL+LINE_SPACER_SMALL) : (FONT_WIDTH_NORMAL+LINE_SPACER_NORMAL));
751 	SelectBoxAction changed = SBA_NONE;
752 	QueueObject obj;
753 	obj.id = id;
754 	obj.pos1 = pos1;
755 	obj.pos2 = pos2;
756 	obj.type = SELECTBOX;
757 	obj.flags = flags;
758 
759 	const int itemsPerPage = int(pos2.y - pos1.y - 10) / FontSize;
760 	int first = (int)(selected / itemsPerPage)*itemsPerPage; //the first visible element in the list
761 
762 	if (!mInactive)
763 	{
764 		// M.W. : Activate cursorless object-highlighting once the up or down key is pressed.
765 		if (mActiveButton == -1)
766 		{
767 			switch (mLastKeyAction)
768 			{
769 				case DOWN:
770 					mActiveButton = 0;
771 					mLastKeyAction = NONE;
772 					break;
773 
774 				case UP:
775 					mActiveButton = mLastWidget;
776 					mLastKeyAction = NONE;
777 					break;
778 
779 				default:
780 					break;
781 			}
782 		}
783 
784 		// Highlight first menu object for arrow key navigation.
785 		if (mActiveButton == 0 && !mButtonReset)
786 			mActiveButton = id;
787 
788 		// React to keyboard input.
789 		if (id == mActiveButton)
790 		{
791 			obj.type = ACTIVESELECTBOX;
792 			switch (mLastKeyAction)
793 			{
794 				case DOWN:
795 					mActiveButton = 0;
796 					mLastKeyAction = NONE;
797 					break;
798 
799 				case UP:
800 					mActiveButton = mLastWidget;
801 					mLastKeyAction = NONE;
802 					break;
803 
804 				case LEFT:
805 					if (selected > 0)
806 					{
807 						selected--;
808 						changed = SBA_SELECT;
809 					}
810 					mLastKeyAction = NONE;
811 					break;
812 
813 				case RIGHT:
814 					if (selected + 1 < entries.size())
815 					{
816 						selected++;
817 						changed = SBA_SELECT;
818 					}
819 					mLastKeyAction = NONE;
820 					break;
821 
822 				default:
823 					break;
824 			}
825 		}
826 
827 		// React to mouse input.
828 		Vector2 mousepos = InputManager::getSingleton()->position();
829 		if (mousepos.x > pos1.x && mousepos.y > pos1.y && mousepos.x < pos2.x && mousepos.y < pos2.y)
830 		{
831 			obj.type = ACTIVESELECTBOX;
832 			if (InputManager::getSingleton()->click())
833 				mActiveButton = id;
834 		}
835 		//entries mouseclick:
836 		if (mousepos.x > pos1.x &&
837 			mousepos.y > pos1.y+5 &&
838 			mousepos.x < pos2.x-35 &&
839 			mousepos.y < pos1.y+5+FontSize*itemsPerPage)
840 		{
841 			if (InputManager::getSingleton()->click())
842 			{
843 				int tmp = (int)((mousepos.y - pos1.y - 5) / FontSize) + first;
844 				/// \todo well, it's not really a doulbe click...
845 				/// we need to do this in inputmanager
846 				if( selected == tmp && InputManager::getSingleton()->doubleClick() )
847 					changed = SBA_DBL_CLICK;
848 
849 				if (tmp >= 0 && static_cast<unsigned int>(tmp) < entries.size())
850 					selected = tmp;
851 
852 				mActiveButton = id;
853 			}
854 			if ((InputManager::getSingleton()->mouseWheelUp()) && (selected > 0))
855 			{
856 				selected--;
857 				changed = SBA_SELECT;
858 			}
859 			if ((InputManager::getSingleton()->mouseWheelDown()) && (selected + 1 < entries.size()))
860 			{
861 				selected++;
862 				changed = SBA_SELECT;
863 			}
864 		}
865 		//arrows mouseclick:
866 		if (mousepos.x > pos2.x-30 && mousepos.x < pos2.x-30+24 && InputManager::getSingleton()->click())
867 		{
868 			if (mousepos.y > pos1.y+3 && mousepos.y < pos1.y+3+24 && selected > 0)
869 			{
870 				selected--;
871 				changed = SBA_SELECT;
872 			}
873 
874 			if (mousepos.y > pos2.y-27 && mousepos.y < pos2.y-27+24 && selected + 1 < entries.size())
875 			{
876 				selected++;
877 				changed = SBA_SELECT;
878 			}
879 		}
880 	}
881 	doImage(GEN_ID, Vector2(pos2.x-15, pos1.y+15), "gfx/pfeil_oben.bmp");
882 	doImage(GEN_ID, Vector2(pos2.x-15, pos2.y-15), "gfx/pfeil_unten.bmp");
883 
884 	first = (selected / itemsPerPage)*itemsPerPage; //recalc first
885 	if ( !entries.empty() )
886 	{
887 		unsigned int last = first + itemsPerPage;
888 		if (last > entries.size())
889 			last = entries.size();
890 
891 		obj.entries = std::vector<std::string>(entries.begin()+first, entries.begin()+last);
892 	}
893 	else
894 		obj.entries = std::vector<std::string>();
895 
896 	obj.selected = selected-first;
897 
898 	mLastWidget = id;
899 	mQueue->push(obj);
900 
901 	return changed;
902 }
903 
doChatbox(int id,const Vector2 & pos1,const Vector2 & pos2,const std::vector<std::string> & entries,unsigned int & selected,const std::vector<bool> & local,unsigned int flags)904 void IMGUI::doChatbox(int id, const Vector2& pos1, const Vector2& pos2, const std::vector<std::string>& entries, unsigned int& selected, const std::vector<bool>& local, unsigned int flags)
905 {
906 	assert( entries.size() == local.size() );
907 	int FontSize = (flags & TF_SMALL_FONT ? (FONT_WIDTH_SMALL+LINE_SPACER_SMALL) : (FONT_WIDTH_NORMAL+LINE_SPACER_NORMAL));
908 	QueueObject obj;
909 	obj.id = id;
910 	obj.pos1 = pos1;
911 	obj.pos2 = pos2;
912 	obj.type = CHAT;
913 	obj.flags = flags;
914 
915 	const unsigned int itemsPerPage = int(pos2.y - pos1.y - 10) / FontSize;
916 
917 	if (!mInactive)
918 	{
919 		// M.W. : Activate cursorless object-highlighting once the up or down key is pressed.
920 		if (mActiveButton == -1)
921 		{
922 			switch (mLastKeyAction)
923 			{
924 				case DOWN:
925 					mActiveButton = 0;
926 					mLastKeyAction = NONE;
927 					break;
928 
929 				case UP:
930 					mActiveButton = mLastWidget;
931 					mLastKeyAction = NONE;
932 					break;
933 
934 				default:
935 					break;
936 			}
937 		}
938 
939 		// Highlight first menu object for arrow key navigation.
940 		if (mActiveButton == 0 && !mButtonReset)
941 			mActiveButton = id;
942 
943 		// React to keyboard input.
944 		if (id == mActiveButton)
945 		{
946 			switch (mLastKeyAction)
947 			{
948 				case DOWN:
949 					mActiveButton = 0;
950 					mLastKeyAction = NONE;
951 					break;
952 
953 				case UP:
954 					mActiveButton = mLastWidget;
955 					mLastKeyAction = NONE;
956 					break;
957 
958 				case LEFT:
959 					if (selected > 0)
960 					{
961 						selected--;
962 					}
963 					mLastKeyAction = NONE;
964 					break;
965 
966 				case RIGHT:
967 					if (selected + 1 < entries.size())
968 					{
969 						selected++;
970 					}
971 					mLastKeyAction = NONE;
972 					break;
973 
974 				default:
975 					break;
976 			}
977 		}
978 
979 		// React to mouse input.
980 		Vector2 mousepos = InputManager::getSingleton()->position();
981 		if (mousepos.x > pos1.x && mousepos.y > pos1.y && mousepos.x < pos2.x && mousepos.y < pos2.y)
982 		{
983 			if (InputManager::getSingleton()->click())
984 				mActiveButton = id;
985 		}
986 		//entries mouseclick:
987 		if (mousepos.x > pos1.x &&
988 			mousepos.y > pos1.y+5 &&
989 			mousepos.x < pos2.x-35 &&
990 			mousepos.y < pos1.y+5+FontSize*itemsPerPage)
991 		{
992 			if ((InputManager::getSingleton()->mouseWheelUp()) && (selected > 0))
993 			{
994 				selected--;
995 			}
996 
997 			if ((InputManager::getSingleton()->mouseWheelDown()) && (selected + 1 < entries.size()))
998 			{
999 				selected++;
1000 			}
1001 		}
1002 		//arrows mouseclick:
1003 		if (mousepos.x > pos2.x-30 && mousepos.x < pos2.x-30+24 && InputManager::getSingleton()->click())
1004 		{
1005 			if (mousepos.y > pos1.y+3 && mousepos.y < pos1.y+3+24 && selected > 0)
1006 			{
1007 				selected--;
1008 			}
1009 
1010 			if (mousepos.y > pos2.y-27 && mousepos.y < pos2.y-27+24 && selected + 1 < entries.size())
1011 			{
1012 				selected++;
1013 			}
1014 		}
1015 	}
1016 	doImage(GEN_ID, Vector2(pos2.x-15, pos1.y+15), "gfx/pfeil_oben.bmp");
1017 	doImage(GEN_ID, Vector2(pos2.x-15, pos2.y-15), "gfx/pfeil_unten.bmp");
1018 
1019 	unsigned int first = (selected / itemsPerPage) * itemsPerPage; //recalc first
1020 	if ( !entries.empty() )
1021 	{
1022 		unsigned int last = selected + 1;
1023 		/// \todo maybe we should adapt selected so we even can't scroll up further!
1024 		// we don't want negative chatlog, so we just scroll upward without coming to negative
1025 		// elements.
1026 		if (last >= itemsPerPage)
1027 			first = last - itemsPerPage;
1028 		else
1029 			first = 0;
1030 
1031 		// check that we don't create out of bounds problems
1032 		if(last > entries.size())
1033 		{
1034 			last = entries.size();
1035 		}
1036 
1037 		obj.entries = std::vector<std::string>(entries.begin()+first, entries.begin()+last);
1038 		// HACK: we use taxt to store information which text is from local player and which from
1039 		//			remote player.
1040 		obj.text = "";
1041 		for(unsigned int i = first; i < last; ++i)
1042 		{
1043 			obj.text += local[i] ? 'L' : 'R';
1044 		}
1045 	}
1046 	else
1047 		obj.entries = std::vector<std::string>();
1048 
1049 	obj.selected = selected-first;
1050 
1051 	mLastWidget = id;
1052 	mQueue->push(obj);
1053 }
1054 
1055 
doBlob(int id,const Vector2 & position,const Color & col)1056 bool IMGUI::doBlob(int id, const Vector2& position, const Color& col)
1057 {
1058 	QueueObject obj;
1059 	obj.id = id;
1060 	obj.pos1 = position;
1061 	obj.type = BLOB;
1062 	obj.col = col;
1063 	mQueue->push(obj);
1064 	return false;
1065 }
1066 
usingCursor() const1067 bool IMGUI::usingCursor() const
1068 {
1069 	return mUsingCursor;
1070 }
1071