1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "gui/editgamedialog.h"
24 
25 #include "backends/keymapper/keymapper.h"
26 
27 #include "common/config-manager.h"
28 #include "common/gui_options.h"
29 #include "common/translation.h"
30 #include "common/system.h"
31 
32 #include "gui/browser.h"
33 #include "gui/gui-manager.h"
34 #include "gui/message.h"
35 #ifdef ENABLE_EVENTRECORDER
36 #include "gui/onscreendialog.h"
37 #include "gui/recorderdialog.h"
38 #include "gui/EventRecorder.h"
39 #endif
40 #include "gui/widgets/edittext.h"
41 #include "gui/widgets/tab.h"
42 #include "gui/widgets/popup.h"
43 #include "gui/widgets/scrollcontainer.h"
44 
45 #if defined(USE_CLOUD) && defined(USE_LIBCURL)
46 #include "backends/cloud/cloudmanager.h"
47 #endif
48 
49 using Common::ConfigManager;
50 
51 namespace GUI {
52 
53 enum {
54 	kStartCmd = 'STRT',
55 	kAboutCmd = 'ABOU',
56 	kOptionsCmd = 'OPTN',
57 	kAddGameCmd = 'ADDG',
58 	kEditGameCmd = 'EDTG',
59 	kRemoveGameCmd = 'REMG',
60 	kLoadGameCmd = 'LOAD',
61 	kQuitCmd = 'QUIT',
62 	kSearchCmd = 'SRCH',
63 	kListSearchCmd = 'LSSR',
64 	kSearchClearCmd = 'SRCL',
65 
66 	kCmdGlobalGraphicsOverride = 'OGFX',
67 	kCmdGlobalShaderOverride = 'OSHD',
68 	kCmdGlobalBackendOverride = 'OBAK',
69 	kCmdGlobalAudioOverride = 'OSFX',
70 	kCmdGlobalMIDIOverride = 'OMID',
71 	kCmdGlobalMT32Override = 'OM32',
72 	kCmdGlobalVolumeOverride = 'OVOL',
73 
74 	kCmdChooseSoundFontCmd = 'chsf',
75 
76 	kCmdExtraBrowser = 'PEXT',
77 	kCmdExtraPathClear = 'PEXC',
78 	kCmdGameBrowser = 'PGME',
79 	kCmdSaveBrowser = 'PSAV',
80 	kCmdSavePathClear = 'PSAC',
81 
82 	kGraphicsTabContainerReflowCmd = 'gtcr'
83 };
84 
85 /*
86 * TODO: Clean up this ugly design: we subclass EditTextWidget to perform
87 * input validation. It would be much more elegant to use a decorator pattern,
88 * or a validation callback, or something like that.
89 */
90 class DomainEditTextWidget : public EditTextWidget {
91 public:
DomainEditTextWidget(GuiObject * boss,const String & name,const U32String & text,const U32String & tooltip=U32String ())92 	DomainEditTextWidget(GuiObject *boss, const String &name, const U32String &text, const U32String &tooltip = U32String())
93 		: EditTextWidget(boss, name, text, tooltip) {}
94 
95 protected:
tryInsertChar(byte c,int pos)96 	bool tryInsertChar(byte c, int pos) override {
97 		if (Common::isAlnum(c) || c == '-' || c == '_') {
98 			_editString.insertChar(c, pos);
99 			return true;
100 		}
101 		return false;
102 	}
103 };
104 
EditGameDialog(const String & domain)105 EditGameDialog::EditGameDialog(const String &domain)
106 	: OptionsDialog(domain, "GameOptions") {
107 	EngineMan.upgradeTargetIfNecessary(domain);
108 
109 	_engineOptions = nullptr;
110 
111 	// Retrieve the plugin, since we need to access the engine's MetaEngine
112 	// implementation.
113 	const Plugin *metaEnginePlugin = nullptr;
114 	const Plugin *enginePlugin = nullptr;
115 	QualifiedGameDescriptor qgd = EngineMan.findTarget(domain, &metaEnginePlugin);
116 	if (!metaEnginePlugin) {
117 		warning("MetaEnginePlugin for target \"%s\" not found!", domain.c_str());
118 	} else {
119 		enginePlugin = PluginMan.getEngineFromMetaEngine(metaEnginePlugin);
120 		if (!enginePlugin) {
121 			warning("Engine Plugin for target \"%s\" not found! Game specific settings might be missing.", domain.c_str());
122 		}
123 	}
124 
125 	// GAME: Path to game data (r/o), extra data (r/o), and save data (r/w)
126 	String gamePath(ConfMan.get("path", _domain));
127 	String extraPath(ConfMan.get("extrapath", _domain));
128 	String savePath(ConfMan.get("savepath", _domain));
129 
130 	// GAME: Determine the description string
131 	String description(ConfMan.get("description", domain));
132 	if (description.empty() && !qgd.description.empty()) {
133 		description = qgd.description;
134 	}
135 
136 	// GUI:  Add tab widget
137 	TabWidget *tab = new TabWidget(this, "GameOptions.TabWidget");
138 
139 	//
140 	// 1) The game tab
141 	//
142 	tab->addTab(_("Game"), "GameOptions_Game");
143 
144 	// GUI:  Label & edit widget for the game ID
145 	if (g_system->getOverlayWidth() > 320)
146 		new StaticTextWidget(tab, "GameOptions_Game.Id", _("ID:"), _("Short game identifier used for referring to saved games and running the game from the command line"));
147 	else
148 		new StaticTextWidget(tab, "GameOptions_Game.Id", _c("ID:", "lowres"), _("Short game identifier used for referring to saved games and running the game from the command line"));
149 	_domainWidget = new DomainEditTextWidget(tab, "GameOptions_Game.Domain", _domain, _("Short game identifier used for referring to saved games and running the game from the command line"));
150 
151 	// GUI:  Label & edit widget for the description
152 	if (g_system->getOverlayWidth() > 320)
153 		new StaticTextWidget(tab, "GameOptions_Game.Name", _("Name:"), _("Full title of the game"));
154 	else
155 		new StaticTextWidget(tab, "GameOptions_Game.Name", _c("Name:", "lowres"), _("Full title of the game"));
156 	_descriptionWidget = new EditTextWidget(tab, "GameOptions_Game.Desc", description, _("Full title of the game"));
157 
158 	// Language popup
159 	_langPopUpDesc = nullptr;
160 	_langPopUp = nullptr;
161 	if (!_guioptions.contains(GUIO_NOLANG)) {
162 		_langPopUpDesc = new StaticTextWidget(tab, "GameOptions_Game.LangPopupDesc", _("Language:"), _("Language of the game. This will not turn your Spanish game version into English"));
163 		_langPopUp = new PopUpWidget(tab, "GameOptions_Game.LangPopup", _("Language of the game. This will not turn your Spanish game version into English"));
164 		_langPopUp->appendEntry(_("<default>"), (uint32)Common::UNK_LANG);
165 		_langPopUp->appendEntry("", (uint32)Common::UNK_LANG);
166 		const Common::LanguageDescription *l = Common::g_languages;
167 		for (; l->code; ++l) {
168 			if (checkGameGUIOptionLanguage(l->id, _guioptionsString))
169 				_langPopUp->appendEntry(l->description, l->id);
170 		}
171 	}
172 
173 	// Platform popup
174 	if (g_system->getOverlayWidth() > 320)
175 		_platformPopUpDesc = new StaticTextWidget(tab, "GameOptions_Game.PlatformPopupDesc", _("Platform:"), _("Platform the game was originally designed for"));
176 	else
177 		_platformPopUpDesc = new StaticTextWidget(tab, "GameOptions_Game.PlatformPopupDesc", _c("Platform:", "lowres"), _("Platform the game was originally designed for"));
178 	_platformPopUp = new PopUpWidget(tab, "GameOptions_Game.PlatformPopup", _("Platform the game was originally designed for"));
179 	_platformPopUp->appendEntry(_("<default>"));
180 	_platformPopUp->appendEntry("");
181 	const Common::PlatformDescription *p = Common::g_platforms;
182 	for (; p->code; ++p) {
183 		_platformPopUp->appendEntry(p->description, p->id);
184 	}
185 
186 	//
187 	// 2) The engine tab (shown only if the engine implements one or there are custom engine options)
188 	//
189 
190 	if (metaEnginePlugin) {
191 		int tabId = tab->addTab(_("Engine"), "GameOptions_Engine");
192 
193 		const MetaEngineDetection &metaEngineDetection = metaEnginePlugin->get<MetaEngineDetection>();
194 		metaEngineDetection.registerDefaultSettings(_domain);
195 		if (enginePlugin)
196 			_engineOptions = enginePlugin->get<MetaEngine>().buildEngineOptionsWidgetDynamic(tab, "GameOptions_Engine.Container", _domain);
197 		if (!_engineOptions)
198 			_engineOptions = metaEngineDetection.buildEngineOptionsWidgetStatic(tab, "GameOptions_Engine.Container", _domain);
199 
200 		if (_engineOptions) {
201 			_engineOptions->setParentDialog(this);
202 		} else {
203 			tab->removeTab(tabId);
204 		}
205 	}
206 
207 	//
208 	// 3) The graphics tab
209 	//
210 	_graphicsTabId = tab->addTab(g_system->getOverlayWidth() > 320 ? _("Graphics") : _("GFX"), "GameOptions_Graphics");
211 	ScrollContainerWidget *graphicsContainer = new ScrollContainerWidget(tab, "GameOptions_Graphics.Container", "GameOptions_Graphics_Container", kGraphicsTabContainerReflowCmd);
212 	graphicsContainer->setBackgroundType(ThemeEngine::kWidgetBackgroundNo);
213 	graphicsContainer->setTarget(this);
214 
215 	if (g_system->getOverlayWidth() > 320)
216 		_globalGraphicsOverride = new CheckboxWidget(graphicsContainer, "GameOptions_Graphics_Container.EnableTabCheckbox", _("Override global graphic settings"), Common::U32String(), kCmdGlobalGraphicsOverride);
217 	else
218 		_globalGraphicsOverride = new CheckboxWidget(graphicsContainer, "GameOptions_Graphics_Container.EnableTabCheckbox", _c("Override global graphic settings", "lowres"), Common::U32String(), kCmdGlobalGraphicsOverride);
219 
220 	addGraphicControls(graphicsContainer, "GameOptions_Graphics_Container.");
221 
222 	//
223 	// The shader tab (currently visible only for Vita platform), visibility checking by features
224 	//
225 
226 	_globalShaderOverride = nullptr;
227 	if (g_system->hasFeature(OSystem::kFeatureShader)) {
228 		tab->addTab(_("Shader"), "GameOptions_Shader");
229 
230 		if (g_system->getOverlayWidth() > 320)
231 			_globalShaderOverride = new CheckboxWidget(tab, "GameOptions_Shader.EnableTabCheckbox", _("Override global shader settings"), Common::U32String(), kCmdGlobalShaderOverride);
232 		else
233 			_globalShaderOverride = new CheckboxWidget(tab, "GameOptions_Shader.EnableTabCheckbox", _c("Override global shader settings", "lowres"), Common::U32String(), kCmdGlobalShaderOverride);
234 
235 		addShaderControls(tab, "GameOptions_Shader.");
236 	}
237 
238 	//
239 	// The Keymap tab
240 	//
241 	Common::KeymapArray keymaps;
242 	if (enginePlugin) {
243 		keymaps = enginePlugin->get<MetaEngine>().initKeymaps(domain.c_str());
244 	}
245 
246 	if (!keymaps.empty()) {
247 		tab->addTab(_("Keymaps"), "GameOptions_KeyMapper");
248 		addKeyMapperControls(tab, "GameOptions_KeyMapper.", keymaps, domain);
249 	}
250 
251 	//
252 	// The backend tab (shown only if the backend implements one)
253 	//
254 	int backendTabId = tab->addTab(_("Backend"), "GameOptions_Backend");
255 
256 	if (g_system->getOverlayWidth() > 320)
257 		_globalBackendOverride = new CheckboxWidget(tab, "GameOptions_Backend.EnableTabCheckbox", _("Override global backend settings"), Common::U32String(), kCmdGlobalBackendOverride);
258 	else
259 		_globalBackendOverride = new CheckboxWidget(tab, "GameOptions_Backend.EnableTabCheckbox", _c("Override global backend settings", "lowres"), Common::U32String(), kCmdGlobalBackendOverride);
260 
261 	g_system->registerDefaultSettings(_domain);
262 	_backendOptions = g_system->buildBackendOptionsWidget(tab, "GameOptions_Backend.Container", _domain);
263 
264 	if (_backendOptions) {
265 		_backendOptions->setParentDialog(this);
266 	} else {
267 		tab->removeTab(backendTabId);
268 	}
269 
270 	//
271 	// 4) The audio tab
272 	//
273 	tab->addTab(_("Audio"), "GameOptions_Audio");
274 
275 	if (g_system->getOverlayWidth() > 320)
276 		_globalAudioOverride = new CheckboxWidget(tab, "GameOptions_Audio.EnableTabCheckbox", _("Override global audio settings"), Common::U32String(), kCmdGlobalAudioOverride);
277 	else
278 		_globalAudioOverride = new CheckboxWidget(tab, "GameOptions_Audio.EnableTabCheckbox", _c("Override global audio settings", "lowres"), Common::U32String(), kCmdGlobalAudioOverride);
279 
280 	addAudioControls(tab, "GameOptions_Audio.");
281 	addSubtitleControls(tab, "GameOptions_Audio.");
282 
283 	//
284 	// 5) The volume tab
285 	//
286 	if (g_system->getOverlayWidth() > 320)
287 		tab->addTab(_("Volume"), "GameOptions_Volume");
288 	else
289 		tab->addTab(_c("Volume", "lowres"), "GameOptions_Volume");
290 
291 	if (g_system->getOverlayWidth() > 320)
292 		_globalVolumeOverride = new CheckboxWidget(tab, "GameOptions_Volume.EnableTabCheckbox", _("Override global volume settings"), Common::U32String(), kCmdGlobalVolumeOverride);
293 	else
294 		_globalVolumeOverride = new CheckboxWidget(tab, "GameOptions_Volume.EnableTabCheckbox", _c("Override global volume settings", "lowres"), Common::U32String(), kCmdGlobalVolumeOverride);
295 
296 	addVolumeControls(tab, "GameOptions_Volume.");
297 
298 	bool showMidi = !_guioptions.contains(GUIO_NOMIDI) && !_guioptions.contains(GUIO_NOMUSIC);
299 
300 	//
301 	// 6) The MIDI tab
302 	//
303 	_globalMIDIOverride = nullptr;
304 	if (showMidi) {
305 		tab->addTab(_("MIDI"), "GameOptions_MIDI");
306 
307 		if (g_system->getOverlayWidth() > 320)
308 			_globalMIDIOverride = new CheckboxWidget(tab, "GameOptions_MIDI.EnableTabCheckbox", _("Override global MIDI settings"), Common::U32String(), kCmdGlobalMIDIOverride);
309 		else
310 			_globalMIDIOverride = new CheckboxWidget(tab, "GameOptions_MIDI.EnableTabCheckbox", _c("Override global MIDI settings", "lowres"), Common::U32String(), kCmdGlobalMIDIOverride);
311 
312 		addMIDIControls(tab, "GameOptions_MIDI.");
313 	}
314 
315 	//
316 	// 7) The MT-32 tab
317 	//
318 	_globalMT32Override = nullptr;
319 	if (showMidi) {
320 		tab->addTab(_("MT-32"), "GameOptions_MT32");
321 
322 		if (g_system->getOverlayWidth() > 320)
323 			_globalMT32Override = new CheckboxWidget(tab, "GameOptions_MT32.EnableTabCheckbox", _("Override global MT-32 settings"), Common::U32String(), kCmdGlobalMT32Override);
324 		else
325 			_globalMT32Override = new CheckboxWidget(tab, "GameOptions_MT32.EnableTabCheckbox", _c("Override global MT-32 settings", "lowres"), Common::U32String(), kCmdGlobalMT32Override);
326 
327 		addMT32Controls(tab, "GameOptions_MT32.");
328 	}
329 
330 	//
331 	// 8) The Paths tab
332 	//
333 	if (g_system->getOverlayWidth() > 320)
334 		tab->addTab(_("Paths"), "GameOptions_Paths");
335 	else
336 		tab->addTab(_c("Paths", "lowres"), "GameOptions_Paths");
337 
338 	// These buttons have to be extra wide, or the text will be truncated
339 	// in the small version of the GUI.
340 
341 	// GUI:  Button + Label for the game path
342 	if (g_system->getOverlayWidth() > 320)
343 		new ButtonWidget(tab, "GameOptions_Paths.Gamepath", _("Game Path:"), Common::U32String(), kCmdGameBrowser);
344 	else
345 		new ButtonWidget(tab, "GameOptions_Paths.Gamepath", _c("Game Path:", "lowres"), Common::U32String(), kCmdGameBrowser);
346 	_gamePathWidget = new StaticTextWidget(tab, "GameOptions_Paths.GamepathText", gamePath);
347 
348 	// GUI:  Button + Label for the additional path
349 	if (g_system->getOverlayWidth() > 320)
350 		new ButtonWidget(tab, "GameOptions_Paths.Extrapath", _("Extra Path:"), _("Specifies path to additional data used by the game"), kCmdExtraBrowser);
351 	else
352 		new ButtonWidget(tab, "GameOptions_Paths.Extrapath", _c("Extra Path:", "lowres"), _("Specifies path to additional data used by the game"), kCmdExtraBrowser);
353 	_extraPathWidget = new StaticTextWidget(tab, "GameOptions_Paths.ExtrapathText", extraPath, _("Specifies path to additional data used by the game"));
354 
355 	_extraPathClearButton = addClearButton(tab, "GameOptions_Paths.ExtraPathClearButton", kCmdExtraPathClear);
356 
357 	// GUI:  Button + Label for the save path
358 	if (g_system->getOverlayWidth() > 320)
359 		new ButtonWidget(tab, "GameOptions_Paths.Savepath", _("Save Path:"), _("Specifies where your saved games are put"), kCmdSaveBrowser);
360 	else
361 		new ButtonWidget(tab, "GameOptions_Paths.Savepath", _c("Save Path:", "lowres"), _("Specifies where your saved games are put"), kCmdSaveBrowser);
362 	_savePathWidget = new StaticTextWidget(tab, "GameOptions_Paths.SavepathText", savePath, _("Specifies where your saved games are put"));
363 
364 	_savePathClearButton = addClearButton(tab, "GameOptions_Paths.SavePathClearButton", kCmdSavePathClear);
365 
366 	//
367 	// 9) The Achievements & The Statistics tabs
368 	//
369 	if (enginePlugin) {
370 		const MetaEngine &metaEngine = enginePlugin->get<MetaEngine>();
371 		AchMan.setActiveDomain(metaEngine.getAchievementsInfo(domain));
372 		if (AchMan.getAchievementCount()) {
373 			tab->addTab(_("Achievements"), "GameOptions_Achievements");
374 			addAchievementsControls(tab, "GameOptions_Achievements.");
375 		}
376 		if (AchMan.getStatCount()) {
377 			tab->addTab(_("Statistics"), "GameOptions_Achievements");
378 			addStatisticsControls(tab, "GameOptions_Achievements.");
379 		}
380 	}
381 
382 	// Activate the first tab
383 	tab->setActiveTab(0);
384 	_tabWidget = tab;
385 
386 	// Add OK & Cancel buttons
387 	new ButtonWidget(this, "GameOptions.Cancel", _("Cancel"), Common::U32String(), kCloseCmd);
388 	new ButtonWidget(this, "GameOptions.Ok", _("OK"), Common::U32String(), kOKCmd);
389 }
390 
setupGraphicsTab()391 void EditGameDialog::setupGraphicsTab() {
392 	OptionsDialog::setupGraphicsTab();
393 	_globalGraphicsOverride->setVisible(true);
394 }
395 
open()396 void EditGameDialog::open() {
397 	OptionsDialog::open();
398 
399 	String extraPath(ConfMan.get("extrapath", _domain));
400 	if (extraPath.empty() || !ConfMan.hasKey("extrapath", _domain)) {
401 		_extraPathWidget->setLabel(_c("None", "path"));
402 	}
403 
404 	String savePath(ConfMan.get("savepath", _domain));
405 	if (savePath.empty() || !ConfMan.hasKey("savepath", _domain)) {
406 		_savePathWidget->setLabel(_("Default"));
407 	}
408 
409 	int sel, i;
410 	bool e;
411 
412 	// En-/disable dialog items depending on whether overrides are active or not.
413 
414 	e = ConfMan.hasKey("gfx_mode", _domain) ||
415 		ConfMan.hasKey("render_mode", _domain) ||
416 		ConfMan.hasKey("stretch_mode", _domain) ||
417 		ConfMan.hasKey("scaler", _domain) ||
418 		ConfMan.hasKey("scale_factor", _domain) ||
419 		ConfMan.hasKey("aspect_ratio", _domain) ||
420 		ConfMan.hasKey("fullscreen", _domain) ||
421 		ConfMan.hasKey("vsync", _domain) ||
422 		ConfMan.hasKey("filtering", _domain) ||
423 		ConfMan.hasKey("renderer", _domain) ||
424 		ConfMan.hasKey("antialiasing", _domain);
425 	_globalGraphicsOverride->setState(e);
426 
427 	if (g_system->hasFeature(OSystem::kFeatureShader)) {
428 		e = ConfMan.hasKey("shader", _domain);
429 		_globalShaderOverride->setState(e);
430 	}
431 
432 	if (_backendOptions) {
433 		e = _backendOptions->hasKeys();
434 		_globalBackendOverride->setState(e);
435 	}
436 
437 	e = ConfMan.hasKey("music_driver", _domain) ||
438 		ConfMan.hasKey("output_rate", _domain) ||
439 		ConfMan.hasKey("opl_driver", _domain) ||
440 		ConfMan.hasKey("subtitles", _domain) ||
441 		ConfMan.hasKey("talkspeed", _domain);
442 	_globalAudioOverride->setState(e);
443 
444 	e = ConfMan.hasKey("music_volume", _domain) ||
445 		ConfMan.hasKey("sfx_volume", _domain) ||
446 		ConfMan.hasKey("speech_volume", _domain);
447 	_globalVolumeOverride->setState(e);
448 
449 	if (!_guioptions.contains(GUIO_NOMIDI) && !_guioptions.contains(GUIO_NOMUSIC)) {
450 		e = ConfMan.hasKey("soundfont", _domain) ||
451 			ConfMan.hasKey("multi_midi", _domain) ||
452 			ConfMan.hasKey("midi_gain", _domain);
453 		_globalMIDIOverride->setState(e);
454 
455 		e = ConfMan.hasKey("native_mt32", _domain) ||
456 			ConfMan.hasKey("enable_gs", _domain);
457 		_globalMT32Override->setState(e);
458 	}
459 
460 	// TODO: game path
461 
462 	if (_langPopUp != nullptr) {
463 		const Common::Language lang = Common::parseLanguage(ConfMan.get("language", _domain));
464 
465 		if (ConfMan.hasKey("language", _domain)) {
466 			_langPopUp->setSelectedTag(lang);
467 		} else {
468 			_langPopUp->setSelectedTag((uint32)Common::UNK_LANG);
469 		}
470 
471 		if (_langPopUp->numEntries() <= 3) { // If only one language is avaliable
472 			_langPopUpDesc->setEnabled(false);
473 			_langPopUp->setEnabled(false);
474 		}
475 	}
476 
477 	if (_engineOptions) {
478 		_engineOptions->load();
479 	}
480 
481 	const Common::PlatformDescription *p = Common::g_platforms;
482 	const Common::Platform platform = Common::parsePlatform(ConfMan.get("platform", _domain));
483 	sel = 0;
484 	for (i = 0; p->code; ++p, ++i) {
485 		if (platform == p->id)
486 			sel = i + 2;
487 	}
488 	_platformPopUp->setSelected(sel);
489 }
490 
apply()491 void EditGameDialog::apply() {
492 	ConfMan.set("description", _descriptionWidget->getEditString(), _domain);
493 
494 	if (_langPopUp != nullptr) {
495 		Common::Language lang = (Common::Language)_langPopUp->getSelectedTag();
496 		if (lang < 0)
497 			ConfMan.removeKey("language", _domain);
498 		else
499 			ConfMan.set("language", Common::getLanguageCode(lang), _domain);
500 	}
501 
502 	U32String gamePath(_gamePathWidget->getLabel());
503 	if (!gamePath.empty())
504 		ConfMan.set("path", gamePath, _domain);
505 
506 	U32String extraPath(_extraPathWidget->getLabel());
507 	if (!extraPath.empty() && (extraPath != _c("None", "path")))
508 		ConfMan.set("extrapath", extraPath, _domain);
509 	else
510 		ConfMan.removeKey("extrapath", _domain);
511 
512 	U32String savePath(_savePathWidget->getLabel());
513 	if (!savePath.empty() && (savePath != _("Default")))
514 		ConfMan.set("savepath", savePath, _domain);
515 	else
516 		ConfMan.removeKey("savepath", _domain);
517 
518 	Common::Platform platform = (Common::Platform)_platformPopUp->getSelectedTag();
519 	if (platform < 0)
520 		ConfMan.removeKey("platform", _domain);
521 	else
522 		ConfMan.set("platform", Common::getPlatformCode(platform), _domain);
523 
524 	if (_engineOptions) {
525 		_engineOptions->save();
526 	}
527 
528 	OptionsDialog::apply();
529 }
530 
handleCommand(CommandSender * sender,uint32 cmd,uint32 data)531 void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
532 	switch (cmd) {
533 	case kCmdGlobalGraphicsOverride:
534 		setGraphicSettingsState(data != 0);
535 		g_gui.scheduleTopDialogRedraw();
536 		break;
537 	case kCmdGlobalShaderOverride:
538 		setShaderSettingsState(data != 0);
539 		g_gui.scheduleTopDialogRedraw();
540 		break;
541 	case kCmdGlobalBackendOverride:
542 		_backendOptions->setEnabled(data != 0);
543 		g_gui.scheduleTopDialogRedraw();
544 		break;
545 	case kCmdGlobalAudioOverride:
546 		setAudioSettingsState(data != 0);
547 		setSubtitleSettingsState(data != 0);
548 		if (_globalVolumeOverride == nullptr)
549 			setVolumeSettingsState(data != 0);
550 		g_gui.scheduleTopDialogRedraw();
551 		break;
552 	case kCmdGlobalMIDIOverride:
553 		setMIDISettingsState(data != 0);
554 		g_gui.scheduleTopDialogRedraw();
555 		break;
556 	case kCmdGlobalMT32Override:
557 		setMT32SettingsState(data != 0);
558 		g_gui.scheduleTopDialogRedraw();
559 		break;
560 	case kCmdGlobalVolumeOverride:
561 		setVolumeSettingsState(data != 0);
562 		g_gui.scheduleTopDialogRedraw();
563 		break;
564 	case kCmdChooseSoundFontCmd:
565 	{
566 		BrowserDialog browser(_("Select SoundFont"), false);
567 
568 		if (browser.runModal() > 0) {
569 			// User made this choice...
570 			Common::FSNode file(browser.getResult());
571 			_soundFont->setLabel(file.getPath());
572 
573 			if (!file.getPath().empty() && (file.getPath() != Common::convertFromU32String(_c("None", "path"))))
574 				_soundFontClearButton->setEnabled(true);
575 			else
576 				_soundFontClearButton->setEnabled(false);
577 
578 			g_gui.scheduleTopDialogRedraw();
579 		}
580 		break;
581 	}
582 
583 	// Change path for the game
584 	case kCmdGameBrowser:
585 	{
586 		BrowserDialog browser(_("Select directory with game data"), true);
587 		if (browser.runModal() > 0) {
588 			// User made his choice...
589 			Common::FSNode dir(browser.getResult());
590 
591 			// TODO: Verify the game can be found in the new directory... Best
592 			// done with optional specific gameid to pluginmgr detectgames?
593 			// FSList files = dir.listDir(FSNode::kListFilesOnly);
594 
595 			_gamePathWidget->setLabel(dir.getPath());
596 			g_gui.scheduleTopDialogRedraw();
597 		}
598 		g_gui.scheduleTopDialogRedraw();
599 		break;
600 	}
601 
602 	// Change path for extra game data (eg, using sword cutscenes when playing via CD)
603 	case kCmdExtraBrowser:
604 	{
605 		BrowserDialog browser(_("Select additional game directory"), true);
606 		if (browser.runModal() > 0) {
607 			// User made his choice...
608 			Common::FSNode dir(browser.getResult());
609 			_extraPathWidget->setLabel(dir.getPath());
610 			g_gui.scheduleTopDialogRedraw();
611 		}
612 		g_gui.scheduleTopDialogRedraw();
613 		break;
614 	}
615 	// Change path for stored save game (perm and temp) data
616 	case kCmdSaveBrowser:
617 	{
618 		BrowserDialog browser(_("Select directory for saved games"), true);
619 		if (browser.runModal() > 0) {
620 			// User made his choice...
621 			Common::FSNode dir(browser.getResult());
622 			if (dir.isWritable()) {
623 				_savePathWidget->setLabel(dir.getPath());
624 			} else {
625 				MessageDialog error(_("The chosen directory cannot be written to. Please select another one."));
626 				error.runModal();
627 				return;
628 			}
629 #if defined(USE_CLOUD) && defined(USE_LIBCURL)
630 			MessageDialog warningMessage(_("Saved games sync feature doesn't work with non-default directories. If you want your saved games to sync, use default directory."));
631 			warningMessage.runModal();
632 #endif
633 			g_gui.scheduleTopDialogRedraw();
634 		}
635 		g_gui.scheduleTopDialogRedraw();
636 		break;
637 	}
638 
639 	case kCmdExtraPathClear:
640 		_extraPathWidget->setLabel(_c("None", "path"));
641 		break;
642 
643 	case kCmdSavePathClear:
644 		_savePathWidget->setLabel(_("Default"));
645 		break;
646 
647 	case kOKCmd:
648 	{
649 		// Write back changes made to config object
650 		String newDomain(Common::convertFromU32String(_domainWidget->getEditString()));
651 		if (newDomain != _domain) {
652 			if (newDomain.empty()
653 				|| newDomain.hasPrefix("_")
654 				|| newDomain == ConfigManager::kApplicationDomain
655 				|| ConfMan.hasGameDomain(newDomain)) {
656 				MessageDialog alert(_("This game ID is already taken. Please choose another one."));
657 				alert.runModal();
658 				return;
659 			}
660 			ConfMan.renameGameDomain(_domain, newDomain);
661 			_domain = newDomain;
662 			if (_engineOptions) {
663 				_engineOptions->setDomain(newDomain);
664 			}
665 			if (_backendOptions) {
666 				_backendOptions->setDomain(newDomain);
667 			}
668 		}
669 	}
670 	// fall through
671 	default:
672 		OptionsDialog::handleCommand(sender, cmd, data);
673 	}
674 }
675 
676 } // End of namespace GUI
677