1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2005, Sven2
6 * Copyright (c) 2017-2022, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18// Startup screen for non-parameterized engine start: Options dialog
19
20#include "C4GuiComboBox.h"
21#include "C4GuiEdit.h"
22#include "C4GuiResource.h"
23#include "C4GuiTabular.h"
24
25#include <C4Include.h>
26#include <C4StartupOptionsDlg.h>
27#include <C4StartupOptionsAdvancedConfigDialog.h>
28
29#include <C4StartupMainDlg.h>
30#include <C4Language.h>
31#include <C4GamePadCon.h>
32#include <C4Game.h>
33#include <C4Log.h>
34#include <C4Wrappers.h>
35
36#include <StdGL.h>
37
38class C4StartupOptionsDlg::ScaleEdit : public C4GUI::SpinBox<int32_t>
39{
40 using Base = C4GUI::SpinBox<int32_t>;
41 C4StartupOptionsDlg *pDlg;
42
43public:
44 ScaleEdit(C4StartupOptionsDlg *pDlg, const C4Rect &rtBounds, bool fFocusEdit = false);
45
46protected:
47 virtual void OnTextChange() override;
48 virtual C4GUI::InputResult OnFinishInput(bool fPasting, bool fPastingMore) override;
49};
50
51class C4StartupOptionsDlg::EditConfig : public C4GUI::LabeledEdit
52{
53public:
54 EditConfig(const C4Rect &rcBounds, const char *szName, ValidatedStdStrBufBase *psConfigVal, int32_t *piConfigVal, bool fMultiline);
55
56private:
57 ValidatedStdStrBufBase *psConfigVal;
58 int32_t *piConfigVal;
59
60public:
61 void Save2Config(); // control to config
62 static bool GetControlSize(int *piWdt, int *piHgt, const char *szForText, bool fMultiline);
63 int32_t GetIntVal();
64 void SetIntVal(int32_t iToVal);
65};
66
67// C4StartupOptionsDlg::SmallButton
68
69void C4StartupOptionsDlg::SmallButton::DrawElement(C4FacetEx &cgo)
70{
71 // draw the button
72 CStdFont &rUseFont = C4Startup::Get()->Graphics.BookFont;
73 // get text pos
74 int32_t x0 = cgo.TargetX + rcBounds.x, y0 = cgo.TargetY + rcBounds.y, x1 = cgo.TargetX + rcBounds.x + rcBounds.Wdt, y1 = cgo.TargetY + rcBounds.y + rcBounds.Hgt;
75 int32_t iTextHgt = rUseFont.GetLineHeight();
76 // draw frame
77 uint32_t dwClrHigh = C4StartupBtnBorderColor1, dwClrLow = C4StartupBtnBorderColor2;
78 if (fDown) std::swap(a&: dwClrHigh, b&: dwClrLow);
79 int32_t iIndent = BoundBy<int32_t>(bval: (rcBounds.Hgt - iTextHgt) / 3, lbound: 2, rbound: 5);
80 int iDrawQuadTop[8] = { x0, y0, x1, y0, x1 - iIndent, y0 + iIndent, x0, y0 + iIndent };
81 int iDrawQuadLeft[8] = { x0, y0, x0 + iIndent, y0, x0 + iIndent, y1 - iIndent, x0, y1 };
82 int iDrawQuadRight[8] = { x1, y0, x1, y1, x1 - iIndent, y1, x1 - iIndent, y0 + iIndent };
83 int iDrawQuadBottom[8] = { x1, y1, x0, y1, x0 + iIndent, y1 - iIndent, x1, y1 - iIndent };
84 lpDDraw->DrawQuadDw(sfcTarget: cgo.Surface, ipVtx: iDrawQuadTop, dwClr1: dwClrHigh, dwClr2: dwClrHigh, dwClr3: dwClrHigh, dwClr4: dwClrHigh);
85 lpDDraw->DrawQuadDw(sfcTarget: cgo.Surface, ipVtx: iDrawQuadLeft, dwClr1: dwClrHigh, dwClr2: dwClrHigh, dwClr3: dwClrHigh, dwClr4: dwClrHigh);
86 lpDDraw->DrawQuadDw(sfcTarget: cgo.Surface, ipVtx: iDrawQuadRight, dwClr1: dwClrLow, dwClr2: dwClrLow, dwClr3: dwClrLow, dwClr4: dwClrLow);
87 lpDDraw->DrawQuadDw(sfcTarget: cgo.Surface, ipVtx: iDrawQuadBottom, dwClr1: dwClrLow, dwClr2: dwClrLow, dwClr3: dwClrLow, dwClr4: dwClrLow);
88 // draw selection highlight
89 int32_t iTxtOff = fDown ? iIndent : 0;
90 if (fEnabled) if (HasDrawFocus() || (fMouseOver && IsInActiveDlg(fForKeyboard: false)))
91 {
92 lpDDraw->SetBlitMode(C4GFXBLIT_ADDITIVE);
93 C4GUI::GetRes()->fctButtonHighlight.DrawX(sfcTarget: cgo.Surface, iX: x0 + 5 + iTxtOff, iY: y0 + 3 + iTxtOff, iWdt: rcBounds.Wdt - 10, iHgt: rcBounds.Hgt - 6);
94 lpDDraw->ResetBlitMode();
95 }
96 // draw button text
97 lpDDraw->TextOut(szText: sText.getData(), rFont&: rUseFont, fZoom: 1.0f, sfcDest: cgo.Surface, iTx: (x0 + x1) / 2 + iTxtOff, iTy: (y0 + y1 - iTextHgt) / 2 + iTxtOff, dwFCol: C4StartupBtnFontClr, byForm: ACenter, fDoMarkup: true);
98}
99
100int32_t C4StartupOptionsDlg::SmallButton::GetDefaultButtonHeight()
101{
102 // button height is used font height plus a small indent
103 CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
104 return pUseFont->GetLineHeight() * 6 / 5 + 6;
105}
106
107// C4StartupOptionsDlg::ResChangeConfirmDlg
108
109C4StartupOptionsDlg::ResChangeConfirmDlg::ResChangeConfirmDlg()
110 : C4GUI::TimedDialog{12, "", LoadResStr(id: C4ResStrTableKey::IDS_MNU_SWITCHRESOLUTION), C4GUI::MessageDialog::btnYesNo, C4GUI::Ico_None}
111{
112 UpdateText();
113}
114
115void C4StartupOptionsDlg::ResChangeConfirmDlg::UpdateText()
116{
117 StdStrBuf text;
118 C4GUI::GetRes()->TextFont.BreakMessage(
119 szMsg: LoadResStr(id: C4ResStrTableKey::IDS_MNU_SWITCHRESOLUTION_TEXT, args: GetRemainingTime()).c_str(),
120 iWdt: GetClientRect().Wdt,
121 pOut: &text,
122 fCheckMarkup: false
123 );
124 SetText(text.getData());
125}
126
127// C4StartupOptionsDlg::ScaleEdit
128
129namespace
130{
131 constexpr int minScale = 100;
132 constexpr int maxScale = 300;
133}
134
135C4StartupOptionsDlg::ScaleEdit::ScaleEdit(C4StartupOptionsDlg *pDlg, const C4Rect &rtBounds, bool fFocusEdit) : Base{rtBounds, fFocusEdit, minScale, maxScale}, pDlg{pDlg}
136{
137 SetColors(dwNewBGClr: C4StartupEditBGColor, dwNewFontClr: C4StartupFontClr, dwNewBorderColor: C4StartupEditBorderColor);
138}
139
140void C4StartupOptionsDlg::ScaleEdit::OnTextChange()
141{
142 Base::OnTextChange();
143 const auto val = GetValue();
144 const auto sliderVal = val - minScale;
145 pDlg->pScaleSlider->SetScrollPos(sliderVal);
146 pDlg->iNewScale = val;
147}
148
149C4GUI::InputResult C4StartupOptionsDlg::ScaleEdit::OnFinishInput(bool fPasting, bool fPastingMore)
150{
151 Base::OnFinishInput(pasting: fPasting, pastingMore: fPastingMore);
152 const auto val = GetValue();
153 const auto sliderVal = val - minScale;
154 pDlg->pScaleSlider->SetScrollPos(sliderVal);
155 pDlg->OnScaleSliderChanged(val: sliderVal);
156 pDlg->OnTestScaleBtn(nullptr);
157 return C4GUI::IR_Abort;
158}
159
160// C4StartupOptionsDlg::KeySelDialog
161
162const char *LoadKeyDescResStr(const int32_t iKeyID)
163{
164 static constexpr C4ResStrTableKeyFormat<> KeyIDStringIDs[C4MaxKey]
165 {
166 C4ResStrTableKey::IDS_CTL_SELECTLEFT, C4ResStrTableKey::IDS_CTL_SELECTTOGGLE, C4ResStrTableKey::IDS_CTL_SELECTRIGHT,
167 C4ResStrTableKey::IDS_CTL_THROW, C4ResStrTableKey::IDS_CTL_UPJUMP, C4ResStrTableKey::IDS_CTL_DIG,
168 C4ResStrTableKey::IDS_CTL_LEFT, C4ResStrTableKey::IDS_CTL_DOWNSTOP, C4ResStrTableKey::IDS_CTL_RIGHT,
169 C4ResStrTableKey::IDS_CTL_PLAYERMENU, C4ResStrTableKey::IDS_CTL_SPECIAL1, C4ResStrTableKey::IDS_CTL_SPECIAL2
170 };
171 if (!Inside<int32_t>(ival: iKeyID, lbound: 0, rbound: C4MaxKey)) return nullptr;
172 return LoadResStr(id: KeyIDStringIDs[iKeyID]);
173}
174
175C4StartupOptionsDlg::KeySelDialog::KeySelDialog(int32_t iKeyID, int32_t iCtrlSet, bool fGamepad)
176 : C4GUI::MessageDialog(LoadResStrChoice(condition: !fGamepad, ifTrue: C4ResStrTableKey::IDS_MSG_PRESSKEY, ifFalse: C4ResStrTableKey::IDS_MSG_PRESSBTN,
177 args: LoadKeyDescResStr(iKeyID), args: iCtrlSet + 1).c_str(), LoadResStr(id: C4ResStrTableKey::IDS_MSG_DEFINEKEY),
178 C4GUI::MessageDialog::btnAbort, fGamepad ? C4GUI::Ico_Gamepad : C4GUI::Ico_Keyboard, C4GUI::MessageDialog::dsRegular),
179 key(KEY_Undefined), fGamepad(fGamepad), iCtrlSet(iCtrlSet)
180{
181 pKeyListener = new C4KeyBinding(C4KeyCodeEx(KEY_Any, KEYS_None), "DefineKey", KEYSCOPE_Gui, new C4GUI::DlgKeyCBPassKey<C4StartupOptionsDlg::KeySelDialog>(*this, &C4StartupOptionsDlg::KeySelDialog::KeyDown), C4CustomKey::PRIO_PlrControl);
182}
183
184C4StartupOptionsDlg::KeySelDialog::~KeySelDialog()
185{
186 delete pKeyListener;
187}
188
189bool C4StartupOptionsDlg::KeySelDialog::KeyDown(C4KeyCodeEx key)
190{
191 // check if key is valid for this set
192 // do not mix gamepad and keyboard keys
193 if (Key_IsGamepad(key: key.Key) != fGamepad) return false;
194 // allow selected gamepad only
195 if (fGamepad && Key_GetGamepad(key: key.Key) != iCtrlSet) return false;
196 // okay, use it
197 this->key = key.Key;
198 Close(fOK: true);
199 return true;
200}
201
202// C4StartupOptionsDlg::KeySelButton
203
204// key display arrangement - -1 denotes no key
205// every key from 0 to C4MaxKey-1 MUST BE present here, or the engine will crash
206const int32_t iKeyPosMaxX = 3, iKeyPosMaxY = 4; // arrange keys in a 4-by-5-array
207const int32_t iKeyPosis[iKeyPosMaxY][iKeyPosMaxX] =
208{
209 { 0, 1, 2 },
210 { 3, 4, 5 },
211 { 6, 7, 8 },
212 { 9, 10, 11 }
213};
214
215void C4StartupOptionsDlg::KeySelButton::DrawElement(C4FacetEx &cgo)
216{
217 // draw key
218 C4Facet cgoDraw(cgo.Surface, rcBounds.x + cgo.TargetX, rcBounds.y + cgo.TargetY, rcBounds.Wdt, rcBounds.Hgt);
219 Game.GraphicsResource.fctKey.Draw(cgo&: cgoDraw, fAspect: true, iPhaseX: fDown);
220 int32_t iKeyIndent = cgoDraw.Wdt / 5;
221 cgoDraw.X += iKeyIndent; cgoDraw.Wdt -= 2 * iKeyIndent;
222 cgoDraw.Y += iKeyIndent * 3 / 4; cgoDraw.Hgt -= 2 * iKeyIndent;
223 if (fDown) cgoDraw.Y += iKeyIndent / 2;
224 bool fDoHightlight = fHighlight || HasDrawFocus() || (fMouseOver && IsInActiveDlg(fForKeyboard: false));
225 bool fHadBlitMod = false; uint32_t dwOldBlitModClr = 0xffffff;
226 if (!fDoHightlight)
227 {
228 uint32_t dwModClr = 0x7f7f7f;
229 if (fHadBlitMod = lpDDraw->GetBlitModulation(rdwColor&: dwOldBlitModClr))
230 ModulateClr(dst&: dwModClr, src: dwOldBlitModClr);
231 lpDDraw->ActivateBlitModulation(dwWithClr: dwModClr);
232 }
233 Game.GraphicsResource.fctCommand.Draw(cgo&: cgoDraw, fAspect: true, iPhaseX: iKeyID, iPhaseY: 0);
234 if (!fDoHightlight)
235 if (fHadBlitMod)
236 lpDDraw->ActivateBlitModulation(dwWithClr: dwOldBlitModClr);
237 else
238 lpDDraw->DeactivateBlitModulation();
239 // draw the labels - beside the key
240 float fZoom;
241 CStdFont &rUseFont = C4Startup::Get()->Graphics.GetBlackFontByHeight(iHgt: cgoDraw.Hgt / 2 + 5, pfZoom: &fZoom);
242 lpDDraw->TextOut(szText: LoadKeyDescResStr(iKeyID), rFont&: rUseFont, fZoom, sfcDest: cgo.Surface, iTx: cgo.TargetX + rcBounds.x + rcBounds.Wdt + 5, iTy: cgoDraw.Y - 3, dwFCol: fDoHightlight ? 0xffff0000 : C4StartupFontClr, byForm: ALeft, fDoMarkup: false);
243 lpDDraw->TextOut(szText: C4KeyCodeEx::KeyCode2String(wCode: key, fHumanReadable: true, fShort: false).c_str(), rFont&: rUseFont, fZoom, sfcDest: cgo.Surface, iTx: cgo.TargetX + rcBounds.x + rcBounds.Wdt + 5, iTy: cgoDraw.Y + cgoDraw.Hgt / 2, dwFCol: fDoHightlight ? 0xffff0000 : C4StartupFontClr, byForm: ALeft, fDoMarkup: false);
244}
245
246C4StartupOptionsDlg::KeySelButton::KeySelButton(int32_t iKeyID, const C4Rect &rcBounds, char cHotkey)
247 : C4GUI::IconButton(C4GUI::Ico_None, rcBounds, cHotkey), iKeyID(iKeyID), key(KEY_Undefined) {}
248
249// C4StartupOptionsDlg::ControlConfigArea
250
251C4StartupOptionsDlg::ControlConfigArea::ControlConfigArea(const C4Rect &rcArea, int32_t iHMargin, int32_t iVMargin, bool fGamepad, C4StartupOptionsDlg *pOptionsDlg)
252 : C4GUI::Window(), fGamepad(fGamepad), pGamepadOpener(nullptr), pOptionsDlg(pOptionsDlg), pGUICtrl(nullptr)
253{
254 CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
255 SetBounds(rcArea);
256 C4GUI::ComponentAligner caArea(rcArea, iHMargin, iVMargin, true);
257 // get number of control sets to be configured
258 iMaxControlSets = 1; // do not devide by zero
259 if (fGamepad && Application.pGamePadControl)
260 iMaxControlSets = (std::max)(a: 1, b: Application.pGamePadControl->GetGamePadCount());
261 if (!fGamepad)
262 iMaxControlSets = C4MaxKeyboardSet;
263 ppKeyControlSetBtns = new C4GUI::IconButton *[iMaxControlSets];
264 // top line buttons to select keyboard set or gamepad
265 C4Facet fctCtrlPic = fGamepad ? Game.GraphicsResource.fctGamepad : Game.GraphicsResource.fctKeyboard;
266 int32_t iCtrlSetWdt = caArea.GetWidth() - caArea.GetHMargin() * 2;
267 int32_t iCtrlSetHMargin = 5, iCtrlSetVMargin = 5;
268 int32_t iCtrlSetBtnWdt = BoundBy<int32_t>(bval: (iCtrlSetWdt - iMaxControlSets * iCtrlSetHMargin * 2) / iMaxControlSets, lbound: 5, rbound: fctCtrlPic.Wdt);
269 int32_t iCtrlSetBtnHgt = fctCtrlPic.GetHeightByWidth(iWidth: iCtrlSetBtnWdt);
270 iCtrlSetHMargin = (iCtrlSetWdt - iCtrlSetBtnWdt * iMaxControlSets) / (iMaxControlSets * 2);
271 C4GUI::ComponentAligner caKeyboardSetSel(caArea.GetFromTop(iHgt: 2 * iCtrlSetVMargin + iCtrlSetBtnHgt), iCtrlSetHMargin, iCtrlSetVMargin);
272 const char *szCtrlSetHotkeys = "1234567890"; /* 2do */
273 uint32_t i;
274 for (i = 0; i < static_cast<uint32_t>(iMaxControlSets); ++i)
275 {
276 char cCtrlSetHotkey = 0;
277 if (i <= strlen(s: szCtrlSetHotkeys)) cCtrlSetHotkey = szCtrlSetHotkeys[i];
278 C4GUI::CallbackButton<C4StartupOptionsDlg::ControlConfigArea, C4GUI::IconButton> *pBtn = new C4GUI::CallbackButton<C4StartupOptionsDlg::ControlConfigArea, C4GUI::IconButton>(C4GUI::Ico_None, caKeyboardSetSel.GetFromLeft(iWdt: iCtrlSetBtnWdt), cCtrlSetHotkey, &C4StartupOptionsDlg::ControlConfigArea::OnCtrlSetBtn, this);
279 pBtn->SetFacet(rCpy: fctCtrlPic);
280 fctCtrlPic.X += fctCtrlPic.Wdt;
281 AddElement(pChild: ppKeyControlSetBtns[i] = pBtn);
282 pBtn->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_MSG_SELECTKEYSET));
283 }
284 iSelectedCtrlSet = fGamepad ? 0 : C4P_Control_Keyboard1;
285 caArea.ExpandTop(iByHgt: caArea.GetVMargin());
286 AddElement(pChild: new C4GUI::HorizontalLine(caArea.GetFromTop(iHgt: 2)));
287 caArea.ExpandTop(iByHgt: caArea.GetVMargin());
288 C4Facet &rfctKey = Game.GraphicsResource.fctKey;
289 int32_t iKeyAreaMaxWdt = caArea.GetWidth() - 2 * caArea.GetHMargin(), iKeyAreaMaxHgt = caArea.GetHeight() - 2 * caArea.GetVMargin();
290 int32_t iKeyWdt = rfctKey.Wdt * 3 / 2, iKeyHgt = rfctKey.Hgt * 3 / 2;
291 int32_t iKeyUseWdt = iKeyWdt + iKeyHgt * 3; // add space for label
292 int32_t iKeyMargin = 20;
293 int32_t iKeyAreaWdt = (iKeyUseWdt + 2 * iKeyMargin) * iKeyPosMaxX, iKeyAreaHgt = (iKeyHgt + 2 * iKeyMargin) * iKeyPosMaxY;
294 if (iKeyAreaWdt > iKeyAreaMaxWdt || iKeyAreaHgt > iKeyAreaMaxHgt)
295 {
296 // scale down
297 float fScaleX = float(iKeyAreaMaxWdt) / float(std::max<int32_t>(a: iKeyAreaWdt, b: 1)),
298 fScaleY = float(iKeyAreaMaxHgt) / float(std::max<int32_t>(a: iKeyAreaHgt, b: 1)), fScale;
299 if (fScaleX > fScaleY) fScale = fScaleY; else fScale = fScaleX;
300 iKeyMargin = int32_t(fScale * iKeyMargin);
301 iKeyWdt = int32_t(fScale * iKeyWdt);
302 iKeyUseWdt = int32_t(fScale * iKeyUseWdt);
303 iKeyHgt = int32_t(fScale * iKeyHgt);
304 iKeyAreaWdt = int32_t(fScale * iKeyAreaWdt);
305 iKeyAreaHgt = int32_t(fScale * iKeyAreaHgt);
306 }
307 C4GUI::ComponentAligner caCtrlKeys(caArea.GetFromTop(iHgt: iKeyAreaHgt, iWdt: iKeyAreaWdt), 0, iKeyMargin);
308 int32_t iKeyNum;
309 for (int iY = 0; iY < iKeyPosMaxY; ++iY)
310 {
311 C4GUI::ComponentAligner caCtrlKeysLine(caCtrlKeys.GetFromTop(iHgt: iKeyHgt), iKeyMargin, 0);
312 for (int iX = 0; iX < iKeyPosMaxX; ++iX)
313 {
314 C4Rect rcKey = caCtrlKeysLine.GetFromLeft(iWdt: iKeyWdt);
315 caCtrlKeysLine.ExpandLeft(iByWdt: iKeyWdt - iKeyUseWdt);
316 if ((iKeyNum = iKeyPosis[iY][iX]) < 0) continue;
317 KeySelButton *pKeyBtn = new C4GUI::CallbackButton<C4StartupOptionsDlg::ControlConfigArea, KeySelButton>(iKeyNum, rcKey, 0 /* no hotkey :( */, &C4StartupOptionsDlg::ControlConfigArea::OnCtrlKeyBtn, this);
318 AddElement(pChild: KeyControlBtns[iKeyNum] = pKeyBtn);
319 pKeyBtn->SetToolTip(LoadKeyDescResStr(iKeyID: iKeyNum));
320 }
321 }
322 // bottom area controls
323 caArea.ExpandBottom(iByHgt: -iKeyHgt / 2);
324 C4GUI::ComponentAligner caKeyBottomBtns(caArea.GetFromBottom(C4GUI_ButtonHgt), 2, 0);
325 // gamepad: Use for GUI
326 if (fGamepad)
327 {
328 int iWdt = 100, iHgt = 20;
329 const char *szResetText = LoadResStr(id: C4ResStrTableKey::IDS_CTL_GAMEPADFORMENU);
330 C4GUI::CheckBox::GetStandardCheckBoxSize(piWdt: &iWdt, piHgt: &iHgt, szForCaptionText: szResetText, pUseFont);
331 pGUICtrl = new C4GUI::CheckBox(caKeyBottomBtns.GetFromLeft(iWdt, iHgt), szResetText, !!Config.Controls.GamepadGuiControl);
332 pGUICtrl->SetOnChecked(new C4GUI::CallbackHandler<C4StartupOptionsDlg::ControlConfigArea>(this, &C4StartupOptionsDlg::ControlConfigArea::OnGUIGamepadCheckChange));
333 pGUICtrl->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_GAMEPADFORMENU));
334 pGUICtrl->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
335 AddElement(pChild: pGUICtrl);
336 }
337 // reset button
338 const char *szBtnText = LoadResStr(id: C4ResStrTableKey::IDS_BTN_RESETKEYBOARD);
339 int32_t iButtonWidth = 100, iButtonHeight = 20; C4GUI::Button *btn;
340 C4GUI::GetRes()->CaptionFont.GetTextExtent(szText: szBtnText, rsx&: iButtonWidth, rsy&: iButtonHeight, fCheckMarkup: true);
341 C4Rect rcResetBtn = caKeyBottomBtns.GetFromRight(iWdt: std::min<int32_t>(a: iButtonWidth + iButtonHeight * 4, b: caKeyBottomBtns.GetInnerWidth()));
342 AddElement(pChild: btn = new C4GUI::CallbackButton<C4StartupOptionsDlg::ControlConfigArea, SmallButton>(szBtnText, rcResetBtn, &C4StartupOptionsDlg::ControlConfigArea::OnResetKeysBtn, this));
343 btn->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_MSG_RESETKEYSETS));
344
345 UpdateCtrlSet();
346}
347
348C4StartupOptionsDlg::ControlConfigArea::~ControlConfigArea()
349{
350 delete[] ppKeyControlSetBtns;
351 delete pGamepadOpener;
352}
353
354void C4StartupOptionsDlg::ControlConfigArea::OnCtrlSetBtn(C4GUI::Control *btn)
355{
356 // select Ctrl set of pressed btn
357 int i;
358 for (i = 0; i < iMaxControlSets; ++i)
359 if (btn == ppKeyControlSetBtns[i])
360 {
361 iSelectedCtrlSet = i;
362 break;
363 }
364 // update shown keys
365 UpdateCtrlSet();
366}
367
368void C4StartupOptionsDlg::ControlConfigArea::UpdateCtrlSet()
369{
370 // selected keyboard set btn gets a highlight
371 int i;
372 for (i = 0; i < iMaxControlSets; ++i)
373 ppKeyControlSetBtns[i]->SetHighlight(i == iSelectedCtrlSet);
374 // update keys by config
375 if (fGamepad)
376 {
377 for (i = 0; i < C4MaxKey; ++i)
378 KeyControlBtns[i]->SetKey(Config.Gamepads[iSelectedCtrlSet].Button[i]);
379 }
380 else
381 for (i = 0; i < C4MaxKey; ++i)
382 KeyControlBtns[i]->SetKey(Config.Controls.Keyboard[iSelectedCtrlSet][i]);
383 // open gamepad
384 if (fGamepad && Config.General.GamepadEnabled)
385 {
386 if (!pGamepadOpener) pGamepadOpener = new C4GamePadOpener(iSelectedCtrlSet);
387 else pGamepadOpener->SetGamePad(iSelectedCtrlSet);
388 }
389 // show/hide gamepad-gui-control checkbox
390 if (fGamepad && pGUICtrl)
391 pGUICtrl->SetVisibility(iSelectedCtrlSet == 0);
392}
393
394void C4StartupOptionsDlg::ControlConfigArea::OnCtrlKeyBtn(C4GUI::Control *btn)
395{
396 // determine which key has been pressed
397 int32_t idKey;
398 for (idKey = 0; idKey < C4MaxKey; ++idKey) if (KeyControlBtns[idKey] == btn) break;
399 if (idKey == C4MaxKey) return; // can't happen
400 // show key selection dialog for it
401 KeySelDialog *pDlg = new KeySelDialog(idKey, iSelectedCtrlSet, fGamepad);
402 pDlg->SetDelOnClose(false);
403 bool fSuccess = GetScreen()->ShowModalDlg(pDlg, fDestruct: false);
404 C4KeyCode key = pDlg->GetKeyCode();
405 delete pDlg;
406 if (!fSuccess) return;
407 // key defined: Set it
408 KeyControlBtns[idKey]->SetKey(key);
409 // and update config
410 if (fGamepad)
411 Config.Gamepads[iSelectedCtrlSet].Button[idKey] = key;
412 else
413 Config.Controls.Keyboard[iSelectedCtrlSet][idKey] = key;
414}
415
416void C4StartupOptionsDlg::ControlConfigArea::OnResetKeysBtn(C4GUI::Control *btn)
417{
418 // default keys and axis reset
419 if (fGamepad)
420 {
421 for (int i = 0; i < C4ConfigMaxGamepads; ++i)
422 Config.Gamepads[i].Reset();
423 }
424 else
425 Config.Controls.ResetKeys();
426 UpdateCtrlSet();
427}
428
429void C4StartupOptionsDlg::ControlConfigArea::OnGUIGamepadCheckChange(C4GUI::Element *pCheckBox)
430{
431 // same as before?
432 bool fChecked = static_cast<C4GUI::CheckBox *>(pCheckBox)->GetChecked();
433 if (fChecked == !!Config.Controls.GamepadGuiControl) return;
434 // reflect change
435 Config.Controls.GamepadGuiControl = fChecked;
436 Game.pGUI->UpdateGamepadGUIControlEnabled();
437 pOptionsDlg->RecreateDialog(fFade: false);
438}
439
440// C4StartupOptionsDlg::NetworkPortConfig
441
442C4StartupOptionsDlg::NetworkPortConfig::NetworkPortConfig(const C4Rect &rcBounds, const char *szName, int32_t *pConfigValue, int32_t iDefault)
443 : C4GUI::Window(), pConfigValue(pConfigValue)
444{
445 CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
446 SetBounds(rcBounds);
447 C4GUI::ComponentAligner caMain(GetClientRect(), 0, 2, true);
448 bool fEnabled = (*pConfigValue != 0);
449 C4GUI::Label *pLbl = new C4GUI::Label(szName, caMain.GetFromTop(iHgt: pUseFont->GetLineHeight()), ALeft, C4StartupFontClr, pUseFont, false);
450 AddElement(pChild: pLbl);
451 C4GUI::ComponentAligner caBottomLine(caMain.GetAll(), 2, 0, false);
452 const char *szText = LoadResStr(id: C4ResStrTableKey::IDS_CTL_ACTIVE);
453 int iWdt = 100, iHgt = 12;
454 C4GUI::CheckBox::GetStandardCheckBoxSize(piWdt: &iWdt, piHgt: &iHgt, szForCaptionText: szText, pUseFont);
455 pEnableCheck = new C4GUI::CheckBox(caBottomLine.GetFromLeft(iWdt, iHgt), szText, fEnabled);
456 pEnableCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
457 pEnableCheck->SetOnChecked(new C4GUI::CallbackHandler<C4StartupOptionsDlg::NetworkPortConfig>(this, &C4StartupOptionsDlg::NetworkPortConfig::OnEnabledCheckChange));
458 AddElement(pChild: pEnableCheck);
459 pPortEdit = new C4GUI::SpinBox<std::int32_t>(caBottomLine.GetAll(), true);
460 pPortEdit->SetMinimum(1);
461 pPortEdit->SetMaximum(65535);
462 pPortEdit->SetColors(dwNewBGClr: C4StartupEditBGColor, dwNewFontClr: C4StartupFontClr, dwNewBorderColor: C4StartupEditBorderColor);
463 pPortEdit->SetFont(pUseFont);
464 pPortEdit->SetValue(value: fEnabled ? *pConfigValue : iDefault, user: false);
465 pPortEdit->SetMaxText(10); // 65535 is five characters long - but allow some more for easier editing
466 pPortEdit->SetVisibility(fEnabled);
467 AddElement(pChild: pPortEdit);
468}
469
470void C4StartupOptionsDlg::NetworkPortConfig::OnEnabledCheckChange(C4GUI::Element *pCheckBox)
471{
472 pPortEdit->SetVisibility(pEnableCheck->GetChecked());
473}
474
475void C4StartupOptionsDlg::NetworkPortConfig::SavePort()
476{
477 *pConfigValue = GetPort();
478}
479
480int32_t C4StartupOptionsDlg::NetworkPortConfig::GetPort()
481{
482 // controls to config
483 if (!pEnableCheck->GetChecked())
484 return 0;
485 else
486 return pPortEdit->GetValue();
487}
488
489bool C4StartupOptionsDlg::NetworkPortConfig::GetControlSize(int *piWdt, int *piHgt)
490{
491 CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
492 // get size needed for control
493 if (piWdt)
494 {
495 const char *szText = LoadResStr(id: C4ResStrTableKey::IDS_CTL_ACTIVE);
496 if (!C4GUI::CheckBox::GetStandardCheckBoxSize(piWdt, piHgt, szForCaptionText: szText, pUseFont)) return false;
497 *piWdt *= 2;
498 }
499 if (piHgt) *piHgt = C4GUI::Edit::GetCustomEditHeight(pUseFont) + pUseFont->GetLineHeight() + 2 * 4;
500 return true;
501}
502
503// C4StartupOptionsDlg::NetworkServerAddressConfig
504
505C4StartupOptionsDlg::NetworkServerAddressConfig::NetworkServerAddressConfig(const C4Rect &rcBounds, const char *szName, bool *pbConfigEnableValue, char *szConfigAddressValue, int iTabWidth)
506 : C4GUI::Window(), pbConfigEnableValue(pbConfigEnableValue), szConfigAddressValue(szConfigAddressValue)
507{
508 CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
509 SetBounds(rcBounds);
510 C4GUI::ComponentAligner caMain(GetClientRect(), 0, 2, true);
511 pEnableCheck = new C4GUI::CheckBox(caMain.GetFromLeft(iWdt: iTabWidth, iHgt: pUseFont->GetLineHeight()), szName, *pbConfigEnableValue);
512 pEnableCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
513 pEnableCheck->SetOnChecked(new C4GUI::CallbackHandler<C4StartupOptionsDlg::NetworkServerAddressConfig>(this, &C4StartupOptionsDlg::NetworkServerAddressConfig::OnEnabledCheckChange));
514 AddElement(pChild: pEnableCheck);
515 caMain.ExpandLeft(iByWdt: -2);
516 pAddressEdit = new C4GUI::Edit(caMain.GetAll(), true);
517 pAddressEdit->SetColors(dwNewBGClr: C4StartupEditBGColor, dwNewFontClr: C4StartupFontClr, dwNewBorderColor: C4StartupEditBorderColor);
518 pAddressEdit->SetFont(pUseFont);
519 pAddressEdit->InsertText(text: szConfigAddressValue, fUser: false);
520 pAddressEdit->SetMaxText(CFG_MaxString);
521 pAddressEdit->SetVisibility(*pbConfigEnableValue);
522 AddElement(pChild: pAddressEdit);
523}
524
525void C4StartupOptionsDlg::NetworkServerAddressConfig::OnEnabledCheckChange(C4GUI::Element *pCheckBox)
526{
527 // warn about using alternate servers
528 if (pEnableCheck->GetChecked())
529 {
530 GetScreen()->ShowMessage(szMessage: LoadResStr(id: C4ResStrTableKey::IDS_NET_NOOFFICIALLEAGUE), szCaption: LoadResStr(id: C4ResStrTableKey::IDS_NET_QUERY_MASTERSRV), icoIcon: C4GUI::Ico_Notify, pbConfigDontShowAgainSetting: &Config.Startup.HideMsgNoOfficialLeague);
531 }
532 // callback when checkbox is ticked
533 pAddressEdit->SetVisibility(pEnableCheck->GetChecked());
534}
535
536void C4StartupOptionsDlg::NetworkServerAddressConfig::Save2Config()
537{
538 // controls to config
539 *pbConfigEnableValue = pEnableCheck->GetChecked();
540 SCopy(szSource: pAddressEdit->GetText(), sTarget: szConfigAddressValue, iMaxL: CFG_MaxString);
541}
542
543bool C4StartupOptionsDlg::NetworkServerAddressConfig::GetControlSize(int *piWdt, int *piHgt, int *piTabPos, const char *szForText)
544{
545 CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
546 int iWdt = 120;
547 C4GUI::CheckBox::GetStandardCheckBoxSize(piWdt: &iWdt, piHgt, szForCaptionText: szForText, pUseFont);
548 int32_t iEdtWdt = 120, iEdtHgt = 24;
549 pUseFont->GetTextExtent(szText: "sorgentelefon@treffpunktclonk.net", rsx&: iEdtWdt, rsy&: iEdtHgt, fCheckMarkup: false);
550 if (piWdt) *piWdt = iWdt + iEdtWdt + 2;
551 if (piTabPos) *piTabPos = iWdt + 2;
552 if (piHgt) *piHgt = C4GUI::Edit::GetCustomEditHeight(pUseFont) + 2 * 2;
553 return true;
554}
555
556// C4StartupOptionsDlg::BoolConfig
557
558C4StartupOptionsDlg::BoolConfig::BoolConfig(const C4Rect &rcBounds, const char *szName, bool *pbVal, bool fInvert, bool *pbRestartChangeCfgVal)
559 : C4GUI::CheckBox(rcBounds, szName, fInvert != *pbVal), pbVal(pbVal), fInvert(fInvert), pbRestartChangeCfgVal(pbRestartChangeCfgVal)
560{
561 SetOnChecked(new C4GUI::CallbackHandler<BoolConfig>(this, &BoolConfig::OnCheckChange));
562}
563
564void C4StartupOptionsDlg::BoolConfig::OnCheckChange(C4GUI::Element *pCheckBox)
565{
566 if (pbVal) *pbVal = (GetChecked() != fInvert);
567 if (pbRestartChangeCfgVal) GetScreen()->ShowMessage(szMessage: LoadResStr(id: C4ResStrTableKey::IDS_MSG_RESTARTCHANGECFG), szCaption: GetText(),
568 icoIcon: C4GUI::Ico_Notify, pbConfigDontShowAgainSetting: pbRestartChangeCfgVal);
569}
570
571// C4StartupOptionsDlg::EditConfig
572
573C4StartupOptionsDlg::EditConfig::EditConfig(const C4Rect &rcBounds, const char *szName, ValidatedStdStrBufBase *psConfigVal, int32_t *piConfigVal, bool fMultiline)
574 : C4GUI::LabeledEdit(rcBounds, szName, fMultiline, psConfigVal ? psConfigVal->getData() : nullptr, &(C4Startup::Get()->Graphics.BookFont), C4StartupFontClr), psConfigVal(psConfigVal), piConfigVal(piConfigVal)
575{
576 GetEdit()->SetColors(dwNewBGClr: C4StartupEditBGColor, dwNewFontClr: C4StartupFontClr, dwNewBorderColor: C4StartupEditBorderColor);
577 if (piConfigVal) SetIntVal(*piConfigVal);
578 GetEdit()->SetMaxText(CFG_MaxString);
579}
580
581void C4StartupOptionsDlg::EditConfig::Save2Config()
582{
583 // controls to config
584 if (psConfigVal)
585 psConfigVal->CopyValidated(szFromVal: GetEdit()->GetText());
586 if (piConfigVal)
587 *piConfigVal = GetIntVal();
588}
589
590bool C4StartupOptionsDlg::EditConfig::GetControlSize(int *piWdt, int *piHgt, const char *szForText, bool fMultiline)
591{
592 CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
593 typedef C4GUI::LabeledEdit BaseEdit;
594 return BaseEdit::GetControlSize(piWdt, piHgt, szForText, pForFont: pUseFont, fMultiline);
595}
596
597struct
598{
599 C4ResStrTableKeyFormat<> caption;
600 DisplayMode mode;
601} static constexpr DisplayModes[]
602{
603 {.caption: C4ResStrTableKey::IDS_MSG_FULLSCREEN, .mode: DisplayMode::Fullscreen},
604 {.caption: C4ResStrTableKey::IDS_MSG_WINDOW, .mode: DisplayMode::Window}
605};
606
607// C4StartupOptionsDlg
608
609C4StartupOptionsDlg::C4StartupOptionsDlg() : C4StartupDlg(LoadResStrNoAmp(id: C4ResStrTableKey::IDS_DLG_OPTIONS).c_str()), fConfigSaved(false), fCanGoBack(true)
610{
611 UpdateSize();
612 bool fSmall = (GetClientRect().Wdt < 750);
613 CStdFont *pUseFont = &(C4Startup::Get()->Graphics.BookFont);
614
615 // key bindings
616 C4CustomKey::CodeList keys;
617 keys.push_back(x: C4KeyCodeEx(K_BACK)); keys.push_back(x: C4KeyCodeEx(K_LEFT));
618 pKeyBack = new C4KeyBinding(keys, "StartupOptionsBack", KEYSCOPE_Gui,
619 new C4GUI::DlgKeyCB<C4StartupOptionsDlg>(*this, &C4StartupOptionsDlg::KeyBack), C4CustomKey::PRIO_Dlg);
620 keys.clear();
621 keys.push_back(x: C4KeyCodeEx(K_F3)); // overloading global toggle with higher priority here, so a new name is required
622 pKeyToggleMusic = new C4KeyBinding(keys, "OptionsMusicToggle", KEYSCOPE_Gui,
623 new C4GUI::DlgKeyCB<C4StartupOptionsDlg>(*this, &C4StartupOptionsDlg::KeyMusicToggle), C4CustomKey::PRIO_Dlg);
624
625 // screen calculations
626 int32_t iButtonWidth, iCaptionFontHgt;
627 int32_t iButtonHeight = C4GUI_ButtonHgt;
628 C4GUI::GetRes()->CaptionFont.GetTextExtent(szText: "<< BACK", rsx&: iButtonWidth, rsy&: iCaptionFontHgt, fCheckMarkup: true);
629 iButtonWidth *= 3;
630 int iIndentX1, iIndentX2, iIndentY1, iIndentY2;
631 if (fSmall)
632 {
633 iIndentX1 = 20; iIndentX2 = 1;
634 }
635 else
636 {
637 iIndentX1 = GetClientRect().Wdt / 40;
638 iIndentX2 = iIndentX1 / 2;
639 }
640 if (fSmall)
641 {
642 iIndentY1 = 1; iIndentY2 = 1;
643 }
644 else
645 {
646 iIndentY1 = GetClientRect().Hgt / 200;
647 iIndentY2 = std::max<int32_t>(a: 1, b: iIndentY1 / 2);
648 }
649 C4GUI::ComponentAligner caMain(GetClientRect(), 0, 0, true);
650 C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(iHgt: caMain.GetHeight() / (fSmall ? 20 : 7)), 0, 0);
651 C4GUI::ComponentAligner caButtons(caButtonArea.GetCentered(iWdt: caMain.GetWidth() * 7 / 8, iHgt: iButtonHeight), 0, 0);
652 C4GUI::ComponentAligner caConfigArea(caMain.GetAll(), fSmall ? 0 : (caMain.GetWidth() * 69 / 1730), fSmall ? 0 : (caMain.GetHeight() / 200));
653
654 // back button
655 C4GUI::CallbackButton<C4StartupOptionsDlg> *btn;
656 AddElement(pChild: btn = new C4GUI::CallbackButton<C4StartupOptionsDlg>(LoadResStr(id: C4ResStrTableKey::IDS_BTN_BACK), caButtons.GetFromLeft(iWdt: iButtonWidth), &C4StartupOptionsDlg::OnBackBtn));
657 btn->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DLGTIP_BACKMAIN));
658
659 // main config area tabular
660 pOptionsTabular = new C4GUI::Tabular(caConfigArea.GetAll(), C4GUI::Tabular::tbLeft);
661 pOptionsTabular->SetGfx(pafctBack: &C4Startup::Get()->Graphics.fctOptionsDlgPaper, pafctClip: &C4Startup::Get()->Graphics.fctOptionsTabClip, pafctIcons: &C4Startup::Get()->Graphics.fctOptionsIcons, paSheetCaptionFont: &C4Startup::Get()->Graphics.BookSmallFont, fResizeByAspect: true);
662 AddElement(pChild: pOptionsTabular);
663 C4GUI::Tabular::Sheet *pSheetGeneral = pOptionsTabular->AddSheet(szTitle: LoadResStr(id: C4ResStrTableKey::IDS_DLG_PROGRAM), icoTitle: 0);
664 C4GUI::Tabular::Sheet *pSheetGraphics = pOptionsTabular->AddSheet(szTitle: LoadResStr(id: C4ResStrTableKey::IDS_DLG_GRAPHICS), icoTitle: 1);
665 C4GUI::Tabular::Sheet *pSheetSound = pOptionsTabular->AddSheet(szTitle: LoadResStr(id: C4ResStrTableKey::IDS_DLG_SOUND), icoTitle: 2);
666 C4GUI::Tabular::Sheet *pSheetKeyboard = pOptionsTabular->AddSheet(szTitle: LoadResStr(id: C4ResStrTableKey::IDS_DLG_KEYBOARD), icoTitle: 3);
667 C4GUI::Tabular::Sheet *pSheetGamepad = pOptionsTabular->AddSheet(szTitle: LoadResStr(id: C4ResStrTableKey::IDS_DLG_GAMEPAD), icoTitle: 4);
668 C4GUI::Tabular::Sheet *pSheetNetwork = pOptionsTabular->AddSheet(szTitle: LoadResStr(id: C4ResStrTableKey::IDS_DLG_NETWORK), icoTitle: 5);
669
670 C4GUI::CheckBox *pCheck; C4GUI::Label *pLbl;
671 int iCheckWdt = 100, iCheckHgt = 20, iEdit2Wdt = 100, iEdit2Hgt = 40;
672 BoolConfig::GetStandardCheckBoxSize(piWdt: &iCheckWdt, piHgt: &iCheckHgt, szForCaptionText: "Default text", pUseFont);
673 EditConfig::GetControlSize(piWdt: &iEdit2Wdt, piHgt: &iEdit2Hgt, szForText: "Default text", fMultiline: false);
674
675 // page program
676 C4GUI::ComponentAligner caSheetProgram(pSheetGeneral->GetClientRect(), caMain.GetWidth() / 20, caMain.GetHeight() / 20, true);
677 // language
678 const char *szLangTip = LoadResStr(id: C4ResStrTableKey::IDS_MSG_SELECTLANG);
679 C4GUI::ComponentAligner caLanguage(caSheetProgram.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: 0, iSectYMax: 8, iSectSizeX: -1, iSectSizeY: -1, fCenterPos: true, iSectNumX: 1, iSectNumY: 2), 0, C4GUI_DefDlgSmallIndent, false);
680 C4GUI::ComponentAligner caLanguageBox(caLanguage.GetFromTop(iHgt: C4GUI::ComboBox::GetDefaultHeight()), 0, 0, false);
681 StdStrBuf sLangStr; sLangStr.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_CTL_LANGUAGE)); sLangStr.AppendChar(cChar: ':');
682 int32_t w, q;
683 pUseFont->GetTextExtent(szText: sLangStr.getData(), rsx&: w, rsy&: q, fCheckMarkup: true);
684 pLbl = new C4GUI::Label(sLangStr.getData(), caLanguageBox.GetFromLeft(iWdt: w + C4GUI_DefDlgSmallIndent), ALeft, C4StartupFontClr, pUseFont, false);
685 pLbl->SetToolTip(szLangTip);
686 pSheetGeneral->AddElement(pChild: pLbl);
687 pUseFont->GetTextExtent(szText: "XX: Top Secret Language", rsx&: w, rsy&: q, fCheckMarkup: true);
688 pLangCombo = new C4GUI::ComboBox(caLanguageBox.GetFromLeft(iWdt: (std::min)(a: w, b: caLanguageBox.GetWidth())));
689 pLangCombo->SetToolTip(szLangTip);
690 pLangCombo->SetComboCB(new C4GUI::ComboBox_FillCallback<C4StartupOptionsDlg>(this, &C4StartupOptionsDlg::OnLangComboFill, &C4StartupOptionsDlg::OnLangComboSelChange));
691 pLangCombo->SetColors(dwFontClr: C4StartupFontClr, dwBGClr: C4StartupEditBGColor, dwBorderClr: C4StartupEditBorderColor);
692 pLangCombo->SetFont(pUseFont);
693 pLangCombo->SetDecoration(&(C4Startup::Get()->Graphics.fctContext));
694 pSheetGeneral->AddElement(pChild: pLangCombo);
695 pLangInfoLabel = new C4GUI::Label("", caLanguage.GetFromTop(iHgt: C4GUI::GetRes()->TextFont.GetLineHeight() * 3), ALeft, C4StartupFontClr, pUseFont, false);
696 pLangInfoLabel->SetToolTip(szLangTip);
697 pSheetGeneral->AddElement(pChild: pLangInfoLabel);
698 UpdateLanguage();
699 // font
700 const char *szFontTip = LoadResStr(id: C4ResStrTableKey::IDS_DESC_SELECTFONT);
701 C4GUI::ComponentAligner caFontBox(caSheetProgram.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: 2, iSectYMax: 9, iSectSizeX: -1, iSectSizeY: C4GUI::ComboBox::GetDefaultHeight(), fCenterPos: true), 0, 0, false);
702 StdStrBuf sFontStr; sFontStr.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_CTL_FONT)); sFontStr.AppendChar(cChar: ':');
703 pUseFont->GetTextExtent(szText: sFontStr.getData(), rsx&: w, rsy&: q, fCheckMarkup: true);
704 pLbl = new C4GUI::Label(sFontStr.getData(), caFontBox.GetFromLeft(iWdt: w + C4GUI_DefDlgSmallIndent), ALeft, C4StartupFontClr, pUseFont, false);
705 pLbl->SetToolTip(szFontTip);
706 pSheetGeneral->AddElement(pChild: pLbl);
707 pUseFont->GetTextExtent(szText: "Comic Sans MS", rsx&: w, rsy&: q, fCheckMarkup: true);
708 pFontFaceCombo = new C4GUI::ComboBox(caFontBox.GetFromLeft(iWdt: std::min<int32_t>(a: caFontBox.GetInnerWidth() * 3 / 4, b: w * 3)));
709 pFontFaceCombo->SetToolTip(szFontTip);
710 pFontFaceCombo->SetComboCB(new C4GUI::ComboBox_FillCallback<C4StartupOptionsDlg>(this, &C4StartupOptionsDlg::OnFontFaceComboFill, &C4StartupOptionsDlg::OnFontComboSelChange));
711 pFontFaceCombo->SetColors(dwFontClr: C4StartupFontClr, dwBGClr: C4StartupEditBGColor, dwBorderClr: C4StartupEditBorderColor);
712 pFontFaceCombo->SetFont(pUseFont);
713 pFontFaceCombo->SetDecoration(&(C4Startup::Get()->Graphics.fctContext));
714 caFontBox.ExpandLeft(iByWdt: -C4GUI_DefDlgSmallIndent);
715 pSheetGeneral->AddElement(pChild: pFontFaceCombo);
716 pFontSizeCombo = new C4GUI::ComboBox(caFontBox.GetFromLeft(iWdt: std::min<int32_t>(a: caFontBox.GetInnerWidth(), b: w)));
717 pFontSizeCombo->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_FONTSIZE));
718 pFontSizeCombo->SetComboCB(new C4GUI::ComboBox_FillCallback<C4StartupOptionsDlg>(this, &C4StartupOptionsDlg::OnFontSizeComboFill, &C4StartupOptionsDlg::OnFontComboSelChange));
719 pFontSizeCombo->SetColors(dwFontClr: C4StartupFontClr, dwBGClr: C4StartupEditBGColor, dwBorderClr: C4StartupEditBorderColor);
720 pFontSizeCombo->SetFont(pUseFont);
721 pFontSizeCombo->SetDecoration(&(C4Startup::Get()->Graphics.fctContext));
722 pSheetGeneral->AddElement(pChild: pFontSizeCombo);
723 UpdateFontControls();
724
725 // white chat
726 C4GUI::ComponentAligner caWhiteChat(caSheetProgram.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: 3, iSectYMax: 9, iSectSizeX: -1, iSectSizeY: C4GUI::ComboBox::GetDefaultHeight(), fCenterPos: true), 0, 0, false);
727
728 StdStrBuf sWhiteChat(LoadResStr(id: C4ResStrTableKey::IDS_MNU_WHITECHAT), false);
729 sWhiteChat.AppendChar(cChar: ':');
730 pUseFont->GetTextExtent(szText: sWhiteChat.getData(), rsx&: w, rsy&: q, fCheckMarkup: true);
731 pLbl = new C4GUI::Label(sWhiteChat.getData(), caWhiteChat.GetFromLeft(iWdt: w + C4GUI_DefDlgSmallIndent + C4GUI::ComboBox::GetDefaultHeight()), ALeft, C4StartupFontClr, pUseFont, false);
732 pLbl->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_WHITECHAT));
733 pSheetGeneral->AddElement(pChild: pLbl);
734 // - ingame
735 const auto szIngame = LoadResStr(id: C4ResStrTableKey::IDS_CTL_WHITECHAT_INGAME);
736 pUseFont->GetTextExtent(szText: szIngame, rsx&: w, rsy&: q, fCheckMarkup: true);
737 pCheck = new BoolConfig(caWhiteChat.GetFromLeft(iWdt: w + C4GUI_DefDlgSmallIndent + 2 * C4GUI::ComboBox::GetDefaultHeight()), szIngame, &Config.General.UseWhiteIngameChat);
738 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_WHITECHAT_INGAME));
739 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
740 pSheetGeneral->AddElement(pChild: pCheck);
741 // - lobby
742 const auto szLobby = LoadResStr(id: C4ResStrTableKey::IDS_CTL_WHITECHAT_LOBBY);
743 pUseFont->GetTextExtent(szText: szLobby, rsx&: w, rsy&: q, fCheckMarkup: true);
744 pCheck = new BoolConfig(caWhiteChat.GetFromLeft(iWdt: w + C4GUI_DefDlgSmallIndent + 2 * C4GUI::ComboBox::GetDefaultHeight()), szLobby, &Config.General.UseWhiteLobbyChat);
745 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_WHITECHAT_LOBBY));
746 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
747 pSheetGeneral->AddElement(pChild: pCheck);
748
749 // timestamps
750 pCheck = new BoolConfig(caSheetProgram.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: 4, iSectYMax: 9, iSectSizeX: -1, iSectSizeY: iCheckHgt, fCenterPos: true), LoadResStr(id: C4ResStrTableKey::IDS_CTL_TIMESTAMPS), &Config.General.ShowLogTimestamps);
751 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_TIMESTAMPS));
752 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
753 pSheetGeneral->AddElement(pChild: pCheck);
754
755 // preloading
756 pCheck = new BoolConfig{caSheetProgram.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: 5, iSectYMax: 9, iSectSizeX: -1, iSectSizeY: iCheckHgt, fCenterPos: true), LoadResStr(id: C4ResStrTableKey::IDS_CTL_PRELOADING), &Config.General.Preloading};
757 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_MSG_PRELOADING_DESC));
758 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
759 pSheetGeneral->AddElement(pChild: pCheck);
760
761 // fair crew strength
762 C4GUI::GroupBox *pGroupFairCrewStrength = new C4GUI::GroupBox(caSheetProgram.GetGridCell(iSectX: 0, iSectXMax: 2, iSectY: 6, iSectYMax: 9, iSectSizeX: -1, iSectSizeY: pUseFont->GetLineHeight() * 2 + iIndentY2 * 2 + C4GUI_ScrollBarHgt, fCenterPos: true, iSectNumX: 1, iSectNumY: 2));
763 pGroupFairCrewStrength->SetTitle(LoadResStr(id: C4ResStrTableKey::IDS_CTL_FAIRCREWSTRENGTH));
764 pGroupFairCrewStrength->SetFont(pUseFont);
765 pGroupFairCrewStrength->SetColors(dwFrameClr: C4StartupEditBorderColor, dwTitleClr: C4StartupFontClr);
766 pSheetGeneral->AddElement(pChild: pGroupFairCrewStrength);
767 C4GUI::ComponentAligner caGroupFairCrewStrength(pGroupFairCrewStrength->GetClientRect(), 1, 0, true);
768 StdStrBuf sLabelTxt; sLabelTxt.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_CTL_FAIRCREWWEAK));
769 w = 20; q = 12; pUseFont->GetTextExtent(szText: sLabelTxt.getData(), rsx&: w, rsy&: q, fCheckMarkup: true);
770 pGroupFairCrewStrength->AddElement(pChild: new C4GUI::Label(sLabelTxt.getData(), caGroupFairCrewStrength.GetFromLeft(iWdt: w, iHgt: q), ACenter, C4StartupFontClr, pUseFont, false, false));
771 sLabelTxt.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_CTL_FAIRCREWSTRONG));
772 pUseFont->GetTextExtent(szText: sLabelTxt.getData(), rsx&: w, rsy&: q, fCheckMarkup: true);
773 pGroupFairCrewStrength->AddElement(pChild: new C4GUI::Label(sLabelTxt.getData(), caGroupFairCrewStrength.GetFromRight(iWdt: w, iHgt: q), ACenter, C4StartupFontClr, pUseFont, false, false));
774 C4GUI::ParCallbackHandler<C4StartupOptionsDlg, int32_t> *pCB = new C4GUI::ParCallbackHandler<C4StartupOptionsDlg, int32_t>(this, &C4StartupOptionsDlg::OnFairCrewStrengthSliderChange);
775 C4GUI::ScrollBar *pSlider = new C4GUI::ScrollBar(caGroupFairCrewStrength.GetCentered(iWdt: caGroupFairCrewStrength.GetInnerWidth(), C4GUI_ScrollBarHgt), true, pCB, 101);
776 pSlider->SetDecoration(pToGfx: &C4Startup::Get()->Graphics.sfctBookScroll, fAutoHide: false);
777 pGroupFairCrewStrength->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_FAIRCREWSTRENGTH));
778 pSlider->SetScrollPos(FairCrewStrength2Slider(iStrengthVal: Config.General.FairCrewStrength));
779 pGroupFairCrewStrength->AddElement(pChild: pSlider);
780 // reset configuration
781 const char *szBtnText = LoadResStr(id: C4ResStrTableKey::IDS_BTN_RESETCONFIG);
782 C4GUI::CallbackButton<C4StartupOptionsDlg, SmallButton> *pSmallBtn;
783 C4GUI::GetRes()->CaptionFont.GetTextExtent(szText: szBtnText, rsx&: iButtonWidth, rsy&: iButtonHeight, fCheckMarkup: true);
784 C4Rect rcResetBtn = caSheetProgram.GetGridCell(iSectX: 1, iSectXMax: 2, iSectY: 8, iSectYMax: 9, iSectSizeX: std::min<int32_t>(a: iButtonWidth + iButtonHeight * 4, b: caSheetProgram.GetInnerWidth() * 2 / 5), iSectSizeY: SmallButton::GetDefaultButtonHeight(), fCenterPos: true);
785 pSheetGeneral->AddElement(pChild: pSmallBtn = new C4GUI::CallbackButton<C4StartupOptionsDlg, SmallButton>(szBtnText, rcResetBtn, &C4StartupOptionsDlg::OnResetConfigBtn, this));
786 pSmallBtn->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_RESETCONFIG));
787
788 szBtnText = LoadResStr(id: C4ResStrTableKey::IDS_DLG_ADVANCED_SETTINGS);
789 C4GUI::GetRes()->CaptionFont.GetTextExtent(szText: szBtnText, rsx&: iButtonWidth, rsy&: iButtonHeight, fCheckMarkup: true);
790 C4Rect rcAdvancedBtn = caSheetProgram.GetGridCell(iSectX: 0, iSectXMax: 2, iSectY: 8, iSectYMax: 9, iSectSizeX: std::min<int32_t>(a: iButtonWidth + iButtonHeight * 4, b: caSheetProgram.GetInnerWidth() * 2 / 5), iSectSizeY: SmallButton::GetDefaultButtonHeight(), fCenterPos: true);
791 pSheetGeneral->AddElement(pChild: pSmallBtn = new C4GUI::CallbackButton<C4StartupOptionsDlg, SmallButton>(szBtnText, rcAdvancedBtn, &C4StartupOptionsDlg::OnAdvancedConfigBtn, this));
792 pSmallBtn->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_ADVANCED_SETTINGS));
793
794 // page graphics
795 C4GUI::ComponentAligner caSheetGraphics(pSheetGraphics->GetClientRect(), iIndentX1, iIndentY1, true);
796
797 C4GUI::GroupBox *pGroupDisplaySettings = new C4GUI::GroupBox(caSheetGraphics.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: 0, iSectYMax: 3));
798 pGroupDisplaySettings->SetTitle(LoadResStrNoAmp(id: C4ResStrTableKey::IDS_CTL_DISPLAY).c_str());
799 pGroupDisplaySettings->SetFont(pUseFont);
800 pGroupDisplaySettings->SetColors(dwFrameClr: C4StartupEditBorderColor, dwTitleClr: C4StartupFontClr);
801 pSheetGraphics->AddElement(pChild: pGroupDisplaySettings);
802
803 C4GUI::ComponentAligner caDisplaySettings(pGroupDisplaySettings->GetClientRect(), iIndentX1, iIndentY2, true);
804
805 C4GUI::ComponentAligner caDisplayModeRow(caDisplaySettings.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: 0, iSectYMax: 2, iSectSizeX: -1, iSectSizeY: C4GUI::ComboBox::GetDefaultHeight(), fCenterPos: true), 0, 0, false);
806
807 StdStrBuf sDisplayModeStr; sDisplayModeStr.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_CTL_DISPLAYMODE)); sDisplayModeStr.AppendChar(cChar: ':');
808 pUseFont->GetTextExtent(szText: sDisplayModeStr.getData(), rsx&: w, rsy&: q, fCheckMarkup: true);
809 pLbl = new C4GUI::Label(sDisplayModeStr.getData(), caDisplayModeRow.GetFromLeft(iWdt: w + C4GUI_DefDlgSmallIndent), ALeft, C4StartupFontClr, pUseFont, false);
810 pGroupDisplaySettings->AddElement(pChild: pLbl);
811 pDisplayModeCombo = new C4GUI::ComboBox(caDisplayModeRow.GetAll());
812 pDisplayModeCombo->SetComboCB(new C4GUI::ComboBox_FillCallback<C4StartupOptionsDlg>(this, &C4StartupOptionsDlg::OnDisplayModeComboFill, &C4StartupOptionsDlg::OnDisplayModeComboSelChange));
813 pDisplayModeCombo->SetColors(dwFontClr: C4StartupFontClr, dwBGClr: C4StartupEditBGColor, dwBorderClr: C4StartupEditBorderColor);
814 pDisplayModeCombo->SetFont(pUseFont);
815 pDisplayModeCombo->SetDecoration(&(C4Startup::Get()->Graphics.fctContext));
816 pGroupDisplaySettings->AddElement(pChild: pDisplayModeCombo);
817
818 for (const auto &mode : DisplayModes)
819 {
820 if (mode.mode == Config.Graphics.UseDisplayMode)
821 {
822 pDisplayModeCombo->SetText(LoadResStr(id: mode.caption));
823 }
824 }
825
826 C4GUI::ComponentAligner caScaleRow(caDisplaySettings.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: 1, iSectYMax: 2, iSectSizeX: -1, iSectSizeY: iEdit2Hgt, fCenterPos: true), 0, 0, false);
827
828 StdStrBuf sScaleStr; sScaleStr.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_CTL_GRAPHICSSCALE)); sScaleStr.AppendChar(cChar: ':');
829 pUseFont->GetTextExtent(szText: sScaleStr.getData(), rsx&: w, rsy&: q, fCheckMarkup: true);
830 pLbl = new C4GUI::Label(sScaleStr.getData(), caScaleRow.GetFromLeft(iWdt: w + C4GUI_DefDlgSmallIndent), ALeft, C4StartupFontClr, pUseFont, false);
831 pGroupDisplaySettings->AddElement(pChild: pLbl);
832
833 szBtnText = LoadResStr(id: C4ResStrTableKey::IDS_BTN_TESTGRAPHICSSCALE);
834 C4GUI::CallbackButton<C4StartupOptionsDlg, SmallButton> *pTestScaleBtn;
835 C4GUI::GetRes()->CaptionFont.GetTextExtent(szText: szBtnText, rsx&: w, rsy&: q, fCheckMarkup: true);
836 pGroupDisplaySettings->AddElement(pChild: pTestScaleBtn = new C4GUI::CallbackButton<C4StartupOptionsDlg, SmallButton>(szBtnText, caScaleRow.GetFromRight(iWdt: w), &C4StartupOptionsDlg::OnTestScaleBtn, this));
837 caScaleRow.GetFromRight(iWdt: 4 * C4GUI_DefDlgSmallIndent);
838
839 pUseFont->GetTextExtent(szText: " %", rsx&: w, rsy&: q, fCheckMarkup: true);
840 pLbl = new C4GUI::Label(" %", caScaleRow.GetFromRight(iWdt: w), ARight, C4StartupFontClr, pUseFont, false);
841 pGroupDisplaySettings->AddElement(pChild: pLbl);
842
843 pUseFont->GetTextExtent(szText: "30000", rsx&: w, rsy&: q, fCheckMarkup: true);
844 pScaleEdit = new ScaleEdit(this, caScaleRow.GetFromRight(iWdt: w + C4GUI_DefDlgSmallIndent));
845 pScaleEdit->SetFont(pUseFont);
846 pScaleEdit->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_GRAPHICSSCALE));
847 pGroupDisplaySettings->AddElement(pChild: pScaleEdit);
848
849 caScaleRow.GetFromTop(iHgt: (caScaleRow.GetHeight() - C4GUI_ScrollBarHgt) / 2);
850 caScaleRow.GetFromBottom(iHgt: caScaleRow.GetHeight() - C4GUI_ScrollBarHgt);
851 auto *pScaleCB = new C4GUI::ParCallbackHandler<C4StartupOptionsDlg, int32_t>(this, &C4StartupOptionsDlg::OnScaleSliderChanged);
852 pScaleSlider = new C4GUI::ScrollBar(caScaleRow.GetAll(), true, pScaleCB, maxScale - minScale + 1);
853
854 pScaleSlider->SetDecoration(pToGfx: &C4Startup::Get()->Graphics.sfctBookScroll, fAutoHide: false);
855 pScaleSlider->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_GRAPHICSSCALE));
856 pScaleSlider->SetScrollPos(Config.Graphics.Scale - minScale);
857 pGroupDisplaySettings->AddElement(pChild: pScaleSlider);
858
859 OnScaleSliderChanged(val: Config.Graphics.Scale - minScale);
860;
861 // --subgroup options
862 int32_t iNumGfxOptions = 5, iOpt = 0;
863 C4GUI::GroupBox *pGroupOptions = new C4GUI::GroupBox(caSheetGraphics.GetGridCell(iSectX: 0, iSectXMax: 2, iSectY: 1, iSectYMax: 3));
864 pGroupOptions->SetTitle(LoadResStrNoAmp(id: C4ResStrTableKey::IDS_DLG_OPTIONS).c_str());
865 pGroupOptions->SetFont(pUseFont);
866 pGroupOptions->SetColors(dwFrameClr: C4StartupEditBorderColor, dwTitleClr: C4StartupFontClr);
867 pSheetGraphics->AddElement(pChild: pGroupOptions);
868 C4GUI::ComponentAligner caGroupOptions(pGroupOptions->GetClientRect(), iIndentX1, iIndentY2, true);
869 // add new crew portraits
870 pCheck = new BoolConfig(caGroupOptions.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: iOpt++, iSectYMax: iNumGfxOptions, iSectSizeX: -1, iSectSizeY: iCheckHgt, fCenterPos: true), LoadResStr(id: C4ResStrTableKey::IDS_MSG_ADDPORTRAITS), &Config.Graphics.AddNewCrewPortraits);
871 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_MSG_ADDPORTRAITS_DESC));
872 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
873 pGroupOptions->AddElement(pChild: pCheck);
874 // store default portraits in crew
875 pCheck = new BoolConfig(caGroupOptions.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: iOpt++, iSectYMax: iNumGfxOptions, iSectSizeX: -1, iSectSizeY: iCheckHgt, fCenterPos: true), LoadResStr(id: C4ResStrTableKey::IDS_MSG_STOREPORTRAITS), &Config.Graphics.SaveDefaultPortraits);
876 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_STOREPORTRAITS));
877 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
878 pGroupOptions->AddElement(pChild: pCheck);
879 // automatic gfx frame skip
880 pCheck = new BoolConfig(caGroupOptions.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: iOpt++, iSectYMax: iNumGfxOptions, iSectSizeX: -1, iSectSizeY: iCheckHgt, fCenterPos: true), LoadResStr(id: C4ResStrTableKey::IDS_MSG_AUTOFRAMESKIP), &Config.Graphics.AutoFrameSkip);
881 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_AUTOFRAMESKIP));
882 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
883 pGroupOptions->AddElement(pChild: pCheck);
884 // hide folder maps
885 pCheck = new BoolConfig(caGroupOptions.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: iOpt++, iSectYMax: iNumGfxOptions, iSectSizeX: -1, iSectSizeY: iCheckHgt, fCenterPos: true), LoadResStr(id: C4ResStrTableKey::IDS_MSG_SHOWFOLDERMAPS), &Config.Graphics.ShowFolderMaps);
886 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_SHOWFOLDERMAPS));
887 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
888 pGroupOptions->AddElement(pChild: pCheck);
889 // disable gamma
890 pCheck = new BoolConfig(caGroupOptions.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: iOpt++, iSectYMax: iNumGfxOptions, iSectSizeX: -1, iSectSizeY: iCheckHgt, fCenterPos: true), LoadResStr(id: C4ResStrTableKey::IDS_CTL_DISABLEGAMMA), &Config.Graphics.DisableGamma);
891 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_MSG_DISABLEGAMMA_DESC));
892 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
893 pGroupOptions->AddElement(pChild: pCheck);
894 // --subgroup effects
895 C4GUI::GroupBox *pGroupEffects = new C4GUI::GroupBox(caSheetGraphics.GetGridCell(iSectX: 1, iSectXMax: 2, iSectY: 1, iSectYMax: 3));
896 pGroupEffects->SetTitle(LoadResStrNoAmp(id: C4ResStrTableKey::IDS_CTL_SMOKE).c_str());
897 pGroupEffects->SetFont(pUseFont);
898 pGroupEffects->SetColors(dwFrameClr: C4StartupEditBorderColor, dwTitleClr: C4StartupFontClr);
899 pSheetGraphics->AddElement(pChild: pGroupEffects);
900 C4GUI::ComponentAligner caGroupEffects(pGroupEffects->GetClientRect(), iIndentX1, iIndentY2, true);
901 iNumGfxOptions = 2; iOpt = 0;
902 // effects level slider
903 C4GUI::ComponentAligner caEffectsLevel(caGroupEffects.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: iOpt++, iSectYMax: iNumGfxOptions), 1, 0, false);
904 StdStrBuf sEffectsTxt; sEffectsTxt.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_CTL_SMOKELOW));
905 w = 20; q = 12; pUseFont->GetTextExtent(szText: sEffectsTxt.getData(), rsx&: w, rsy&: q, fCheckMarkup: true);
906 pGroupEffects->AddElement(pChild: new C4GUI::Label(sEffectsTxt.getData(), caEffectsLevel.GetFromLeft(iWdt: w, iHgt: q), ACenter, C4StartupFontClr, pUseFont, false, false));
907 sEffectsTxt.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_CTL_SMOKEHI));
908 w = 20; q = 12; pUseFont->GetTextExtent(szText: sEffectsTxt.getData(), rsx&: w, rsy&: q, fCheckMarkup: true);
909 pGroupEffects->AddElement(pChild: new C4GUI::Label(sEffectsTxt.getData(), caEffectsLevel.GetFromRight(iWdt: w, iHgt: q), ACenter, C4StartupFontClr, pUseFont, false, false));
910 pEffectLevelSlider = new C4GUI::ScrollBar(caEffectsLevel.GetCentered(iWdt: caEffectsLevel.GetInnerWidth(), C4GUI_ScrollBarHgt), true, new C4GUI::ParCallbackHandler<C4StartupOptionsDlg, int32_t>(this, &C4StartupOptionsDlg::OnEffectsSliderChange), 301);
911 pEffectLevelSlider->SetDecoration(pToGfx: &C4Startup::Get()->Graphics.sfctBookScroll, fAutoHide: false);
912 pEffectLevelSlider->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_MSG_PARTICLES_DESC));
913 pEffectLevelSlider->SetScrollPos(Config.Graphics.SmokeLevel);
914 pGroupEffects->AddElement(pChild: pEffectLevelSlider);
915 // fire particles
916 pCheck = new BoolConfig(caGroupEffects.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: iOpt++, iSectYMax: iNumGfxOptions, iSectSizeX: -1, iSectSizeY: iCheckHgt, fCenterPos: true), LoadResStr(id: C4ResStrTableKey::IDS_MSG_FIREPARTICLES), &Config.Graphics.FireParticles);
917 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_MSG_FIREPARTICLES_DESC));
918 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
919 pGroupEffects->AddElement(pChild: pCheck);
920
921 // page sound
922 C4GUI::ComponentAligner caSheetSound(pSheetSound->GetClientRect(), iIndentX1, iIndentY1, true);
923 if (!C4GUI::CheckBox::GetStandardCheckBoxSize(piWdt: &iCheckWdt, piHgt: &iCheckHgt, szForCaptionText: "Lorem ipsum", pUseFont)) { iCheckWdt = 100; iCheckHgt = 20; }
924 int32_t iGridWdt = iCheckWdt * 2, iGridHgt = iCheckHgt * 5 / 2;
925 // --subgroup menu system sound
926 C4GUI::GroupBox *pGroupFESound = new C4GUI::GroupBox(caSheetSound.GetGridCell(iSectX: 0, iSectXMax: 2, iSectY: 0, iSectYMax: 5, iSectSizeX: iGridWdt, iSectSizeY: iGridHgt, fCenterPos: false, iSectNumX: 1, iSectNumY: 2));
927 pGroupFESound->SetTitle(LoadResStrNoAmp(id: C4ResStrTableKey::IDS_CTL_FRONTEND).c_str());
928 pGroupFESound->SetFont(pUseFont);
929 pGroupFESound->SetColors(dwFrameClr: C4StartupEditBorderColor, dwTitleClr: C4StartupFontClr);
930 pSheetSound->AddElement(pChild: pGroupFESound);
931 C4GUI::ComponentAligner caGroupFESound(pGroupFESound->GetClientRect(), iIndentX1, iIndentY2, true);
932 // menu system music
933 pCheck = pFEMusicCheck = new C4GUI::CheckBox(caGroupFESound.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: 0, iSectYMax: 2, iSectSizeX: -1, iSectSizeY: iCheckHgt, fCenterPos: true), LoadResStr(id: C4ResStrTableKey::IDS_CTL_MUSIC), !!Config.Sound.FEMusic);
934 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_MENUMUSIC));
935 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
936 pCheck->SetOnChecked(new C4GUI::CallbackHandler<C4StartupOptionsDlg>(this, &C4StartupOptionsDlg::OnFEMusicCheck));
937 pGroupFESound->AddElement(pChild: pCheck);
938 // menu system sound effects
939 pCheck = new BoolConfig(caGroupFESound.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: 1, iSectYMax: 2, iSectSizeX: -1, iSectSizeY: iCheckHgt, fCenterPos: true), LoadResStr(id: C4ResStrTableKey::IDS_CTL_SOUNDFX), &Config.Sound.FESamples);
940 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_MENUSOUND));
941 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
942 pGroupFESound->AddElement(pChild: pCheck);
943 // --subgroup game sound
944 C4GUI::GroupBox *pGroupRXSound = new C4GUI::GroupBox(caSheetSound.GetGridCell(iSectX: 1, iSectXMax: 2, iSectY: 0, iSectYMax: 5, iSectSizeX: iGridWdt, iSectSizeY: iGridHgt, fCenterPos: false, iSectNumX: 1, iSectNumY: 2));
945 pGroupRXSound->SetTitle(LoadResStrNoAmp(id: C4ResStrTableKey::IDS_CTL_GAME).c_str());
946 pGroupRXSound->SetFont(pUseFont);
947 pGroupRXSound->SetColors(dwFrameClr: C4StartupEditBorderColor, dwTitleClr: C4StartupFontClr);
948 pSheetSound->AddElement(pChild: pGroupRXSound);
949 C4GUI::ComponentAligner caGroupRXSound(pGroupRXSound->GetClientRect(), iIndentX1, iIndentY2, true);
950 // game music
951 pCheck = new BoolConfig(caGroupRXSound.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: 0, iSectYMax: 2, iSectSizeX: -1, iSectSizeY: iCheckHgt, fCenterPos: true), LoadResStr(id: C4ResStrTableKey::IDS_CTL_MUSIC), &Config.Sound.RXMusic);
952 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_GAMEMUSIC));
953 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
954 pGroupRXSound->AddElement(pChild: pCheck);
955 // game sound effects
956 pCheck = new BoolConfig(caGroupRXSound.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: 1, iSectYMax: 2, iSectSizeX: -1, iSectSizeY: iCheckHgt, fCenterPos: true), LoadResStr(id: C4ResStrTableKey::IDS_CTL_SOUNDFX), &Config.Sound.RXSound);
957 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_GAMESOUND));
958 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
959 pGroupRXSound->AddElement(pChild: pCheck);
960 // -- subgroup volume
961 C4GUI::GroupBox *pGroupVolume = new C4GUI::GroupBox(caSheetSound.GetGridCell(iSectX: 0, iSectXMax: 2, iSectY: 2, iSectYMax: 5, iSectSizeX: iGridWdt, iSectSizeY: iGridHgt, fCenterPos: false, iSectNumX: 2, iSectNumY: 3));
962 pGroupVolume->SetTitle(LoadResStrNoAmp(id: C4ResStrTableKey::IDS_BTN_VOLUME).c_str());
963 pGroupVolume->SetFont(pUseFont);
964 pGroupVolume->SetColors(dwFrameClr: C4StartupEditBorderColor, dwTitleClr: C4StartupFontClr);
965 pSheetSound->AddElement(pChild: pGroupVolume);
966 C4GUI::ComponentAligner caGroupVolume(pGroupVolume->GetClientRect(), iIndentX1, iIndentY2, true);
967 // volume sliders
968 int32_t i;
969 for (i = 0; i < 2; ++i)
970 {
971 C4GUI::ComponentAligner caVolumeSlider(caGroupVolume.GetGridCell(iSectX: 0, iSectXMax: 1, iSectY: i, iSectYMax: 2, iSectSizeX: -1, iSectSizeY: pUseFont->GetLineHeight() + iIndentY2 * 2 + C4GUI_ScrollBarHgt, fCenterPos: true), 1, 0, false);
972 pGroupVolume->AddElement(pChild: new C4GUI::Label(std::format(fmt: "{}:", args: LoadResStrChoice(condition: i, ifTrue: C4ResStrTableKey::IDS_CTL_SOUNDFX, ifFalse: C4ResStrTableKey::IDS_CTL_MUSIC)).c_str(), caVolumeSlider.GetFromTop(iHgt: pUseFont->GetLineHeight()), ALeft, C4StartupFontClr, pUseFont, false, false));
973 sLabelTxt.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_CTL_SILENT));
974 w = 20; q = 12; pUseFont->GetTextExtent(szText: sLabelTxt.getData(), rsx&: w, rsy&: q, fCheckMarkup: true);
975 pGroupVolume->AddElement(pChild: new C4GUI::Label(sLabelTxt.getData(), caVolumeSlider.GetFromLeft(iWdt: w, iHgt: q), ACenter, C4StartupFontClr, pUseFont, false, false));
976 sLabelTxt.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_CTL_LOUD));
977 pUseFont->GetTextExtent(szText: sLabelTxt.getData(), rsx&: w, rsy&: q, fCheckMarkup: true);
978 pGroupVolume->AddElement(pChild: new C4GUI::Label(sLabelTxt.getData(), caVolumeSlider.GetFromRight(iWdt: w, iHgt: q), ACenter, C4StartupFontClr, pUseFont, false, false));
979 C4GUI::ParCallbackHandler<C4StartupOptionsDlg, int32_t> *pCB = new C4GUI::ParCallbackHandler<C4StartupOptionsDlg, int32_t>(this, i ? &C4StartupOptionsDlg::OnSoundVolumeSliderChange : &C4StartupOptionsDlg::OnMusicVolumeSliderChange);
980 C4GUI::ScrollBar *pSlider = new C4GUI::ScrollBar(caVolumeSlider.GetCentered(iWdt: caVolumeSlider.GetInnerWidth(), C4GUI_ScrollBarHgt), true, pCB, 101);
981 pSlider->SetDecoration(pToGfx: &C4Startup::Get()->Graphics.sfctBookScroll, fAutoHide: false);
982 pSlider->SetToolTip(i ? LoadResStr(id: C4ResStrTableKey::IDS_DESC_VOLUMESOUND) : LoadResStr(id: C4ResStrTableKey::IDS_DESC_VOLUMEMUSIC));
983 pSlider->SetScrollPos(i ? Config.Sound.SoundVolume : Config.Sound.MusicVolume);
984 pGroupVolume->AddElement(pChild: pSlider);
985 }
986
987 // page keyboard controls
988 pSheetKeyboard->AddElement(pChild: new ControlConfigArea(pSheetKeyboard->GetClientRect(), caMain.GetWidth() / 20, caMain.GetHeight() / 40, false, this));
989
990 // page gamepad
991 pSheetGamepad->AddElement(pChild: new ControlConfigArea(pSheetGamepad->GetClientRect(), caMain.GetWidth() / 20, caMain.GetHeight() / 40, true, this));
992
993 // page network
994 C4GUI::ComponentAligner caSheetNetwork(pSheetNetwork->GetClientRect(), caMain.GetWidth() / 20, caMain.GetHeight() / 20, true);
995 int iPortCfgWdt = 200, iPortCfgHgt = 48; NetworkPortConfig::GetControlSize(piWdt: &iPortCfgWdt, piHgt: &iPortCfgHgt);
996 pPortCfgTCP = new NetworkPortConfig(caSheetNetwork.GetGridCell(iSectX: 0, iSectXMax: 2, iSectY: 0, iSectYMax: 2, iSectSizeX: iPortCfgWdt, iSectSizeY: iPortCfgHgt), LoadResStr(id: C4ResStrTableKey::IDS_NET_PORT_TCP), &(Config.Network.PortTCP), C4NetStdPortTCP);
997 pPortCfgUDP = new NetworkPortConfig(caSheetNetwork.GetGridCell(iSectX: 1, iSectXMax: 2, iSectY: 0, iSectYMax: 2, iSectSizeX: iPortCfgWdt, iSectSizeY: iPortCfgHgt), LoadResStr(id: C4ResStrTableKey::IDS_NET_PORT_UDP), &(Config.Network.PortUDP), C4NetStdPortUDP);
998 pPortCfgRef = new NetworkPortConfig(caSheetNetwork.GetGridCell(iSectX: 0, iSectXMax: 2, iSectY: 1, iSectYMax: 2, iSectSizeX: iPortCfgWdt, iSectSizeY: iPortCfgHgt), LoadResStr(id: C4ResStrTableKey::IDS_NET_PORT_REFERENCE), &(Config.Network.PortRefServer), C4NetStdPortRefServer);
999 pPortCfgDsc = new NetworkPortConfig(caSheetNetwork.GetGridCell(iSectX: 1, iSectXMax: 2, iSectY: 1, iSectYMax: 2, iSectSizeX: iPortCfgWdt, iSectSizeY: iPortCfgHgt), LoadResStr(id: C4ResStrTableKey::IDS_NET_PORT_DISCOVERY), &(Config.Network.PortDiscovery), C4NetStdPortDiscovery);
1000 pPortCfgTCP->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_NET_PORT_TCP_DESC));
1001 pPortCfgUDP->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_NET_PORT_UDP_DESC));
1002 pPortCfgRef->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_NET_PORT_REFERENCE_DESC));
1003 pPortCfgDsc->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_NET_PORT_DISCOVERY_DESC));
1004 pSheetNetwork->AddElement(pChild: pPortCfgTCP);
1005 pSheetNetwork->AddElement(pChild: pPortCfgUDP);
1006 pSheetNetwork->AddElement(pChild: pPortCfgRef);
1007 pSheetNetwork->AddElement(pChild: pPortCfgDsc);
1008 int iNetHgt0 = pPortCfgDsc->GetBounds().GetBottom();
1009 caSheetNetwork.ExpandTop(iByHgt: -iNetHgt0);
1010 int iServerCfgWdt = 120, iServerCfgHgt = 20, iServerCfgWdtMid = 0;
1011 StdStrBuf sServerText; sServerText.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_CTL_USEOTHERSERVER));
1012 NetworkServerAddressConfig::GetControlSize(piWdt: &iServerCfgWdt, piHgt: &iServerCfgHgt, piTabPos: &iServerCfgWdtMid, szForText: sServerText.getData());
1013 pLeagueServerCfg = new NetworkServerAddressConfig(caSheetNetwork.GetFromTop(iHgt: iServerCfgHgt), sServerText.getData(), &(Config.Network.UseAlternateServer), Config.Network.AlternateServerAddress, iServerCfgWdtMid);
1014 pLeagueServerCfg->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_NET_MASTERSRV_DESC));
1015 pSheetNetwork->AddElement(pChild: pLeagueServerCfg);
1016 pCheck = new BoolConfig(caSheetNetwork.GetFromTop(iHgt: pUseFont->GetLineHeight()), LoadResStr(id: C4ResStrTableKey::IDS_CTL_AUTOMATICUPDATES), &Config.Network.AutomaticUpdate, false);
1017 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_AUTOMATICUPDATES));
1018 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
1019 pSheetNetwork->AddElement(pChild: pCheck);
1020 // UPnP
1021 pCheck = new BoolConfig{caSheetNetwork.GetFromTop(iHgt: pUseFont->GetLineHeight()), LoadResStr(id: C4ResStrTableKey::IDS_CTL_UPNP), &Config.Network.EnableUPnP, false};
1022 pCheck->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_UPNP));
1023 pCheck->SetFont(pFont: pUseFont, dwEnabledClr: C4StartupFontClr, dwDisabledClr: C4StartupFontClrDisabled);
1024 pSheetNetwork->AddElement(pChild: pCheck);
1025 const char *szNameCfgText = LoadResStr(id: C4ResStrTableKey::IDS_NET_COMPUTERNAME);
1026 int iNameCfgWdt = 200, iNameCfgHgt = 48; C4StartupOptionsDlg::EditConfig::GetControlSize(piWdt: &iNameCfgWdt, piHgt: &iNameCfgHgt, szForText: szNameCfgText, fMultiline: false);
1027 iNameCfgWdt += 5;
1028 pNetworkNameEdit = new EditConfig(caSheetNetwork.GetGridCell(iSectX: 0, iSectXMax: 2, iSectY: 0, iSectYMax: 1, iSectSizeX: iNameCfgWdt, iSectSizeY: iNameCfgHgt), szNameCfgText, &Config.Network.LocalName, nullptr, false);
1029 pNetworkNickEdit = new EditConfig(caSheetNetwork.GetGridCell(iSectX: 1, iSectXMax: 2, iSectY: 0, iSectYMax: 1, iSectSizeX: iNameCfgWdt, iSectSizeY: iNameCfgHgt), LoadResStr(id: C4ResStrTableKey::IDS_NET_USERNAME), &Config.Network.Nick, nullptr, false);
1030 pNetworkNameEdit->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_NET_COMPUTERNAME_DESC));
1031 pNetworkNickEdit->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_NET_USERNAME_DESC));
1032 pSheetNetwork->AddElement(pChild: pNetworkNameEdit);
1033 pSheetNetwork->AddElement(pChild: pNetworkNickEdit);
1034 StdStrBuf NickBuf(Config.Network.Nick);
1035 if (!NickBuf.getLength()) NickBuf.Copy(Buf2: Config.Network.LocalName);
1036 pNetworkNickEdit->GetEdit()->SetText(text: NickBuf.getData(), fUser: false);
1037
1038 // initial focus is on tab selection
1039 SetFocus(pCtrl: pOptionsTabular, fByMouse: false);
1040}
1041
1042C4StartupOptionsDlg::~C4StartupOptionsDlg()
1043{
1044 delete pKeyToggleMusic;
1045 delete pKeyBack;
1046}
1047
1048void C4StartupOptionsDlg::OnClosed(bool fOK)
1049{
1050 // callback when dlg got closed - save config
1051 SaveConfig(fForce: true, fKeepOpen: false);
1052 C4StartupDlg::OnClosed(fOK);
1053}
1054
1055int32_t C4StartupOptionsDlg::FairCrewSlider2Strength(int32_t iSliderVal)
1056{
1057 // slider linear in rank to value linear in exp
1058 return int32_t(pow(x: double(iSliderVal) / 9.5, y: 1.5) * 1000.0);
1059}
1060
1061int32_t C4StartupOptionsDlg::FairCrewStrength2Slider(int32_t iStrengthVal)
1062{
1063 // value linear in exp to slider linear in rank
1064 return int32_t(pow(x: double(iStrengthVal) / 1000.0, y: 1.0 / 1.5) * 9.5);
1065}
1066
1067void C4StartupOptionsDlg::OnFairCrewStrengthSliderChange(int32_t iNewVal)
1068{
1069 // fair crew strength determined by exponential fn
1070 Config.General.FairCrewStrength = FairCrewSlider2Strength(iSliderVal: iNewVal);
1071}
1072
1073void C4StartupOptionsDlg::OnResetConfigBtn(C4GUI::Control *btn)
1074{
1075 // confirmation
1076 StdStrBuf sWarningText; sWarningText.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_MSG_PROMPTRESETCONFIG));
1077 sWarningText.AppendChar(cChar: '|');
1078 sWarningText.Append(pnData: LoadResStr(id: C4ResStrTableKey::IDS_MSG_RESTARTCHANGECFG));
1079 if (!GetScreen()->ShowMessageModal(szMessage: sWarningText.getData(), szCaption: LoadResStr(id: C4ResStrTableKey::IDS_BTN_RESETCONFIG), dwButtons: C4GUI::MessageDialog::btnYesNo, icoIcon: C4GUI::Ico_Notify))
1080 // user cancelled
1081 return;
1082 // reset cfg
1083 Config.Default();
1084 Config.fConfigLoaded = true;
1085 // engine must be restarted now, because some crucial fields such as resolution and used gfx engine do not match their initialization
1086 Application.Quit();
1087}
1088
1089void C4StartupOptionsDlg::OnAdvancedConfigBtn(C4GUI::Control *)
1090{
1091 if (C4StartupOptionsAdvancedConfigDialog::ShowModal(screen: Game.pGUI))
1092 {
1093 RecreateDialog(fFade: false);
1094 }
1095}
1096
1097void C4StartupOptionsDlg::OnScaleSliderChanged(int32_t val)
1098{
1099 iNewScale = val + minScale;
1100 pScaleEdit->SetValue(value: iNewScale, user: true);
1101}
1102
1103void C4StartupOptionsDlg::OnTestScaleBtn(C4GUI::Control *)
1104{
1105 const auto oldScaleInt = Config.Graphics.Scale;
1106 const auto oldScale = Application.GetScale();
1107 if (iNewScale == oldScaleInt) return;
1108
1109 const auto oldResX = Config.Graphics.ResX;
1110 const auto oldResY = Config.Graphics.ResY;
1111
1112 Config.Graphics.Scale = iNewScale;
1113 const auto realResX = static_cast<int32_t>(floorf(x: oldResX * oldScale));
1114 const auto realResY = static_cast<int32_t>(floorf(x: oldResY * oldScale));
1115
1116 Application.SetResolution(iNewResX: realResX, iNewResY: realResY);
1117 Application.SetGameFont(szFontFace: Config.General.RXFontName, iFontSize: Config.General.RXFontSize);
1118
1119 // do it manually and temporarily here, beacuse RecreateDialog would delete this the second time which leads to a crash after this function returns
1120 FadeOut(fCloseWithOK: true);
1121
1122 int32_t iPage = pOptionsTabular->GetActiveSheetIndex();
1123 std::unique_ptr<C4StartupOptionsDlg> pNewDlg{new C4StartupOptionsDlg};
1124 pNewDlg->FadeIn(pOnScreen: Game.pGUI);
1125 pNewDlg->pOptionsTabular->SelectSheet(iIndex: iPage, fByUser: false);
1126 pNewDlg->fCanGoBack = false;
1127
1128 ResChangeConfirmDlg *pConfirmDlg = new ResChangeConfirmDlg;
1129
1130 if (!GetScreen()->ShowModalDlg(pDlg: pConfirmDlg, fDestruct: true))
1131 {
1132 Config.Graphics.Scale = oldScaleInt;
1133
1134 if (C4GUI::IsGUIValid())
1135 {
1136 Application.SetResolution(iNewResX: realResX, iNewResY: realResY);
1137 Application.SetGameFont(szFontFace: Config.General.RXFontName, iFontSize: Config.General.RXFontSize);
1138 }
1139 else
1140 {
1141 // make sure config is restored even if the program is closed during the confirmation dialog
1142 Config.Graphics.ResX = oldResX;
1143 Config.Graphics.ResY = oldResY;
1144 }
1145 }
1146
1147 RecreateDialog(fFade: false);
1148}
1149
1150bool C4StartupOptionsDlg::SaveConfig(bool fForce, bool fKeepOpen)
1151{
1152 // prevent double save
1153 if (fConfigSaved) return true;
1154 // store some config values
1155 SaveGfxTroubleshoot();
1156 // save any config fields that are not stored directly; return whether all values are OK
1157 // check port validity
1158 if (!fForce)
1159 {
1160 StdStrBuf strError(LoadResStr(id: C4ResStrTableKey::IDS_ERR_CONFIG));
1161 if (pPortCfgTCP->GetPort() > 0 && pPortCfgTCP->GetPort() == pPortCfgRef->GetPort())
1162 {
1163 GetScreen()->ShowMessage(szMessage: LoadResStr(id: C4ResStrTableKey::IDS_NET_ERR_PORT_TCPREF), szCaption: strError.getData(), icoIcon: C4GUI::Ico_Error);
1164 return false;
1165 }
1166 if (pPortCfgUDP->GetPort() > 0 && pPortCfgUDP->GetPort() == pPortCfgDsc->GetPort())
1167 {
1168 GetScreen()->ShowMessage(szMessage: LoadResStr(id: C4ResStrTableKey::IDS_NET_ERR_PORT_UDPDISC), szCaption: strError.getData(), icoIcon: C4GUI::Ico_Error);
1169 return false;
1170 }
1171 }
1172 pPortCfgTCP->SavePort();
1173 pPortCfgUDP->SavePort();
1174 pPortCfgRef->SavePort();
1175 pPortCfgDsc->SavePort();
1176 pLeagueServerCfg->Save2Config();
1177 pNetworkNameEdit->Save2Config();
1178 pNetworkNickEdit->Save2Config();
1179 // if nick is same as LocalName, don't save in config
1180 // so LocalName updates will change the nick as well
1181 if (SEqual(szStr1: Config.Network.Nick.getData(), szStr2: Config.Network.LocalName.getData())) Config.Network.Nick.Clear();
1182 // make sure config is saved, in case the game crashes later on or another instance is started
1183 Config.Save();
1184 if (!fKeepOpen) fConfigSaved = true;
1185 // done; config OK
1186 return true;
1187}
1188
1189void C4StartupOptionsDlg::DoBack()
1190{
1191 if (!SaveConfig(fForce: false, fKeepOpen: false)) return;
1192 // back 2 main
1193 C4Startup::Get()->SwitchDialog(eToDlg: fCanGoBack ? (C4Startup::SDID_Back) : (C4Startup::SDID_Main));
1194}
1195
1196void C4StartupOptionsDlg::UpdateLanguage()
1197{
1198 // find currently specified language in language list and display its info
1199 const C4LanguageInfo *const info{Languages.FindInfo(code: Config.General.Language)};
1200 if (info)
1201 {
1202 pLangCombo->SetText(std::format(fmt: "{}{} - {}", args: info->Code[0], args: info->Code[1], args: info->Name.c_str()).c_str());
1203 pLangInfoLabel->SetText(toText: info->Info);
1204 }
1205 else
1206 {
1207 pLangCombo->SetText(std::format(fmt: "unknown ({})", args: +Config.General.Language).c_str());
1208 pLangInfoLabel->SetText(szText: LoadResStr(id: C4ResStrTableKey::IDS_CTL_NOLANGINFO));
1209 return; // no need to mess with fallbacks
1210 }
1211 // update language fallbacks
1212 char *szLang = Config.General.LanguageEx;
1213 szLang[0] = info->Code[0];
1214 szLang[1] = info->Code[1];
1215 szLang[2] = '\0';
1216 if (!info->Fallback.empty())
1217 {
1218 SAppend(szSource: ",", szTarget: szLang);
1219 Config.General.GetLanguageSequence(strSource: info->Fallback.c_str(), strTarget: szLang + SLen(sptr: szLang));
1220 }
1221 // internal fallbacks
1222 if (!SSearch(szString: Config.General.LanguageEx, szIndex: "US"))
1223 {
1224 if (*szLang) SAppendChar(cChar: ',', szStr: szLang);
1225 SAppend(szSource: "US", szTarget: szLang);
1226 }
1227 if (!SSearch(szString: Config.General.LanguageEx, szIndex: "DE"))
1228 {
1229 if (*szLang) SAppendChar(cChar: ',', szStr: szLang);
1230 SAppend(szSource: "DE", szTarget: szLang);
1231 }
1232}
1233
1234void C4StartupOptionsDlg::OnLangComboFill(C4GUI::ComboBox_FillCB *pFiller)
1235{
1236 // fill with all possible languages
1237 for (const auto &info : Languages)
1238 {
1239 pFiller->AddEntry(szText: std::format(fmt: "{}{} - {}", args: info.Code[0], args: info.Code[1], args: info.Name.c_str()).c_str(), id: static_cast<unsigned char>(info.Code[0]) + (static_cast<unsigned char>(info.Code[1]) << 8));
1240 }
1241}
1242
1243bool C4StartupOptionsDlg::OnLangComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection)
1244{
1245 // set new language by two-character-code
1246 Config.General.Language[0] = idNewSelection & 0xff;
1247 Config.General.Language[1] = (idNewSelection & 0xff00) >> 8;
1248 Config.General.Language[2] = '\0';
1249 UpdateLanguage();
1250 Languages.LoadLanguage(strLanguages: Config.General.LanguageEx);
1251 // recreate everything to reflect language changes
1252 RecreateDialog(fFade: true);
1253 return true;
1254}
1255
1256void C4StartupOptionsDlg::UpdateFontControls()
1257{
1258 // display current language and size in comboboxes
1259 pFontFaceCombo->SetText(Config.General.RXFontName);
1260 pFontSizeCombo->SetText(std::to_string(val: Config.General.RXFontSize).c_str());
1261}
1262
1263const char *DefaultFonts[] = { "Arial Unicode MS", "Comic Sans MS", "Endeavour", "Verdana", nullptr };
1264
1265void C4StartupOptionsDlg::OnFontFaceComboFill(C4GUI::ComboBox_FillCB *pFiller)
1266{
1267 // 2do: enumerate Fonts.txt fonts; then enumerate system fonts
1268 for (int32_t i = 0; DefaultFonts[i]; ++i) pFiller->AddEntry(szText: DefaultFonts[i], id: i);
1269}
1270
1271void C4StartupOptionsDlg::OnFontSizeComboFill(C4GUI::ComboBox_FillCB *pFiller)
1272{
1273 // 2do: enumerate possible font sizes by the font here
1274 // 2do: Hide font sizes that would be too large for the selected resolution
1275 pFiller->AddEntry(szText: "8", id: 8);
1276 pFiller->AddEntry(szText: "10", id: 10);
1277 pFiller->AddEntry(szText: "12", id: 12);
1278 pFiller->AddEntry(szText: "14", id: 14);
1279 pFiller->AddEntry(szText: "16", id: 16);
1280 pFiller->AddEntry(szText: "18", id: 18);
1281 pFiller->AddEntry(szText: "20", id: 20);
1282 pFiller->AddEntry(szText: "24", id: 24);
1283 pFiller->AddEntry(szText: "28", id: 28);
1284}
1285
1286bool C4StartupOptionsDlg::OnFontComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection)
1287{
1288 // set new value
1289 const char *szNewFontFace = Config.General.RXFontName;
1290 int32_t iNewFontSize = Config.General.RXFontSize;
1291 if (pForCombo == pFontFaceCombo)
1292 szNewFontFace = DefaultFonts[idNewSelection];
1293 else if (pForCombo == pFontSizeCombo)
1294 iNewFontSize = idNewSelection;
1295 else
1296 // can't happen
1297 return true;
1298 // set new fonts
1299 if (!Application.SetGameFont(szFontFace: szNewFontFace, iFontSize: iNewFontSize))
1300 {
1301 GetScreen()->ShowErrorMessage(szMessage: LoadResStr(id: C4ResStrTableKey::IDS_ERR_INITFONTS));
1302 return true;
1303 }
1304 // recreate everything to reflect font changes
1305 RecreateDialog(fFade: true);
1306 return true;
1307}
1308
1309void C4StartupOptionsDlg::OnDisplayModeComboFill(C4GUI::ComboBox_FillCB *pFiller)
1310{
1311 for (const auto &mode : DisplayModes)
1312 {
1313 pFiller->AddEntry(szText: LoadResStr(id: mode.caption), id: static_cast<int>(mode.mode));
1314 }
1315}
1316
1317bool C4StartupOptionsDlg::OnDisplayModeComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection)
1318{
1319 Config.Graphics.UseDisplayMode = static_cast<DisplayMode>(idNewSelection);
1320 Application.SetDisplayMode(Config.Graphics.UseDisplayMode);
1321 RecreateDialog(fFade: true);
1322 return true;
1323}
1324
1325void C4StartupOptionsDlg::RecreateDialog(bool fFade)
1326{
1327 // MUST fade for now, or calling function will fail because dialog is deleted immediately
1328 fFade = true;
1329 // this actually breaks the possibility to go back :(
1330 int32_t iPage = pOptionsTabular->GetActiveSheetIndex();
1331 C4StartupOptionsDlg *pNewDlg = static_cast<C4StartupOptionsDlg *>(C4Startup::Get()->SwitchDialog(eToDlg: C4Startup::SDID_Options, fFade));
1332 pNewDlg->pOptionsTabular->SelectSheet(iIndex: iPage, fByUser: false);
1333 pNewDlg->fCanGoBack = false;
1334}
1335
1336void C4StartupOptionsDlg::SaveGfxTroubleshoot()
1337{
1338 lpDDraw->InvalidateDeviceObjects();
1339 lpDDraw->RestoreDeviceObjects();
1340}
1341
1342void C4StartupOptionsDlg::OnEffectsSliderChange(int32_t iNewVal)
1343{
1344 Config.Graphics.SmokeLevel = iNewVal;
1345}
1346
1347void C4StartupOptionsDlg::OnFEMusicCheck(C4GUI::Element *pCheckBox)
1348{
1349 // option change is reflected immediately
1350 bool fIsOn = static_cast<C4GUI::CheckBox *>(pCheckBox)->GetChecked();
1351 if (Config.Sound.FEMusic = fIsOn)
1352 {
1353 Application.MusicSystem->PlayFrontendMusic();
1354 }
1355 else
1356 {
1357 Application.MusicSystem->Stop();
1358 }
1359}
1360
1361void C4StartupOptionsDlg::OnMusicVolumeSliderChange(int32_t iNewVal)
1362{
1363 // option change is reflected immediately;
1364 Config.Sound.MusicVolume = iNewVal;
1365 Application.MusicSystem->UpdateVolume();
1366}
1367
1368void C4StartupOptionsDlg::OnSoundVolumeSliderChange(int32_t iNewVal)
1369{
1370 // sound system reads this value directly
1371 Config.Sound.SoundVolume = iNewVal;
1372 // test sound
1373 StartSoundEffect(name: "ArrowHit", loop: false, volume: 100, obj: nullptr);
1374}
1375
1376bool C4StartupOptionsDlg::KeyMusicToggle()
1377{
1378 // do toggle and reflect in checkbox
1379 pFEMusicCheck->SetChecked(Application.MusicSystem->ToggleOnOff());
1380 // key processed
1381 return true;
1382}
1383
1384int32_t C4StartupOptionsDlg::EditConfig::GetIntVal()
1385{
1386 return atoi(nptr: GetEdit()->GetText());
1387}
1388
1389void C4StartupOptionsDlg::EditConfig::SetIntVal(int32_t iToVal)
1390{
1391 GetEdit()->SetText(text: std::format(fmt: "{}", args&: iToVal).c_str(), fUser: false);
1392}
1393