openclonk/src/gui/C4StartupOptionsDlg.cpp

1490 lines
66 KiB
C++

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2005-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
// Startup screen for non-parameterized engine start: Options dialog
#include "C4Include.h"
#include "C4ForbidLibraryCompilation.h"
#include "gui/C4StartupOptionsDlg.h"
#include "game/C4Application.h"
#include "graphics/C4Draw.h"
#include "graphics/C4GraphicsResource.h"
#include "gui/C4MouseControl.h"
#include "gui/C4KeyboardInput.h"
#include "gui/C4StartupMainDlg.h"
#include "network/C4Network2.h"
#include "platform/C4GamePadCon.h"
#include "graphics/C4DrawGL.h"
// ------------------------------------------------
// --- C4StartupOptionsDlg::SmallButton
void C4StartupOptionsDlg::SmallButton::DrawElement(C4TargetFacet &cgo)
{
// draw the button
CStdFont &rUseFont = C4Startup::Get()->Graphics.BookFont;
// get text pos
float x0 = cgo.TargetX + rcBounds.x, y0 = cgo.TargetY + rcBounds.y, x1 = cgo.TargetX + rcBounds.x + rcBounds.Wdt, y1 = cgo.TargetY + rcBounds.y + rcBounds.Hgt;
int32_t iTextHgt = rUseFont.GetLineHeight();
// draw frame
uint32_t dwClrHigh = C4StartupBtnBorderColor1, dwClrLow = C4StartupBtnBorderColor2;
if (fDown) std::swap(dwClrHigh, dwClrLow);
int32_t iIndent = Clamp<int32_t>((rcBounds.Hgt-iTextHgt)/3, 2, 5);
float iDrawQuadTop[8] = { x0,y0, x1,y0, x1-iIndent,y0+iIndent, x0,y0+iIndent };
float iDrawQuadLeft[8] = { x0,y0, x0+iIndent,y0, x0+iIndent,y1-iIndent, x0,y1 };
float iDrawQuadRight[8] = { x1,y0, x1,y1, x1-iIndent,y1, x1-iIndent,y0+iIndent };
float iDrawQuadBottom[8] = { x1,y1, x0,y1, x0+iIndent,y1-iIndent, x1,y1-iIndent };
// TODO: Switch to PerformMultiTris
pDraw->DrawQuadDw(cgo.Surface, iDrawQuadTop, dwClrHigh,dwClrHigh,dwClrHigh,dwClrHigh, nullptr);
pDraw->DrawQuadDw(cgo.Surface, iDrawQuadLeft, dwClrHigh,dwClrHigh,dwClrHigh,dwClrHigh, nullptr);
pDraw->DrawQuadDw(cgo.Surface, iDrawQuadRight, dwClrLow,dwClrLow,dwClrLow,dwClrLow, nullptr);
pDraw->DrawQuadDw(cgo.Surface, iDrawQuadBottom, dwClrLow,dwClrLow,dwClrLow,dwClrLow, nullptr);
// draw selection highlight
int32_t iTxtOff = fDown ? iIndent : 0;
if (fEnabled) if (HasDrawFocus() || (fMouseOver && IsInActiveDlg(false)))
{
pDraw->SetBlitMode(C4GFXBLIT_ADDITIVE);
::GraphicsResource.fctButtonHighlight.DrawX(cgo.Surface, x0+5+iTxtOff, y0+3+iTxtOff, rcBounds.Wdt-10, rcBounds.Hgt-6);
pDraw->ResetBlitMode();
}
// draw button text
pDraw->TextOut(sText.getData(), rUseFont, 1.0f, cgo.Surface, (x0+x1)/2 + iTxtOff, (y0+y1-iTextHgt)/2 + iTxtOff, C4StartupBtnFontClr, ACenter, true);
}
int32_t C4StartupOptionsDlg::SmallButton::GetDefaultButtonHeight()
{
// button height is used font height plus a small indent
CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
return pUseFont->GetLineHeight()*6/5+6;
}
// ------------------------------------------------
// --- C4StartupOptionsDlg::ResChangeConfirmDlg
C4StartupOptionsDlg::ResChangeConfirmDlg::ResChangeConfirmDlg()
: C4GUI::Dialog(C4GUI_MessageDlgWdt, 100 /* will be resized */, LoadResStr("IDS_MNU_SWITCHRESOLUTION"), false)
{
// update-timer
Application.Add(this);
// An independant group of fourteen highly trained apes and one blind lawnmower have determined
// that twelve seconds is just right for normal people
iResChangeSwitchTime = 12;
// However, some people need more time
// Those can be identified by their configuration settings
if (SEqualNoCase(Config.GetRegistrationData("Nick"), "flgr")) iResChangeSwitchTime *= 10;
// get positions
C4GUI::ComponentAligner caMain(GetClientRect(), C4GUI_DefDlgIndent, C4GUI_DefDlgIndent, true);
// place icon
C4Rect rcIcon = caMain.GetFromLeft(C4GUI_IconWdt); rcIcon.Hgt = C4GUI_IconHgt;
C4GUI::Icon *pIcon = new C4GUI::Icon(rcIcon, C4GUI::Ico_Confirm); AddElement(pIcon);
// place message labels
// use text with line breaks
StdStrBuf sMsgBroken;
int iMsgHeight = ::GraphicsResource.TextFont.BreakMessage(LoadResStr("IDS_MNU_SWITCHRESOLUTION_LIKEIT"), caMain.GetInnerWidth(), &sMsgBroken, true);
C4GUI::Label *pLblMessage = new C4GUI::Label(sMsgBroken.getData(), caMain.GetFromTop(iMsgHeight), ACenter, C4GUI_MessageFontClr, &::GraphicsResource.TextFont, false);
AddElement(pLblMessage);
iMsgHeight = ::GraphicsResource.TextFont.BreakMessage(FormatString(LoadResStr("IDS_MNU_SWITCHRESOLUTION_UNDO"),
(int)iResChangeSwitchTime).getData(),
caMain.GetInnerWidth(), &sMsgBroken, true);
pOperationCancelLabel = new C4GUI::Label(sMsgBroken.getData(), caMain.GetFromTop(iMsgHeight), ACenter, C4GUI_MessageFontClr, &::GraphicsResource.TextFont, false, false);
AddElement(pOperationCancelLabel);
// place buttons
C4GUI::ComponentAligner caButtonArea(caMain.GetFromTop(C4GUI_ButtonAreaHgt), 0,0);
int32_t iButtonCount = 2;
C4Rect rcBtn = caButtonArea.GetCentered(iButtonCount*C4GUI_DefButton2Wdt+(iButtonCount-1)*C4GUI_DefButton2HSpace, C4GUI_ButtonHgt);
rcBtn.Wdt = C4GUI_DefButton2Wdt;
// Yes
C4GUI::Button *pBtnYes = new C4GUI::YesButton(rcBtn);
AddElement(pBtnYes);
rcBtn.x += C4GUI_DefButton2Wdt+C4GUI_DefButton2HSpace;
// No
C4GUI::Button *pBtnNo = new C4GUI::NoButton(rcBtn);
AddElement(pBtnNo);
// initial focus on abort button, to prevent accidental acceptance of setting by "blind" users
SetFocus(pBtnNo, false);
// resize to actually needed size
SetClientSize(GetClientRect().Wdt, GetClientRect().Hgt - caMain.GetHeight());
}
C4StartupOptionsDlg::ResChangeConfirmDlg::~ResChangeConfirmDlg()
{
Application.Remove(this);
}
void C4StartupOptionsDlg::ResChangeConfirmDlg::OnSec1Timer()
{
// timer ran out? Then cancel dlg
if (!--iResChangeSwitchTime)
{
Close(false); return;
}
// update timer label
StdStrBuf sTimerText;
::GraphicsResource.TextFont.BreakMessage(FormatString(LoadResStr("IDS_MNU_SWITCHRESOLUTION_UNDO"),
(int)iResChangeSwitchTime).getData(),
pOperationCancelLabel->GetBounds().Wdt, &sTimerText, true);
pOperationCancelLabel->SetText(sTimerText.getData());
}
// ------------------------------------------------
// --- C4StartupOptionsDlg::KeySelDialog
C4StartupOptionsDlg::KeySelDialog::KeySelDialog(const C4PlayerControlAssignment *assignment, const C4PlayerControlAssignmentSet *assignment_set)
: C4GUI::MessageDialog(GetDlgMessage(assignment, assignment_set).getData(), LoadResStr("IDS_MSG_DEFINEKEY"), C4GUI::MessageDialog::btnAbort | C4GUI::MessageDialog::btnReset, GetDlgIcon(assignment_set), C4GUI::MessageDialog::dsRegular),
key(KEY_Undefined), assignment(assignment), assignment_set(assignment_set)
{
const uint32_t KEYS_Allmod = KEYS_Shift | KEYS_Control | KEYS_Alt;
C4CustomKey::CodeList keys;
static_assert(KEYS_None == 0, "");
for (uint32_t k = KEYS_None; k <= KEYS_Allmod; k++) {
if (~KEYS_Allmod & k) // There is some efficient bit-twiddling (k = KEYS_Allmod & (k - KEYS_Allmod) instead of k++), but I figure that's overkill. (Relies on (defined) unsigned overflow)
continue;
keys.emplace_back(KEY_Any, k);
}
KeyListeners = std::make_unique<C4KeyBinding>(
keys,
"DefineKey",
KEYSCOPE_Gui,
new C4GUI::DlgKeyCBPassKey<C4StartupOptionsDlg::KeySelDialog>(
*this,
&C4StartupOptionsDlg::KeySelDialog::KeyDown,
&C4StartupOptionsDlg::KeySelDialog::KeyUp
),
C4CustomKey::PRIO_PlrControl
);
}
StdStrBuf C4StartupOptionsDlg::KeySelDialog::GetDlgMessage(const C4PlayerControlAssignment *assignment, const C4PlayerControlAssignmentSet *assignment_set)
{
// compose message asking for key, gamepad button and/or mouse button depending on used control set
if (!assignment || !assignment_set) return StdStrBuf("err");
StdStrBuf result_string;
if (assignment_set->HasGamepad())
result_string.Take(FormatString(LoadResStr("IDS_MSG_PRESSBTN"), assignment->GetGUIName(Game.PlayerControlDefs)));
else
result_string.Take(FormatString(LoadResStr("IDS_MSG_PRESSKEY"), assignment->GetGUIName(Game.PlayerControlDefs)));
const char *ctrl_desc = assignment->GetGUIDesc(Game.PlayerControlDefs);
if (ctrl_desc && *ctrl_desc)
{
result_string.Append("||");
result_string.Append(ctrl_desc);
}
return result_string;
}
C4GUI::Icons C4StartupOptionsDlg::KeySelDialog::GetDlgIcon(const C4PlayerControlAssignmentSet *assignment_set)
{
if (!assignment_set) return C4GUI::Ico_Error;
if (assignment_set->HasGamepad()) return C4GUI::Ico_Gamepad;
return C4GUI::Ico_Keyboard;
}
void C4StartupOptionsDlg::KeySelDialog::MouseInput(C4GUI::CMouse& rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
{
// Original action
C4GUI::MessageDialog::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
// Find out to where the input was made (code duplication with C4GUI::Window::MouseInput)
// get client pos
C4Rect &rcClientRect = GetClientRect(), &rcBounds = GetBounds();
iX -= rcClientRect.x - rcBounds.x; iY -= rcClientRect.y - rcBounds.y;
// forward to topmost child element
for (Element *pChild = pLast; pChild; pChild = pChild->GetPrev())
if (pChild->fVisible && pChild->GetBounds().Contains(iX, iY))
{
if (dynamic_cast<C4GUI::Label*>(pChild))
break;
else
return;
}
// Only process the input event if the user certainly didn't click a button or anything...
if (iButton == C4MC_Button_None) // mouse got moved
return;
bool down;
auto key = C4KeyCodeEx::FromC4MC(0 /* FIXME: more mice! */, iButton, dwKeyParam, &down);
KeyPress(key, down);
}
bool C4StartupOptionsDlg::KeySelDialog::KeyPress(const C4KeyCodeEx &key, bool fDown)
{
// safety
if (!assignment || !assignment_set) return false;
// check if key is valid for this set
if (Key_IsGamepad(key.Key))
{
if (!assignment_set->HasGamepad()) return false;
}
else if (Key_IsMouse(key.Key))
{
if (!assignment_set->HasMouse()) return false;
}
else
{
if (!assignment_set->HasKeyboard()) return false;
}
// FIXME: There doesn't seem to be any kind of KEY_IsModifier function…
if ((KEY_IsModifier(key.Key) && fDown) ||
(!KEY_IsModifier(key.Key) && !fDown))
return false;
// okay, use it
this->key=key;
Close(true);
return true;
}
// ------------------------------------------------
// --- C4StartupOptionsDlg::ControlConfigListBox::ControlAssignmentLabel
C4StartupOptionsDlg::ControlConfigListBox::ControlAssignmentLabel::ControlAssignmentLabel(class C4PlayerControlAssignment *assignment, class C4PlayerControlAssignmentSet *assignment_set, const C4Rect &bounds)
: C4GUI::Label("", bounds, ALeft, 0xffffffff, nullptr, false, false, true), assignment(assignment), assignment_set(assignment_set)
{
UpdateAssignmentString();
}
void C4StartupOptionsDlg::ControlConfigListBox::ControlAssignmentLabel::MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
{
// left-click to change key
if (iButton == C4MC_Button_LeftDown && assignment)
{
if(!assignment->IsGUIDisabled())
{
KeySelDialog *dlg = new KeySelDialog(assignment, assignment_set);
dlg->SetDelOnClose(false);
bool success = GetScreen()->ShowModalDlg(dlg, false);
C4KeyCodeEx key = dlg->GetKeyCode();
delete dlg;
if (success)
{
// dialog closed by pressing a key or by the Reset button (in which case, key==KEY_Undefined)
// assign new config
C4StartupOptionsDlg::ControlConfigListBox::SetUserKey(assignment_set, assignment, key);
UpdateAssignmentString();
}
}
}
// inherited
Element::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
}
void C4StartupOptionsDlg::ControlConfigListBox::ControlAssignmentLabel::UpdateAssignmentString()
{
// assignment label text from assigned key
StdStrBuf strKey;
C4KeyCodeEx key(0);
if (assignment)
{
SetText(assignment->GetKeysAsString(true, false).getData());
}
else
{
SetText("");
}
DWORD color = C4GUI_CaptionFontClr;
if (assignment)
{
if(assignment->IsGUIDisabled())
color = C4GUI_InactCaptionFontClr;
else if(assignment->IsKeyChanged())
color = C4GUI_Caption2FontClr;
}
SetColor(color);
}
// ------------------------------------------------
// --- C4StartupOptionsDlg::ControlConfigListBox::ListItem
C4StartupOptionsDlg::ControlConfigListBox::ListItem::ListItem(ControlConfigListBox *parent_list, class C4PlayerControlAssignment *assignment, class C4PlayerControlAssignmentSet *assignment_set, bool has_extra_spacing)
: C4GUI::Window(), parent_list(parent_list), assignment_label(nullptr), has_extra_spacing(has_extra_spacing)
{
int32_t margin = 2;
// adding to listbox will size the element horizontally and move to proper position
int32_t height = ::GraphicsResource.TextFont.GetLineHeight() + 2 * margin;
SetBounds(C4Rect(0,0,42,height));
parent_list->InsertElement(this, nullptr);
int32_t name_col_width = GetBounds().Wdt * 2/3;
// child elements: two labels for two columns
const char *gui_name = assignment->GetGUIName(Game.PlayerControlDefs);
const char *gui_desc = assignment->GetGUIDesc(Game.PlayerControlDefs);
C4GUI::Label *name_label = new C4GUI::Label(gui_name ? gui_name : "?undefined?", margin, margin);
C4Rect assignment_label_bounds = C4Rect(name_col_width + margin, margin, GetBounds().Wdt - name_col_width - margin, GetBounds().Hgt - 2 * margin);
assignment_label = new ControlAssignmentLabel(assignment, assignment_set, assignment_label_bounds);
AddElement(name_label);
AddElement(assignment_label);
if (gui_desc && *gui_desc) SetToolTip(gui_desc);
}
// ------------------------------------------------
// --- C4StartupOptionsDlg::ControlConfigListBox
C4StartupOptionsDlg::ControlConfigListBox::ControlConfigListBox(const C4Rect &rcBounds, class C4PlayerControlAssignmentSet *set)
: C4GUI::ListBox(rcBounds), set(nullptr)
{
SetAssignmentSet(set);
}
void C4StartupOptionsDlg::ControlConfigListBox::SetAssignmentSet(class C4PlayerControlAssignmentSet *new_set)
{
set = new_set;
// clear previous elements
if (pClientWindow) pClientWindow->ClearChildren();
// populate with new assignments
if (set)
{
C4PlayerControlAssignment *assignment;
std::vector<C4PlayerControlAssignment *> grouped_assignments;
for (int32_t i=0; (assignment = set->GetAssignmentByIndex(i)); ++i)
grouped_assignments.push_back(assignment);
std::stable_sort(grouped_assignments.begin(),grouped_assignments.end(),&C4StartupOptionsDlg::ControlConfigListBox::sort_by_group);
int32_t current_group = 0;
for (auto & grouped_assignment : grouped_assignments)
{
assignment = grouped_assignment;
bool first_element_of_group = assignment->GetGUIGroup() > current_group;
current_group = assignment->GetGUIGroup();
// only show assignments of GUI-named controls
const char *gui_name = assignment->GetGUIName(Game.PlayerControlDefs);
if (gui_name && *gui_name)
{
ListItem *element = new ListItem(this, assignment, set, first_element_of_group);
AddElement(element);
}
}
}
}
void C4StartupOptionsDlg::ControlConfigListBox::SetUserKey(class C4PlayerControlAssignmentSet *assignment_set, class C4PlayerControlAssignment *assignment, C4KeyCodeEx &key)
{
// change key in the specified assignment set to the specified value
// also changes config values so change is kept after restart
// safety
if (!assignment || !assignment_set) return;
class C4PlayerControlAssignmentSet *config_set = Config.Controls.UserSets.GetSetByName(assignment_set->GetName());
// change key
if (key.Key == KEY_Undefined)
{
// reset to default
assignment->ResetKeyToInherited();
// also reset in config
if (config_set)
{
config_set->RemoveAssignmentByControlName(assignment->GetControlName());
if (!config_set->GetAssignmentByIndex(0))
{
// if config set is empty, no overrides exist and the set can be deleted (unless it's a custom config set)
if (Game.PlayerControlDefaultAssignmentSets.GetSetByName(config_set->GetName()))
{
Config.Controls.UserSets.RemoveSetByName(assignment_set->GetName());
}
}
}
}
else
{
// set to specified value
assignment->SetKey(key);
// also set in config
if (!config_set) config_set = Config.Controls.UserSets.CreateEmptySetByTemplate(*assignment_set);
C4PlayerControlAssignment *config_assignment = config_set->GetAssignmentByControlName(assignment->GetControlName());
if (!config_assignment) config_assignment = config_set->CreateAssignmentForControl(assignment->GetControlName());
config_assignment->SetKey(key);
// check for duplicates. Allow them in principle because they can be used with priorities
// however, do warn because usually it may be unintended
// Just deleting the old assignments is also usually undesirable, because half of the time a newbi changes a control and creates a conflict,
// they may want to change the key they just assigned to something else instead
C4PlayerControlAssignment *other_assignment;
for (int32_t i = 0; (other_assignment = assignment_set->GetAssignmentByIndex(i)); ++i)
if (other_assignment != assignment)
if (other_assignment->GetTriggerKey() == key)
{
const char *gui_name = other_assignment->GetGUIName(Game.PlayerControlDefs);
// only warn for assignable keys, because other keys are typically derived from the settable keys
if (gui_name && *gui_name)
{
C4GUI::TheScreen.ShowMessage(FormatString(LoadResStr("IDS_MSG_DEFINEKEYDUPLICATE"), key.ToString(true, false).getData(), gui_name).getData(), LoadResStr("IDS_MSG_DEFINEKEY"), C4GUI::Ico_Error);
}
}
}
}
// ------------------------------------------------
// --- C4StartupOptionsDlg::ControlConfigArea
C4StartupOptionsDlg::ControlConfigArea::ControlConfigArea(const C4Rect &rcArea, int32_t iHMargin, int32_t iVMargin, bool fGamepad, C4StartupOptionsDlg *pOptionsDlg)
: C4GUI::Window(), fGamepad(fGamepad), pOptionsDlg(pOptionsDlg), pGUICtrl(nullptr)
{
CStdFont *pUseFontSmall = &(C4Startup::Get()->Graphics.BookSmallFont);
SetBounds(rcArea);
C4GUI::ComponentAligner caArea(rcArea, iHMargin, iVMargin, true);
// get number of control sets to be configured
C4PlayerControlAssignmentSets &assignment_sets = Game.PlayerControlUserAssignmentSets;
iMaxControlSets = std::max<size_t>(assignment_sets.GetSetCount(),1u); // do not devide by zero
ppKeyControlSetBtns = new C4GUI::IconButton *[iMaxControlSets];
// top line buttons to select control configuration
C4Facet fctCtrlDefPic = ::GraphicsResource.fctKeyboard; //
int32_t iCtrlSetWdt = caArea.GetWidth() - caArea.GetHMargin()*2;
int32_t iCtrlSetHMargin = 5, iCtrlSetVMargin = 5;
int32_t iCtrlSetBtnWdt = Clamp<int32_t>((iCtrlSetWdt - iMaxControlSets*iCtrlSetHMargin*2) / iMaxControlSets, 5, fctCtrlDefPic.Wdt);
int32_t iCtrlSetBtnHgt = fctCtrlDefPic.GetHeightByWidth(iCtrlSetBtnWdt);
iCtrlSetHMargin = (iCtrlSetWdt - iCtrlSetBtnWdt*iMaxControlSets) / (iMaxControlSets*2);
C4GUI::ComponentAligner caKeyboardSetSel(caArea.GetFromTop(2*iCtrlSetVMargin+iCtrlSetBtnHgt), iCtrlSetHMargin, iCtrlSetVMargin);
const char *szCtrlSetHotkeys = "1234567890"; /* 2do */
uint32_t i;
for (i = 0; i < assignment_sets.GetSetCount(); ++i)
{
C4PlayerControlAssignmentSet *assignment_set = assignment_sets.GetSetByIndex(i);
char cCtrlSetHotkey = 0;
if (i <= strlen(szCtrlSetHotkeys)) cCtrlSetHotkey = szCtrlSetHotkeys[i];
C4GUI::CallbackButton<C4StartupOptionsDlg::ControlConfigArea, C4GUI::IconButton> *pBtn = new C4GUI::CallbackButton<C4StartupOptionsDlg::ControlConfigArea, C4GUI::IconButton>(C4GUI::Ico_None, caKeyboardSetSel.GetFromLeft(iCtrlSetBtnWdt), cCtrlSetHotkey, &C4StartupOptionsDlg::ControlConfigArea::OnCtrlSetBtn, this);
C4Facet fctCtrlPic = assignment_set->GetPicture();
pBtn->SetFacet(fctCtrlPic);
AddElement(ppKeyControlSetBtns[i] = pBtn);
pBtn->SetText(assignment_set->GetGUIName());
pBtn->SetFont(pUseFontSmall, C4StartupFontClr);
pBtn->SetToolTip(LoadResStr("IDS_DLGTIP_CHANGECTRL"));
}
iSelectedCtrlSet = 0;
caArea.ExpandTop(caArea.GetVMargin());
AddElement(new C4GUI::HorizontalLine(caArea.GetFromTop(2)));
caArea.ExpandTop(caArea.GetVMargin());
control_list = new ControlConfigListBox(caArea.GetFromLeft(caArea.GetInnerWidth()), nullptr);
AddElement(control_list);
UpdateCtrlSet();
}
C4StartupOptionsDlg::ControlConfigArea::~ControlConfigArea()
{
delete [] ppKeyControlSetBtns;
}
void C4StartupOptionsDlg::ControlConfigArea::OnCtrlSetBtn(C4GUI::Control *btn)
{
// select Ctrl set of pressed btn
int i;
for (i = 0; i < iMaxControlSets; ++i)
if (btn == ppKeyControlSetBtns[i])
{
iSelectedCtrlSet = i;
break;
}
// update shown keys
UpdateCtrlSet();
}
void C4StartupOptionsDlg::ControlConfigArea::UpdateCtrlSet()
{
// selected keyboard set btn gets a highlight
int i;
for (i = 0; i < iMaxControlSets; ++i)
ppKeyControlSetBtns[i]->SetHighlight(i == iSelectedCtrlSet);
// update list
control_list->SetAssignmentSet(Game.PlayerControlUserAssignmentSets.GetSetByIndex(iSelectedCtrlSet));
}
void C4StartupOptionsDlg::ControlConfigArea::OnResetKeysBtn(C4GUI::Control *btn)
{
// default keys and axis reset
Config.Controls.ResetKeys();
UpdateCtrlSet();
}
void C4StartupOptionsDlg::ControlConfigArea::OnGUIGamepadCheckChange(C4GUI::Element *pCheckBox)
{
// same as before?
bool fChecked = ((C4GUI::CheckBox *)(pCheckBox))->GetChecked();
if (fChecked == !!Config.Controls.GamepadGuiControl) return;
// reflect change
Config.Controls.GamepadGuiControl = fChecked;
::pGUI->UpdateGamepadGUIControlEnabled();
pOptionsDlg->RecreateDialog(false);
}
// ------------------------------------------------
// --- C4StartupOptionsDlg::NetworkPortConfig
C4StartupOptionsDlg::NetworkPortConfig::NetworkPortConfig(const C4Rect &rcBounds, const char *szName, int32_t *pConfigValue, int32_t iDefault)
: C4GUI::Window(), pConfigValue(pConfigValue)
{
// ctor
CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
SetBounds(rcBounds);
C4GUI::ComponentAligner caMain(GetClientRect(), 0,2, true);
bool fEnabled = (*pConfigValue != -1);
C4GUI::Label *pLbl = new C4GUI::Label(szName, caMain.GetFromTop(pUseFont->GetLineHeight()), ALeft, C4StartupFontClr, pUseFont, false);
AddElement(pLbl);
C4GUI::ComponentAligner caBottomLine(caMain.GetAll(), 2,0, false);
const char *szText = LoadResStr("IDS_CTL_ACTIVE");
int iWdt=100, iHgt=12;
C4GUI::CheckBox::GetStandardCheckBoxSize(&iWdt, &iHgt, szText, pUseFont);
pEnableCheck = new C4GUI::CheckBox(caBottomLine.GetFromLeft(iWdt, iHgt), szText, fEnabled);
pEnableCheck->SetFont(pUseFont, C4StartupFontClr, C4StartupFontClrDisabled);
pEnableCheck->SetOnChecked(new C4GUI::CallbackHandler<C4StartupOptionsDlg::NetworkPortConfig>(this, &C4StartupOptionsDlg::NetworkPortConfig::OnEnabledCheckChange));
AddElement(pEnableCheck);
pPortEdit = new C4GUI::Edit(caBottomLine.GetAll(), true);
pPortEdit->SetColors(C4StartupEditBGColor, C4StartupFontClr, C4StartupEditBorderColor);
pPortEdit->SetFont(pUseFont);
pPortEdit->InsertText(FormatString("%d", fEnabled ? ((int) *pConfigValue) : (int) iDefault).getData(), false);
pPortEdit->SetMaxText(10); // 65535 is five characters long - but allow some more for easier editing
pPortEdit->SetVisibility(fEnabled);
AddElement(pPortEdit);
}
void C4StartupOptionsDlg::NetworkPortConfig::OnEnabledCheckChange(C4GUI::Element *pCheckBox)
{
pPortEdit->SetVisibility(pEnableCheck->GetChecked());
}
void C4StartupOptionsDlg::NetworkPortConfig::SavePort()
{
*pConfigValue=GetPort();
}
int32_t C4StartupOptionsDlg::NetworkPortConfig::GetPort()
{
// controls to config
if (!pEnableCheck->GetChecked())
return -1;
else
return atoi(pPortEdit->GetText());
}
bool C4StartupOptionsDlg::NetworkPortConfig::GetControlSize(int *piWdt, int *piHgt)
{
CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
// get size needed for control
if (piWdt)
{
const char *szText = LoadResStr("IDS_CTL_ACTIVE");
if (!C4GUI::CheckBox::GetStandardCheckBoxSize(piWdt, piHgt, szText, pUseFont)) return false;
*piWdt *= 2;
}
if (piHgt) *piHgt = C4GUI::Edit::GetCustomEditHeight(pUseFont) + pUseFont->GetLineHeight()+2*4;
return true;
}
// ------------------------------------------------
// --- C4StartupOptionsDlg::NetworkServerAddressConfig
C4StartupOptionsDlg::NetworkServerAddressConfig::NetworkServerAddressConfig(const C4Rect &rcBounds, const char *szName, int32_t *piConfigEnableValue, char *szConfigAddressValue, int iTabWidth)
: C4GUI::Window(), piConfigEnableValue(piConfigEnableValue), szConfigAddressValue(szConfigAddressValue)
{
// ctor
CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
SetBounds(rcBounds);
C4GUI::ComponentAligner caMain(GetClientRect(), 0,2, true);
pEnableCheck = new C4GUI::CheckBox(caMain.GetFromLeft(iTabWidth, pUseFont->GetLineHeight()), szName, !!*piConfigEnableValue);
pEnableCheck->SetFont(pUseFont, C4StartupFontClr, C4StartupFontClrDisabled);
pEnableCheck->SetOnChecked(new C4GUI::CallbackHandler<C4StartupOptionsDlg::NetworkServerAddressConfig>(this, &C4StartupOptionsDlg::NetworkServerAddressConfig::OnEnabledCheckChange));
AddElement(pEnableCheck);
caMain.ExpandLeft(-2);
pAddressEdit = new C4GUI::Edit(caMain.GetAll(), true);
pAddressEdit->SetColors(C4StartupEditBGColor, C4StartupFontClr, C4StartupEditBorderColor);
pAddressEdit->SetFont(pUseFont);
pAddressEdit->InsertText(szConfigAddressValue, false);
pAddressEdit->SetMaxText(CFG_MaxString);
pAddressEdit->SetVisibility(!!*piConfigEnableValue);
AddElement(pAddressEdit);
}
void C4StartupOptionsDlg::NetworkServerAddressConfig::OnEnabledCheckChange(C4GUI::Element *pCheckBox)
{
// warn about using alternate servers
if (pEnableCheck->GetChecked())
{
GetScreen()->ShowMessage(LoadResStr("IDS_NET_NOOFFICIALLEAGUE"), LoadResStr("IDS_NET_QUERY_MASTERSRV"), C4GUI::Ico_Notify, &Config.Startup.HideMsgNoOfficialLeague);
}
// callback when checkbox is ticked
pAddressEdit->SetVisibility(pEnableCheck->GetChecked());
}
void C4StartupOptionsDlg::NetworkServerAddressConfig::Save2Config()
{
// controls to config
*piConfigEnableValue = pEnableCheck->GetChecked();
SCopy(pAddressEdit->GetText(), szConfigAddressValue, CFG_MaxString);
}
bool C4StartupOptionsDlg::NetworkServerAddressConfig::GetControlSize(int *piWdt, int *piHgt, int *piTabPos, const char *szForText)
{
CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
int iWdt=120;
C4GUI::CheckBox::GetStandardCheckBoxSize(&iWdt, piHgt, szForText, pUseFont);
int32_t iEdtWdt=120, iEdtHgt=24;
pUseFont->GetTextExtent("sorgentelefon@treffpunktclonk.net", iEdtWdt, iEdtHgt, false);
if (piWdt) *piWdt = iWdt+iEdtWdt+2;
if (piTabPos) *piTabPos = iWdt+2;
if (piHgt) *piHgt = C4GUI::Edit::GetCustomEditHeight(pUseFont)+2*2;
return true;
}
// ------------------------------------------------
// --- C4StartupOptionsDlg::BoolConfig
C4StartupOptionsDlg::BoolConfig::BoolConfig(const C4Rect &rcBounds, const char *szName, bool *pbVal, int32_t *piVal, bool fInvert, int32_t *piRestartChangeCfgVal)
: C4GUI::CheckBox(rcBounds, szName, fInvert != (pbVal ? *pbVal : !!*piVal)), pbVal(pbVal), fInvert(fInvert), piVal(piVal), piRestartChangeCfgVal(piRestartChangeCfgVal)
{
SetOnChecked(new C4GUI::CallbackHandler<BoolConfig>(this, &BoolConfig::OnCheckChange));
}
void C4StartupOptionsDlg::BoolConfig::OnCheckChange(C4GUI::Element *pCheckBox)
{
if (pbVal) *pbVal = (GetChecked() != fInvert);
if (piVal) *piVal = (GetChecked() != fInvert);
if (piRestartChangeCfgVal) GetScreen()->ShowMessage(LoadResStr("IDS_MSG_RESTARTCHANGECFG"), GetText(),
C4GUI::Ico_Notify, piRestartChangeCfgVal);
}
// ------------------------------------------------
// --- C4StartupOptionsDlg::EditConfig
C4StartupOptionsDlg::EditConfig::EditConfig(const C4Rect &rcBounds, const char *szName, ValidatedStdCopyStrBufBase *psConfigVal, int32_t *piConfigVal, bool fMultiline)
: C4GUI::LabeledEdit(rcBounds, szName, fMultiline, psConfigVal ? psConfigVal->getData() : nullptr, &(C4Startup::Get()->Graphics.BookFont), C4StartupFontClr), psConfigVal(psConfigVal), piConfigVal(piConfigVal)
{
// ctor
GetEdit()->SetColors(C4StartupEditBGColor, C4StartupFontClr, C4StartupEditBorderColor);
if (piConfigVal) SetIntVal(*piConfigVal);
GetEdit()->SetMaxText(CFG_MaxString);
}
void C4StartupOptionsDlg::EditConfig::Save2Config()
{
// controls to config
if (psConfigVal)
psConfigVal->CopyValidated(GetEdit()->GetText());
if (piConfigVal)
*piConfigVal = GetIntVal();
}
bool C4StartupOptionsDlg::EditConfig::GetControlSize(int *piWdt, int *piHgt, const char *szForText, bool fMultiline)
{
CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
typedef C4GUI::LabeledEdit BaseEdit;
return BaseEdit::GetControlSize(piWdt, piHgt, szForText, pUseFont, fMultiline);
}
// ------------------------------------------------
// --- C4StartupOptionsDlg
C4StartupOptionsDlg::C4StartupOptionsDlg() : C4StartupDlg(LoadResStrNoAmp("IDS_DLG_OPTIONS"))
{
// ctor
UpdateSize();
bool fSmall = (GetClientRect().Wdt < 750);
CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
// key bindings
C4CustomKey::CodeList keys;
keys.emplace_back(K_BACK); keys.emplace_back(K_LEFT);
pKeyBack = new C4KeyBinding(keys, "StartupOptionsBack", KEYSCOPE_Gui,
new C4GUI::DlgKeyCB<C4StartupOptionsDlg>(*this, &C4StartupOptionsDlg::KeyBack), C4CustomKey::PRIO_Dlg);
keys.clear();
keys.emplace_back(K_F3); // overloading global toggle with higher priority here, so a new name is required
pKeyToggleMusic = new C4KeyBinding(keys, "OptionsMusicToggle", KEYSCOPE_Gui,
new C4GUI::DlgKeyCB<C4StartupOptionsDlg>(*this, &C4StartupOptionsDlg::KeyMusicToggle), C4CustomKey::PRIO_Dlg);
// screen calculations
int32_t iButtonWidth,iCaptionFontHgt;
int32_t iButtonHeight = C4GUI_ButtonHgt;
::GraphicsResource.CaptionFont.GetTextExtent("<< BACK", iButtonWidth, iCaptionFontHgt, true);
iButtonWidth *= 3;
int iIndentX1, iIndentY1, iIndentY2;
if (fSmall)
{
iIndentX1=20;
}
else
{
iIndentX1 = GetClientRect().Wdt/40;
}
if (fSmall)
{
iIndentY1=1; iIndentY2=1;
}
else
{
iIndentY1 = GetClientRect().Hgt/200;
iIndentY2 = std::max<int32_t>(1, iIndentY1/2);
}
C4GUI::ComponentAligner caMain(GetClientRect(), 0,0, true);
C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(caMain.GetHeight()/(fSmall ? 20 : 7)),0,0);
C4GUI::ComponentAligner caButtons(caButtonArea.GetCentered(caMain.GetWidth()*7/8, iButtonHeight),0,0);
C4GUI::ComponentAligner caConfigArea(caMain.GetAll(), fSmall ? 0 : (caMain.GetWidth()*69/1730), fSmall ? 0 : (caMain.GetHeight()/200));
// back button
C4GUI::CallbackButton<C4StartupOptionsDlg> *btn;
AddElement(btn = new C4GUI::CallbackButton<C4StartupOptionsDlg>(LoadResStr("IDS_BTN_BACK"), caButtons.GetFromLeft(iButtonWidth), &C4StartupOptionsDlg::OnBackBtn));
btn->SetToolTip(LoadResStr("IDS_DLGTIP_BACKMAIN"));
// main config area tabular
pOptionsTabular = new C4GUI::Tabular(caConfigArea.GetAll(), C4GUI::Tabular::tbLeft);
pOptionsTabular->SetGfx(&C4Startup::Get()->Graphics.fctDlgPaper, &C4Startup::Get()->Graphics.fctOptionsTabClip, &C4Startup::Get()->Graphics.fctOptionsIcons, &C4Startup::Get()->Graphics.BookSmallFont, true);
AddElement(pOptionsTabular);
C4GUI::Tabular::Sheet *pSheetGeneral = pOptionsTabular->AddSheet(LoadResStr("IDS_DLG_PROGRAM") , 0);
C4GUI::Tabular::Sheet *pSheetGraphics = pOptionsTabular->AddSheet(LoadResStr("IDS_DLG_GRAPHICS"), 1);
C4GUI::Tabular::Sheet *pSheetSound = pOptionsTabular->AddSheet(LoadResStr("IDS_DLG_SOUND") , 2);
C4GUI::Tabular::Sheet *pSheetControls= pOptionsTabular->AddSheet(LoadResStr("IDS_DLG_CONTROLS"), 3);
C4GUI::Tabular::Sheet *pSheetNetwork = pOptionsTabular->AddSheet(LoadResStr("IDS_DLG_NETWORK") , 5);
C4GUI::CheckBox *pCheck; C4GUI::Label *pLbl;
int iCheckWdt=100, iCheckHgt=20, iEdit2Wdt=100, iEdit2Hgt=40;
BoolConfig::GetStandardCheckBoxSize(&iCheckWdt, &iCheckHgt, "Default text", pUseFont);
EditConfig::GetControlSize(&iEdit2Wdt, &iEdit2Hgt, "Default text", false);
// --- page program
C4GUI::ComponentAligner caSheetProgram(pSheetGeneral->GetClientRect(), caMain.GetWidth()/20, caMain.GetHeight()/20, true);
// language
const char *szLangTip = LoadResStr("IDS_MSG_SELECTLANG");
C4GUI::ComponentAligner caLanguage(caSheetProgram.GetGridCell(0,1,0,7,-1,-1,true,1,2), 0, C4GUI_DefDlgSmallIndent, false);
C4GUI::ComponentAligner caLanguageBox(caLanguage.GetFromTop(C4GUI::ComboBox::GetDefaultHeight()), 0, 0, false);
StdStrBuf sLangStr; sLangStr.Copy(LoadResStr("IDS_CTL_LANGUAGE")); sLangStr.AppendChar(':');
int32_t w,q;
pUseFont->GetTextExtent(sLangStr.getData(), w,q,true);
pLbl = new C4GUI::Label(sLangStr.getData(), caLanguageBox.GetFromLeft(w+C4GUI_DefDlgSmallIndent), ALeft, C4StartupFontClr, pUseFont, false);
pLbl->SetToolTip(szLangTip);
pSheetGeneral->AddElement(pLbl);
pUseFont->GetTextExtent("XX: Top Secret Language", w,q,true);
pLangCombo = new C4GUI::ComboBox(caLanguageBox.GetFromLeft(std::min(w, caLanguageBox.GetWidth())));
pLangCombo->SetToolTip(szLangTip);
pLangCombo->SetComboCB(new C4GUI::ComboBox_FillCallback<C4StartupOptionsDlg>(this, &C4StartupOptionsDlg::OnLangComboFill, &C4StartupOptionsDlg::OnLangComboSelChange));
pLangCombo->SetColors(C4StartupFontClr, C4StartupEditBGColor, C4StartupEditBorderColor);
pLangCombo->SetFont(pUseFont);
pLangCombo->SetDecoration(&(C4Startup::Get()->Graphics.fctContext));
pSheetGeneral->AddElement(pLangCombo);
pLangInfoLabel = new C4GUI::Label(nullptr, caLanguage.GetFromTop(::GraphicsResource.TextFont.GetLineHeight()*3), ALeft, C4StartupFontClr, pUseFont, false);
pLangInfoLabel->SetToolTip(szLangTip);
pSheetGeneral->AddElement(pLangInfoLabel);
UpdateLanguage();
// font
const char *szFontTip = LoadResStr("IDS_DESC_SELECTFONT");
C4GUI::ComponentAligner caFontBox(caSheetProgram.GetGridCell(0,1,2,7,-1,C4GUI::ComboBox::GetDefaultHeight(), true), 0, 0, false);
StdStrBuf sFontStr; sFontStr.Copy(LoadResStr("IDS_CTL_FONT")); sFontStr.AppendChar(':');
pUseFont->GetTextExtent(sFontStr.getData(), w,q,true);
pLbl = new C4GUI::Label(sFontStr.getData(), caFontBox.GetFromLeft(w+C4GUI_DefDlgSmallIndent), ALeft, C4StartupFontClr, pUseFont, false);
pLbl->SetToolTip(szFontTip);
pSheetGeneral->AddElement(pLbl);
pUseFont->GetTextExtent("Comic Sans MS", w,q,true);
pFontFaceCombo = new C4GUI::ComboBox(caFontBox.GetFromLeft(std::min<int32_t>(caFontBox.GetInnerWidth()*3/4, w*3)));
pFontFaceCombo->SetToolTip(szFontTip);
pFontFaceCombo->SetComboCB(new C4GUI::ComboBox_FillCallback<C4StartupOptionsDlg>(this, &C4StartupOptionsDlg::OnFontFaceComboFill, &C4StartupOptionsDlg::OnFontComboSelChange));
pFontFaceCombo->SetColors(C4StartupFontClr, C4StartupEditBGColor, C4StartupEditBorderColor);
pFontFaceCombo->SetFont(pUseFont);
pFontFaceCombo->SetDecoration(&(C4Startup::Get()->Graphics.fctContext));
caFontBox.ExpandLeft(-C4GUI_DefDlgSmallIndent);
pSheetGeneral->AddElement(pFontFaceCombo);
pFontSizeCombo = new C4GUI::ComboBox(caFontBox.GetFromLeft(std::min<int32_t>(caFontBox.GetInnerWidth(), w)));
pFontSizeCombo->SetToolTip(LoadResStr("IDS_DESC_FONTSIZE"));
pFontSizeCombo->SetComboCB(new C4GUI::ComboBox_FillCallback<C4StartupOptionsDlg>(this, &C4StartupOptionsDlg::OnFontSizeComboFill, &C4StartupOptionsDlg::OnFontComboSelChange));
pFontSizeCombo->SetColors(C4StartupFontClr, C4StartupEditBGColor, C4StartupEditBorderColor);
pFontSizeCombo->SetFont(pUseFont);
pFontSizeCombo->SetDecoration(&(C4Startup::Get()->Graphics.fctContext));
pSheetGeneral->AddElement(pFontSizeCombo);
UpdateFontControls();
// MM timer
pCheck = new BoolConfig(caSheetProgram.GetGridCell(0,1,3,7,-1,iCheckHgt, true), LoadResStr("IDS_CTL_MMTIMER"), nullptr, &Config.General.MMTimer, true, &Config.Startup.HideMsgMMTimerChange);
pCheck->SetToolTip(LoadResStr("IDS_MSG_MMTIMER_DESC"));
pCheck->SetFont(pUseFont, C4StartupFontClr, C4StartupFontClrDisabled);
pSheetGeneral->AddElement(pCheck);
// reset configuration
const char *szBtnText = LoadResStr("IDS_BTN_RESETCONFIG");
C4GUI::CallbackButton<C4StartupOptionsDlg, SmallButton> *pSmallBtn;
::GraphicsResource.CaptionFont.GetTextExtent(szBtnText, iButtonWidth, iButtonHeight, true);
C4Rect rcResetBtn = caSheetProgram.GetGridCell(1,2,6,7, std::min<int32_t>(iButtonWidth+iButtonHeight*4, caSheetProgram.GetInnerWidth()*2/5), SmallButton::GetDefaultButtonHeight(), true);
pSmallBtn = new C4GUI::CallbackButton<C4StartupOptionsDlg, SmallButton>(szBtnText, rcResetBtn, &C4StartupOptionsDlg::OnResetConfigBtn, this);
pSheetGeneral->AddElement(pSmallBtn);
pSmallBtn->SetToolTip(LoadResStr("IDS_DESC_RESETCONFIG"));
// --- page graphics
C4GUI::ComponentAligner caSheetGraphics(pSheetGraphics->GetClientRect(), iIndentX1, iIndentY1, true);
// --subgroup display
C4GUI::GroupBox *pGroupResolution = new C4GUI::GroupBox(caSheetGraphics.GetGridCell(0,1,0,2));
pGroupResolution->SetTitle(LoadResStrNoAmp("IDS_CTL_DISPLAY"));
pGroupResolution->SetFont(pUseFont);
pGroupResolution->SetColors(C4StartupEditBorderColor, C4StartupFontClr);
pSheetGraphics->AddElement(pGroupResolution);
C4GUI::ComponentAligner caGroupResolution(pGroupResolution->GetClientRect(), iIndentX1, iIndentY2, true);
int32_t iNumGfxOptions = 3, iOpt = 0;
// resolution combobox
C4GUI::ComponentAligner resBox(caGroupResolution.GetGridCell(0,1,iOpt++,iNumGfxOptions), 0, 0, false);
w=20; q=12; pUseFont->GetTextExtent(LoadResStr("IDS_CTL_RESOLUTION"), w,q, true);
pGroupResolution->AddElement(new C4GUI::Label(LoadResStr("IDS_CTL_RESOLUTION"), resBox.GetFromLeft(w+C4GUI_DefDlgSmallIndent,q), ALeft, C4StartupFontClr, pUseFont, false, false));
pUseFont->GetTextExtent("1600 x 1200", w,q,true); w = std::min<int32_t>(caGroupResolution.GetInnerWidth(), w+40);
C4GUI::ComboBox *pGfxResCombo = new C4GUI::ComboBox(resBox.GetFromLeft(w+40,C4GUI::ComboBox::GetDefaultHeight()));
pGfxResCombo->SetToolTip(LoadResStr("IDS_MSG_RESOLUTION_DESC"));
pGfxResCombo->SetComboCB(new C4GUI::ComboBox_FillCallback<C4StartupOptionsDlg>(this, &C4StartupOptionsDlg::OnGfxResComboFill, &C4StartupOptionsDlg::OnGfxResComboSelChange));
pGfxResCombo->SetColors(C4StartupFontClr, C4StartupEditBGColor, C4StartupEditBorderColor);
pGfxResCombo->SetFont(pUseFont);
pGfxResCombo->SetDecoration(&(C4Startup::Get()->Graphics.fctContext));
pGfxResCombo->SetText(GetGfxResString(Config.Graphics.ResX, Config.Graphics.ResY).getData());
pGroupResolution->AddElement(pGfxResCombo);
// fullscreen combobox
C4GUI::ComponentAligner fsBox(caGroupResolution.GetGridCell(0,1,iOpt++,iNumGfxOptions), 0, 0, false);
w=20; q=12; pUseFont->GetTextExtent(LoadResStr("IDS_CTL_FULLSCREENMODE"), w,q, true);
pGroupResolution->AddElement(new C4GUI::Label(LoadResStr("IDS_CTL_FULLSCREENMODE"), fsBox.GetFromLeft(w+C4GUI_DefDlgSmallIndent,q), ALeft, C4StartupFontClr, pUseFont, false, false));
uint32_t wmax = 0;
for(int i = 0; i < 3; ++i)
{
pUseFont->GetTextExtent(GetWindowedName(i),w,q,true);
wmax = std::max<int32_t>(w, wmax);
}
C4GUI::ComboBox * pCombo = new C4GUI::ComboBox(fsBox.GetFromLeft(w+40,C4GUI::ComboBox::GetDefaultHeight()));
pCombo->SetComboCB(new C4GUI::ComboBox_FillCallback<C4StartupOptionsDlg>(this, &C4StartupOptionsDlg::OnWindowedModeComboFill, &C4StartupOptionsDlg::OnWindowedModeComboSelChange));
pCombo->SetToolTip(LoadResStr("IDS_MSG_FULLSCREEN_DESC"));
pCombo->SetColors(C4StartupFontClr, C4StartupEditBGColor, C4StartupEditBorderColor);
pCombo->SetFont(pUseFont);
pCombo->SetDecoration(&(C4Startup::Get()->Graphics.fctContext));
pCombo->SetText(GetWindowedName());
pGroupResolution->AddElement(pCombo);
// --subgroup options
iNumGfxOptions = 5, iOpt=0;
C4GUI::GroupBox *pGroupOptions = new C4GUI::GroupBox(caSheetGraphics.GetGridCell(0,1,1,2));
pGroupOptions->SetTitle(LoadResStrNoAmp("IDS_DLG_OPTIONS"));
pGroupOptions->SetFont(pUseFont);
pGroupOptions->SetColors(C4StartupEditBorderColor, C4StartupFontClr);
pSheetGraphics->AddElement(pGroupOptions);
C4GUI::ComponentAligner caGroupOptions(pGroupOptions->GetClientRect(), iIndentX1, iIndentY2, true);
// multisampling
C4GUI::ComponentAligner msBox(caGroupOptions.GetFromTop(C4GUI::ComboBox::GetDefaultHeight()), 0, 0, false);
w=20; q=12; pUseFont->GetTextExtent(LoadResStr("IDS_CTL_ANTIALIASING"), w,q, true);
pGroupOptions->AddElement(new C4GUI::Label(LoadResStr("IDS_CTL_ANTIALIASING"), msBox.GetFromLeft(w+C4GUI_DefDlgSmallIndent), ALeft, C4StartupFontClr, pUseFont, false, false));
pUseFont->GetTextExtent("Off", w, q, true);
C4GUI::ComboBox *pGfxMSCombo = new C4GUI::ComboBox(msBox.GetFromLeft(w+40,C4GUI::ComboBox::GetDefaultHeight()));
pGfxMSCombo->SetToolTip(LoadResStr("IDS_MSG_ANTIALIASING_DESC"));
pGfxMSCombo->SetComboCB(new C4GUI::ComboBox_FillCallback<C4StartupOptionsDlg>(this, &C4StartupOptionsDlg::OnGfxMSComboFill, &C4StartupOptionsDlg::OnGfxMSComboSelChange));
pGfxMSCombo->SetColors(C4StartupFontClr, C4StartupEditBGColor, C4StartupEditBorderColor);
pGfxMSCombo->SetFont(pUseFont);
pGfxMSCombo->SetDecoration(&(C4Startup::Get()->Graphics.fctContext));
// Pre-Select current setting
StdStrBuf Current;
if(Config.Graphics.MultiSampling) Current.Format("%dx", Config.Graphics.MultiSampling);
else Current.Copy("Off");
pGfxMSCombo->SetText(Current.getData());
// Set control read only if multisampling is not available
std::vector<int> multisamples;
Application.pWindow->EnumerateMultiSamples(multisamples);
pGfxMSCombo->SetReadOnly(multisamples.empty());
pGroupOptions->AddElement(pGfxMSCombo);
// automatic gfx frame skip
pCheck = new BoolConfig(caGroupOptions.GetGridCell(0,1,iOpt++,iNumGfxOptions,-1,iCheckHgt,true), LoadResStr("IDS_MSG_AUTOFRAMESKIP"), nullptr, &Config.Graphics.AutoFrameSkip);
pCheck->SetToolTip(LoadResStr("IDS_DESC_AUTOFRAMESKIP"));
pCheck->SetFont(pUseFont, C4StartupFontClr, C4StartupFontClrDisabled);
pGroupOptions->AddElement(pCheck);
// --- page sound
C4GUI::ComponentAligner caSheetSound(pSheetSound->GetClientRect(), iIndentX1, iIndentY1, true);
if (!C4GUI::CheckBox::GetStandardCheckBoxSize(&iCheckWdt, &iCheckHgt, "Lorem ipsum", pUseFont)) { iCheckWdt=100; iCheckHgt=20; }
int32_t iGridWdt = iCheckWdt * 2, iGridHgt = iCheckHgt * 5/2;
// --subgroup menu system sound
C4GUI::GroupBox *pGroupFESound = new C4GUI::GroupBox(caSheetSound.GetGridCell(0,2,0,5, iGridWdt, iGridHgt, false, 1,2));
pGroupFESound->SetTitle(LoadResStrNoAmp("IDS_CTL_FRONTEND"));
pGroupFESound->SetFont(pUseFont);
pGroupFESound->SetColors(C4StartupEditBorderColor, C4StartupFontClr);
pSheetSound->AddElement(pGroupFESound);
C4GUI::ComponentAligner caGroupFESound(pGroupFESound->GetClientRect(), iIndentX1, iIndentY2, true);
// menu system music
pCheck = pFEMusicCheck = new C4GUI::CheckBox(caGroupFESound.GetGridCell(0,1,0,2,-1,iCheckHgt,true), LoadResStr("IDS_CTL_MUSIC"), !!Config.Sound.FEMusic);
pCheck->SetToolTip(LoadResStr("IDS_DESC_MENUMUSIC"));
pCheck->SetFont(pUseFont, C4StartupFontClr, C4StartupFontClrDisabled);
pCheck->SetOnChecked(new C4GUI::CallbackHandler<C4StartupOptionsDlg>(this, &C4StartupOptionsDlg::OnFEMusicCheck));
pGroupFESound->AddElement(pCheck);
// menu system sound effects
pCheck = pFESoundCheck = new BoolConfig(caGroupFESound.GetGridCell(0,1,1,2,-1,iCheckHgt,true), LoadResStr("IDS_CTL_SOUNDFX"), nullptr, &Config.Sound.FESamples);
pCheck->SetToolTip(LoadResStr("IDS_DESC_MENUSOUND"));
pCheck->SetFont(pUseFont, C4StartupFontClr, C4StartupFontClrDisabled);
pGroupFESound->AddElement(pCheck);
// --subgroup game sound
C4GUI::GroupBox *pGroupRXSound = new C4GUI::GroupBox(caSheetSound.GetGridCell(1,2,0,5, iGridWdt, iGridHgt, false, 1,2));
pGroupRXSound->SetTitle(LoadResStrNoAmp("IDS_CTL_GAME"));
pGroupRXSound->SetFont(pUseFont);
pGroupRXSound->SetColors(C4StartupEditBorderColor, C4StartupFontClr);
pSheetSound->AddElement(pGroupRXSound);
C4GUI::ComponentAligner caGroupRXSound(pGroupRXSound->GetClientRect(), iIndentX1, iIndentY2, true);
// game music
pCheck = new BoolConfig(caGroupRXSound.GetGridCell(0,1,0,2,-1,iCheckHgt,true), LoadResStr("IDS_CTL_MUSIC"), nullptr, &Config.Sound.RXMusic);
pCheck->SetToolTip(LoadResStr("IDS_DESC_GAMEMUSIC"));
pCheck->SetFont(pUseFont, C4StartupFontClr, C4StartupFontClrDisabled);
pGroupRXSound->AddElement(pCheck);
// game sound effects
pCheck = new C4GUI::CheckBox(caGroupRXSound.GetGridCell(0,1,1,2,-1,iCheckHgt,true), LoadResStr("IDS_CTL_SOUNDFX"), !!Config.Sound.RXSound);
pCheck->SetToolTip(LoadResStr("IDS_DESC_GAMESOUND"));
pCheck->SetFont(pUseFont, C4StartupFontClr, C4StartupFontClrDisabled);
pCheck->SetOnChecked(new C4GUI::CallbackHandler<C4StartupOptionsDlg>(this, &C4StartupOptionsDlg::OnRXSoundCheck));
pGroupRXSound->AddElement(pCheck);
// -- subgroup volume
C4GUI::GroupBox *pGroupVolume = new C4GUI::GroupBox(caSheetSound.GetGridCell(0,2,2,5, iGridWdt, iGridHgt, false, 2,3));
pGroupVolume->SetTitle(LoadResStrNoAmp("IDS_BTN_VOLUME"));
pGroupVolume->SetFont(pUseFont);
pGroupVolume->SetColors(C4StartupEditBorderColor, C4StartupFontClr);
pSheetSound->AddElement(pGroupVolume);
C4GUI::ComponentAligner caGroupVolume(pGroupVolume->GetClientRect(), iIndentX1, iIndentY2, true);
// volume sliders
int32_t i;
for (i=0; i<2; ++i)
{
StdStrBuf sLabelTxt;
C4GUI::ComponentAligner caVolumeSlider(caGroupVolume.GetGridCell(0,1,i,2, -1, pUseFont->GetLineHeight()+iIndentY2*2+C4GUI_ScrollBarHgt, true), 1,0,false);
pGroupVolume->AddElement(new C4GUI::Label(FormatString("%s:", LoadResStr(i ? "IDS_CTL_SOUNDFX" : "IDS_CTL_MUSIC")).getData(), caVolumeSlider.GetFromTop(pUseFont->GetLineHeight()), ALeft, C4StartupFontClr, pUseFont, false, false));
sLabelTxt.Copy(LoadResStr("IDS_CTL_SILENT"));
w=20; q=12; pUseFont->GetTextExtent(sLabelTxt.getData(), w,q, true);
pGroupVolume->AddElement(new C4GUI::Label(sLabelTxt.getData(), caVolumeSlider.GetFromLeft(w,q), ACenter, C4StartupFontClr, pUseFont, false, false));
sLabelTxt.Copy(LoadResStr("IDS_CTL_LOUD"));
pUseFont->GetTextExtent(sLabelTxt.getData(), w,q, true);
pGroupVolume->AddElement(new C4GUI::Label(sLabelTxt.getData(), caVolumeSlider.GetFromRight(w,q), ACenter, C4StartupFontClr, pUseFont, false, false));
auto *pCB = new C4GUI::ParCallbackHandler<C4StartupOptionsDlg, int32_t>(this, i ? &C4StartupOptionsDlg::OnSoundVolumeSliderChange : &C4StartupOptionsDlg::OnMusicVolumeSliderChange);
auto *pSlider = new C4GUI::ScrollBar(caVolumeSlider.GetCentered(caVolumeSlider.GetInnerWidth(), C4GUI_ScrollBarHgt), true, pCB, 101);
pSlider->SetDecoration(&C4Startup::Get()->Graphics.sfctBookScroll, false);
pSlider->SetToolTip(i ? LoadResStr("IDS_DESC_VOLUMESOUND") : LoadResStr("IDS_DESC_VOLUMEMUSIC"));
pSlider->SetScrollPos(i ? Config.Sound.SoundVolume : Config.Sound.MusicVolume);
pGroupVolume->AddElement(pSlider);
}
// --- page controls
pSheetControls->AddElement(pControlConfigArea = new ControlConfigArea(pSheetControls->GetClientRect(), caMain.GetWidth()/20, caMain.GetHeight()/40, false, this));
// --- page network
int iPortCfgWdt=200, iPortCfgHgt=48; NetworkPortConfig::GetControlSize(&iPortCfgWdt, &iPortCfgHgt);
int iServerCfgWdt = 120, iServerCfgHgt = 20, iServerCfgWdtMid = 0;
StdStrBuf sServerText; sServerText.Copy(LoadResStr("IDS_CTL_USEOTHERSERVER"));
NetworkServerAddressConfig::GetControlSize(&iServerCfgWdt, &iServerCfgHgt, &iServerCfgWdtMid, sServerText.getData());
int32_t net_component_hgt = iPortCfgHgt * 4 + iServerCfgHgt + 3 * pUseFont->GetLineHeight();
C4GUI::ComponentAligner caSheetNetwork(pSheetNetwork->GetClientRect(), caMain.GetWidth() / 20, std::max<int32_t>(0, (caMain.GetHeight() - net_component_hgt)/20), true);
pPortCfgTCP = new NetworkPortConfig(caSheetNetwork.GetGridCell(0,2,0,2, iPortCfgWdt, iPortCfgHgt), LoadResStr("IDS_NET_PORT_TCP"), &(Config.Network.PortTCP), C4NetStdPortTCP);
pPortCfgUDP = new NetworkPortConfig(caSheetNetwork.GetGridCell(1,2,0,2, iPortCfgWdt, iPortCfgHgt), LoadResStr("IDS_NET_PORT_UDP"), &(Config.Network.PortUDP), C4NetStdPortUDP);
pPortCfgRef = new NetworkPortConfig(caSheetNetwork.GetGridCell(0,2,1,2, iPortCfgWdt, iPortCfgHgt), LoadResStr("IDS_NET_PORT_REFERENCE"), &(Config.Network.PortRefServer), C4NetStdPortRefServer);
pPortCfgDsc = new NetworkPortConfig(caSheetNetwork.GetGridCell(1,2,1,2, iPortCfgWdt, iPortCfgHgt), LoadResStr("IDS_NET_PORT_DISCOVERY"), &(Config.Network.PortDiscovery), C4NetStdPortDiscovery);
pPortCfgTCP->SetToolTip(LoadResStr("IDS_NET_PORT_TCP_DESC"));
pPortCfgUDP->SetToolTip(LoadResStr("IDS_NET_PORT_UDP_DESC"));
pPortCfgRef->SetToolTip(LoadResStr("IDS_NET_PORT_REFERENCE_DESC"));
pPortCfgDsc->SetToolTip(LoadResStr("IDS_NET_PORT_DISCOVERY_DESC"));
pSheetNetwork->AddElement(pPortCfgTCP);
pSheetNetwork->AddElement(pPortCfgUDP);
pSheetNetwork->AddElement(pPortCfgRef);
pSheetNetwork->AddElement(pPortCfgDsc);
int iNetHgt0=pPortCfgDsc->GetBounds().GetBottom();
caSheetNetwork.ExpandTop(-iNetHgt0);
pLeagueServerCfg = new NetworkServerAddressConfig(caSheetNetwork.GetFromTop(iServerCfgHgt), sServerText.getData(), &(Config.Network.UseAlternateServer), Config.Network.AlternateServerAddress, iServerCfgWdtMid);
pLeagueServerCfg->SetToolTip(LoadResStr("IDS_NET_MASTERSRV_DESC"));
pSheetNetwork->AddElement(pLeagueServerCfg);
#ifdef WITH_AUTOMATIC_UPDATE
pCheck = new BoolConfig(caSheetNetwork.GetFromTop(pUseFont->GetLineHeight()), LoadResStr("IDS_CTL_AUTOMATICUPDATES"), nullptr, &Config.Network.AutomaticUpdate, false);
pCheck->SetToolTip(LoadResStr("IDS_DESC_AUTOMATICUPDATES"));
pCheck->SetFont(pUseFont, C4StartupFontClr, C4StartupFontClrDisabled);
pSheetNetwork->AddElement(pCheck);
#endif
pCheck = new BoolConfig(caSheetNetwork.GetFromTop(pUseFont->GetLineHeight()), LoadResStr("IDS_CTL_UPNP"), nullptr, &Config.Network.EnableUPnP, false);
pCheck->SetToolTip(LoadResStr("IDS_DESC_UPNP"));
pCheck->SetFont(pUseFont, C4StartupFontClr, C4StartupFontClrDisabled);
pSheetNetwork->AddElement(pCheck);
pCheck = new BoolConfig(caSheetNetwork.GetFromTop(pUseFont->GetLineHeight()), LoadResStr("IDS_CTL_PACKETLOGGING"), nullptr, &Config.Network.PacketLogging, false);
pCheck->SetToolTip(LoadResStr("IDS_DESC_PACKETLOGGING"));
pCheck->SetFont(pUseFont, C4StartupFontClr, C4StartupFontClrDisabled);
pSheetNetwork->AddElement(pCheck);
const char *szNameCfgText = LoadResStr("IDS_NET_USERNAME");
int iNameCfgWdt=200, iNameCfgHgt=48; C4StartupOptionsDlg::EditConfig::GetControlSize(&iNameCfgWdt, &iNameCfgHgt, szNameCfgText, false);
iNameCfgWdt += 5;
pNetworkNickEdit = new EditConfig(caSheetNetwork.GetFromTop(iNameCfgHgt), szNameCfgText, &Config.Network.Nick, nullptr, false);
pNetworkNickEdit->SetToolTip(LoadResStr("IDS_NET_USERNAME_DESC"));
pSheetNetwork->AddElement(pNetworkNickEdit);
StdCopyStrBuf NickBuf(Config.Network.Nick);
if (!NickBuf.getLength()) NickBuf.Copy(Config.GetRegistrationData("Nick"));
pNetworkNickEdit->GetEdit()->SetText(NickBuf.getData(), false);
// initial focus is on tab selection
SetFocus(pOptionsTabular, false);
}
C4StartupOptionsDlg::~C4StartupOptionsDlg()
{
delete pKeyToggleMusic;
delete pKeyBack;
}
void C4StartupOptionsDlg::OnClosed(bool fOK)
{
// callback when dlg got closed - save config
SaveConfig(true, false);
C4StartupDlg::OnClosed(fOK);
}
void C4StartupOptionsDlg::OnResetConfigBtn(C4GUI::Control *btn)
{
// confirmation
StdStrBuf sWarningText; sWarningText.Copy(LoadResStr("IDS_MSG_PROMPTRESETCONFIG"));
sWarningText.AppendChar('|');
sWarningText.Append(LoadResStr("IDS_MSG_RESTARTCHANGECFG"));
if (!GetScreen()->ShowMessageModal(sWarningText.getData(), LoadResStr("IDS_BTN_RESETCONFIG"), C4GUI::MessageDialog::btnYesNo, C4GUI::Ico_Notify))
// user cancelled
return;
// reset cfg
Config.Default();
Config.fConfigLoaded = true;
// engine must be restarted now, because some crucial fields such as resolution and used gfx engine do not match their initialization
Application.Quit();
}
void C4StartupOptionsDlg::OnGfxMSComboFill(C4GUI::ComboBox_FillCB *pFiller)
{
// clear all old entries first to allow a clean refill
pFiller->ClearEntries();
pFiller->AddEntry("Off", 0);
std::vector<int> multisamples;
Application.pWindow->EnumerateMultiSamples(multisamples);
std::sort(multisamples.begin(), multisamples.end());
for(int multisample : multisamples)
{
StdStrBuf text;
text.Format("%dx", multisample);
pFiller->AddEntry(text.getData(), multisample);
}
}
bool C4StartupOptionsDlg::OnGfxMSComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection)
{
if(pTexMgr) pTexMgr->IntLock();
#ifndef USE_CONSOLE
pDraw->InvalidateDeviceObjects();
// Note: This assumes there is only one GL context (the main context). This
// is true in fullscreen mode, and since the startup dlg is only shown in
// fullscreen mode we are safe this way.
if(pGL) pGL->pMainCtx->Clear(true);
#endif
int32_t PrevMultiSampling = Config.Graphics.MultiSampling;
Config.Graphics.MultiSampling = idNewSelection;
bool success = Application.pWindow->ReInit(&Application);
#ifndef USE_CONSOLE
if(pGL) pGL->pMainCtx->Init(Application.pWindow, &Application);
pDraw->RestoreDeviceObjects();
if(pGL) pGL->InitShaders(&::GraphicsResource.Files);
#endif
if(pTexMgr) pTexMgr->IntUnlock();
if(!success) Config.Graphics.MultiSampling = PrevMultiSampling;
return !success;
}
void C4StartupOptionsDlg::OnGfxResComboFill(C4GUI::ComboBox_FillCB *pFiller)
{
// clear all old entries first to allow a clean refill
pFiller->ClearEntries();
pFiller->AddEntry(LoadResStr("IDS_MNU_DEFAULTRESOLUTION"), -1);
// fill with all possible resolutions
int32_t idx = 0, iXRes, iYRes, iBitDepth, iRefreshRate;
while (Application.GetIndexedDisplayMode(idx++, &iXRes, &iYRes, &iBitDepth, &iRefreshRate, Config.Graphics.Monitor))
#ifdef _WIN32 // why only WIN32?
if (iBitDepth == C4Draw::COLOR_DEPTH)
#endif
{
StdStrBuf sGfxString = GetGfxResString(iXRes, iYRes);
if (!pFiller->FindEntry(sGfxString.getData()))
pFiller->AddEntry(sGfxString.getData(), iXRes + (uint32_t(iYRes) << 12) + (uint32_t(iRefreshRate) << 24));
}
}
bool C4StartupOptionsDlg::OnGfxResComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection)
{
// get new resolution from string
int iResX=(idNewSelection & 0xfff), iResY=(uint32_t(idNewSelection) >> 12) & 0xfff, iRefreshRate = (uint32_t(idNewSelection) >> 24) & 0xff;
if (idNewSelection == -1)
{
iResX = iResY = -1;
iRefreshRate = 0;
}
// different than current?
if (iResX == Config.Graphics.ResX && iResY == Config.Graphics.ResY && iRefreshRate == Config.Graphics.RefreshRate) return true;
// try setting it
if (!TryNewResolution(iResX, iResY, iRefreshRate))
{
// didn't work or declined by user
return true; // do not change label, because dialog might hae been recreated!
}
// dialog has been recreated; so do not change the combo label
return true;
}
bool C4StartupOptionsDlg::TryNewResolution(int32_t iResX, int32_t iResY, int32_t iRefreshRate)
{
int32_t iOldResX = Config.Graphics.ResX, iOldResY = Config.Graphics.ResY;
int32_t iOldRefreshRate = Config.Graphics.RefreshRate;
int32_t iOldFontSize = Config.General.RXFontSize;
C4GUI::Screen *pScreen = GetScreen();
// resolution change may imply font size change
int32_t iNewFontSize = 14; // default (at 800x600)
if (iResY >= 0 && iResY < 600)
iNewFontSize = 12;
else if (iResY > 800)
iNewFontSize = 16;
// call application to set it
if (!Application.SetVideoMode(iResX, iResY, iRefreshRate, Config.Graphics.Monitor, !Config.Graphics.Windowed))
{
StdCopyStrBuf strChRes(LoadResStr("IDS_MNU_SWITCHRESOLUTION"));
pScreen->ShowMessage(FormatString(LoadResStr("IDS_ERR_SWITCHRES"), Application.GetLastError()).getData(), strChRes.getData(), C4GUI::Ico_Clonk, nullptr);
return false;
}
// implied font change
if (iNewFontSize != iOldFontSize)
if (!Application.SetGameFont(Config.General.RXFontName, iNewFontSize))
{
// not changing font size is not fatal - just keep old size
iNewFontSize = iOldFontSize;
}
// Set new resolution in config before dialog recreation so that the initial combo box value is correct (#230)
Config.Graphics.ResX = iResX;
Config.Graphics.ResY = iResY;
Config.Graphics.RefreshRate = iRefreshRate;
// since the resolution was changed, everything needs to be moved around a bit
RecreateDialog(false);
// Now set old resolution again to make sure config is restored even if the program is closed during the confirmation dialog
Config.Graphics.ResX = iOldResX;
Config.Graphics.ResY = iOldResY;
Config.Graphics.RefreshRate = iOldRefreshRate;
// Show confirmation dialog
ResChangeConfirmDlg *pConfirmDlg = new ResChangeConfirmDlg();
if (!pScreen->ShowModalDlg(pConfirmDlg, true))
{
// abort: Restore screen, if this was not some program abort
if (Application.SetVideoMode(iOldResX, iOldResY, iOldRefreshRate, Config.Graphics.Monitor, !Config.Graphics.Windowed))
{
if (iNewFontSize != iOldFontSize) Application.SetGameFont(Config.General.RXFontName, iOldFontSize);
RecreateDialog(false);
}
return false;
}
// resolution may be kept!
Config.Graphics.ResX = iResX;
Config.Graphics.ResY = iResY;
Config.Graphics.RefreshRate = iRefreshRate;
if(Config.Graphics.Windowed)
Application.SetVideoMode(Application.GetConfigWidth(), Application.GetConfigHeight(), Config.Graphics.RefreshRate, Config.Graphics.Monitor, false);
return true;
}
StdStrBuf C4StartupOptionsDlg::GetGfxResString(int32_t iResX, int32_t iResY)
{
// Display in format like "640 x 480"
if (iResX == -1)
return StdStrBuf(LoadResStr("IDS_MNU_DEFAULTRESOLUTION"));
return FormatString("%d x %d", (int)iResX, (int)iResY);
}
const char * C4StartupOptionsDlg::GetWindowedName(int32_t mode /* = -1*/)
{
if(mode == -1)
mode = Config.Graphics.Windowed;
if(mode == 0) return LoadResStr("IDS_MSG_FULLSCREEN");
else if(mode == 1) return LoadResStr("IDS_MSG_WINDOWED");
else if(mode == 2) return LoadResStr("IDS_MSG_AUTOWINDOWED");
assert(!"Requested name for config value which does not exist");
return "ERR: Unknown";
}
void C4StartupOptionsDlg::OnWindowedModeComboFill(C4GUI::ComboBox_FillCB *pFiller)
{
pFiller->ClearEntries();
for(int32_t i = 0; i < 3; ++i)
pFiller->AddEntry(GetWindowedName(i), i);
}
bool C4StartupOptionsDlg::OnWindowedModeComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection)
{
Config.Graphics.Windowed = idNewSelection;
Application.SetVideoMode(Config.Graphics.ResX, Config.Graphics.ResY, Config.Graphics.RefreshRate, Config.Graphics.Monitor, !Config.Graphics.Windowed);
pForCombo->SetText(GetWindowedName(idNewSelection));
return true;
}
bool C4StartupOptionsDlg::SaveConfig(bool fForce, bool fKeepOpen)
{
// prevent double save
if (fConfigSaved) return true;
// save any config fields that are not stored directly; return whether all values are OK
// check port validity
if (!fForce)
{
StdCopyStrBuf strError(LoadResStr("IDS_ERR_CONFIG"));
if (pPortCfgTCP->GetPort()>0 && pPortCfgTCP->GetPort() == pPortCfgRef->GetPort())
{
GetScreen()->ShowMessage(LoadResStr("IDS_NET_ERR_PORT_TCPREF"), strError.getData(), C4GUI::Ico_Error);
return false;
}
if (pPortCfgUDP->GetPort()>0 && pPortCfgUDP->GetPort() == pPortCfgDsc->GetPort())
{
GetScreen()->ShowMessage(LoadResStr("IDS_NET_ERR_PORT_UDPDISC"), strError.getData(), C4GUI::Ico_Error);
return false;
}
}
// store some config values
pPortCfgTCP->SavePort();
pPortCfgUDP->SavePort();
pPortCfgRef->SavePort();
pPortCfgDsc->SavePort();
pLeagueServerCfg->Save2Config();
pNetworkNickEdit->Save2Config();
// if nick is same as default by registry, don't save in config
// so regkey updates will change the nick as well
const char *szRegNick = Config.GetRegistrationData("Nick");
if (SEqual(Config.Network.Nick.getData(), szRegNick)) Config.Network.Nick.Clear();
// make sure config is saved, in case the game crashes later on or another instance is started
Config.Save();
if (!fKeepOpen) fConfigSaved = true;
// done; config OK
return true;
}
void C4StartupOptionsDlg::DoBack()
{
if (!SaveConfig(false, false)) return;
// back 2 main
C4Startup::Get()->SwitchDialog(fCanGoBack ? (C4Startup::SDID_Back) : (C4Startup::SDID_Main));
}
bool C4StartupOptionsDlg::SetSubscreen(const char *szToScreen)
{
// go to specified property sheet
// option sheets do not have a good identifier associated to them - just lookup from a static array
const char *page_names[] = { "general", "graphics", "sound", "controls", "network", nullptr };
int32_t i = 0;
while (page_names[i])
{
if (SEqualNoCase(szToScreen, page_names[i])) break;
++i;
}
// page name does not match?
if (!page_names[i]) return false;
// page name OK; switch it
pOptionsTabular->SelectSheet(i, false);
return true;
}
void C4StartupOptionsDlg::UpdateLanguage()
{
// find currently specified language in language list and display its info
C4LanguageInfo *pNfo = Languages.FindInfo(Config.General.Language);
if (pNfo)
{
pLangCombo->SetText(FormatString("%s - %s", pNfo->Code, pNfo->Name).getData());
pLangInfoLabel->SetText(pNfo->Info);
}
else
{
pLangCombo->SetText(FormatString("unknown (%s)", Config.General.Language).getData());
pLangInfoLabel->SetText(LoadResStr("IDS_CTL_NOLANGINFO"));
return; // no need to mess with fallbacks
}
// update language fallbacks
char *szLang = Config.General.LanguageEx;
SCopy(pNfo->Code, szLang);
if (*(pNfo->Fallback))
{
SAppend(",", szLang);
Config.General.GetLanguageSequence(pNfo->Fallback, szLang + SLen(szLang));
}
// internal fallbacks
if (!SSearch(Config.General.LanguageEx, "US"))
{
if (*szLang) SAppendChar(',', szLang);
SAppend("US", szLang);
}
if (!SSearch(Config.General.LanguageEx, "DE"))
{
if (*szLang) SAppendChar(',', szLang);
SAppend("DE", szLang);
}
}
void C4StartupOptionsDlg::OnLangComboFill(C4GUI::ComboBox_FillCB *pFiller)
{
// fill with all possible languages
C4LanguageInfo *pNfo;
for (int i=0; i<Languages.GetInfoCount(); ++i)
if ((pNfo = Languages.GetInfo(i)))
pFiller->AddEntry(FormatString("%s - %s", pNfo->Code, pNfo->Name).getData(), (unsigned char)(pNfo->Code[0]) + ((unsigned char)(pNfo->Code[1])<<8));
}
bool C4StartupOptionsDlg::OnLangComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection)
{
// set new language by two-character-code
Config.General.Language[0] = idNewSelection & 0xff;
Config.General.Language[1] = (idNewSelection & 0xff00) >> 8;
Config.General.Language[2] = '\0';
UpdateLanguage();
Languages.LoadLanguage(Config.General.LanguageEx);
Game.UpdateLanguage();
// recreate everything to reflect language changes
RecreateDialog(true);
return true;
}
void C4StartupOptionsDlg::UpdateFontControls()
{
// display current language and size in comboboxes
pFontFaceCombo->SetText(Config.General.RXFontName);
StdStrBuf sSize; sSize.Format("%d", (int) Config.General.RXFontSize);
pFontSizeCombo->SetText(sSize.getData());
}
#ifdef _WIN32
const char *DefaultFonts[] = { "Arial Unicode MS", "Comic Sans MS", C4DEFAULT_FONT_NAME, "Verdana", nullptr };
#else
const char *DefaultFonts[] = { C4DEFAULT_FONT_NAME, nullptr };
#endif
void C4StartupOptionsDlg::OnFontFaceComboFill(C4GUI::ComboBox_FillCB *pFiller)
{
// 2do: enumerate Fonts.txt fonts; then enumerate system fonts
for (int32_t i=0; DefaultFonts[i]; ++i) pFiller->AddEntry(DefaultFonts[i], i);
}
void C4StartupOptionsDlg::OnFontSizeComboFill(C4GUI::ComboBox_FillCB *pFiller)
{
// 2do: enumerate possible font sizes by the font here
// 2do: Hide font sizes that would be too large for the selected resolution
pFiller->AddEntry("10", 10);
pFiller->AddEntry("12", 12);
pFiller->AddEntry("14", 14);
pFiller->AddEntry("16", 16);
pFiller->AddEntry("18", 18);
pFiller->AddEntry("20", 20);
pFiller->AddEntry("22", 22);
pFiller->AddEntry("24", 24);
pFiller->AddEntry("26", 26);
pFiller->AddEntry("28", 28);
}
bool C4StartupOptionsDlg::OnFontComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection)
{
// set new value
const char *szNewFontFace = Config.General.RXFontName;
int32_t iNewFontSize = Config.General.RXFontSize;
if (pForCombo == pFontFaceCombo)
szNewFontFace = DefaultFonts[idNewSelection];
else if (pForCombo == pFontSizeCombo)
iNewFontSize = idNewSelection;
else
// can't happen
return true;
// set new fonts
if (!Application.SetGameFont(szNewFontFace, iNewFontSize))
{
GetScreen()->ShowErrorMessage(LoadResStr("IDS_ERR_INITFONTS"));
return true;
}
// recreate everything to reflect font changes
RecreateDialog(true);
return true;
}
void C4StartupOptionsDlg::RecreateDialog(bool fFade)
{
// MUST fade for now, or calling function will fail because dialog is deleted immediately
fFade = true;
// this actually breaks the possibility to go back :(
int32_t iPage = pOptionsTabular->GetActiveSheetIndex();
C4StartupOptionsDlg *pNewDlg = static_cast<C4StartupOptionsDlg *>(C4Startup::Get()->SwitchDialog(C4Startup::SDID_Options, fFade));
pNewDlg->pOptionsTabular->SelectSheet(iPage, false);
pNewDlg->fCanGoBack = false;
}
void C4StartupOptionsDlg::OnFEMusicCheck(C4GUI::Element *pCheckBox)
{
// option change is reflected immediately
bool fIsOn = static_cast<C4GUI::CheckBox *>(pCheckBox)->GetChecked();
if ((Config.Sound.FEMusic = fIsOn))
Application.MusicSystem.Play();
else
Application.MusicSystem.Stop();
}
void C4StartupOptionsDlg::OnMusicVolumeSliderChange(int32_t iNewVal)
{
// option change is reflected immediately;
Config.Sound.MusicVolume = iNewVal;
Application.MusicSystem.UpdateVolume();
}
void C4StartupOptionsDlg::OnSoundVolumeSliderChange(int32_t iNewVal)
{
// sound system reads this value directly
Config.Sound.SoundVolume = iNewVal;
// test sound
StartSoundEffect("Objects::Arrow::HitGround", false, 100, nullptr);
}
void C4StartupOptionsDlg::OnRXSoundCheck(C4GUI::Element *pCheckBox)
{
// toggling sounds on off must init/deinit sound system
bool fIsOn = static_cast<C4GUI::CheckBox *>(pCheckBox)->GetChecked();
if (fIsOn == !!Config.Sound.RXSound) return;
if (fIsOn)
{
Config.Sound.RXSound = true;
if (!Application.SoundSystem.Init())
{
GetScreen()->ShowMessage(StdCopyStrBuf(LoadResStr("IDS_PRC_NOSND")).getData(), StdCopyStrBuf(LoadResStr("IDS_DLG_LOG")).getData(), C4GUI::Ico_Error);
Application.SoundSystem.Clear();
Config.Sound.RXSound = false;
static_cast<C4GUI::CheckBox *>(pCheckBox)->SetChecked(false);
fIsOn = false;
}
}
else
{
Application.SoundSystem.Clear();
Config.Sound.RXSound = false;
}
// FE sound only enabled if global sound is enabled
pFESoundCheck->SetEnabled(fIsOn);
pFESoundCheck->SetChecked(fIsOn ? !!Config.Sound.FESamples : false);
}
bool C4StartupOptionsDlg::KeyMusicToggle()
{
// do toggle
Application.MusicSystem.ToggleOnOff();
// reflect in checkbox
pFEMusicCheck->SetChecked(!!Config.Sound.FEMusic);
// key processed
return true;
}
void C4StartupOptionsDlg::OnKeyboardLayoutChanged()
{
// keyboard layout changed and thus some keys might have been updated from scan codes
// update display in control set
pControlConfigArea->UpdateCtrlSet();
}