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