1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 /* This is a C++ class for handling a GUI, and associated widgets */
24 
25 #include "ultima/nuvie/core/nuvie_defs.h"
26 #include "ultima/nuvie/conf/configuration.h"
27 #include "ultima/nuvie/gui/gui.h"
28 #include "ultima/nuvie/gui/gui_types.h"
29 #include "ultima/nuvie/keybinding/keys.h"
30 
31 namespace Ultima {
32 namespace Nuvie {
33 
34 const int GUI::mouseclick_delay = 300; /* SB-X */
35 
36 
37 /* Number of widget elements to allocate at once */
38 #define WIDGET_ARRAYCHUNK   32
39 
40 GUI *GUI::gui = NULL;
41 
GUI(Configuration * c,Screen * s)42 GUI:: GUI(Configuration *c, Screen *s) {
43 	Graphics::ManagedSurface *sdl_surface;
44 
45 	gui = this;
46 	config = c;
47 	screen = s;
48 	numwidgets = 0;
49 	maxwidgets = 0;
50 	widgets = NULL;
51 	display = 1;
52 	running = 0;
53 
54 	screen_scale_factor = screen->get_scale_factor();
55 
56 	dragging = false;
57 	full_redraw = true;
58 	focused_widget = locked_widget = NULL;
59 	block_input = false;
60 
61 	sdl_surface = screen->get_sdl_surface();
62 
63 	selected_color = new GUI_Color(10, 10, 50);
64 	selected_color->map_color(sdl_surface);
65 
66 	gui_font = new GUI_Font();
67 	gui_drag_manager = new GUI_DragManager(screen);
68 }
69 
~GUI()70 GUI:: ~GUI() {
71 	if (widgets != NULL) {
72 		for (int i = 0; i < numwidgets; ++i) {
73 			delete widgets[i];
74 		}
75 		free(widgets);
76 	}
77 
78 	delete selected_color;
79 
80 	delete gui_font;
81 	delete gui_drag_manager;
82 }
83 
84 /* Add a widget to the GUI.
85    The widget will be automatically deleted when the GUI is deleted.
86  */
87 int
AddWidget(GUI_Widget * widget)88 GUI:: AddWidget(GUI_Widget *widget) {
89 	int i;
90 
91 	/* Look for deleted widgets */
92 	for (i = 0; i < numwidgets; ++i) {
93 		if (widgets[i]->Status() == WIDGET_DELETED) {
94 			delete widgets[i];
95 			break;
96 		}
97 	}
98 	if (i == numwidgets) {
99 		/* Expand the widgets array if necessary */
100 		if (numwidgets == maxwidgets) {
101 			GUI_Widget **newarray;
102 			int maxarray;
103 
104 			maxarray = maxwidgets + WIDGET_ARRAYCHUNK;
105 			if ((newarray = (GUI_Widget **)realloc(widgets,
106 			                                       maxarray * sizeof(*newarray))) == NULL) {
107 				return (-1);
108 			}
109 			widgets = newarray;
110 			maxwidgets = maxarray;
111 		}
112 		++numwidgets;
113 	}
114 	widgets[i] = widget;
115 	widget->PlaceOnScreen(screen, gui_drag_manager, 0, 0);
116 
117 	return (0);
118 }
119 
120 /* remove widget from gui system but don't delete it */
removeWidget(GUI_Widget * widget)121 bool GUI::removeWidget(GUI_Widget *widget) {
122 	int i;
123 
124 	for (i = 0; i < numwidgets; ++i) {
125 		if (widgets[i] == widget) {
126 			for (int j = i + 1; j < numwidgets; ++j) { //shuffle remaining widgets down.
127 				widgets[j - 1] = widgets[j];
128 			}
129 
130 			--numwidgets;
131 			force_full_redraw();
132 			Display();
133 			return true;
134 		}
135 	}
136 
137 	return false;
138 }
139 
CleanupDeletedWidgets(bool redraw)140 void GUI::CleanupDeletedWidgets(bool redraw) {
141 	/* Garbage collection */
142 	if (locked_widget && locked_widget->Status() == WIDGET_DELETED)
143 		locked_widget = 0;
144 	if (focused_widget && focused_widget->Status() == WIDGET_DELETED)
145 		focused_widget = 0;
146 
147 	for (int i = 0; i < numwidgets;) {
148 		if (widgets[i]->Status() == WIDGET_DELETED) {
149 			delete widgets[i];
150 
151 			for (int j = i + 1; j < numwidgets; ++j) //shuffle remaining widgets down.
152 				widgets[j - 1] = widgets[j];
153 
154 			--numwidgets;
155 			if (redraw) {
156 				// CHECKME: is it really necessary to redraw after each deletion?
157 				force_full_redraw();
158 				Display();
159 			}
160 		} else
161 			++i;
162 	}
163 }
164 
moveWidget(GUI_Widget * widget,uint32 dx,uint32 dy)165 bool GUI::moveWidget(GUI_Widget *widget, uint32 dx, uint32 dy) {
166 	if (!widget)
167 		return false;
168 
169 	widget->MoveRelative(dx, dy);
170 
171 	if (widget->Status() == WIDGET_VISIBLE)
172 		widget->Redraw();//force_full_redraw();
173 
174 	return true;
175 }
176 
force_full_redraw()177 void GUI::force_full_redraw() {
178 	full_redraw = true;
179 }
180 
Display()181 void GUI::Display() {
182 	int i;
183 	bool complete_redraw = false;
184 
185 	//  hack for now to make everyhing under the cursor draw until I find a better
186 	//  way of doing this...
187 	if (dragging || full_redraw)
188 		complete_redraw = true;
189 
190 	for (i = 0; i < numwidgets; ++i) {
191 		if (widgets[i]->Status() == WIDGET_VISIBLE) {
192 			widgets[i]->Display(complete_redraw);
193 			//screen->update(widgets[i]->area.left,widgets[i]->area.top,widgets[i]->area.width(),widgets[i]->area.height());
194 		}
195 	}
196 	//SDL_UpdateRect(screen, 0, 0, 0, 0);
197 
198 	int mx, my;
199 	screen->get_mouse_location(&mx, &my);
200 
201 	gui_drag_manager->draw(mx, my);
202 
203 	if (full_redraw)
204 		full_redraw = false;
205 }
206 
207 /* Function to handle a GUI status */
208 void
HandleStatus(GUI_status status)209 GUI:: HandleStatus(GUI_status status) {
210 	switch (status) {
211 	case GUI_QUIT:
212 		running = 0;
213 		break;
214 	case GUI_REDRAW:
215 		display = 1;
216 		break;
217 	case GUI_DRAG_AND_DROP:
218 		dragging = true;
219 		break;
220 	default:
221 		break;
222 	}
223 }
224 
225 /* Handle an event, passing it to widgets until they return a status */
HandleEvent(Common::Event * event)226 GUI_status GUI:: HandleEvent(Common::Event *event) {
227 	int i;
228 	int hit;
229 	GUI_status status = GUI_PASS;
230 
231 	if (screen_scale_factor != 1) {
232 		if (Shared::isMouseDownEvent(event->type) || Shared::isMouseUpEvent(event->type)) {
233 			event->mouse.x /= screen_scale_factor;
234 			event->mouse.y /= screen_scale_factor;
235 		}
236 		if (event->type == Common::EVENT_MOUSEMOVE) {
237 			event->mouse.x /= screen_scale_factor;
238 			event->mouse.y /= screen_scale_factor;
239 		}
240 	}
241 
242 	if (dragging) { //&& !block_input)
243 		if (Shared::isMouseUpEvent(event->type)) { //FIX for button up that doesn't hit a widget.
244 			for (hit = false, i = numwidgets - 1; (i >= 0) && (hit == false); --i) {
245 				if (widgets[i]->Status() == WIDGET_VISIBLE && widgets[i]->is_drop_target() && widgets[i]->HitRect(event->mouse.x, event->mouse.y)) {
246 					gui_drag_manager->drop((GUI_DragArea *)widgets[i], event->mouse.x, event->mouse.y);
247 					dragging = false;
248 					Display(); // redraw the widget to get rid of the drop graphic.
249 					break;
250 				}
251 			}
252 		}
253 	} else if (!block_input) {
254 		if (event->type == Common::EVENT_JOYAXIS_MOTION ||
255 				event->type == Common::EVENT_JOYBUTTON_DOWN ||
256 				event->type == Common::EVENT_JOYBUTTON_UP) {
257 			event->kbd.keycode = Game::get_game()->get_keybinder()->get_key_from_joy_events(event);
258 			if (event->kbd.keycode == Common::KEYCODE_INVALID) { // isn't mapped, is in deadzone, or axis didn't return to center before moving again
259 				HandleStatus(status);
260 				CleanupDeletedWidgets(status != GUI_QUIT);
261 				return status; // pretend nothing happened
262 			}
263 			event->type = Common::EVENT_KEYDOWN;
264 			event->kbd.flags = 0;
265 		}
266 
267 		switch (event->type) {
268 		/* SDL_QUIT events quit the GUI */
269 		// case SDL_QUIT:
270 		//   status = GUI_QUIT;
271 		//   break;
272 
273 		/* Keyboard and mouse events go to widgets */
274 
275 		case Common::EVENT_MOUSEMOVE:
276 		case Common::EVENT_LBUTTONDOWN:
277 		case Common::EVENT_RBUTTONDOWN:
278 		case Common::EVENT_MBUTTONDOWN:
279 		case Common::EVENT_LBUTTONUP:
280 		case Common::EVENT_RBUTTONUP:
281 		case Common::EVENT_MBUTTONUP:
282 		case Common::EVENT_KEYDOWN:
283 		case Common::EVENT_KEYUP:
284 		case Common::EVENT_WHEELDOWN:
285 		case Common::EVENT_WHEELUP:
286 //			 /* Go through widgets, topmost first */
287 //			 status = GUI_PASS;
288 //			 for (i=numwidgets-1; (i>=0)&&(status==GUI_PASS); --i) {
289 //				 if ( widgets[i]->Status() == WIDGET_VISIBLE ) {
290 //				   status = widgets[i]->HandleEvent(event);
291 //				 }
292 //			 }
293 //			 break;
294 			/* Send everything to locked widget. */
295 			if (locked_widget && locked_widget->Status() == WIDGET_VISIBLE) {
296 				status = locked_widget->HandleEvent(event);
297 				if (status == GUI_PASS) // can't bypass the lock
298 					status = GUI_YUM;
299 			}
300 			/* Go through widgets, focused first, then from the
301 			   top.*/
302 			else {
303 				status = GUI_PASS;
304 				if (focused_widget && focused_widget->Status() == WIDGET_VISIBLE) {
305 					status = focused_widget->HandleEvent(event);
306 				}
307 				for (i = numwidgets - 1; (i >= 0) && (status == GUI_PASS); --i) {
308 					if (widgets[i]->Status() == WIDGET_VISIBLE
309 					        && widgets[i] != focused_widget) {  // don't send to focused twice
310 						status = widgets[i]->HandleEvent(event);
311 					}
312 				}
313 			}
314 			break;
315 
316 		/* Ignore unhandled events */
317 		default:
318 			status = GUI_PASS;
319 			break;
320 		}
321 	}
322 
323 	HandleStatus(status);
324 
325 	CleanupDeletedWidgets(status != GUI_QUIT);
326 
327 	return status;
328 }
329 
330 /* Run the GUI.
331    This returns when either a widget requests a quit, the idle
332    function requests a quit, or the SDL window has been closed.
333  */
Run(GUI_IdleProc idle,int once,int multitaskfriendly)334 void GUI::Run(GUI_IdleProc idle, int once, int multitaskfriendly) {
335 	int i;
336 	Common::Event event;
337 
338 	/* If there's nothing to do, return immediately */
339 	if ((numwidgets == 0) && (idle == NULL)) {
340 		return;
341 	}
342 
343 	running = 1;
344 	if (! once) {
345 		display = 1;
346 	}
347 	do {
348 		CleanupDeletedWidgets();
349 
350 		/* Display widgets if necessary */
351 		if (display) {
352 			Display();
353 			display = 0;
354 		}
355 
356 ///////////////////////////////////////////////////////////////// Polling is time consuming - instead:
357 		if (multitaskfriendly && (idle == NULL)) {
358 			SDL_WaitEvent(&event);
359 			HandleEvent(&event);
360 		} else
361 /////////////////////////////////////////////////////////////////
362 			/* Handle events, or run idle functions */
363 			if (SDL_PollEvent(&event)) {
364 				/* Handle all pending events */
365 				do {
366 					HandleEvent(&event);
367 				} while (SDL_PollEvent(&event));
368 			} else {
369 				if (idle != NULL) {
370 					HandleStatus(idle());
371 				}
372 				for (i = numwidgets - 1; i >= 0; --i) {
373 					HandleStatus(widgets[i]->Idle());
374 				}
375 			}
376 		//ERIC SDL_Delay(10);
377 	} while (running && ! once);
378 }
379 
get_font()380 GUI_Font *GUI::get_font() {
381 	return gui_font;
382 }
383 
384 
385 // SB-X
Idle()386 void GUI::Idle() {
387 	if (locked_widget) {
388 		locked_widget->Idle();
389 		return;
390 	}
391 
392 	for (int i = numwidgets - 1; i >= 0; --i) {
393 		HandleStatus(widgets[i]->Idle());
394 	}
395 }
396 
set_focus(GUI_Widget * widget)397 bool GUI::set_focus(GUI_Widget *widget) {
398 	/*
399 	    for(int i = 0; i < numwidgets; ++i)
400 	      {
401 	        if(!widget || (widgets[i] == widget)) // must be managed by GUI
402 	            {
403 	*/
404 	focused_widget = widget;
405 	return true;
406 //            }
407 //      }
408 
409 // return false;
410 }
411 
lock_input(GUI_Widget * widget)412 void GUI::lock_input(GUI_Widget *widget) {
413 	for (int i = 0; i < numwidgets; ++i)
414 		if (!widget || (widgets[i] == widget)) // must be managed by GUI
415 			locked_widget = widget;
416 }
417 
get_data_dir()418 Std::string GUI::get_data_dir() {
419 	Std::string datadir;
420 
421 	config->value("config/datadir", datadir, "");
422 
423 	return datadir;
424 }
425 
426 } // End of namespace Nuvie
427 } // End of namespace Ultima
428