1 /*---------------------------------------------------------------------------*
2 
3   Copyright (C) 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 ////////////////////////////////////////////////////
14 //
15 // Double Combo Box functions
16 //
17 ////////////////////////////////////////////////////
18 
Add(const DoubleComboBox & item)19 DoubleComboBox* DoubleComboBox::Add(const DoubleComboBox& item)
20 {
21     ASSERT(item.count > 0 && "Double combo boxes cannot be empty!");
22 
23     DoubleComboBox* pItem = new DoubleComboBox(item);
24     item.window->menuItems.push_back(pItem);
25     item.window->AddTabbing(pItem);
26     return pItem;
27 }
28 
29 	// Determines if the cursor is over this combo box
CursorOver()30 bool DoubleComboBox::CursorOver()
31 {
32 	if (CursorColliding(this->x, this->y, this->width, this->height))
33 		return true;
34 
35 ////////////////////////////////////////////////////////////////////////////////////////////////////
36 	if (this->status < 2)
37 		return false;
38 
39 	if (this->bar1.status == 2)
40 		return true;
41 
42 	int visibleCount = this->count - this->bar1.pos;
43 	if (visibleCount > COMBOBOX_COUNT)
44 		visibleCount = COMBOBOX_COUNT;
45 
46     float flipMul = 1.0;
47     if (this->flipped)
48         flipMul = -1.0;
49 
50 	if (CursorColliding(this->x, this->y - (this->height / 2 + this->height * visibleCount / 2) * flipMul, this->width / 2, this->height * visibleCount, true))
51 		return true;
52 
53 ////////////////////////////////////////////////////////////////////////////////////////////////////
54 	if (this->status < 3)
55 		return false;
56 
57 	if (this->bar2.status == 2)
58 		return true;
59 
60 	visibleCount = this->data[this->pos1]->GetCount() - this->bar2.pos;
61 	if (visibleCount > COMBOBOX_COUNT)
62 		visibleCount = COMBOBOX_COUNT;
63 
64     float lOffset = (this->height + this->off * this->height) * flipMul - this->offset;
65 
66 	if (CursorColliding(this->x + this->width / 2, this->y + this->height / 2 - this->height * visibleCount / 2 - lOffset, this->width / 2, this->height * visibleCount, true))
67 		return true;
68 
69 	return false;
70 }
71 
72 	// Updates the combo box
Update()73 bool DoubleComboBox::Update()
74 {
75 		// Going back
76 	if (NegativeTriggered())
77 	{
78 		if (status >= 2)
79 			TabSubIndex() = -1;
80 
81 		if (this->status >= 3)
82 		{
83 			this->status = 2;
84 			TabSubIndex() = this->pos1 - this->bar1.pos;
85 		}
86 		else
87 		{
88 			this->pos1 = (*this->target1);
89 			this->pos2 = (*this->target2);
90 			this->status = 0;
91 		}
92 	}
93 
94 		// Also going back
95 	if (TabIndex() && TabIndex()->object == this && this->status >= 3 && LeftTriggered())
96 	{
97 		this->status = 2;
98 		TabSubIndex() = this->pos1 - this->bar1.pos;
99 	}
100 
101 		// Activate tabbing if we're in PAD mode
102 	if (this->status >= 2 && !TabIndex() && UsingPad())
103 	{
104         this->bar1.Reset();
105         this->bar2.Reset();
106 
107 		TabIndex() = window->DummyTab();
108 		TabSubIndex() = 0;
109 
110 		while (TabIndex()->object != this)
111 			TabIndex() = TabIndex()->next;
112 	}
113 
114     float flipMul = 1.0;
115     if (this->flipped)
116         flipMul = -1.0;
117 
118     bool overSecond = false;
119 
120 		// The second list of options
121 	if (this->status >= 3)
122 	{
123 		int count = this->data[this->pos1]->GetCount();
124         float lOffset = (this->height + this->off * this->height) * flipMul - this->offset;
125 
126 		int pos = this->pos1;
127 		if (this->status == 2)
128 			pos = this->over1;
129 
130         int position = this->bar1.pos + 0.5;
131 
132         gIgnoreBounds = true;
133         this->bar2.x = this->x + size.x * 3 / 4 - SCROLLBAR_WIDTH / 2;
134         this->bar2.y = this->y - (5.5 + pos - position) * this->height;
135         this->bar2.count = count;
136 		this->bar2.Update();
137         gIgnoreBounds = false;
138 
139 		float widthOffset = 0;
140 
141 		int visibleCount = count - this->bar2.pos;
142 		if (visibleCount > COMBOBOX_COUNT)
143 			visibleCount = COMBOBOX_COUNT;
144 
145 		if (count > COMBOBOX_COUNT)
146 			widthOffset = this->bar2.width / 2;
147 
148 			// Combo boxes deal specially with tabbing
149 		if (TabIndex())
150 		{
151 				// Tabbed away from us!
152 			if (TabIndex()->object != this)
153 			{
154                 this->pos1 = (*this->target1);
155                 this->pos2 = (*this->target2);
156 				this->status = 0;
157 
158 				this->over1 = -1;
159 				this->over2 = -1;
160 				return true;
161 			}
162 
163             bool downTriggered = false;
164             bool upTriggered = false;
165 
166             downTriggered = DownTriggered();
167             upTriggered = UpTriggered();
168 
169 			if (downTriggered)
170 			{
171 				TabSubIndex() ++;
172 				if (TabSubIndex() > visibleCount - 1)
173 				{
174 					TabSubIndex() = visibleCount - 1;
175 
176 					this->bar2.pos ++;
177 
178 					if (this->bar2.pos > count - COMBOBOX_COUNT)
179 						this->bar2.pos --;
180 				}
181 			}
182 
183 			if (upTriggered)
184 			{
185 				TabSubIndex() --;
186 				if (TabSubIndex() < 0)
187 				{
188 					TabSubIndex() = 0;
189 
190 					this->bar2.pos --;
191 
192 					if (this->bar2.pos < 0)
193 						this->bar2.pos ++;
194 				}
195 			}
196 
197 			CursorY() = window->GetY() + this->y - this->height * TabSubIndex() - lOffset;
198 			CursorX() = window->GetX() + this->x + this->width / 2 - widthOffset;
199 		}
200 
201 			// See if we're over one of the choices
202 		if (CursorColliding(this->x + this->width / 2 - widthOffset, this->y + this->height / 2 - this->height * visibleCount / 2 - lOffset, this->width / 2 - widthOffset * 2, this->height * visibleCount, true))
203 		{
204 			int choice = (window->GetY() + this->y + this->height / 2 - CursorY() - lOffset) / this->height;
205 
206 			if (choice < 0)
207 				choice = 0;
208 
209 				// We've found our choice!
210 			this->over2 = this->bar2.pos + choice;
211 
212 			if (PositiveTriggered())
213 			{
214 				this->pos2 = this->bar2.pos + choice;
215 				this->status = 0;
216                 (*this->target1) = this->pos1;
217                 (*this->target2) = this->pos2;
218 
219 				TabSubIndex() = -1;
220 
221                 this->func(this->funcExtra);
222 
223 				return false;
224 			}
225 		}
226 		else
227 		{
228 			this->over2 = -1;
229 		}
230 
231 		if (CursorColliding(this->x + this->width / 2, this->y + this->height / 2 - this->height * visibleCount / 2 - lOffset, this->width / 2, this->height * visibleCount, true))
232             overSecond = true;
233 	}
234 
235 		// The first list of options
236 	if (this->status >= 2)
237 	{
238         gIgnoreBounds = true;
239         this->bar1.x = this->x + size.x / 4 - SCROLLBAR_WIDTH / 2;
240         this->bar1.y = this->y - this->height * 5.5;
241         this->bar1.count = this->count;
242 		this->bar1.Update();
243         gIgnoreBounds = false;
244 
245 		float widthOffset = 0;
246 
247 		int visibleCount = this->count - this->bar1.pos;
248 		if (visibleCount > COMBOBOX_COUNT)
249 			visibleCount = COMBOBOX_COUNT;
250 
251 		if (this->count > COMBOBOX_COUNT)
252 			widthOffset = this->bar1.width / 2;
253 
254 			// Combo boxes deal specially with tabbing
255 		if (status == 2 && TabIndex())
256 		{
257 				// Tabbed away from us!
258 			if (TabIndex()->object != this)
259 			{
260                 this->pos1 = (*this->target1);
261                 this->pos2 = (*this->target2);
262 				this->status = 0;
263 				return true;
264 			}
265 
266 				// Just started
267 			if (TabSubIndex() == -1)
268 				TabSubIndex() = 0;
269 
270             bool downTriggered = false;
271             bool upTriggered = false;
272 
273             if (this->flipped)
274             {
275                 downTriggered = UpTriggered();
276                 upTriggered = DownTriggered();
277             }
278             else
279             {
280                 downTriggered = DownTriggered();
281                 upTriggered = UpTriggered();
282             }
283 
284 			if (downTriggered)
285 			{
286 				TabSubIndex() ++;
287 				if (TabSubIndex() > visibleCount - 1)
288 				{
289 					TabSubIndex() = visibleCount - 1;
290 
291 					this->bar1.pos ++;
292 
293 					if (this->bar1.pos > this->count - COMBOBOX_COUNT)
294 						this->bar1.pos --;
295 				}
296 			}
297 
298 			if (upTriggered)
299 			{
300 				TabSubIndex() --;
301 				if (TabSubIndex() < 0)
302 				{
303 					TabSubIndex() = 0;
304 
305 					this->bar1.pos --;
306 
307 					if (this->bar1.pos < 0)
308 						this->bar1.pos ++;
309 				}
310 			}
311 
312 			CursorY() = window->GetY() + this->y - (this->height + this->height * TabSubIndex()) * flipMul;
313 			CursorX() = window->GetX() + this->x;
314 		}
315 
316 			// See if we're over one of the choices
317 		if (CursorColliding(this->x - widthOffset, this->y - (this->height / 2 + this->height * visibleCount / 2) * flipMul, this->width / 2 - widthOffset * 2, this->height * visibleCount, true))
318 		{
319 			int choice = (window->GetY() + this->y - (this->height / 2) * flipMul - CursorY()) / this->height * flipMul;
320 
321 			if (choice < 0)
322 				choice = 0;
323 
324 				// We've found our choice!
325 			this->over1 = this->bar1.pos + choice;
326 
327             this->bar2.pos = 0;
328             this->bar2.wasMax = false;
329 
330 			if (PositiveTriggered() || (TabIndex() && RightTriggered()))
331 			{
332 				int old = this->pos1;
333 				this->off = choice;
334 				this->pos1 = this->bar1.pos + choice;
335 				this->pos2 = -1;
336                 this->over2 = -1;
337 
338 				if (this->status == 4 && old == this->pos1)
339 					this->status = 3;
340 				else
341 					this->status = 4;
342 
343 				if (TabIndex())
344 					TabSubIndex() = 0;
345 
346 				return false;
347 			}
348 
349 			if (this->status == 3)
350 			{
351 				this->status = 2;
352 				this->over2 = -1;
353 				this->pos2 = -1;
354 			}
355 		}
356 		else if (status >= 3) {}
357 			// If we moved to the right, select the choice anyway!
358 		else if (this->over1 >= 0 && CursorColliding(this->x + this->width / 2 - widthOffset, this->y - (this->height / 2 + this->height * COMBOBOX_COUNT / 2 + this->over1 * this->height) * flipMul, this->width / 2 - widthOffset * 2, this->height * COMBOBOX_COUNT, true))
359 		{
360 			this->off = this->over1 - this->bar1.pos + 0.5;
361 			this->pos1 = this->over1;
362 			this->pos2 = -1;
363 			this->status = 3;
364 
365 			return Update();
366 		}
367 		else
368 		{
369 			this->over1 = -1;
370 			this->over2 = -1;
371 		}
372 
373 		if (overSecond || CursorColliding(this->x, this->y - (this->height / 2 + this->height * visibleCount / 2) * flipMul, this->width / 2, this->height * visibleCount, true))
374             return false;
375 
376 		if (PositiveTriggered())
377 		{
378             this->pos1 = (*this->target1);
379             this->pos2 = (*this->target2);
380 			this->status = 0;
381 		}
382 
383         if (this->CursorOver())
384             return false;
385 
386 		return true;
387 	}
388 
389     this->pos1 = (*this->target1);
390     this->pos2 = (*this->target2);
391 
392     this->bar1.Reset();
393     this->bar2.Reset();
394 
395 	int status;
396 
397 		// We don't care as much if the cursor isn't being triggered
398 	if (!PositiveTriggered())
399 		status = 1;
400 	else
401 		status = 2;
402 
403 		// See if we just hit something
404 	if (CursorColliding(this->x, this->y, this->width, this->height))
405 		this->status = status;
406 	else
407 		this->status = 0;
408 
409 	this->over1 = -1;
410 	this->over2 = -1;
411 
412     return (this->status == 0);
413 }
414 
415 	// Draws the combo box
Draw()416 void DoubleComboBox::Draw()
417 {
418     float arrowWidth = this->height;
419 
420         // The main box itself
421 	if (this->status == 1)
422 	{
423 		DrawBox(CVec3(this->x, this->y, this->z + 0.003), size, CVec4(0.13, 0.13, 0.13));
424 		DrawBox(CVec3(this->x + (this->width - arrowWidth) / 2, this->y, this->z + 0.0035), CVec2(arrowWidth, this->height), CVec4(0.1, 0.1, 0.1));
425 	}
426 	else
427 	{
428 		DrawBox(CVec3(this->x, this->y, this->z + 0.003), size, CVec4(0.03, 0.03, 0.03));
429 		DrawBox(CVec3(this->x + (this->width - arrowWidth) / 2, this->y, this->z + 0.0035), CVec2(arrowWidth, this->height), CVec4(0.0, 0.0, 0.0));
430 	}
431 
432         // The "selected" text
433 	char buffer[50];
434 	sprintf(buffer, "%s - %s", this->data[(*this->target1)]->GetName(), this->data[(*this->target1)]->GetPointers()[(*this->target2)]->GetName());
435 
436 	DrawText(buffer, CVec3(this->x - arrowWidth / 2, this->y, this->z + 0.01), CVec2(this->width - arrowWidth, this->height * 0.7), CVec4(1.0, 1.0, 1.0));
437 
438         // The arrow on the right
439 	DrawTexQuad(*GetTexture(CW_TEXTURE_COMBODOWNARROW), CVec3(this->x + (this->width - arrowWidth) / 2, this->y, this->z + 0.005), CVec2(arrowWidth / 2, this->height / 2), CVec4(0.8, 0.8, 0.8));
440 
441 		// If the combo box is selected...
442 	if (this->status >= 2)
443 	{
444             // Draw regardless of depth
445         GX2SetDepthOnlyControl(GX2_ENABLE,
446                                GX2_ENABLE,
447                                GX2_COMPARE_ALWAYS);
448 
449         float widthOffset = 0;
450 
451         if (this->count > COMBOBOX_COUNT)
452             widthOffset = this->bar1.width / 2;
453 
454 		int count = COMBOBOX_COUNT;
455 		if (count > this->count)
456 			count = this->count;
457 
458         this->flipped = false;
459         if (window->GetY() + this->y - this->height / 2 - this->height * count < -1.0 && window->GetY() + this->y + this->height / 2 + this->height * count < 1.0)
460             this->flipped = true;
461 
462         float flipMul = 1.0;
463         if (this->flipped)
464             flipMul = -1.0;
465 
466 		DrawBox(CVec3(this->x, this->y - (this->height / 2 + this->height * count / 2) * flipMul, this->z + 0.5), CVec2(this->width / 2, this->height * count), CVec4(0.0, 0.0, 0.0));
467 
468 		int position = this->bar1.pos + 0.5;
469 
470 		float startY = this->y - this->height * flipMul;
471 
472 			// Draw a "highlight" if a choice is over
473 		if (this->over1 >= position && this->over1 < position + COMBOBOX_COUNT)
474 			DrawQuad(CVec3(this->x, startY - (this->over1 - position) * this->height * flipMul, this->z + 0.51), CVec2(this->width / 2, this->height), CVec4(0.2, 0.2, 0.2));
475 
476 			// Draw a "highlight" if a choice is selected
477 		if (this->pos1 >= position && this->pos1 < position + COMBOBOX_COUNT && status >= 3)
478 			DrawQuad(CVec3(this->x - widthOffset, startY - (this->pos1 - position) * this->height * flipMul, this->z + 0.52), CVec2(this->width / 2 - widthOffset * 2, this->height), CVec4(0.3, 0.3, 0.3));
479 
480 		if (count > position + COMBOBOX_COUNT)
481 			count = position + COMBOBOX_COUNT;
482 
483 			// Figure out what color most of them will be
484 		float color = 0.4;
485 		if (this->status == 2)
486 			color = 1.0;
487 
488 			// Draw only the elements we want to see!
489 		for (int i = 0; i < count; ++i)
490 		{
491 			float tempColor = color;
492 			if (i + position == this->over1 || i + position == this->pos1)
493 				tempColor = 1.0;
494 
495 			DrawText(this->data[i + position]->GetName(), CVec3(this->x - this->bar1.width / 2, startY, this->z + 0.53), CVec2(this->width / 2 - this->bar1.width, this->height * 0.5), CVec4(tempColor, tempColor, tempColor));
496 
497 			startY -= this->height * flipMul;
498 		}
499             // Draw regardless of depth
500         GX2SetDepthOnlyControl(GX2_ENABLE,
501                                GX2_ENABLE,
502                                GX2_COMPARE_ALWAYS);
503 
504         this->bar1.x = this->x + size.x / 4 - SCROLLBAR_WIDTH / 2;
505         this->bar1.y = this->y - this->height * 5.5 * flipMul;
506         this->bar1.count = this->count;
507 		this->bar1.Draw();
508 
509 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
510 			// If an option in the first combo box is selected...
511 		int pos = this->pos1;
512 		if (this->status == 2)
513 			pos = this->over1;
514 
515 		if (pos >= 0)
516 		{
517             float widthOffset = 0;
518 
519 			int count = this->data[pos]->GetCount();
520 
521             if (count > COMBOBOX_COUNT)
522             {
523                 widthOffset = this->bar2.width / 2;
524 				count = COMBOBOX_COUNT;
525             }
526 
527 			int position = this->bar1.pos + 0.5;
528 			int position2 = this->bar2.pos + 0.5;
529 
530 			float startY = this->y - (this->height + (pos - position) * this->height) * flipMul;
531 
532             this->offset = 0;
533             if (window->GetY() + startY - this->height / 2 - this->height * count < -1.0)
534                 this->offset = -window->GetY() - startY - this->height / 2 + this->height * count - 1.0;
535 
536             startY += this->offset;
537 
538 			DrawBox(CVec3(this->x + this->width / 2, this->y + this->height / 2 - this->height * count / 2 - (this->height + (pos - position) * this->height) * flipMul + this->offset, this->z + 0.5), CVec2(this->width / 2, this->height * count), CVec4(0.0, 0.0, 0.0));
539 
540 				// Draw a "highlight" if a choice is over or selected
541 			if (this->status >= 3)
542 			{
543 				if (this->over2 >= position2 && this->over2 < position2 + COMBOBOX_COUNT)
544 					DrawQuad(CVec3(this->x - widthOffset + this->width / 2, startY - (this->over2 - position2) * this->height, this->z + 0.51), CVec2(this->width / 2 - widthOffset * 2, this->height), CVec4(0.2, 0.2, 0.2));
545 			}
546 
547 			//float arrowY = startY;
548 
549 				// Draw only the elements we want to see!
550 			SubCombo** pointers = this->data[pos]->GetPointers();
551 			for (int i = 0; i < count; ++i)
552 			{
553 				DrawText(pointers[i + position2]->GetName(), CVec3(this->x + this->width / 2 - this->bar2.width / 2, startY, this->z + 0.52), CVec2(this->width / 2 - this->bar2.width, this->height * 0.5), CVec4(1.0, 1.0, 1.0));
554 
555 				startY -= this->height;
556 			}
557 
558 				// Display a helpful arrow to point at the other box!
559 			//DrawTexQuad(arrowTex[RIGHTARROW], this->x + this->width / 4, arrowY, this->z + 0.53, this->height * 0.7, this->height * 0.7, 0.8, 0.8, 0.8);
560 
561             this->bar2.count = this->data[pos]->GetCount();
562             this->bar2.x = this->x + size.x * 3 / 4 - SCROLLBAR_WIDTH / 2;
563             this->bar2.y = startY + this->height / 2 + this->height * count / 2;
564 			this->bar2.Draw();
565 		}
566 
567             // Draw normally again
568         GX2SetDepthOnlyControl(GX2_ENABLE,
569                                GX2_ENABLE,
570                                GX2_COMPARE_LESS);
571 	}
572 }
573 
Reset()574 void DoubleComboBox::Reset()
575 {
576     if (this->status > 0)
577         this->status = 0;
578 	this->bar1.Reset();
579 	this->bar2.Reset();
580 }
581