1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (C) 2004-2010 by The Allacrost Project
3 // All Rights Reserved
4 //
5 // This code is licensed under the GNU GPL version 2. It is free software
6 // and you may modify it and/or redistribute it under the terms of this license.
7 // See http://www.gnu.org/copyleft/gpl.html for details.
8 ///////////////////////////////////////////////////////////////////////////////
9
10 #include <sstream>
11
12 #include "video.h"
13 #include "menu_window.h"
14
15 using namespace std;
16 using namespace hoa_utils;
17 using namespace hoa_video;
18 using namespace hoa_video::private_video;
19 using namespace hoa_gui::private_gui;
20
21 namespace hoa_gui {
22
MenuWindow()23 MenuWindow::MenuWindow() :
24 _skin(NULL),
25 _window_state(VIDEO_MENU_STATE_HIDDEN),
26 _display_timer(0),
27 _display_mode(VIDEO_MENU_INSTANT),
28 _is_scissored(false)
29 {
30 _id = GUIManager->_GetNextMenuWindowID();
31 _initialized = IsInitialized(_initialization_errors);
32 }
33
34
35
Create(string skin_name,float w,float h,int32 visible_flags,int32 shared_flags)36 bool MenuWindow::Create(string skin_name, float w, float h, int32 visible_flags, int32 shared_flags) {
37 _skin = GUIManager->_GetMenuSkin(skin_name);
38 if (_skin == NULL) {
39 IF_PRINT_WARNING(VIDEO_DEBUG) << "the requested menu skin was not found: " << skin_name << endl;
40 return false;
41 }
42
43 return Create(w, h, visible_flags, shared_flags);
44 }
45
46
47
Create(float w,float h,int32 visible_flags,int32 shared_flags)48 bool MenuWindow::Create(float w, float h, int32 visible_flags, int32 shared_flags) {
49 if (w <= 0 || h <= 0) {
50 IF_PRINT_WARNING(VIDEO_DEBUG) << "width and/or height argument was invalid: "
51 << "(width = " << w << ", height = " << h << ")" << endl;
52 return false;
53 }
54
55 _width = w;
56 _height = h;
57 _edge_visible_flags = visible_flags;
58 _edge_shared_flags = shared_flags;
59
60 if (_skin == NULL) {
61 _skin = GUIManager->_GetDefaultMenuSkin();
62 }
63 if (_skin == NULL) {
64 IF_PRINT_WARNING(VIDEO_DEBUG) << "a default menu skin was unavailable (no skins were loaded)" << endl;
65 return false;
66 }
67
68 if (_RecreateImage() == false) {
69 return false;
70 }
71
72 // Add the new menu window to the menu map
73 GUIManager->_AddMenuWindow(this);
74 _initialized = IsInitialized(_initialization_errors);
75 return true;
76 }
77
78
79
Destroy()80 void MenuWindow::Destroy() {
81 _skin = NULL;
82 GUIManager->_RemoveMenuWindow(this);
83 }
84
85
86
Update(uint32 frame_time)87 void MenuWindow::Update(uint32 frame_time) {
88 _display_timer += frame_time;
89
90 if (_display_timer >= VIDEO_MENU_SCROLL_TIME) {
91 if (_window_state == VIDEO_MENU_STATE_SHOWING)
92 _window_state = VIDEO_MENU_STATE_SHOWN;
93 else if (_window_state == VIDEO_MENU_STATE_HIDING)
94 _window_state = VIDEO_MENU_STATE_HIDDEN;
95 }
96
97 // TODO: This does not need to be done every update call (it always retuns the same thing so long
98 // as the window does not move. This is a performance problem and should be fixed so that this is
99 // only done with the window size or alignment changes.
100 if (_window_state == VIDEO_MENU_STATE_HIDDEN || _window_state == VIDEO_MENU_STATE_SHOWN) {
101 // if (_is_scissored == true) {
102 float x_buffer = (_width - _inner_width) / 2;
103 float y_buffer = (_height - _inner_height) / 2;
104
105 float left, right, bottom, top;
106 left = 0.0f;
107 right = _width;
108 bottom = 0.0f;
109 top = _height;
110
111 VideoManager->PushState();
112 VideoManager->SetDrawFlags(_xalign, _yalign, 0);
113 CalculateAlignedRect(left, right, bottom, top);
114 VideoManager->PopState();
115
116 _scissor_rect = VideoManager->CalculateScreenRect(left, right, bottom, top);
117
118 _scissor_rect.left += static_cast<int32>(x_buffer);
119 _scissor_rect.width -= static_cast<int32>(x_buffer * 2);
120 _scissor_rect.top += static_cast<int32>(y_buffer);
121 _scissor_rect.height -= static_cast<int32>(y_buffer * 2);
122 // }
123
124 _is_scissored = false;
125 return;
126 }
127
128 _is_scissored = true;
129
130 // Holds the amount of the window that should be drawn (1.0 == 100%)
131 float draw_percent = 1.0f;
132
133 if (_display_mode != VIDEO_MENU_INSTANT && _window_state != VIDEO_MENU_STATE_SHOWN) {
134 float time = static_cast<float>(_display_timer) / static_cast<float>(VIDEO_MENU_SCROLL_TIME);
135 if (time > 1.0f)
136 time = 1.0f;
137
138 if (_window_state == VIDEO_MENU_STATE_HIDING)
139 time = 1.0f - time;
140
141 draw_percent = time;
142 }
143
144 if (IsFloatEqual(draw_percent, 1.0f) == false) {
145 if (_display_mode == VIDEO_MENU_EXPAND_FROM_CENTER) {
146 float left, right, bottom, top;
147 left = 0.0f;
148 right = _width;
149 bottom = 0.0f;
150 top = _height;
151
152 VideoManager->PushState();
153 VideoManager->SetDrawFlags(_xalign, _yalign, 0);
154 CalculateAlignedRect(left, right, bottom, top);
155 VideoManager->PopState();
156
157 float center = (top + bottom) * 0.5f;
158
159 bottom = center * (1.0f - draw_percent) + bottom * draw_percent;
160 top = center * (1.0f - draw_percent) + top * draw_percent;
161
162 _scissor_rect = VideoManager->CalculateScreenRect(left, right, bottom, top);
163 }
164 }
165 } // void MenuWindow::Update(uint32 frame_time)
166
167
168
Draw()169 void MenuWindow::Draw() {
170 if (_initialized == false) {
171 IF_PRINT_WARNING(VIDEO_DEBUG) << "the menu window was not initialized:\n" << _initialization_errors << endl;
172 return;
173 }
174
175 if (_window_state == VIDEO_MENU_STATE_HIDDEN)
176 return;
177
178 VideoManager->PushState();
179 VideoManager->SetDrawFlags(_xalign, _yalign, VIDEO_BLEND, 0);
180
181 if (_is_scissored) {
182 ScreenRect rect = _scissor_rect;
183 if (VideoManager->IsScissoringEnabled()) {
184 rect.Intersect(VideoManager->GetScissorRect());
185 }
186 else {
187 VideoManager->EnableScissoring();
188 }
189 VideoManager->SetScissorRect(rect);
190 }
191
192 VideoManager->Move(_x_position, _y_position);
193 _menu_image.Draw(Color::white);
194
195 if (GUIManager->DEBUG_DrawOutlines() == true) {
196 _DEBUG_DrawOutline();
197 }
198
199 VideoManager->PopState();
200 return;
201 } // void MenuWindow::Draw()
202
203
204
Show()205 void MenuWindow::Show() {
206 if (_initialized == false) {
207 IF_PRINT_WARNING(VIDEO_DEBUG) << "the menu window was not initialized:\n" << _initialization_errors << endl;
208 return;
209 }
210
211 if (_window_state == VIDEO_MENU_STATE_SHOWING || _window_state == VIDEO_MENU_STATE_SHOWN) {
212 return;
213 }
214
215 _display_timer = 0;
216
217 if (_display_mode == VIDEO_MENU_INSTANT)
218 _window_state = VIDEO_MENU_STATE_SHOWN;
219 else
220 _window_state = VIDEO_MENU_STATE_SHOWING;
221 } // void MenuWindow::Show()
222
223
224
Hide()225 void MenuWindow::Hide() {
226 if (_initialized == false) {
227 IF_PRINT_WARNING(VIDEO_DEBUG) << "the menu window was not initialized:\n" << _initialization_errors << endl;
228 return;
229 }
230
231 if (_window_state == VIDEO_MENU_STATE_HIDING || _window_state == VIDEO_MENU_STATE_HIDDEN) {
232 return;
233 }
234
235 _display_timer = 0;
236
237 if (_display_mode == VIDEO_MENU_INSTANT)
238 _window_state = VIDEO_MENU_STATE_HIDDEN;
239 else
240 _window_state = VIDEO_MENU_STATE_HIDING;
241 } // void MenuWindow::Hide()
242
243
244
IsInitialized(string & errors)245 bool MenuWindow::IsInitialized(string& errors) {
246 errors.clear();
247 ostringstream stream;
248
249 // Check width
250 if (_width <= 0.0f || _width > 1024.0f)
251 stream << "* Invalid width (" << _width << ")" << endl;
252
253 // Check height
254 if (_height <= 0.0f || _height > 768.0f)
255 stream << "* Invalid height (" << _height << ")" << endl;
256
257 // Check display mode
258 if (_display_mode <= VIDEO_MENU_INVALID || _display_mode >= VIDEO_MENU_TOTAL)
259 stream << "* Invalid display mode (" << _display_mode << ")" << endl;
260
261 // Check state
262 if (_window_state <= VIDEO_MENU_STATE_INVALID || _window_state >= VIDEO_MENU_STATE_TOTAL)
263 stream << "* Invalid state (" << _window_state << ")" << endl;
264
265 // Check to see that a valid menu skin is being used
266 if (_skin == NULL)
267 stream << "* No menu skin is assigned" << endl;
268
269 // Check to see if the composite image composing the window is valid
270 if (_menu_image.GetWidth() == 0)
271 stream << "* Menu image is not valid" << endl;
272
273 errors = stream.str();
274 if (errors.empty()) {
275 _initialized = true;
276 }
277 else {
278 _initialized = false;
279 }
280
281 return _initialized;
282 } // bool MenuWindow::IsInitialized(string& errors)
283
284
285
SetDimensions(float w,float h)286 void MenuWindow::SetDimensions(float w, float h) {
287 if (w <= 0.0f) {
288 IF_PRINT_WARNING(VIDEO_DEBUG) << "invalid width argument: " << w << endl;
289 return;
290 }
291
292 if (h <= 0.0f) {
293 IF_PRINT_WARNING(VIDEO_DEBUG) << "invalid height argument: " << h << endl;
294 return;
295 }
296
297 _width = w;
298 _height = h;
299 _RecreateImage();
300 }
301
302
303
SetMenuSkin(string & skin_name)304 void MenuWindow::SetMenuSkin(string& skin_name) {
305 MenuSkin* new_skin = GUIManager->_GetMenuSkin(skin_name);
306 if (new_skin == NULL) {
307 IF_PRINT_WARNING(VIDEO_DEBUG) << "the skin_name \"" << skin_name << "\" was invalid" << endl;
308 return;
309 }
310
311 _skin = new_skin;
312 _RecreateImage();
313 }
314
315
316
SetDisplayMode(VIDEO_MENU_DISPLAY_MODE mode)317 void MenuWindow::SetDisplayMode(VIDEO_MENU_DISPLAY_MODE mode) {
318 if (mode <= VIDEO_MENU_INVALID || mode >= VIDEO_MENU_TOTAL) {
319 IF_PRINT_WARNING(VIDEO_DEBUG) << "invalid mode argument passed to function: " << mode << endl;
320 return;
321 }
322
323 _display_mode = mode;
324 _initialized = IsInitialized(_initialization_errors);
325 }
326
327
328
_RecreateImage()329 bool MenuWindow::_RecreateImage() {
330 if (_skin == NULL) {
331 IF_PRINT_WARNING(VIDEO_DEBUG) << "no menu skin set when function was invoked" << endl;
332 return false;
333 }
334
335 _menu_image.Clear();
336
337 // Get information about the border sizes
338 float left_border_size = _skin->borders[1][0].GetWidth();
339 float right_border_size = _skin->borders[1][2].GetWidth();
340 float top_border_size = _skin->borders[2][1].GetHeight();
341 float bottom_border_size = _skin->borders[0][1].GetHeight();
342
343 float horizontal_border_size = left_border_size + right_border_size;
344 float vertical_border_size = top_border_size + bottom_border_size;
345
346 float top_width = _skin->borders[2][1].GetWidth();
347 float left_height = _skin->borders[1][0].GetHeight();
348
349 // Calculate how many times the top/bottom images have to be tiled in order to make up the width of the window
350 _inner_width = _width - horizontal_border_size;
351 _inner_height = _height - vertical_border_size;
352
353 if (_inner_width < 0.0f) {
354 IF_PRINT_WARNING(VIDEO_DEBUG) << "_inner_width was computed as negative" << endl;
355 return false;
356 }
357
358 if (_inner_height < 0.0f) {
359 IF_PRINT_WARNING(VIDEO_DEBUG) << "_inner_height was computed as negative" << endl;
360 return false;
361 }
362
363 // Will be true if there is a background image for the menu skin being used
364 bool background_loaded = _skin->background.GetWidth();
365
366 // Find how many times we have to tile the border images to fit the dimensions given
367 float num_x_tiles = _inner_width / top_width;
368 float num_y_tiles = _inner_height / left_height;
369
370 int32 inum_x_tiles = static_cast<int32>(num_x_tiles);
371 int32 inum_y_tiles = static_cast<int32>(num_y_tiles);
372
373 // Ideally, the border sizes should evenly divide into the window sizes but the person who
374 // created the window might have passed in a width and height that is not a multiple of
375 // the border sizes. If this is the case, we have to extend the dimensions a little bit.
376 float dnum_x_tiles = num_x_tiles - inum_x_tiles;
377 float dnum_y_tiles = num_y_tiles - inum_y_tiles;
378
379 if (dnum_x_tiles > 0.001f) {
380 float width_adjust = (1.0f - dnum_x_tiles) * top_width;
381 _width += width_adjust;
382 _inner_width += width_adjust;
383 ++inum_x_tiles;
384 }
385
386 if (dnum_y_tiles > 0.001f) {
387 float height_adjust = (1.0f - dnum_y_tiles) * top_width;
388 _height += height_adjust;
389 _inner_height += height_adjust;
390 ++inum_y_tiles;
391 }
392
393 // Now we have all the information we need to create the window.
394 // Begin by re-creating the overlay at the correct width and height
395 Color c[4];
396 _skin->borders[1][1].GetVertexColor(c[0], 0);
397 _skin->borders[1][1].GetVertexColor(c[1], 1);
398 _skin->borders[1][1].GetVertexColor(c[2], 2);
399 _skin->borders[1][1].GetVertexColor(c[3], 3);
400
401 _skin->borders[1][1].SetDimensions(left_border_size, top_border_size);
402 _skin->borders[1][1].SetVertexColors(c[0], c[1], c[2], c[3]);
403 // VideoManager->LoadImage(_skin->borders[1][1]);
404
405 // If a valid background image is loaded, then tile the interior of the window with it
406 if (background_loaded) {
407 _skin->background.SetVertexColors(c[0], c[1], c[2], c[3]);
408
409 float width = _skin->background.GetWidth();
410 float height = _skin->background.GetHeight();
411
412 float min_x = 0;
413 float min_y = 0;
414
415 float max_x = _inner_width + horizontal_border_size;
416 float max_y = _inner_height + vertical_border_size;
417
418 if (_edge_visible_flags & VIDEO_MENU_EDGE_TOP)
419 max_y -= (top_border_size / 2);
420 if (_edge_visible_flags & VIDEO_MENU_EDGE_BOTTOM)
421 min_y += (bottom_border_size / 2);
422 if (_edge_visible_flags & VIDEO_MENU_EDGE_LEFT)
423 min_x += (left_border_size / 2);
424 if (_edge_visible_flags & VIDEO_MENU_EDGE_RIGHT)
425 max_x -= (right_border_size / 2);
426
427 for (float y = min_y ; y < max_y; y += height) {
428 for (float x = min_x; x < max_x; x += width) {
429 float u = 1.0, v = 1.0;
430
431 if (x + width > max_x)
432 u = (max_x - x) / width;
433 if (y + height > max_y)
434 v = (max_y - y) / height;
435
436 _menu_image.AddImage(_skin->background, x, y, 0.0f, 0.0f, u, v);
437 }
438 }
439 }
440 else {
441 // Otherwise re-create the overlay at the correct width and height
442 _skin->borders[1][1].SetDimensions(_inner_width, _inner_height);
443 _skin->borders[1][1].SetVertexColors(c[0], c[1], c[2], c[3]);
444 _menu_image.AddImage(_skin->borders[1][1], left_border_size, bottom_border_size);
445 }
446
447 // First create the corners of the image
448 float max_x = left_border_size + inum_x_tiles * top_width;
449 float max_y = bottom_border_size + inum_y_tiles * left_height;
450 float min_x = 0.0f;
451 float min_y = 0.0f;
452
453 // Bottom left
454 if (_edge_visible_flags & VIDEO_MENU_EDGE_LEFT && _edge_visible_flags & VIDEO_MENU_EDGE_BOTTOM) {
455 if (_edge_shared_flags & VIDEO_MENU_EDGE_LEFT && _edge_shared_flags & VIDEO_MENU_EDGE_BOTTOM)
456 _menu_image.AddImage(_skin->connectors[4], min_x, min_y);
457 else if (_edge_shared_flags & VIDEO_MENU_EDGE_LEFT)
458 _menu_image.AddImage(_skin->connectors[1], min_x, min_y);
459 else if (_edge_shared_flags & VIDEO_MENU_EDGE_BOTTOM)
460 _menu_image.AddImage(_skin->connectors[2], min_x, min_y);
461 else
462 _menu_image.AddImage(_skin->borders[2][0], min_x, min_y);
463 }
464 else if (_edge_visible_flags & VIDEO_MENU_EDGE_LEFT)
465 _menu_image.AddImage(_skin->borders[1][0], min_x, min_y);
466 else if (_edge_visible_flags & VIDEO_MENU_EDGE_BOTTOM)
467 _menu_image.AddImage(_skin->borders[0][1], min_x, min_y);
468 else if (!background_loaded)
469 _menu_image.AddImage(_skin->borders[1][1], min_x, min_y);
470
471 // Bottom right
472 if (_edge_visible_flags & VIDEO_MENU_EDGE_RIGHT && _edge_visible_flags & VIDEO_MENU_EDGE_BOTTOM) {
473 if (_edge_shared_flags & VIDEO_MENU_EDGE_RIGHT && _edge_shared_flags & VIDEO_MENU_EDGE_BOTTOM)
474 _menu_image.AddImage(_skin->connectors[4], max_x, min_y);
475 else if (_edge_shared_flags & VIDEO_MENU_EDGE_RIGHT)
476 _menu_image.AddImage(_skin->connectors[1], max_x, min_y);
477 else if (_edge_shared_flags & VIDEO_MENU_EDGE_BOTTOM)
478 _menu_image.AddImage(_skin->connectors[3], max_x, min_y);
479 else
480 _menu_image.AddImage(_skin->borders[2][2], max_x, min_y);
481 }
482 else if (_edge_visible_flags & VIDEO_MENU_EDGE_RIGHT)
483 _menu_image.AddImage(_skin->borders[1][2], max_x, min_y);
484 else if (_edge_visible_flags & VIDEO_MENU_EDGE_BOTTOM)
485 _menu_image.AddImage(_skin->borders[2][1], max_x, min_y);
486 else if (!background_loaded)
487 _menu_image.AddImage(_skin->borders[1][1], max_x, min_y);
488
489 // Top left
490 if (_edge_visible_flags & VIDEO_MENU_EDGE_LEFT && _edge_visible_flags & VIDEO_MENU_EDGE_TOP) {
491 if (_edge_shared_flags & VIDEO_MENU_EDGE_LEFT && _edge_shared_flags & VIDEO_MENU_EDGE_TOP)
492 _menu_image.AddImage(_skin->connectors[4], min_x, max_y);
493 else if (_edge_shared_flags & VIDEO_MENU_EDGE_LEFT)
494 _menu_image.AddImage(_skin->connectors[0], min_x, max_y);
495 else if (_edge_shared_flags & VIDEO_MENU_EDGE_TOP)
496 _menu_image.AddImage(_skin->connectors[2], min_x, max_y);
497 else
498 _menu_image.AddImage(_skin->borders[0][0], min_x, max_y);
499 }
500 else if (_edge_visible_flags & VIDEO_MENU_EDGE_LEFT)
501 _menu_image.AddImage(_skin->borders[1][0], min_x, max_y);
502 else if (_edge_visible_flags & VIDEO_MENU_EDGE_TOP)
503 _menu_image.AddImage(_skin->borders[0][1], min_x, max_y);
504 else if (!background_loaded)
505 _menu_image.AddImage(_skin->borders[1][1], min_x, max_y);
506
507 // Top right
508 if (_edge_visible_flags & VIDEO_MENU_EDGE_TOP && _edge_visible_flags & VIDEO_MENU_EDGE_RIGHT) {
509 if (_edge_shared_flags & VIDEO_MENU_EDGE_RIGHT && _edge_shared_flags & VIDEO_MENU_EDGE_TOP)
510 _menu_image.AddImage(_skin->connectors[4], max_x, max_y);
511 else if (_edge_shared_flags & VIDEO_MENU_EDGE_RIGHT)
512 _menu_image.AddImage(_skin->connectors[0], max_x, max_y);
513 else if (_edge_shared_flags & VIDEO_MENU_EDGE_TOP)
514 _menu_image.AddImage(_skin->connectors[3], max_x, max_y);
515 else
516 _menu_image.AddImage(_skin->borders[0][2], max_x, max_y);
517 }
518 else if (_edge_visible_flags & VIDEO_MENU_EDGE_TOP)
519 _menu_image.AddImage(_skin->borders[0][1], max_x, max_y);
520 else if (_edge_visible_flags & VIDEO_MENU_EDGE_RIGHT)
521 _menu_image.AddImage(_skin->borders[1][2], max_x, max_y);
522 else if (!background_loaded)
523 _menu_image.AddImage(_skin->borders[1][1], max_x, max_y);
524
525 // Iterate from left to right and fill in the horizontal borders
526 for (int32 tile_x = 0; tile_x < inum_x_tiles; ++tile_x) {
527 if (_edge_visible_flags & VIDEO_MENU_EDGE_TOP)
528 _menu_image.AddImage(_skin->borders[0][1], left_border_size + top_width * tile_x, max_y);
529 else if (!background_loaded)
530 _menu_image.AddImage(_skin->borders[1][1], left_border_size + top_width * tile_x, max_y);
531
532 if (_edge_visible_flags & VIDEO_MENU_EDGE_BOTTOM)
533 _menu_image.AddImage(_skin->borders[2][1], left_border_size + top_width * tile_x, 0.0f);
534 else if (!background_loaded)
535 _menu_image.AddImage(_skin->borders[1][1], left_border_size + top_width * tile_x, 0.0f);
536 }
537
538 // Iterate from bottom to top and fill in the vertical borders
539 for (int32 tile_y = 0; tile_y < inum_y_tiles; ++tile_y) {
540 if (_edge_visible_flags & VIDEO_MENU_EDGE_LEFT)
541 _menu_image.AddImage(_skin->borders[1][0], 0.0f, bottom_border_size + left_height * tile_y);
542 else if (!background_loaded)
543 _menu_image.AddImage(_skin->borders[1][1], 0.0f, bottom_border_size + left_height * tile_y);
544
545 if (_edge_visible_flags & VIDEO_MENU_EDGE_RIGHT)
546 _menu_image.AddImage(_skin->borders[1][2], max_x, bottom_border_size + left_height * tile_y);
547 else if (!background_loaded)
548 _menu_image.AddImage(_skin->borders[1][1], max_x, bottom_border_size + left_height * tile_y);
549 }
550
551 return true;
552 }
553
554 } // namespace hoa_gui
555