gzdoom/wadsrc/static/zscript/menu/optionmenuitems.txt
Christoph Oelckers f3ac82e112 - do not allow outside access to the variable storing the CCMD for OptionMenuItemCommand.
This can be abused to execute atbitrary commands from inside script code.
2017-12-13 22:14:30 +01:00

1292 lines
31 KiB
Text

/*
** optionmenuitems.txt
** Control items for option menus
**
**---------------------------------------------------------------------------
** Copyright 2010-2017 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
class OptionMenuItem : MenuItemBase
{
String mLabel;
bool mCentered;
protected void Init(String label, String command, bool center = false)
{
Super.Init(0, 0, command);
mLabel = label;
mCentered = center;
}
protected int drawLabel(int indent, int y, int color, bool grayed = false)
{
String label = Stringtable.Localize(mLabel);
int overlay = grayed? Color(96,48,0,0) : 0;
int x;
int w = SmallFont.StringWidth(label) * CleanXfac_1;
if (!mCentered) x = indent - w;
else x = (screen.GetWidth() - w) / 2;
screen.DrawText (SmallFont, color, x, y, label, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay);
return x;
}
int CursorSpace()
{
return (14 * CleanXfac_1);
}
override bool Selectable()
{
return true;
}
override int GetIndent()
{
if (mCentered) return 0;
return SmallFont.StringWidth(Stringtable.Localize(mLabel));
}
override bool MouseEvent(int type, int x, int y)
{
if (Selectable() && type == Menu.MOUSE_Release)
{
return Menu.GetCurrentMenu().MenuEvent(Menu.MKEY_Enter, true);
}
return false;
}
}
//=============================================================================
//
// opens a submenu, command is a submenu name
//
//=============================================================================
class OptionMenuItemSubmenu : OptionMenuItem
{
int mParam;
OptionMenuItemSubmenu Init(String label, Name command, int param = 0, bool centered = false)
{
Super.init(label, command, centered);
mParam = param;
return self;
}
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
int x = drawLabel(indent, y, selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColorMore);
if (mCentered)
{
return x - 16*CleanXfac_1;
}
return indent;
}
override bool Activate()
{
Menu.MenuSound("menu/choose");
Menu.SetMenu(mAction, mParam);
return true;
}
}
//=============================================================================
//
// Executes a CCMD, command is a CCMD name
//
//=============================================================================
class OptionMenuItemCommand : OptionMenuItemSubmenu
{
private String ccmd; // do not allow access to this from the outside.
OptionMenuItemCommand Init(String label, Name command, bool centered = false)
{
Super.Init(label, command, 0, centered);
ccmd = command;
return self;
}
private native static void DoCommand(String cmd); // This is very intentionally limited to this menu item to prevent abuse.
override bool Activate()
{
// This needs to perform a few checks to prevent abuse by malicious modders.
if (GetClass() != "OptionMenuItemSafeCommand")
{
let m = OptionMenu(Menu.GetCurrentMenu());
// don't execute if no menu is active
if (m == null) return false;
// don't execute if this item cannot be found in the current menu.
if (m.GetItem(mAction) != self) return false;
}
Menu.MenuSound("menu/choose");
DoCommand(ccmd);
return true;
}
}
//=============================================================================
//
// Executes a CCMD after confirmation, command is a CCMD name
//
//=============================================================================
class OptionMenuItemSafeCommand : OptionMenuItemCommand
{
String mPrompt;
OptionMenuItemSafeCommand Init(String label, Name command, String prompt = "")
{
Super.Init(label, command);
mPrompt = prompt;
return self;
}
override bool MenuEvent (int mkey, bool fromcontroller)
{
if (mkey == Menu.MKEY_MBYes)
{
Super.Activate();
return true;
}
return Super.MenuEvent(mkey, fromcontroller);
}
override bool Activate()
{
String msg = mPrompt.Length() > 0 ? mPrompt : "$SAFEMESSAGE";
msg = StringTable.Localize(msg);
String actionLabel = StringTable.localize(mLabel);
String FullString;
FullString = String.Format("%s%s%s\n\n%s", TEXTCOLOR_WHITE, actionLabel, TEXTCOLOR_NORMAL, msg);
Menu.StartMessage(FullString, 0);
return true;
}
}
//=============================================================================
//
// Base class for option lists
//
//=============================================================================
class OptionMenuItemOptionBase : OptionMenuItem
{
// command is a CVAR
Name mValues; // Entry in OptionValues table
CVar mGrayCheck;
int mCenter;
const OP_VALUES = 0x11001;
protected void Init(String label, Name command, Name values, CVar graycheck, int center)
{
Super.Init(label, command);
mValues = values;
mGrayCheck = graycheck;
mCenter = center;
}
override bool SetString(int i, String newtext)
{
if (i == OP_VALUES)
{
int cnt = OptionValues.GetCount(mValues);
if (cnt >= 0)
{
mValues = newtext;
int s = GetSelection();
if (s >= cnt) s = 0;
SetSelection(s); // readjust the CVAR if its value is outside the range now
return true;
}
}
return false;
}
//=============================================================================
virtual int GetSelection()
{
return 0;
}
virtual void SetSelection(int Selection)
{
}
//=============================================================================
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
bool grayed = mGrayCheck != null && !(mGrayCheck.GetInt());
if (mCenter)
{
indent = (screen.GetWidth() / 2);
}
drawLabel(indent, y, selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, grayed);
int overlay = grayed? Color(96,48,0,0) : 0;
int Selection = GetSelection();
String text = StringTable.Localize(OptionValues.GetText(mValues, Selection));
if (text.Length() == 0) text = "Unknown";
screen.DrawText (SmallFont, OptionMenuSettings.mFontColorValue, indent + CursorSpace(), y, text, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay);
return indent;
}
//=============================================================================
override bool MenuEvent (int mkey, bool fromcontroller)
{
int cnt = OptionValues.GetCount(mValues);
if (cnt > 0)
{
int Selection = GetSelection();
if (mkey == Menu.MKEY_Left)
{
if (Selection == -1) Selection = 0;
else if (--Selection < 0) Selection = cnt - 1;
}
else if (mkey == Menu.MKEY_Right || mkey == Menu.MKEY_Enter)
{
if (++Selection >= cnt) Selection = 0;
}
else
{
return Super.MenuEvent(mkey, fromcontroller);
}
SetSelection(Selection);
Menu.MenuSound("menu/change");
}
else
{
return Super.MenuEvent(mkey, fromcontroller);
}
return true;
}
override bool Selectable()
{
return mGrayCheck == null || mGrayCheck.GetInt();
}
}
//=============================================================================
//
// Change a CVAR, command is the CVAR name
//
//=============================================================================
class OptionMenuItemOption : OptionMenuItemOptionBase
{
CVar mCVar;
OptionMenuItemOption Init(String label, Name command, Name values, CVar graycheck = null, int center = 0)
{
Super.Init(label, command, values, graycheck, center);
mCVar = CVar.FindCVar(mAction);
return self;
}
//=============================================================================
override int GetSelection()
{
int Selection = -1;
int cnt = OptionValues.GetCount(mValues);
if (cnt > 0 && mCVar != null)
{
if (OptionValues.GetTextValue(mValues, 0).Length() == 0)
{
let f = mCVar.GetFloat();
for(int i = 0; i < cnt; i++)
{
if (f ~== OptionValues.GetValue(mValues, i))
{
Selection = i;
break;
}
}
}
else
{
String cv = mCVar.GetString();
for(int i = 0; i < cnt; i++)
{
if (cv ~== OptionValues.GetTextValue(mValues, i))
{
Selection = i;
break;
}
}
}
}
return Selection;
}
override void SetSelection(int Selection)
{
int cnt = OptionValues.GetCount(mValues);
if (cnt > 0 && mCVar != null)
{
if (OptionValues.GetTextValue(mValues, 0).Length() == 0)
{
mCVar.SetFloat(OptionValues.GetValue(mValues, Selection));
}
else
{
mCVar.SetString(OptionValues.GetTextValue(mValues, Selection));
}
}
}
}
//=============================================================================
//
// This class is used to capture the key to be used as the new key binding
// for a control item
//
//=============================================================================
class EnterKey : Menu
{
OptionMenuItemControlBase mOwner;
void Init(Menu parent, OptionMenuItemControlBase owner)
{
Super.Init(parent);
mOwner = owner;
SetMenuMessage(1);
menuactive = Menu.WaitKey; // There should be a better way to disable GUI capture...
}
override bool TranslateKeyboardEvents()
{
return false;
}
private void SetMenuMessage(int which)
{
let parent = OptionMenu(mParentMenu);
if (parent != null)
{
let it = parent.GetItem('Controlmessage');
if (it != null)
{
it.SetValue(0, which);
}
}
}
override bool OnInputEvent(InputEvent ev)
{
// This menu checks raw keys, not GUI keys because it needs the raw codes for binding.
if (ev.type == InputEvent.Type_KeyDown)
{
mOwner.SendKey(ev.KeyScan);
menuactive = Menu.On;
SetMenuMessage(0);
Close();
mParentMenu.MenuEvent((ev.KeyScan == InputEvent.KEY_ESCAPE)? Menu.MKEY_Abort : Menu.MKEY_Input, 0);
return true;
}
return false;
}
override void Drawer()
{
mParentMenu.Drawer();
}
}
//=============================================================================
//
// // Edit a key binding, Action is the CCMD to bind
//
//=============================================================================
class OptionMenuItemControlBase : OptionMenuItem
{
KeyBindings mBindings;
int mInput;
bool mWaiting;
protected void Init(String label, Name command, KeyBindings bindings)
{
Super.init(label, command);
mBindings = bindings;
mWaiting = false;
}
//=============================================================================
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
drawLabel(indent, y, mWaiting? OptionMenuSettings.mFontColorHighlight:
(selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor));
String description;
int Key1, Key2;
[Key1, Key2] = mBindings.GetKeysForCommand(mAction);
description = KeyBindings.NameKeys (Key1, Key2);
if (description.Length() > 0)
{
Menu.DrawConText(Font.CR_WHITE, indent + CursorSpace(), y + (OptionMenuSettings.mLinespacing-8)*CleanYfac_1, description);
}
else
{
screen.DrawText(SmallFont, Font.CR_BLACK, indent + CursorSpace(), y + (OptionMenuSettings.mLinespacing-8)*CleanYfac_1, "---", DTA_CleanNoMove_1, true);
}
return indent;
}
//=============================================================================
override bool MenuEvent(int mkey, bool fromcontroller)
{
if (mkey == Menu.MKEY_Input)
{
mWaiting = false;
mBindings.SetBind(mInput, mAction);
return true;
}
else if (mkey == Menu.MKEY_Clear)
{
mBindings.UnbindACommand(mAction);
return true;
}
else if (mkey == Menu.MKEY_Abort)
{
mWaiting = false;
return true;
}
return false;
}
void SendKey(int key)
{
mInput = key;
}
override bool Activate()
{
Menu.MenuSound("menu/choose");
mWaiting = true;
let input = new("EnterKey");
input.Init(Menu.GetCurrentMenu(), self);
input.ActivateMenu();
return true;
}
}
class OptionMenuItemControl : OptionMenuItemControlBase
{
OptionMenuItemControl Init(String label, Name command)
{
Super.Init(label, command, Bindings);
return self;
}
}
class OptionMenuItemMapControl : OptionMenuItemControlBase
{
OptionMenuItemMapControl Init(String label, Name command)
{
Super.Init(label, command, AutomapBindings);
return self;
}
}
//=============================================================================
//
//
//
//=============================================================================
class OptionMenuItemStaticText : OptionMenuItem
{
int mColor;
// this function is only for use from MENUDEF, it needs to do some strange things with the color for backwards compatibility.
OptionMenuItemStaticText Init(String label, int cr = -1)
{
Super.Init(label, 'None', true);
mColor = OptionMenuSettings.mFontColor;
if ((cr & 0xffff0000) == 0x12340000) mColor = cr & 0xffff;
else if (cr > 0) mColor = OptionMenuSettings.mFontColorHeader;
return self;
}
OptionMenuItemStaticText InitDirect(String label, int cr)
{
Super.Init(label, 'None', true);
mColor = cr;
return self;
}
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
drawLabel(indent, y, mColor);
return -1;
}
override bool Selectable()
{
return false;
}
}
//=============================================================================
//
//
//
//=============================================================================
class OptionMenuItemStaticTextSwitchable : OptionMenuItem
{
int mColor;
String mAltText;
int mCurrent;
// this function is only for use from MENUDEF, it needs to do some strange things with the color for backwards compatibility.
OptionMenuItemStaticTextSwitchable Init(String label, String label2, Name command, int cr = -1)
{
Super.Init(label, command, true);
mAltText = label2;
mCurrent = 0;
mColor = OptionMenuSettings.mFontColor;
if ((cr & 0xffff0000) == 0x12340000) mColor = cr & 0xffff;
else if (cr > 0) mColor = OptionMenuSettings.mFontColorHeader;
return self;
}
OptionMenuItemStaticTextSwitchable InitDirect(String label, String label2, Name command, int cr)
{
Super.Init(label, command, true);
mColor = cr;
mAltText = label2;
mCurrent = 0;
return self;
}
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
String txt = StringTable.Localize(mCurrent? mAltText : mLabel);
int w = SmallFont.StringWidth(txt) * CleanXfac_1;
int x = (screen.GetWidth() - w) / 2;
screen.DrawText (SmallFont, mColor, x, y, txt, DTA_CleanNoMove_1, true);
return -1;
}
override bool SetValue(int i, int val)
{
if (i == 0)
{
mCurrent = val;
return true;
}
return false;
}
override bool SetString(int i, String newtext)
{
if (i == 0)
{
mAltText = newtext;
return true;
}
return false;
}
override bool Selectable()
{
return false;
}
}
//=============================================================================
//
//
//
//=============================================================================
class OptionMenuSliderBase : OptionMenuItem
{
// command is a CVAR
double mMin, mMax, mStep;
int mShowValue;
int mDrawX;
int mSliderShort;
protected void Init(String label, double min, double max, double step, int showval, Name command = 'none')
{
Super.Init(label, command);
mMin = min;
mMax = max;
mStep = step;
mShowValue = showval;
mDrawX = 0;
mSliderShort = 0;
}
virtual double GetSliderValue()
{
return 0;
}
virtual void SetSliderValue(double val)
{
}
//=============================================================================
//
// Draw a slider. Set fracdigits negative to not display the current value numerically.
//
//=============================================================================
protected void DrawSlider (int x, int y, double min, double max, double cur, int fracdigits, int indent)
{
String formater = String.format("%%.%df", fracdigits); // The format function cannot do the '%.*f' syntax.
String textbuf;
double range;
int maxlen = 0;
int right = x + (12*8 + 4) * CleanXfac_1;
int cy = y + (OptionMenuSettings.mLinespacing-8)*CleanYfac_1;
range = max - min;
double ccur = clamp(cur, min, max) - min;
if (fracdigits >= 0)
{
textbuf = String.format(formater, max);
maxlen = SmallFont.StringWidth(textbuf) * CleanXfac_1;
}
mSliderShort = right + maxlen > screen.GetWidth();
if (!mSliderShort)
{
Menu.DrawConText(Font.CR_WHITE, x, cy, "\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12");
Menu.DrawConText(Font.FindFontColor(gameinfo.mSliderColor), x + int((5 + ((ccur * 78) / range)) * CleanXfac_1), cy, "\x13");
}
else
{
// On 320x200 we need a shorter slider
Menu.DrawConText(Font.CR_WHITE, x, cy, "\x10\x11\x11\x11\x11\x11\x12");
Menu.DrawConText(Font.FindFontColor(gameinfo.mSliderColor), x + int((5 + ((ccur * 38) / range)) * CleanXfac_1), cy, "\x13");
right -= 5*8*CleanXfac_1;
}
if (fracdigits >= 0 && right + maxlen <= screen.GetWidth())
{
textbuf = String.format(formater, cur);
screen.DrawText(SmallFont, Font.CR_DARKGRAY, right, y, textbuf, DTA_CleanNoMove_1, true);
}
}
//=============================================================================
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
drawLabel(indent, y, selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor);
mDrawX = indent + CursorSpace();
DrawSlider (mDrawX, y, mMin, mMax, GetSliderValue(), mShowValue, indent);
return indent;
}
//=============================================================================
override bool MenuEvent (int mkey, bool fromcontroller)
{
double value = GetSliderValue();
if (mkey == Menu.MKEY_Left)
{
value -= mStep;
}
else if (mkey == Menu.MKEY_Right)
{
value += mStep;
}
else
{
return OptionMenuItem.MenuEvent(mkey, fromcontroller);
}
if (value ~== 0) value = 0; // This is to prevent formatting anomalies with very small values
SetSliderValue(clamp(value, mMin, mMax));
Menu.MenuSound("menu/change");
return true;
}
override bool MouseEvent(int type, int x, int y)
{
let lm = OptionMenu(Menu.GetCurrentMenu());
if (type != Menu.MOUSE_Click)
{
if (!lm.CheckFocus(self)) return false;
}
if (type == Menu.MOUSE_Release)
{
lm.ReleaseFocus();
}
int slide_left = mDrawX+8*CleanXfac_1;
int slide_right = slide_left + (10*8*CleanXfac_1 >> mSliderShort); // 12 char cells with 8 pixels each.
if (type == Menu.MOUSE_Click)
{
if (x < slide_left || x >= slide_right) return true;
}
x = clamp(x, slide_left, slide_right);
double v = mMin + ((x - slide_left) * (mMax - mMin)) / (slide_right - slide_left);
if (v != GetSliderValue())
{
SetSliderValue(v);
//Menu.MenuSound("menu/change");
}
if (type == Menu.MOUSE_Click)
{
lm.SetFocus(self);
}
return true;
}
}
//=============================================================================
//
//
//
//=============================================================================
class OptionMenuItemSlider : OptionMenuSliderBase
{
CVar mCVar;
OptionMenuItemSlider Init(String label, Name command, double min, double max, double step, int showval = 1)
{
Super.Init(label, min, max, step, showval, command);
mCVar =CVar.FindCVar(command);
return self;
}
override double GetSliderValue()
{
if (mCVar != null)
{
return mCVar.GetFloat();
}
else
{
return 0;
}
}
override void SetSliderValue(double val)
{
if (mCVar != null)
{
mCVar.SetFloat(val);
}
}
}
//=============================================================================
//
// // Edit a key binding, Action is the CCMD to bind
//
//=============================================================================
class OptionMenuItemColorPicker : OptionMenuItem
{
CVar mCVar;
const CPF_RESET = 0x20001;
OptionMenuItemColorPicker Init(String label, Name command)
{
Super.Init(label, command);
CVar cv = CVar.FindCVar(command);
if (cv != null && cv.GetRealType() != CVar.CVAR_Color) cv = null;
mCVar = cv;
return self;
}
//=============================================================================
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
drawLabel(indent, y, selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor);
if (mCVar != null)
{
int box_x = indent + CursorSpace();
int box_y = y + CleanYfac_1;
screen.Clear (box_x, box_y, box_x + 32*CleanXfac_1, box_y + OptionMenuSettings.mLinespacing*CleanYfac_1, mCVar.GetInt() | 0xff000000);
}
return indent;
}
override bool SetValue(int i, int v)
{
if (i == CPF_RESET && mCVar != null)
{
mCVar.ResetToDefault();
return true;
}
return false;
}
override bool Activate()
{
if (mCVar != null)
{
Menu.MenuSound("menu/choose");
// This code is a bit complicated because it should allow subclassing the
// colorpicker menu.
// New color pickers must inherit from the internal one to work here.
let desc = MenuDescriptor.GetDescriptor('Colorpickermenu');
if (desc != NULL && (desc.mClass == null || desc.mClass is "ColorPickerMenu"))
{
let odesc = OptionMenuDescriptor(desc);
if (odesc != null)
{
let cls = desc.mClass;
if (cls == null) cls = "ColorpickerMenu";
let picker = ColorpickerMenu(new(cls));
picker.Init(Menu.GetCurrentMenu(), mLabel, odesc, mCVar);
picker.ActivateMenu();
return true;
}
}
}
return false;
}
}
class OptionMenuItemScreenResolution : OptionMenuItem
{
String mResTexts[3];
int mSelection;
int mHighlight;
int mMaxValid;
enum EValues
{
SRL_INDEX = 0x30000,
SRL_SELECTION = 0x30003,
SRL_HIGHLIGHT = 0x30004,
};
OptionMenuItemScreenResolution Init(String command)
{
Super.Init("", command);
mSelection = 0;
mHighlight = -1;
return self;
}
override bool SetValue(int i, int v)
{
if (i == SRL_SELECTION)
{
mSelection = v;
return true;
}
else if (i == SRL_HIGHLIGHT)
{
mHighlight = v;
return true;
}
return false;
}
override bool, int GetValue(int i)
{
if (i == SRL_SELECTION)
{
return true, mSelection;
}
return false, 0;
}
override bool SetString(int i, String newtext)
{
if (i >= SRL_INDEX && i <= SRL_INDEX+2)
{
mResTexts[i-SRL_INDEX] = newtext;
if (mResTexts[0].Length() == 0) mMaxValid = -1;
else if (mResTexts[1].Length() == 0) mMaxValid = 0;
else if (mResTexts[2].Length() == 0) mMaxValid = 1;
else mMaxValid = 2;
return true;
}
return false;
}
override bool, String GetString(int i)
{
if (i >= SRL_INDEX && i <= SRL_INDEX+2)
{
return true, mResTexts[i-SRL_INDEX];
}
return false, "";
}
override bool MenuEvent (int mkey, bool fromcontroller)
{
if (mkey == Menu.MKEY_Left)
{
if (--mSelection < 0) mSelection = mMaxValid;
Menu.MenuSound ("menu/cursor");
return true;
}
else if (mkey == Menu.MKEY_Right)
{
if (++mSelection > mMaxValid) mSelection = 0;
Menu.MenuSound ("menu/cursor");
return true;
}
else
{
return Super.MenuEvent(mkey, fromcontroller);
}
return false;
}
override bool MouseEvent(int type, int x, int y)
{
int colwidth = screen.GetWidth() / 3;
mSelection = x / colwidth;
return Super.MouseEvent(type, x, y);
}
override bool Activate()
{
Menu.MenuSound("menu/choose");
Menu.SetVideoMode();
return true;
}
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
int colwidth = screen.GetWidth() / 3;
int col;
for (int x = 0; x < 3; x++)
{
if (selected && mSelection == x)
col = OptionMenuSettings.mFontColorSelection;
else if (x == mHighlight)
col = OptionMenuSettings.mFontColorHighlight;
else
col = OptionMenuSettings.mFontColorValue;
screen.DrawText (SmallFont, col, colwidth * x + 20 * CleanXfac_1, y, mResTexts[x], DTA_CleanNoMove_1, true);
}
return colwidth * mSelection + 20 * CleanXfac_1 - CursorSpace();
}
override bool Selectable()
{
return mMaxValid >= 0;
}
override void Ticker()
{
if (Selectable() && mSelection > mMaxValid)
{
mSelection = mMaxValid;
}
}
}
//=============================================================================
//
// [TP] OptionMenuFieldBase
//
// Base class for input fields
//
//=============================================================================
class OptionMenuFieldBase : OptionMenuItem
{
void Init (String label, Name command, CVar graycheck = null)
{
Super.Init(label, command);
mCVar = CVar.FindCVar(mAction);
mGrayCheck = graycheck;
}
String GetCVarString()
{
if (mCVar == null)
return "";
return mCVar.GetString();
}
virtual String Represent()
{
return GetCVarString();
}
override int Draw (OptionMenuDescriptor d, int y, int indent, bool selected)
{
bool grayed = mGrayCheck != null && !mGrayCheck.GetInt();
drawLabel(indent, y, selected ? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, grayed);
int overlay = grayed? Color(96, 48, 0, 0) : 0;
screen.DrawText(SmallFont, OptionMenuSettings.mFontColorValue, indent + CursorSpace(), y, Represent(), DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay);
return indent;
}
override bool, String GetString (int i)
{
if (i == 0)
{
return true, GetCVarString();
}
return false, "";
}
override bool SetString (int i, String s)
{
if (i == 0)
{
if (mCVar) mCVar.SetString(s);
return true;
}
return false;
}
CVar mCVar;
CVar mGrayCheck;
}
//=============================================================================
//
// [TP] OptionMenuTextField
//
// A text input field widget, for use with string CVars.
//
//=============================================================================
class OptionMenuItemTextField : OptionMenuFieldBase
{
TextEnterMenu mEnter;
OptionMenuItemTextField Init (String label, Name command, CVar graycheck = null)
{
Super.Init(label, command, graycheck);
mEnter = null;
return self;
}
override String Represent()
{
if (mEnter) return mEnter.GetText() .. SmallFont.GetCursor();
else return GetCVarString();
}
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
if (mEnter)
{
// reposition the text so that the cursor is visible when in entering mode.
String text = Represent();
int tlen = SmallFont.StringWidth(text) * CleanXfac_1;
int newindent = screen.GetWidth() - tlen - CursorSpace();
if (newindent < indent) indent = newindent;
}
return Super.Draw(desc, y, indent, selected);
}
override bool MenuEvent (int mkey, bool fromcontroller)
{
if (mkey == Menu.MKEY_Enter)
{
Menu.MenuSound("menu/choose");
mEnter = TextEnterMenu.Open(Menu.GetCurrentMenu(), GetCVarString(), -1, 2, fromcontroller);
mEnter.ActivateMenu();
return true;
}
else if (mkey == Menu.MKEY_Input)
{
if (mCVar) mCVar.SetString(mEnter.GetText());
mEnter = null;
return true;
}
else if (mkey == Menu.MKEY_Abort)
{
mEnter = null;
return true;
}
return Super.MenuEvent(mkey, fromcontroller);
}
}
//=============================================================================
//
// [TP] FOptionMenuNumberField
//
// A numeric input field widget, for use with number CVars where sliders are inappropriate (i.e.
// where the user is interested in the exact value specifically)
//
//=============================================================================
class OptionMenuItemNumberField : OptionMenuFieldBase
{
OptionMenuItemNumberField Init (String label, Name command, float minimum = 0, float maximum = 100, float step = 1, CVar graycheck = null)
{
Super.Init(label, command, graycheck);
mMinimum = min(minimum, maximum);
mMaximum = max(minimum, maximum);
mStep = max(1, step);
return self;
}
override String Represent()
{
if (mCVar == null) return "";
return String.format("%.3f", mCVar.GetFloat());
}
override bool MenuEvent (int mkey, bool fromcontroller)
{
if (mCVar)
{
float value = mCVar.GetFloat();
if (mkey == Menu.MKEY_Left)
{
value -= mStep;
if (value < mMinimum) value = mMaximum;
}
else if (mkey == Menu.MKEY_Right || mkey == Menu.MKEY_Enter)
{
value += mStep;
if (value > mMaximum) value = mMinimum;
}
else
return Super.MenuEvent(mkey, fromcontroller);
mCVar.SetFloat(value);
Menu.MenuSound("menu/change");
}
else return Super.MenuEvent(mkey, fromcontroller);
return true;
}
float mMinimum;
float mMaximum;
float mStep;
}
//=============================================================================
//
// A special slider that displays plain text for special settings related
// to scaling.
//
//=============================================================================
class OptionMenuItemScaleSlider : OptionMenuItemSlider
{
String TextZero;
String TextNegOne;
int mClickVal;
OptionMenuItemScaleSlider Init(String label, Name command, double min, double max, double step, String zero, String negone = "")
{
Super.Init(label, command, min, max, step, 0);
mCVar =CVar.FindCVar(command);
TextZero = zero;
TextNEgOne = negone;
mClickVal = -10;
return self;
}
//=============================================================================
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
drawLabel(indent, y, selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor);
int Selection = int(GetSliderValue());
if ((Selection == 0 || Selection == -1) && mClickVal <= 0)
{
String text = Selection == 0? TextZero : Selection == -1? TextNegOne : "";
screen.DrawText (SmallFont, OptionMenuSettings.mFontColorValue, indent + CursorSpace(), y, text, DTA_CleanNoMove_1, true);
}
else
{
mDrawX = indent + CursorSpace();
DrawSlider (mDrawX, y, mMin, mMax, GetSliderValue(), mShowValue, indent);
}
return indent;
}
override bool MouseEvent(int type, int x, int y)
{
int value = int(GetSliderValue());
switch (type)
{
case Menu.MOUSE_Click:
mClickVal = value;
if (value <= 0) return false;
return Super.MouseEvent(type, x, y);
case Menu.MOUSE_Move:
if (mClickVal <= 0) return false;
return Super.MouseEvent(type, x, y);
case Menu.MOUSE_Release:
if (mClickVal <= 0)
{
mClickVal = -10;
SetSliderValue(value + 1);
return true;
}
mClickVal = -10;
return Super.MouseEvent(type, x, y);
}
return false;
}
}