1 /*
2 ** optionmenuitems.h
3 ** Control items for option menus
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 2010 Christoph Oelckers
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 #include "v_text.h"
35 #include "gstrings.h"
36
37
38 void M_DrawConText (int color, int x, int y, const char *str);
39 void M_SetVideoMode();
40
41
42
43 //=============================================================================
44 //
45 // opens a submenu, action is a submenu name
46 //
47 //=============================================================================
48
49 class FOptionMenuItemSubmenu : public FOptionMenuItem
50 {
51 int mParam;
52 public:
53 FOptionMenuItemSubmenu(const char *label, const char *menu, int param = 0)
FOptionMenuItem(label,menu)54 : FOptionMenuItem(label, menu)
55 {
56 mParam = param;
57 }
58
Draw(FOptionMenuDescriptor * desc,int y,int indent,bool selected)59 int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
60 {
61 drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColorMore);
62 return indent;
63 }
64
Activate()65 bool Activate()
66 {
67 S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
68 M_SetMenu(mAction, mParam);
69 return true;
70 }
71 };
72
73
74 //=============================================================================
75 //
76 // Executes a CCMD, action is a CCMD name
77 //
78 //=============================================================================
79
80 class FOptionMenuItemCommand : public FOptionMenuItemSubmenu
81 {
82 public:
FOptionMenuItemCommand(const char * label,const char * menu)83 FOptionMenuItemCommand(const char *label, const char *menu)
84 : FOptionMenuItemSubmenu(label, menu)
85 {
86 }
87
Activate()88 bool Activate()
89 {
90 S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
91 C_DoCommand(mAction);
92 return true;
93 }
94
95 };
96
97 //=============================================================================
98 //
99 // Executes a CCMD after confirmation, action is a CCMD name
100 //
101 //=============================================================================
102
103 class FOptionMenuItemSafeCommand : public FOptionMenuItemCommand
104 {
105 // action is a CCMD
106 public:
FOptionMenuItemSafeCommand(const char * label,const char * menu)107 FOptionMenuItemSafeCommand(const char *label, const char *menu)
108 : FOptionMenuItemCommand(label, menu)
109 {
110 }
111
MenuEvent(int mkey,bool fromcontroller)112 bool MenuEvent (int mkey, bool fromcontroller)
113 {
114 if (mkey == MKEY_MBYes)
115 {
116 C_DoCommand(mAction);
117 return true;
118 }
119 return FOptionMenuItemCommand::MenuEvent(mkey, fromcontroller);
120 }
121
Activate()122 bool Activate()
123 {
124 M_StartMessage("Do you really want to do this?", 0);
125 return true;
126 }
127 };
128
129 //=============================================================================
130 //
131 // Base class for option lists
132 //
133 //=============================================================================
134
135 class FOptionMenuItemOptionBase : public FOptionMenuItem
136 {
137 protected:
138 // action is a CVAR
139 FName mValues; // Entry in OptionValues table
140 FBaseCVar *mGrayCheck;
141 int mCenter;
142 public:
143
144 enum
145 {
146 OP_VALUES = 0x11001
147 };
148
FOptionMenuItemOptionBase(const char * label,const char * menu,const char * values,const char * graycheck,int center)149 FOptionMenuItemOptionBase(const char *label, const char *menu, const char *values, const char *graycheck, int center)
150 : FOptionMenuItem(label, menu)
151 {
152 mValues = values;
153 mGrayCheck = (FBoolCVar*)FindCVar(graycheck, NULL);
154 mCenter = center;
155 }
156
SetString(int i,const char * newtext)157 bool SetString(int i, const char *newtext)
158 {
159 if (i == OP_VALUES)
160 {
161 FOptionValues **opt = OptionValues.CheckKey(newtext);
162 mValues = newtext;
163 if (opt != NULL && *opt != NULL)
164 {
165 int s = GetSelection();
166 if (s >= (int)(*opt)->mValues.Size()) s = 0;
167 SetSelection(s); // readjust the CVAR if its value is outside the range now
168 return true;
169 }
170 }
171 return false;
172 }
173
174
175
176 //=============================================================================
177 virtual int GetSelection() = 0;
178 virtual void SetSelection(int Selection) = 0;
179
180 //=============================================================================
Draw(FOptionMenuDescriptor * desc,int y,int indent,bool selected)181 int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
182 {
183 bool grayed = mGrayCheck != NULL && !(mGrayCheck->GetGenericRep(CVAR_Bool).Bool);
184
185 if (mCenter)
186 {
187 indent = (screen->GetWidth() / 2);
188 }
189 drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor, grayed);
190
191 int overlay = grayed? MAKEARGB(96,48,0,0) : 0;
192 const char *text;
193 int Selection = GetSelection();
194 FOptionValues **opt = OptionValues.CheckKey(mValues);
195 if (Selection < 0 || opt == NULL || *opt == NULL)
196 {
197 text = "Unknown";
198 }
199 else
200 {
201 text = (*opt)->mValues[Selection].Text;
202 }
203 if (*text == '$') text = GStrings(text + 1);
204 screen->DrawText (SmallFont, OptionSettings.mFontColorValue, indent + CURSORSPACE, y,
205 text, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE);
206 return indent;
207 }
208
209 //=============================================================================
MenuEvent(int mkey,bool fromcontroller)210 bool MenuEvent (int mkey, bool fromcontroller)
211 {
212 FOptionValues **opt = OptionValues.CheckKey(mValues);
213 if (opt != NULL && *opt != NULL && (*opt)->mValues.Size() > 0)
214 {
215 int Selection = GetSelection();
216 if (mkey == MKEY_Left)
217 {
218 if (Selection == -1) Selection = 0;
219 else if (--Selection < 0) Selection = (*opt)->mValues.Size()-1;
220 }
221 else if (mkey == MKEY_Right || mkey == MKEY_Enter)
222 {
223 if (++Selection >= (int)(*opt)->mValues.Size()) Selection = 0;
224 }
225 else
226 {
227 return FOptionMenuItem::MenuEvent(mkey, fromcontroller);
228 }
229 SetSelection(Selection);
230 S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
231 }
232 return true;
233 }
234
Selectable()235 bool Selectable()
236 {
237 return !(mGrayCheck != NULL && !(mGrayCheck->GetGenericRep(CVAR_Bool).Bool));
238 }
239 };
240
241 //=============================================================================
242 //
243 // Change a CVAR, action is the CVAR name
244 //
245 //=============================================================================
246
247 class FOptionMenuItemOption : public FOptionMenuItemOptionBase
248 {
249 // action is a CVAR
250 FBaseCVar *mCVar;
251 public:
252
FOptionMenuItemOption(const char * label,const char * menu,const char * values,const char * graycheck,int center)253 FOptionMenuItemOption(const char *label, const char *menu, const char *values, const char *graycheck, int center)
254 : FOptionMenuItemOptionBase(label, menu, values, graycheck, center)
255 {
256 mCVar = FindCVar(mAction, NULL);
257 }
258
259 //=============================================================================
GetSelection()260 int GetSelection()
261 {
262 int Selection = -1;
263 FOptionValues **opt = OptionValues.CheckKey(mValues);
264 if (opt != NULL && *opt != NULL && mCVar != NULL && (*opt)->mValues.Size() > 0)
265 {
266 if ((*opt)->mValues[0].TextValue.IsEmpty())
267 {
268 UCVarValue cv = mCVar->GetGenericRep(CVAR_Float);
269 for(unsigned i = 0; i < (*opt)->mValues.Size(); i++)
270 {
271 if (fabs(cv.Float - (*opt)->mValues[i].Value) < FLT_EPSILON)
272 {
273 Selection = i;
274 break;
275 }
276 }
277 }
278 else
279 {
280 UCVarValue cv = mCVar->GetGenericRep(CVAR_String);
281 for(unsigned i = 0; i < (*opt)->mValues.Size(); i++)
282 {
283 if ((*opt)->mValues[i].TextValue.CompareNoCase(cv.String) == 0)
284 {
285 Selection = i;
286 break;
287 }
288 }
289 }
290 }
291 return Selection;
292 }
293
SetSelection(int Selection)294 void SetSelection(int Selection)
295 {
296 UCVarValue value;
297 FOptionValues **opt = OptionValues.CheckKey(mValues);
298 if (opt != NULL && *opt != NULL && mCVar != NULL && (*opt)->mValues.Size() > 0)
299 {
300 if ((*opt)->mValues[0].TextValue.IsEmpty())
301 {
302 value.Float = (float)(*opt)->mValues[Selection].Value;
303 mCVar->SetGenericRep (value, CVAR_Float);
304 }
305 else
306 {
307 value.String = (*opt)->mValues[Selection].TextValue.LockBuffer();
308 mCVar->SetGenericRep (value, CVAR_String);
309 (*opt)->mValues[Selection].TextValue.UnlockBuffer();
310 }
311 }
312 }
313 };
314
315 //=============================================================================
316 //
317 // This class is used to capture the key to be used as the new key binding
318 // for a control item
319 //
320 //=============================================================================
321
322 class DEnterKey : public DMenu
323 {
DECLARE_CLASS(DEnterKey,DMenu)324 DECLARE_CLASS(DEnterKey, DMenu)
325
326 int *pKey;
327
328 public:
329 DEnterKey(DMenu *parent, int *keyptr)
330 : DMenu(parent)
331 {
332 pKey = keyptr;
333 SetMenuMessage(1);
334 menuactive = MENU_WaitKey; // There should be a better way to disable GUI capture...
335 }
336
TranslateKeyboardEvents()337 bool TranslateKeyboardEvents()
338 {
339 return false;
340 }
341
SetMenuMessage(int which)342 void SetMenuMessage(int which)
343 {
344 if (mParentMenu->IsKindOf(RUNTIME_CLASS(DOptionMenu)))
345 {
346 DOptionMenu *m = barrier_cast<DOptionMenu*>(mParentMenu);
347 FListMenuItem *it = m->GetItem(NAME_Controlmessage);
348 if (it != NULL)
349 {
350 it->SetValue(0, which);
351 }
352 }
353 }
354
Responder(event_t * ev)355 bool Responder(event_t *ev)
356 {
357 if (ev->type == EV_KeyDown)
358 {
359 *pKey = ev->data1;
360 menuactive = MENU_On;
361 SetMenuMessage(0);
362 Close();
363 mParentMenu->MenuEvent((ev->data1 == KEY_ESCAPE)? MKEY_Abort : MKEY_Input, 0);
364 return true;
365 }
366 return false;
367 }
368
Drawer()369 void Drawer()
370 {
371 mParentMenu->Drawer();
372 }
373 };
374
375 #ifndef NO_IMP
IMPLEMENT_ABSTRACT_CLASS(DEnterKey)376 IMPLEMENT_ABSTRACT_CLASS(DEnterKey)
377 #endif
378
379 //=============================================================================
380 //
381 // // Edit a key binding, Action is the CCMD to bind
382 //
383 //=============================================================================
384
385 class FOptionMenuItemControl : public FOptionMenuItem
386 {
387 FKeyBindings *mBindings;
388 int mInput;
389 bool mWaiting;
390 public:
391
392 FOptionMenuItemControl(const char *label, const char *menu, FKeyBindings *bindings)
393 : FOptionMenuItem(label, menu)
394 {
395 mBindings = bindings;
396 mWaiting = false;
397 }
398
399
400 //=============================================================================
401 int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
402 {
403 drawLabel(indent, y, mWaiting? OptionSettings.mFontColorHighlight:
404 (selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor));
405
406 char description[64];
407 int Key1, Key2;
408
409 mBindings->GetKeysForCommand(mAction, &Key1, &Key2);
410 C_NameKeys (description, Key1, Key2);
411 if (description[0])
412 {
413 M_DrawConText(CR_WHITE, indent + CURSORSPACE, y + (OptionSettings.mLinespacing-8)*CleanYfac_1, description);
414 }
415 else
416 {
417 screen->DrawText(SmallFont, CR_BLACK, indent + CURSORSPACE, y + (OptionSettings.mLinespacing-8)*CleanYfac_1, "---",
418 DTA_CleanNoMove_1, true, TAG_DONE);
419 }
420 return indent;
421 }
422
423 //=============================================================================
424 bool MenuEvent(int mkey, bool fromcontroller)
425 {
426 if (mkey == MKEY_Input)
427 {
428 mWaiting = false;
429 mBindings->SetBind(mInput, mAction);
430 return true;
431 }
432 else if (mkey == MKEY_Clear)
433 {
434 mBindings->UnbindACommand(mAction);
435 return true;
436 }
437 else if (mkey == MKEY_Abort)
438 {
439 mWaiting = false;
440 return true;
441 }
442 return false;
443 }
444
445 bool Activate()
446 {
447 S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
448 mWaiting = true;
449 DMenu *input = new DEnterKey(DMenu::CurrentMenu, &mInput);
450 M_ActivateMenu(input);
451 return true;
452 }
453 };
454
455 //=============================================================================
456 //
457 //
458 //
459 //=============================================================================
460
461 class FOptionMenuItemStaticText : public FOptionMenuItem
462 {
463 EColorRange mColor;
464 public:
FOptionMenuItemStaticText(const char * label,bool header)465 FOptionMenuItemStaticText(const char *label, bool header)
466 : FOptionMenuItem(label, NAME_None, true)
467 {
468 mColor = header? OptionSettings.mFontColorHeader : OptionSettings.mFontColor;
469 }
470
Draw(FOptionMenuDescriptor * desc,int y,int indent,bool selected)471 int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
472 {
473 drawLabel(indent, y, mColor);
474 return -1;
475 }
476
Selectable()477 bool Selectable()
478 {
479 return false;
480 }
481
482 };
483
484 //=============================================================================
485 //
486 //
487 //
488 //=============================================================================
489
490 class FOptionMenuItemStaticTextSwitchable : public FOptionMenuItem
491 {
492 EColorRange mColor;
493 FString mAltText;
494 int mCurrent;
495
496 public:
FOptionMenuItemStaticTextSwitchable(const char * label,const char * label2,FName action,bool header)497 FOptionMenuItemStaticTextSwitchable(const char *label, const char *label2, FName action, bool header)
498 : FOptionMenuItem(label, action, true)
499 {
500 mColor = header? OptionSettings.mFontColorHeader : OptionSettings.mFontColor;
501 mAltText = label2;
502 mCurrent = 0;
503 }
504
Draw(FOptionMenuDescriptor * desc,int y,int indent,bool selected)505 int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
506 {
507 const char *txt = mCurrent? (const char*)mAltText : mLabel;
508 if (*txt == '$') txt = GStrings(txt + 1);
509 int w = SmallFont->StringWidth(txt) * CleanXfac_1;
510 int x = (screen->GetWidth() - w) / 2;
511 screen->DrawText (SmallFont, mColor, x, y, txt, DTA_CleanNoMove_1, true, TAG_DONE);
512 return -1;
513 }
514
SetValue(int i,int val)515 bool SetValue(int i, int val)
516 {
517 if (i == 0)
518 {
519 mCurrent = val;
520 return true;
521 }
522 return false;
523 }
524
SetString(int i,const char * newtext)525 bool SetString(int i, const char *newtext)
526 {
527 if (i == 0)
528 {
529 mAltText = newtext;
530 return true;
531 }
532 return false;
533 }
534
Selectable()535 bool Selectable()
536 {
537 return false;
538 }
539 };
540
541 //=============================================================================
542 //
543 //
544 //
545 //=============================================================================
546
547 class FOptionMenuSliderBase : public FOptionMenuItem
548 {
549 // action is a CVAR
550 double mMin, mMax, mStep;
551 int mShowValue;
552 int mDrawX;
553 int mSliderShort;
554
555 public:
FOptionMenuSliderBase(const char * label,double min,double max,double step,int showval)556 FOptionMenuSliderBase(const char *label, double min, double max, double step, int showval)
557 : FOptionMenuItem(label, NAME_None)
558 {
559 mMin = min;
560 mMax = max;
561 mStep = step;
562 mShowValue = showval;
563 mDrawX = 0;
564 mSliderShort = 0;
565 }
566
567 virtual double GetSliderValue() = 0;
568 virtual void SetSliderValue(double val) = 0;
569
570 //=============================================================================
571 //
572 // Draw a slider. Set fracdigits negative to not display the current value numerically.
573 //
574 //=============================================================================
575
DrawSlider(int x,int y,double min,double max,double cur,int fracdigits,int indent)576 void DrawSlider (int x, int y, double min, double max, double cur, int fracdigits, int indent)
577 {
578 char textbuf[16];
579 double range;
580 int maxlen = 0;
581 int right = x + (12*8 + 4) * CleanXfac_1;
582 int cy = y + (OptionSettings.mLinespacing-8)*CleanYfac_1;
583
584 range = max - min;
585 double ccur = clamp(cur, min, max) - min;
586
587 if (fracdigits >= 0)
588 {
589 mysnprintf(textbuf, countof(textbuf), "%.*f", fracdigits, max);
590 maxlen = SmallFont->StringWidth(textbuf) * CleanXfac_1;
591 }
592
593 mSliderShort = right + maxlen > screen->GetWidth();
594
595 if (!mSliderShort)
596 {
597 M_DrawConText(CR_WHITE, x, cy, "\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12");
598 M_DrawConText(CR_ORANGE, x + int((5 + ((ccur * 78) / range)) * CleanXfac_1), cy, "\x13");
599 }
600 else
601 {
602 // On 320x200 we need a shorter slider
603 M_DrawConText(CR_WHITE, x, cy, "\x10\x11\x11\x11\x11\x11\x12");
604 M_DrawConText(CR_ORANGE, x + int((5 + ((ccur * 38) / range)) * CleanXfac_1), cy, "\x13");
605 right -= 5*8*CleanXfac_1;
606 }
607
608 if (fracdigits >= 0 && right + maxlen <= screen->GetWidth())
609 {
610 mysnprintf(textbuf, countof(textbuf), "%.*f", fracdigits, cur);
611 screen->DrawText(SmallFont, CR_DARKGRAY, right, y, textbuf, DTA_CleanNoMove_1, true, TAG_DONE);
612 }
613 }
614
615
616 //=============================================================================
Draw(FOptionMenuDescriptor * desc,int y,int indent,bool selected)617 int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
618 {
619 drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor);
620 mDrawX = indent + CURSORSPACE;
621 DrawSlider (mDrawX, y, mMin, mMax, GetSliderValue(), mShowValue, indent);
622 return indent;
623 }
624
625 //=============================================================================
MenuEvent(int mkey,bool fromcontroller)626 bool MenuEvent (int mkey, bool fromcontroller)
627 {
628 double value = GetSliderValue();
629
630 if (mkey == MKEY_Left)
631 {
632 value -= mStep;
633 }
634 else if (mkey == MKEY_Right)
635 {
636 value += mStep;
637 }
638 else
639 {
640 return FOptionMenuItem::MenuEvent(mkey, fromcontroller);
641 }
642 SetSliderValue(clamp(value, mMin, mMax));
643 S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
644 return true;
645 }
646
MouseEvent(int type,int x,int y)647 bool MouseEvent(int type, int x, int y)
648 {
649 DOptionMenu *lm = static_cast<DOptionMenu*>(DMenu::CurrentMenu);
650 if (type != DMenu::MOUSE_Click)
651 {
652 if (!lm->CheckFocus(this)) return false;
653 }
654 if (type == DMenu::MOUSE_Release)
655 {
656 lm->ReleaseFocus();
657 }
658
659 int slide_left = mDrawX+8*CleanXfac_1;
660 int slide_right = slide_left + (10*8*CleanXfac_1 >> mSliderShort); // 12 char cells with 8 pixels each.
661
662 if (type == DMenu::MOUSE_Click)
663 {
664 if (x < slide_left || x >= slide_right) return true;
665 }
666
667 x = clamp(x, slide_left, slide_right);
668 double v = mMin + ((x - slide_left) * (mMax - mMin)) / (slide_right - slide_left);
669 if (v != GetSliderValue())
670 {
671 SetSliderValue(v);
672 //S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
673 }
674 if (type == DMenu::MOUSE_Click)
675 {
676 lm->SetFocus(this);
677 }
678 return true;
679 }
680
681 };
682
683 //=============================================================================
684 //
685 //
686 //
687 //=============================================================================
688
689 class FOptionMenuSliderCVar : public FOptionMenuSliderBase
690 {
691 FBaseCVar *mCVar;
692 public:
FOptionMenuSliderCVar(const char * label,const char * menu,double min,double max,double step,int showval)693 FOptionMenuSliderCVar(const char *label, const char *menu, double min, double max, double step, int showval)
694 : FOptionMenuSliderBase(label, min, max, step, showval)
695 {
696 mCVar = FindCVar(menu, NULL);
697 }
698
GetSliderValue()699 double GetSliderValue()
700 {
701 if (mCVar != NULL)
702 {
703 return mCVar->GetGenericRep(CVAR_Float).Float;
704 }
705 else
706 {
707 return 0;
708 }
709 }
710
SetSliderValue(double val)711 void SetSliderValue(double val)
712 {
713 if (mCVar != NULL)
714 {
715 UCVarValue value;
716 value.Float = (float)val;
717 mCVar->SetGenericRep(value, CVAR_Float);
718 }
719 }
720 };
721
722 //=============================================================================
723 //
724 //
725 //
726 //=============================================================================
727
728 class FOptionMenuSliderVar : public FOptionMenuSliderBase
729 {
730 float *mPVal;
731 public:
732
FOptionMenuSliderVar(const char * label,float * pVal,double min,double max,double step,int showval)733 FOptionMenuSliderVar(const char *label, float *pVal, double min, double max, double step, int showval)
734 : FOptionMenuSliderBase(label, min, max, step, showval)
735 {
736 mPVal = pVal;
737 }
738
GetSliderValue()739 double GetSliderValue()
740 {
741 return *mPVal;
742 }
743
SetSliderValue(double val)744 void SetSliderValue(double val)
745 {
746 *mPVal = (float)val;
747 }
748 };
749
750 //=============================================================================
751 //
752 // // Edit a key binding, Action is the CCMD to bind
753 //
754 //=============================================================================
755
756 class FOptionMenuItemColorPicker : public FOptionMenuItem
757 {
758 FColorCVar *mCVar;
759 public:
760
761 enum
762 {
763 CPF_RESET = 0x20001,
764 };
765
FOptionMenuItemColorPicker(const char * label,const char * menu)766 FOptionMenuItemColorPicker(const char *label, const char *menu)
767 : FOptionMenuItem(label, menu)
768 {
769 FBaseCVar *cv = FindCVar(menu, NULL);
770 if (cv != NULL && cv->GetRealType() == CVAR_Color)
771 {
772 mCVar = (FColorCVar*)cv;
773 }
774 else mCVar = NULL;
775 }
776
777 //=============================================================================
Draw(FOptionMenuDescriptor * desc,int y,int indent,bool selected)778 int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
779 {
780 drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor);
781
782 if (mCVar != NULL)
783 {
784 int box_x = indent + CURSORSPACE;
785 int box_y = y + CleanYfac_1;
786 screen->Clear (box_x, box_y, box_x + 32*CleanXfac_1, box_y + OptionSettings.mLinespacing*CleanYfac_1,
787 -1, (uint32)*mCVar | 0xff000000);
788 }
789 return indent;
790 }
791
SetValue(int i,int v)792 bool SetValue(int i, int v)
793 {
794 if (i == CPF_RESET && mCVar != NULL)
795 {
796 mCVar->ResetToDefault();
797 return true;
798 }
799 return false;
800 }
801
Activate()802 bool Activate()
803 {
804 if (mCVar != NULL)
805 {
806 S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
807 DMenu *picker = StartPickerMenu(DMenu::CurrentMenu, mLabel, mCVar);
808 if (picker != NULL)
809 {
810 M_ActivateMenu(picker);
811 return true;
812 }
813 }
814 return false;
815 }
816 };
817
818 class FOptionMenuScreenResolutionLine : public FOptionMenuItem
819 {
820 FString mResTexts[3];
821 int mSelection;
822 int mHighlight;
823 int mMaxValid;
824 public:
825
826 enum
827 {
828 SRL_INDEX = 0x30000,
829 SRL_SELECTION = 0x30003,
830 SRL_HIGHLIGHT = 0x30004,
831 };
832
FOptionMenuScreenResolutionLine(const char * action)833 FOptionMenuScreenResolutionLine(const char *action)
834 : FOptionMenuItem("", action)
835 {
836 mSelection = 0;
837 mHighlight = -1;
838 }
839
SetValue(int i,int v)840 bool SetValue(int i, int v)
841 {
842 if (i == SRL_SELECTION)
843 {
844 mSelection = v;
845 return true;
846 }
847 else if (i == SRL_HIGHLIGHT)
848 {
849 mHighlight = v;
850 return true;
851 }
852 return false;
853 }
854
GetValue(int i,int * v)855 bool GetValue(int i, int *v)
856 {
857 if (i == SRL_SELECTION)
858 {
859 *v = mSelection;
860 return true;
861 }
862 return false;
863 }
864
SetString(int i,const char * newtext)865 bool SetString(int i, const char *newtext)
866 {
867 if (i >= SRL_INDEX && i <= SRL_INDEX+2)
868 {
869 mResTexts[i-SRL_INDEX] = newtext;
870 if (mResTexts[0].IsEmpty()) mMaxValid = -1;
871 else if (mResTexts[1].IsEmpty()) mMaxValid = 0;
872 else if (mResTexts[2].IsEmpty()) mMaxValid = 1;
873 else mMaxValid = 2;
874 return true;
875 }
876 return false;
877 }
878
GetString(int i,char * s,int len)879 bool GetString(int i, char *s, int len)
880 {
881 if (i >= SRL_INDEX && i <= SRL_INDEX+2)
882 {
883 strncpy(s, mResTexts[i-SRL_INDEX], len-1);
884 s[len-1] = 0;
885 return true;
886 }
887 return false;
888 }
889
MenuEvent(int mkey,bool fromcontroller)890 bool MenuEvent (int mkey, bool fromcontroller)
891 {
892 if (mkey == MKEY_Left)
893 {
894 if (--mSelection < 0) mSelection = mMaxValid;
895 S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
896 return true;
897 }
898 else if (mkey == MKEY_Right)
899 {
900 if (++mSelection > mMaxValid) mSelection = 0;
901 S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
902 return true;
903 }
904 else
905 {
906 return FOptionMenuItem::MenuEvent(mkey, fromcontroller);
907 }
908 return false;
909 }
910
MouseEvent(int type,int x,int y)911 bool MouseEvent(int type, int x, int y)
912 {
913 int colwidth = screen->GetWidth() / 3;
914 mSelection = x / colwidth;
915 return FOptionMenuItem::MouseEvent(type, x, y);
916 }
917
Activate()918 bool Activate()
919 {
920 S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
921 M_SetVideoMode();
922 return true;
923 }
924
Draw(FOptionMenuDescriptor * desc,int y,int indent,bool selected)925 int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
926 {
927 int colwidth = screen->GetWidth() / 3;
928 EColorRange color;
929
930 for (int x = 0; x < 3; x++)
931 {
932 if (selected && mSelection == x)
933 color = OptionSettings.mFontColorSelection;
934 else if (x == mHighlight)
935 color = OptionSettings.mFontColorHighlight;
936 else
937 color = OptionSettings.mFontColorValue;
938
939 screen->DrawText (SmallFont, color, colwidth * x + 20 * CleanXfac_1, y, mResTexts[x], DTA_CleanNoMove_1, true, TAG_DONE);
940 }
941 return colwidth * mSelection + 20 * CleanXfac_1 - CURSORSPACE;
942 }
943
Selectable()944 bool Selectable()
945 {
946 return mMaxValid >= 0;
947 }
948
Ticker()949 void Ticker()
950 {
951 if (Selectable() && mSelection > mMaxValid)
952 {
953 mSelection = mMaxValid;
954 }
955 }
956 };
957
958
959 //=============================================================================
960 //
961 // [TP] FOptionMenuFieldBase
962 //
963 // Base class for input fields
964 //
965 //=============================================================================
966
967 class FOptionMenuFieldBase : public FOptionMenuItem
968 {
969 public:
FOptionMenuFieldBase(const char * label,const char * menu,const char * graycheck)970 FOptionMenuFieldBase ( const char* label, const char* menu, const char* graycheck ) :
971 FOptionMenuItem ( label, menu ),
972 mCVar ( FindCVar( mAction, NULL )),
973 mGrayCheck (( graycheck && strlen( graycheck )) ? FindCVar( graycheck, NULL ) : NULL ) {}
974
GetCVarString()975 const char* GetCVarString()
976 {
977 if ( mCVar == NULL )
978 return "";
979
980 return mCVar->GetGenericRep( CVAR_String ).String;
981 }
982
Represent()983 virtual FString Represent()
984 {
985 return GetCVarString();
986 }
987
Draw(FOptionMenuDescriptor *,int y,int indent,bool selected)988 int Draw ( FOptionMenuDescriptor*, int y, int indent, bool selected )
989 {
990 bool grayed = mGrayCheck != NULL && !( mGrayCheck->GetGenericRep( CVAR_Bool ).Bool );
991 drawLabel( indent, y, selected ? OptionSettings.mFontColorSelection : OptionSettings.mFontColor, grayed );
992 int overlay = grayed? MAKEARGB( 96, 48, 0, 0 ) : 0;
993
994 screen->DrawText( SmallFont, OptionSettings.mFontColorValue, indent + CURSORSPACE, y,
995 Represent().GetChars(), DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE );
996 return indent;
997 }
998
GetString(int i,char * s,int len)999 bool GetString ( int i, char* s, int len )
1000 {
1001 if ( i == 0 )
1002 {
1003 strncpy( s, GetCVarString(), len );
1004 s[len - 1] = '\0';
1005 return true;
1006 }
1007
1008 return false;
1009 }
1010
SetString(int i,const char * s)1011 bool SetString ( int i, const char* s )
1012 {
1013 if ( i == 0 )
1014 {
1015 if ( mCVar )
1016 {
1017 UCVarValue vval;
1018 vval.String = s;
1019 mCVar->SetGenericRep( vval, CVAR_String );
1020 }
1021
1022 return true;
1023 }
1024
1025 return false;
1026 }
1027
1028 protected:
1029 // Action is a CVar in this class and derivatives.
1030 FBaseCVar* mCVar;
1031 FBaseCVar* mGrayCheck;
1032 };
1033
1034 //=============================================================================
1035 //
1036 // [TP] FOptionMenuTextField
1037 //
1038 // A text input field widget, for use with string CVars.
1039 //
1040 //=============================================================================
1041
1042 class FOptionMenuTextField : public FOptionMenuFieldBase
1043 {
1044 public:
FOptionMenuTextField(const char * label,const char * menu,const char * graycheck)1045 FOptionMenuTextField ( const char *label, const char* menu, const char* graycheck ) :
1046 FOptionMenuFieldBase ( label, menu, graycheck ),
1047 mEntering ( false ) {}
1048
Represent()1049 FString Represent()
1050 {
1051 FString text = mEntering ? mEditName : GetCVarString();
1052
1053 if ( mEntering )
1054 text += ( gameinfo.gametype & GAME_DoomStrifeChex ) ? '_' : '[';
1055
1056 return text;
1057 }
1058
Draw(FOptionMenuDescriptor * desc,int y,int indent,bool selected)1059 int Draw(FOptionMenuDescriptor*desc, int y, int indent, bool selected)
1060 {
1061 if (mEntering)
1062 {
1063 // reposition the text so that the cursor is visible when in entering mode.
1064 FString text = Represent();
1065 int tlen = SmallFont->StringWidth(text) * CleanXfac_1;
1066 int newindent = screen->GetWidth() - tlen - CURSORSPACE;
1067 if (newindent < indent) indent = newindent;
1068 }
1069 return FOptionMenuFieldBase::Draw(desc, y, indent, selected);
1070 }
1071
MenuEvent(int mkey,bool fromcontroller)1072 bool MenuEvent ( int mkey, bool fromcontroller )
1073 {
1074 if ( mkey == MKEY_Enter )
1075 {
1076 S_Sound( CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE );
1077 strcpy( mEditName, GetCVarString() );
1078 mEntering = true;
1079 DMenu* input = new DTextEnterMenu ( DMenu::CurrentMenu, mEditName, sizeof mEditName, 2, fromcontroller );
1080 M_ActivateMenu( input );
1081 return true;
1082 }
1083 else if ( mkey == MKEY_Input )
1084 {
1085 if ( mCVar )
1086 {
1087 UCVarValue vval;
1088 vval.String = mEditName;
1089 mCVar->SetGenericRep( vval, CVAR_String );
1090 }
1091
1092 mEntering = false;
1093 return true;
1094 }
1095 else if ( mkey == MKEY_Abort )
1096 {
1097 mEntering = false;
1098 return true;
1099 }
1100
1101 return FOptionMenuItem::MenuEvent( mkey, fromcontroller );
1102 }
1103
1104 private:
1105 bool mEntering;
1106 char mEditName[128];
1107 };
1108
1109 //=============================================================================
1110 //
1111 // [TP] FOptionMenuNumberField
1112 //
1113 // A numeric input field widget, for use with number CVars where sliders are inappropriate (i.e.
1114 // where the user is interested in the exact value specifically)
1115 //
1116 //=============================================================================
1117
1118 class FOptionMenuNumberField : public FOptionMenuFieldBase
1119 {
1120 public:
FOptionMenuNumberField(const char * label,const char * menu,float minimum,float maximum,float step,const char * graycheck)1121 FOptionMenuNumberField ( const char *label, const char* menu, float minimum, float maximum,
1122 float step, const char* graycheck )
1123 : FOptionMenuFieldBase ( label, menu, graycheck ),
1124 mMinimum ( minimum ),
1125 mMaximum ( maximum ),
1126 mStep ( step )
1127 {
1128 if ( mMaximum <= mMinimum )
1129 swapvalues( mMinimum, mMaximum );
1130
1131 if ( mStep <= 0 )
1132 mStep = 1;
1133 }
1134
MenuEvent(int mkey,bool fromcontroller)1135 bool MenuEvent ( int mkey, bool fromcontroller )
1136 {
1137 if ( mCVar )
1138 {
1139 float value = mCVar->GetGenericRep( CVAR_Float ).Float;
1140
1141 if ( mkey == MKEY_Left )
1142 {
1143 value -= mStep;
1144
1145 if ( value < mMinimum )
1146 value = mMaximum;
1147 }
1148 else if ( mkey == MKEY_Right || mkey == MKEY_Enter )
1149 {
1150 value += mStep;
1151
1152 if ( value > mMaximum )
1153 value = mMinimum;
1154 }
1155 else
1156 return FOptionMenuItem::MenuEvent( mkey, fromcontroller );
1157
1158 UCVarValue vval;
1159 vval.Float = value;
1160 mCVar->SetGenericRep( vval, CVAR_Float );
1161 S_Sound( CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE );
1162 }
1163
1164 return true;
1165 }
1166
1167 private:
1168 float mMinimum;
1169 float mMaximum;
1170 float mStep;
1171 };
1172