1 /*---------------------------------------------------------------------------*
2 
3   Copyright (C) 2010-2012 Nintendo.  All rights reserved.
4 
5   These coded instructions, statements, and computer programs contain
6   proprietary information of Nintendo of America Inc. and/or Nintendo
7   Company Ltd., and are protected by Federal copyright law.  They may
8   not be disclosed to third parties or copied or duplicated in any form,
9   in whole or in part, without the prior written consent of Nintendo.
10 
11  *---------------------------------------------------------------------------*/
12 
13     // Creates a window manager
WindowManager(Peripheral * peripheral,GX2Texture ** textureList,float screenWidth)14 WindowManager::WindowManager(Peripheral* peripheral, GX2Texture** textureList, float screenWidth)
15     : dummyWindow(this, "", CVec2(0, 0), CVec2(0, 0)), windowOver(-1), windowActive(-1), windowDragging(false), oldWindowX(0), oldWindowY(0), cursorTime(0), tabIndex(NULL), tabSubIndex(-1), tabX(0), tabY(0)
16 {
17     this->peripheral = peripheral;
18 
19         // Get all the textures
20     if (textureList)
21         memcpy(this->textureList, textureList, sizeof(this->textureList));
22     else
23         memset(this->textureList, NULL, sizeof(this->textureList));
24 
25     this->screenWidth = screenWidth;
26 }
27 
28     // Initialization in case we want to reinitialize something
SetPeripheral(Peripheral * peripheral)29 void WindowManager::SetPeripheral(Peripheral* peripheral)
30 {
31     this->peripheral = peripheral;
32 }
33 
34     // Initialization in case we want to reinitialize something
SetTextureList(GX2Texture ** textureList)35 void WindowManager::SetTextureList(GX2Texture** textureList)
36 {
37     memcpy(this->textureList, textureList, sizeof(this->textureList));
38 }
39 
40     // Initialization in case we want to reinitialize something
SetScreenWidth(float screenWidth)41 void WindowManager::SetScreenWidth(float screenWidth)
42 {
43     this->screenWidth = screenWidth;
44 }
45 
46     // Updates the input for this window manager
UpdateInput()47 void WindowManager::UpdateInput()
48 {
49     tabX = 0;
50     tabY = 0;
51 
52         // This is all we need for held functionality
53     cursorTime ++;
54 
55     if (peripheral->PositiveTriggered())
56         cursorTime = 0;
57 
58         // +Control Pad
59     int dX = peripheral->RightPressed() - peripheral->LeftPressed();
60     int dY = peripheral->UpPressed() - peripheral->DownPressed();
61 
62     if ((dX || dY) && tabSubIndex == -1)
63     {
64         if (!peripheral->UsingPad())
65         {
66             peripheral->CursorX() = 0;
67             peripheral->CursorY() = 0;
68 
69             peripheral->UsingPad() = true;
70         }
71 
72         peripheral->CursorX() += dX * CURSOR_SPEED;
73         peripheral->CursorY() += dY * CURSOR_SPEED;
74 
75         if (peripheral->CursorX() < -screenWidth)
76             peripheral->CursorX() = -screenWidth;
77         else if (peripheral->CursorX() > screenWidth)
78             peripheral->CursorX() = screenWidth;
79 
80         if (peripheral->CursorY() < -1.0)
81             peripheral->CursorY() = -1.0;
82         else if (peripheral->CursorY() > 1.0)
83             peripheral->CursorY() = 1.0;
84 
85         tabIndex = NULL;
86         tabSubIndex = -1;
87 
88         return;
89     }
90 
91     if (!peripheral->UsingPad())
92     {
93         tabIndex = NULL;
94         tabSubIndex = -1;
95     }
96 
97         // Tabbing controls
98     if (tabIndex)
99     {
100         if (peripheral->TabRightTriggered())
101         {
102             tabIndex = tabIndex->next;
103             tabSubIndex = -1;
104             ForceValidTabForward();
105             return;
106         }
107         if (peripheral->TabLeftTriggered())
108         {
109             tabIndex = tabIndex->prev;
110             tabSubIndex = -1;
111             ForceValidTabBack();
112             return;
113         }
114 
115     }
116     else
117     {
118         if (peripheral->TabRightTriggered() || peripheral->TabLeftTriggered())
119         {
120             tabIndex = &dummyWindow.dummyTab;
121             tabSubIndex = -1;
122             ForceValidTabForward();
123             peripheral->UsingPad() = true;
124             peripheral->UsingWiimote() = false;
125             return;
126         }
127     }
128 
129         // Special tabbing controls
130     if (dX || dY)
131     {
132             // Can't be handled here, so let something else (combo box) handle it
133         tabX = dX;
134         tabY = dY;
135 
136         peripheral->UsingPad() = true;
137 
138         return;
139     }
140 }
141 
142     // Constants for the close button
143 const float CLOSEBUTTON_X = WINDOW_HANDLE_HEIGHT / 2;
144 const float CLOSEBUTTON_Y = WINDOW_HANDLE_HEIGHT / 2;
145 const float CLOSEBUTTON_WIDTH = WINDOW_HANDLE_HEIGHT * 0.8;
146 const float CLOSEBUTTON_HEIGHT = WINDOW_HANDLE_HEIGHT * 0.8;
147 
148     // A special update for the close button
UpdateCloseButton(Window * window)149 bool WindowManager::UpdateCloseButton(Window* window)
150 {
151     if (!window->canKill)
152         return false;
153 
154 	int status;
155 
156 		// We don't care as much if the cursor isn't being triggered
157 	if (peripheral->PositiveTriggered())
158 		status = 2;
159 	else
160 		status = 1;
161 
162 		// See if we just hit something
163 	if (peripheral->CursorColliding(window, window->GetWidth() / 2 - CLOSEBUTTON_X, window->GetTop() + CLOSEBUTTON_Y - window->GetY(), CLOSEBUTTON_WIDTH, CLOSEBUTTON_HEIGHT, true))
164 		window->closeStatus = status;
165 	else
166 		window->closeStatus = 0;
167 
168 	if (window->closeStatus == 2)
169 		return true;
170 
171 	return false;
172 }
173 
174     // A special draw for the close button
DrawCloseButton(Window * window)175 void WindowManager::DrawCloseButton(Window* window)
176 {
177     if (!window->canKill)
178         return;
179 
180 	CVec4 color = BlackLighter(window->closeStatus);
181 	CVec4 color2 = WhiteDarker(window->closeStatus);
182 
183 	peripheral->DrawBox(CVec3(window->GetX() + window->GetWidth() / 2 - CLOSEBUTTON_X, window->GetTop() + CLOSEBUTTON_Y, 0.999), CVec2(CLOSEBUTTON_WIDTH, CLOSEBUTTON_HEIGHT), color);
184     peripheral->DrawTextCenter("X", CVec3(window->GetX() + window->GetWidth() / 2 - CLOSEBUTTON_X, window->GetTop() + CLOSEBUTTON_Y, 0.9991), CVec2(CLOSEBUTTON_WIDTH, CLOSEBUTTON_HEIGHT), color2);
185 }
186 
187     // Sorts the list by z value
SortList(std::list<MenuItem * > & list)188 void SortList(std::list<MenuItem*>& list)
189 {
190         // Bubble sort!
191     for (std::list<MenuItem*>::iterator iter = list.begin(); iter != list.end(); ++iter)
192     {
193         std::list<MenuItem*>::iterator iter2 = iter;
194         ++iter2;
195 
196         for (; iter2 != list.end(); ++iter2)
197         {
198             if ((*iter)->z < (*iter2)->z)
199             {
200                 MenuItem* temp = *iter2;
201                 *iter2 = *iter;
202                 *iter = temp;
203             }
204         }
205     }
206 }
207 
208     // Draws the windows
Draw()209 void WindowManager::Draw()
210 {
211         // This is how the manager typically draws!
212     GX2SetDepthOnlyControl(GX2_ENABLE,
213                            GX2_ENABLE,
214                            GX2_COMPARE_LESS);
215 
216     for (int i = windowList.size() - 1; i >= 0; --i)
217     {
218         Window* window = windowList[windowOrder[i]];
219         if (window->visible && !window->master)
220         {
221                 // First, draw the window quad and title
222             peripheral->DrawWindowQuad(CVec2(window->GetX(), window->GetTop()), CVec2(window->GetWidth(), window->GetHeight()), i == 0, window->Enabled(), CVec3(0.1, 0.1, 0.1), window->GetName());
223 
224                 // Then draw the close button
225             DrawCloseButton(window);
226 
227                 // Sort them by z!
228             window->sortedDrawItems = window->menuItems;
229             SortList(window->sortedDrawItems);
230 
231                 // Then draw the window and its items
232             window->PreDraw();
233             window->Draw();
234             for (std::list<MenuItem*>::reverse_iterator iter = window->sortedDrawItems.rbegin(); iter != window->sortedDrawItems.rend(); ++iter)
235             {
236                 if ((*iter)->visible)
237                     (*iter)->Draw();
238             }
239 
240                 // Then draw the child window
241             float y = window->y - window->height;
242             Window* custom = window->child;
243             while (custom)
244             {
245                     // Sort them by z!
246                 custom->sortedDrawItems = custom->menuItems;
247                 SortList(custom->sortedDrawItems);
248 
249                     // Drawn at an offset!
250                 custom->x = window->x;
251                 custom->y = y;
252                 custom->offY  = window->offY;
253                 custom->Draw();
254                 for (std::list<MenuItem*>::reverse_iterator iter = custom->sortedDrawItems.rbegin(); iter != custom->sortedDrawItems.rend(); ++iter)
255                 {
256                     if ((*iter)->visible)
257                         (*iter)->Draw();
258                 }
259 
260                 y -= custom->maxHeight;
261                 custom = custom->child;
262             }
263         }
264     }
265 
266         // The cursor!
267     peripheral->DrawTexQuad(*GetTexture(CW_TEXTURE_CURSOR), CVec3(peripheral->CursorX(), peripheral->CursorY(), 1.0), CVec2(0.25, 0.25), CVec4(1.0, 1.0, 1.0, 1.0));
268 }
269 
270     // Updates the windows
Update(bool canBeOver)271 void WindowManager::Update(bool canBeOver)
272 {
273     this->canBeOver = canBeOver;
274 
275     UpdateInput();
276 
277     if (tabIndex)
278         BringWindowToFront(tabIndex->window);
279 
280     bool overCore[MAX_WINDOWS] = {0};
281     bool overHandle[MAX_WINDOWS] = {0};
282 
283     if (!peripheral->PositivePressed() || tabIndex)
284         windowDragging = false;
285 
286     int oldWindowActive = windowActive;
287 
288         // Drag the window!
289     if (windowDragging)
290     {
291         Window* window = windowList[windowOrder[0]];
292 
293         window->x += peripheral->CursorX() - oldWindowX;
294         window->y += peripheral->CursorY() - oldWindowY;
295 
296         oldWindowX = peripheral->CursorX();
297         oldWindowY = peripheral->CursorY();
298     }
299 
300         // Figure out which window we're over
301     windowOver = -1;
302     for (int i = 0; i < windowList.size(); ++i)
303     {
304         Window* window = windowList[windowOrder[i]];
305         if (!window->visible || window->master)
306             continue;
307 
308         overCore[windowOrder[i]] = window->CursorOver();
309         overHandle[windowOrder[i]] = peripheral->CursorColliding(window, window->x - window->GetX(), window->y + WINDOW_HANDLE_HEIGHT / 2 - window->GetY(), window->GetWidth(), WINDOW_HANDLE_HEIGHT, true);
310         if (overCore[windowOrder[i]] || overHandle[windowOrder[i]])
311         {
312             windowOver = windowOrder[i];
313 
314             if (!window->enabled)
315                 windowOver = -1;
316 
317             break;
318         }
319     }
320 
321         // Move the window to the front!
322     if (peripheral->PositiveTriggered())
323     {
324         BringWindowToFront();
325 
326         if (windowActive != -1 && !overCore[windowActive] && windowList[windowActive]->Active())
327         {
328             oldWindowX = peripheral->CursorX();
329             oldWindowY = peripheral->CursorY();
330             windowDragging = true;
331         }
332     }
333 
334     if (windowActive != oldWindowActive && oldWindowActive != -1)
335         windowList[oldWindowActive]->Reset();
336 
337     bool canUpdate = true;
338 
339 
340     std::vector<Window*> windows(windowList.size());
341     std::vector<bool> bools(windowList.size());
342 
343     for (int i = 0; i < windowList.size(); ++i)
344     {
345         windows[i] = windowList[windowOrder[i]];
346         bools[i] = (overCore[windowOrder[i]] || overHandle[windowOrder[i]]);
347     }
348 
349     for (int i = 0; i < windows.size(); ++i)
350     {
351         Window* window = windows[i];
352         if (window->Active() && !window->master)
353         {
354                 // Check for close button activity
355             if (canUpdate && UpdateCloseButton(window))
356             {
357                 window->Hide();
358                 windowDragging = false;
359                 canUpdate = false;
360                 continue;
361             }
362 
363                 // Then update the window and its items
364             canUpdate = window->Update(canUpdate);
365 
366                 // Sort them by z!
367             window->sortedItems = window->menuItems;
368             SortList(window->sortedItems);
369 
370                 // Then update the child windows
371             float y = window->y - window->height;
372             Window* custom = window->child;
373             while (custom)
374             {
375                     // Update at an offset!
376                 custom->x = window->x;
377                 custom->y = y;
378                 custom->offY = window->offY;
379                 canUpdate = custom->Update(canUpdate);
380 
381                     // Sort them by z!
382                 custom->sortedItems = custom->menuItems;
383                 SortList(custom->sortedItems);
384 
385                 y -= custom->maxHeight;
386                 custom = custom->child;
387             }
388 
389             if (canUpdate)
390             {
391                 Window* custom = window;
392 
393                 while (custom)
394                 {
395                     std::list<MenuItem*>::iterator iter = custom->sortedItems.begin();
396                     std::list<MenuItem*>::iterator preiter = custom->sortedItems.begin();
397 
398                         // If an object has priority, it can block the ones after it!
399                     for ( ; preiter != custom->sortedItems.end(); ++preiter)
400                     {
401                         if ((*preiter)->status >= 2 && (*preiter)->visible)
402                         {
403                             canUpdate = (*preiter)->Update();
404                             goto HIGH_PRIORITY_OBJECT_DONE;
405                         }
406                     }
407 
408                     custom = custom->child;
409                 }
410 
411                     // We're done with the focused object!
412 HIGH_PRIORITY_OBJECT_DONE:
413 
414                 canUpdate = window->PreUpdate(canUpdate);
415 
416                 custom = window;
417                 while (custom)
418                 {
419                     std::list<MenuItem*>::iterator iter = custom->sortedItems.begin();
420 
421                     if (canUpdate)
422                     {
423                             // If the cursor is over an object, it can block the ones after it!
424                         for ( ; iter != custom->sortedItems.end(); ++iter)
425                         {
426                             if ((*iter)->status < 2 && (*iter)->status != -1 && (*iter)->visible && !(*iter)->Update())
427                             {
428                                 canUpdate = false;
429                                 ++iter;
430                                 break;
431                             }
432                         }
433                     }
434 
435                         // The rest just get reset
436                     for ( ; iter != custom->sortedItems.end(); ++iter)
437                     {
438                         if ((*iter)->status < 2)
439                             (*iter)->Reset();
440                     }
441 
442                     custom = custom->child;
443                 }
444             }
445             else
446             {
447                     // Do the PreUpdate, but with false instead of true!
448                 window->PreUpdate(false);
449 
450                     // Reset all the objects...
451                 std::list<MenuItem*>::iterator iter = window->sortedItems.begin();
452                 for ( ; iter != window->sortedItems.end(); ++iter)
453                     (*iter)->Reset();
454 
455                     // Including the ones in child windows!
456                 Window* custom = window->child;
457                 while (custom)
458                 {
459                     std::list<MenuItem*>::iterator iter = custom->sortedItems.begin();
460                     for ( ; iter != custom->sortedItems.end(); ++iter)
461                         (*iter)->Reset();
462 
463                     custom = custom->child;
464                 }
465             }
466 
467             if (bools[i])
468                 canUpdate = false;
469         }
470     }
471 
472         // Clean up any windows that need deleted
473     for (int i = 0; i < windowList.size(); ++i)
474     {
475         if (windowList[i]->Deleted())
476         {
477             delete windowList[i];
478             --i;
479         }
480     }
481 
482         // Now update the tabbing!
483     UpdateTabbing();
484 }
485 
486     // Brings a specific window to the front
BringWindowToFront(Window * window)487 void WindowManager::BringWindowToFront(Window* window)
488 {
489     while (window->master)
490         window = window->master;
491 
492     windowOver = window->number;
493     BringWindowToFront();
494 }
495 
496     // Actually reorders the windows to put the active one in front
BringWindowToFront()497 void WindowManager::BringWindowToFront()
498 {
499     windowActive = windowOver;
500 
501     for (int i = windowList.size() - 2; i >= 0; --i)
502     {
503         if (windowOver == windowOrder[i + 1])
504         {
505             windowOrder[i + 1] = windowOrder[i];
506             windowOrder[i] = windowOver;
507         }
508     }
509 }
510 
511     // Adds the window to the list of windows
AddToWindowList(Window * window)512 void WindowManager::AddToWindowList(Window* window)
513 {
514     if (window->manager)
515         window->manager->RemoveFromWindowList(window);
516 
517     window->number = windowList.size();
518 
519     windowList.push_back(window);
520 
521     windowOrder.push_back(window->number);
522 
523     window->manager = this;
524 }
525 
526     // Removes the window from the list of windows
RemoveFromWindowList(Window * window)527 void WindowManager::RemoveFromWindowList(Window* window)
528 {
529     if (!window || window->manager != this)
530         return;
531 
532     window->Hide();
533 
534     windowList.erase(windowList.begin() + window->number);
535 
536     for (int i = 0; i < windowOrder.size(); ++i)
537     {
538         if (windowOrder[i] > window->number)
539             windowOrder[i] --;
540         else if (windowOrder[i] == window->number)
541         {
542             windowOrder.erase(windowOrder.begin() + i);
543             --i;
544         }
545     }
546 
547     for (int i = window->number; i < windowList.size(); ++i)
548         windowList[i]->number --;
549 
550     window->manager = NULL;
551 
552     if (windowActive == window->number)
553     {
554         windowActive = -1;
555         windowOver = -1;
556 
557         tabIndex = NULL;
558         tabSubIndex = -1;
559     }
560     if (windowOver == window->number)
561     {
562         windowOver = -1;
563 
564         tabIndex = NULL;
565         tabSubIndex = -1;
566     }
567 }
568 
569     // A function to get the texture, in case it changes at some point
GetTexture(CWTextures num)570 GX2Texture** WindowManager::GetTexture(CWTextures num)
571 {
572     return &this->textureList[num];
573 }
574 
575     // A function to get the texture list itself
GetTextureList()576 GX2Texture** WindowManager::GetTextureList()
577 {
578     return this->textureList;
579 }
580