1 /*
2 * Copyright (C) 2002-2020 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20 #include "ui_fsmenu/options.h"
21
22 #include <memory>
23
24 #include <boost/algorithm/string.hpp>
25
26 #include "base/i18n.h"
27 #include "base/log.h"
28 #include "base/wexception.h"
29 #include "graphic/default_resolution.h"
30 #include "graphic/font_handler.h"
31 #include "graphic/graphic.h"
32 #include "graphic/mouse_cursor.h"
33 #include "graphic/text/bidi.h"
34 #include "graphic/text/font_set.h"
35 #include "graphic/text_layout.h"
36 #include "io/filesystem/disk_filesystem.h"
37 #include "io/filesystem/layered_filesystem.h"
38 #include "logic/filesystem_constants.h"
39 #include "scripting/lua_interface.h"
40 #include "scripting/lua_table.h"
41 #include "sound/sound_handler.h"
42 #include "wlapplication.h"
43 #include "wlapplication_options.h"
44
45 namespace {
46
47 // Locale identifiers can look like this: ca_ES@valencia.UTF-8
48 // The contents of 'selected_locale' will be changed to match the 'current_locale'
find_selected_locale(std::string * selected_locale,const std::string & current_locale)49 void find_selected_locale(std::string* selected_locale, const std::string& current_locale) {
50 if (selected_locale->empty()) {
51 std::vector<std::string> parts;
52 boost::split(parts, current_locale, boost::is_any_of("."));
53 if (current_locale == parts[0]) {
54 *selected_locale = current_locale;
55 } else {
56 boost::split(parts, parts[0], boost::is_any_of("@"));
57 if (current_locale == parts[0]) {
58 *selected_locale = current_locale;
59 } else {
60 boost::split(parts, parts[0], boost::is_any_of("_"));
61 if (current_locale == parts[0]) {
62 *selected_locale = current_locale;
63 }
64 }
65 }
66 }
67 }
68
69 } // namespace
70
FullscreenMenuOptions(OptionsCtrl::OptionsStruct opt)71 FullscreenMenuOptions::FullscreenMenuOptions(OptionsCtrl::OptionsStruct opt)
72 : FullscreenMenuBase(),
73
74 // Values for alignment and size
75 padding_(10),
76
77 // Title
78 title_(this,
79 0,
80 0,
81 0,
82 0,
83 _("Options"),
84 UI::Align::kCenter,
85 g_gr->styles().font_style(UI::FontStyle::kFsMenuTitle)),
86
87 // Buttons
88 button_box_(this, 0, 0, UI::Box::Horizontal),
89 cancel_(&button_box_, "cancel", 0, 0, 0, 0, UI::ButtonStyle::kFsMenuSecondary, _("Cancel")),
90 apply_(&button_box_, "apply", 0, 0, 0, 0, UI::ButtonStyle::kFsMenuSecondary, _("Apply")),
91 ok_(&button_box_, "ok", 0, 0, 0, 0, UI::ButtonStyle::kFsMenuPrimary, _("OK")),
92
93 // Tabs
94 tabs_(this, UI::TabPanelStyle::kFsMenu),
95
96 box_interface_(&tabs_, 0, 0, UI::Box::Horizontal, 0, 0, padding_),
97 box_interface_left_(&box_interface_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
98 box_windows_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
99 box_sound_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
100 box_saving_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
101 box_game_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
102
103 // Interface options
104 language_dropdown_(&box_interface_left_,
105 "dropdown_language",
106 0,
107 0,
108 100, // 100 is arbitrary, will be resized in layout().
109 50,
110 24,
111 _("Language"),
112 UI::DropdownType::kTextual,
113 UI::PanelStyle::kFsMenu,
114 UI::ButtonStyle::kFsMenuMenu),
115 resolution_dropdown_(&box_interface_left_,
116 "dropdown_resolution",
117 0,
118 0,
119 100, // 100 is arbitrary, will be resized in layout().
120 50,
121 24,
122 _("Window Size"),
123 UI::DropdownType::kTextual,
124 UI::PanelStyle::kFsMenu,
125 UI::ButtonStyle::kFsMenuMenu),
126
127 fullscreen_(&box_interface_left_, Vector2i::zero(), _("Fullscreen"), "", 0),
128 inputgrab_(&box_interface_left_, Vector2i::zero(), _("Grab Input"), "", 0),
129 sdl_cursor_(&box_interface_left_, Vector2i::zero(), _("Use system mouse cursor"), "", 0),
130 sb_maxfps_(&box_interface_left_,
131 0,
132 0,
133 0,
134 0,
135 opt.maxfps,
136 0,
137 99,
138 UI::PanelStyle::kFsMenu,
139 _("Maximum FPS:")),
140 translation_info_(&box_interface_, 0, 0, 100, 100, UI::PanelStyle::kFsMenu),
141
142 // Windows options
143 snap_win_overlap_only_(
144 &box_windows_, Vector2i::zero(), _("Snap windows only when overlapping"), "", 0),
145 dock_windows_to_edges_(&box_windows_, Vector2i::zero(), _("Dock windows to edges"), "", 0),
146 animate_map_panning_(
147 &box_windows_, Vector2i::zero(), _("Animate automatic map movements"), "", 0),
148
149 sb_dis_panel_(&box_windows_,
150 0,
151 0,
152 0,
153 0,
154 opt.panel_snap_distance,
155 0,
156 99,
157 UI::PanelStyle::kFsMenu,
158 _("Distance for windows to snap to other panels:"),
159 UI::SpinBox::Units::kPixels),
160
161 sb_dis_border_(&box_windows_,
162 0,
163 0,
164 0,
165 0,
166 opt.border_snap_distance,
167 0,
168 99,
169 UI::PanelStyle::kFsMenu,
170 _("Distance for windows to snap to borders:"),
171 UI::SpinBox::Units::kPixels),
172
173 // Sound options
174 sound_options_(box_sound_, UI::SliderStyle::kFsMenu),
175
176 // Saving options
177 sb_autosave_(&box_saving_,
178 0,
179 0,
180 0,
181 0,
182 opt.autosave / 60,
183 0,
184 100,
185 UI::PanelStyle::kFsMenu,
186 _("Save game automatically every:"),
187 UI::SpinBox::Units::kMinutes,
188 UI::SpinBox::Type::kBig),
189
190 sb_rolling_autosave_(&box_saving_,
191 0,
192 0,
193 0,
194 0,
195 opt.rolling_autosave,
196 1,
197 20,
198 UI::PanelStyle::kFsMenu,
199 _("Maximum number of autosave files:"),
200 UI::SpinBox::Units::kNone,
201 UI::SpinBox::Type::kBig),
202
203 zip_(&box_saving_,
204 Vector2i::zero(),
205 _("Compress widelands data files (maps, replays and savegames)"),
206 "",
207 0),
208 write_syncstreams_(&box_saving_,
209 Vector2i::zero(),
210 _("Write syncstreams in network games to debug desyncs"),
211 "",
212 0),
213
214 // Game options
215 auto_roadbuild_mode_(
216 &box_game_, Vector2i::zero(), _("Start building road after placing a flag")),
217 transparent_chat_(
218 &box_game_, Vector2i::zero(), _("Show in-game chat with transparent background"), "", 0),
219
220 /** TRANSLATORS: A watchwindow is a window where you keep watching an object or a map region,*/
221 /** TRANSLATORS: and it also lets you jump to it on the map. */
222 single_watchwin_(&box_game_, Vector2i::zero(), _("Use single watchwindow mode")),
223 /** TRANSLATORS: This refers to to zooming with the scrollwheel.*/
224 ctrl_zoom_(&box_game_, Vector2i::zero(), _("Zoom only when Ctrl is pressed")),
225 game_clock_(&box_game_, Vector2i::zero(), _("Display game time in the top left corner")),
226 os_(opt) {
227
228 // Buttons
229 button_box_.add(UI::g_fh->fontset()->is_rtl() ? &ok_ : &cancel_);
230 button_box_.add_inf_space();
231 button_box_.add(&apply_);
232 button_box_.add_inf_space();
233 button_box_.add(UI::g_fh->fontset()->is_rtl() ? &cancel_ : &ok_);
234
235 // Tabs
236 tabs_.add("options_interface", _("Interface"), &box_interface_, "");
237 tabs_.add("options_windows", _("Windows"), &box_windows_, "");
238 tabs_.add("options_sound", _("Sound"), &box_sound_, "");
239 tabs_.add("options_saving", _("Saving"), &box_saving_, "");
240 tabs_.add("options_game", _("Game"), &box_game_, "");
241
242 // We want the last active tab when "Apply" was clicked.
243 if (os_.active_tab < tabs_.tabs().size()) {
244 tabs_.activate(os_.active_tab);
245 }
246
247 // Interface
248 box_interface_.add(&box_interface_left_);
249 box_interface_.add(&translation_info_, UI::Box::Resizing::kExpandBoth);
250 box_interface_left_.add(&language_dropdown_);
251 box_interface_left_.add(&resolution_dropdown_);
252 box_interface_left_.add(&fullscreen_);
253 box_interface_left_.add(&inputgrab_);
254 box_interface_left_.add(&sdl_cursor_);
255 box_interface_left_.add(&sb_maxfps_);
256
257 // Windows
258 box_windows_.add(&snap_win_overlap_only_);
259 box_windows_.add(&dock_windows_to_edges_);
260 box_windows_.add(&animate_map_panning_);
261 box_windows_.add(&sb_dis_panel_);
262 box_windows_.add(&sb_dis_border_);
263
264 // Sound
265 box_sound_.add(&sound_options_);
266
267 // Saving
268 box_saving_.add(&sb_autosave_);
269 box_saving_.add(&sb_rolling_autosave_);
270 box_saving_.add(&zip_);
271 box_saving_.add(&write_syncstreams_);
272
273 // Game
274 box_game_.add(&auto_roadbuild_mode_);
275 box_game_.add(&transparent_chat_);
276 box_game_.add(&single_watchwin_);
277 box_game_.add(&ctrl_zoom_);
278 box_game_.add(&game_clock_);
279
280 // Bind actions
281 language_dropdown_.selected.connect([this]() { update_language_stats(false); });
282 cancel_.sigclicked.connect([this]() { clicked_cancel(); });
283 apply_.sigclicked.connect([this]() { clicked_apply(); });
284 ok_.sigclicked.connect([this]() { clicked_ok(); });
285
286 /** TRANSLATORS: Options: Save game automatically every: */
287 sb_autosave_.add_replacement(0, _("Off"));
288
289 // Fill in data
290 // Interface options
291 for (int modes = 0; modes < SDL_GetNumDisplayModes(0); ++modes) {
292 SDL_DisplayMode mode;
293 SDL_GetDisplayMode(0, modes, &mode);
294 if (800 <= mode.w && 600 <= mode.h &&
295 (SDL_BITSPERPIXEL(mode.format) == 32 || SDL_BITSPERPIXEL(mode.format) == 24)) {
296 ScreenResolution this_res = {
297 mode.w, mode.h, static_cast<int32_t>(SDL_BITSPERPIXEL(mode.format))};
298 if (this_res.depth == 24) {
299 this_res.depth = 32;
300 }
301 if (resolutions_.empty() || this_res.xres != resolutions_.rbegin()->xres ||
302 this_res.yres != resolutions_.rbegin()->yres) {
303 resolutions_.push_back(this_res);
304 }
305 }
306 }
307
308 bool did_select_a_res = false;
309 for (uint32_t i = 0; i < resolutions_.size(); ++i) {
310 const bool selected = resolutions_[i].xres == opt.xres && resolutions_[i].yres == opt.yres;
311 did_select_a_res |= selected;
312 resolution_dropdown_.add(
313 /** TRANSLATORS: Screen resolution, e.g. 800 x 600*/
314 (boost::format(_("%1% x %2%")) % resolutions_[i].xres % resolutions_[i].yres).str(), i,
315 nullptr, selected);
316 }
317 if (!did_select_a_res) {
318 uint32_t entry = resolutions_.size();
319 resolutions_.resize(entry + 1);
320 resolutions_[entry].xres = opt.xres;
321 resolutions_[entry].yres = opt.yres;
322 resolution_dropdown_.add(
323 (boost::format(_("%1% x %2%")) % opt.xres % opt.yres).str(), entry, nullptr, true);
324 }
325
326 fullscreen_.set_state(opt.fullscreen);
327 inputgrab_.set_state(opt.inputgrab);
328 sdl_cursor_.set_state(opt.sdl_cursor);
329
330 // Windows options
331 snap_win_overlap_only_.set_state(opt.snap_win_overlap_only);
332 dock_windows_to_edges_.set_state(opt.dock_windows_to_edges);
333 animate_map_panning_.set_state(opt.animate_map_panning);
334
335 // Saving options
336 zip_.set_state(opt.zip);
337 write_syncstreams_.set_state(opt.write_syncstreams);
338
339 // Game options
340 auto_roadbuild_mode_.set_state(opt.auto_roadbuild_mode);
341 transparent_chat_.set_state(opt.transparent_chat);
342 single_watchwin_.set_state(opt.single_watchwin);
343 ctrl_zoom_.set_state(opt.ctrl_zoom);
344 game_clock_.set_state(opt.game_clock);
345
346 // Language options
347 add_languages_to_list(opt.language);
348 update_language_stats(true);
349 layout();
350 }
351
layout()352 void FullscreenMenuOptions::layout() {
353
354 // Values for alignment and size
355 butw_ = get_w() / 5;
356 buth_ = get_h() * 9 / 200;
357 hmargin_ = get_w() * 19 / 200;
358 int tab_panel_width = get_inner_w() - 2 * hmargin_;
359 tab_panel_y_ = get_h() * 14 / 100;
360
361 // Title
362 title_.set_size(get_w(), title_.get_h());
363 title_.set_pos(Vector2i(0, buth_));
364
365 // Buttons
366 cancel_.set_desired_size(butw_, buth_);
367 apply_.set_desired_size(butw_, buth_);
368 ok_.set_desired_size(butw_, buth_);
369 button_box_.set_pos(Vector2i(hmargin_ + butw_ / 3, get_inner_h() - hmargin_));
370 button_box_.set_size(tab_panel_width - 2 * butw_ / 3, buth_);
371
372 // Tabs
373 tabs_.set_pos(Vector2i(hmargin_, tab_panel_y_));
374 tabs_.set_size(tab_panel_width, get_inner_h() - tab_panel_y_ - buth_ - hmargin_);
375
376 tab_panel_width -= padding_;
377 const int column_width = tab_panel_width / 2;
378
379 // Interface
380 box_interface_left_.set_desired_size(column_width + padding_, tabs_.get_inner_h());
381 box_interface_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
382 language_dropdown_.set_desired_size(column_width, language_dropdown_.get_h());
383 language_dropdown_.set_height(tabs_.get_h() - language_dropdown_.get_y() - buth_ - 3 * padding_);
384 resolution_dropdown_.set_desired_size(column_width, resolution_dropdown_.get_h());
385 resolution_dropdown_.set_height(tabs_.get_h() - resolution_dropdown_.get_y() - buth_ -
386 3 * padding_);
387
388 fullscreen_.set_desired_size(column_width, fullscreen_.get_h());
389 inputgrab_.set_desired_size(column_width, inputgrab_.get_h());
390 sdl_cursor_.set_desired_size(column_width, sdl_cursor_.get_h());
391 sb_maxfps_.set_unit_width(column_width / 2);
392 sb_maxfps_.set_desired_size(column_width, sb_maxfps_.get_h());
393
394 // Windows options
395 snap_win_overlap_only_.set_desired_size(tab_panel_width, snap_win_overlap_only_.get_h());
396 dock_windows_to_edges_.set_desired_size(tab_panel_width, dock_windows_to_edges_.get_h());
397 animate_map_panning_.set_desired_size(tab_panel_width, animate_map_panning_.get_h());
398 sb_dis_panel_.set_unit_width(200);
399 sb_dis_panel_.set_desired_size(tab_panel_width, sb_dis_panel_.get_h());
400 sb_dis_border_.set_unit_width(200);
401 sb_dis_border_.set_desired_size(tab_panel_width, sb_dis_border_.get_h());
402
403 // Sound options
404 sound_options_.set_desired_size(tab_panel_width, tabs_.get_inner_h());
405
406 // Saving options
407 sb_autosave_.set_unit_width(250);
408 sb_autosave_.set_desired_size(tab_panel_width, sb_autosave_.get_h());
409 sb_rolling_autosave_.set_unit_width(250);
410 sb_rolling_autosave_.set_desired_size(tab_panel_width, sb_rolling_autosave_.get_h());
411 zip_.set_desired_size(tab_panel_width, zip_.get_h());
412 write_syncstreams_.set_desired_size(tab_panel_width, write_syncstreams_.get_h());
413
414 // Game options
415 auto_roadbuild_mode_.set_desired_size(tab_panel_width, auto_roadbuild_mode_.get_h());
416 transparent_chat_.set_desired_size(tab_panel_width, transparent_chat_.get_h());
417 single_watchwin_.set_desired_size(tab_panel_width, single_watchwin_.get_h());
418 ctrl_zoom_.set_desired_size(tab_panel_width, ctrl_zoom_.get_h());
419 game_clock_.set_desired_size(tab_panel_width, game_clock_.get_h());
420 }
421
add_languages_to_list(const std::string & current_locale)422 void FullscreenMenuOptions::add_languages_to_list(const std::string& current_locale) {
423
424 // We want these two entries on top - the most likely user's choice and the default.
425 language_dropdown_.add(_("Try system language"), "", nullptr, current_locale == "");
426 language_dropdown_.add("English", "en", nullptr, current_locale == "en");
427
428 // Handle non-standard setups where the locale directory might be missing
429 if (!g_fs->is_directory(i18n::get_localedir())) {
430 return;
431 }
432
433 // Add translation directories to the list. Using the LanguageEntries' sortnames as a key for
434 // getting a sorted result.
435 std::map<std::string, LanguageEntry> entries;
436 std::string selected_locale;
437
438 try { // Begin read locales table
439 LuaInterface lua;
440 std::unique_ptr<LuaTable> all_locales(lua.run_script("i18n/locales.lua"));
441 all_locales->do_not_warn_about_unaccessed_keys(); // We are only reading partial information
442 // as needed
443
444 // We start with the locale directory so we can pick up locales
445 // that don't have a configuration file yet.
446 std::unique_ptr<FileSystem> fs(&FileSystem::create(i18n::get_localedir()));
447 FilenameSet files = fs->list_directory(".");
448
449 for (const std::string& localename : files) { // Begin scan locales directory
450 const char* path = localename.c_str();
451 if (!strcmp(FileSystem::fs_filename(path), ".") ||
452 !strcmp(FileSystem::fs_filename(path), "..") || !fs->is_directory(path)) {
453 continue;
454 }
455
456 try { // Begin read locale from table
457 std::unique_ptr<LuaTable> table = all_locales->get_table(localename);
458 table->do_not_warn_about_unaccessed_keys();
459
460 std::string name = i18n::make_ligatures(table->get_string("name").c_str());
461 const std::string sortname = table->get_string("sort_name");
462 std::unique_ptr<LanguageEntry> entry(new LanguageEntry(localename, name));
463 entries.insert(std::make_pair(sortname, *entry));
464 language_entries_.insert(std::make_pair(localename, *entry));
465
466 if (localename == current_locale) {
467 selected_locale = current_locale;
468 }
469
470 } catch (const WException&) {
471 log("Could not read locale for: %s\n", localename.c_str());
472 entries.insert(std::make_pair(localename, LanguageEntry(localename, localename)));
473 } // End read locale from table
474 } // End scan locales directory
475 } catch (const LuaError& err) {
476 log("Could not read locales information from file: %s\n", err.what());
477 return; // Nothing more can be done now.
478 } // End read locales table
479
480 find_selected_locale(&selected_locale, current_locale);
481 for (const auto& entry : entries) {
482 const LanguageEntry& language_entry = entry.second;
483 language_dropdown_.add(language_entry.descname.c_str(), language_entry.localename, nullptr,
484 language_entry.localename == selected_locale, "");
485 }
486 }
487
488 /**
489 * Updates the language statistics message according to the currently selected locale.
490 * @param include_system_lang We only want to include the system lang if it matches the Widelands
491 * locale.
492 */
update_language_stats(bool include_system_lang)493 void FullscreenMenuOptions::update_language_stats(bool include_system_lang) {
494 int percent = 100;
495 std::string message = "";
496 if (language_dropdown_.has_selection()) {
497 std::string locale = language_dropdown_.get_selected();
498 // Empty locale means try system locale
499 if (locale.empty() && include_system_lang) {
500 std::vector<std::string> parts;
501 boost::split(parts, i18n::get_locale(), boost::is_any_of("."));
502 if (language_entries_.count(parts[0]) == 1) {
503 locale = parts[0];
504 } else {
505 boost::split(parts, parts[0], boost::is_any_of("@"));
506 if (language_entries_.count(parts[0]) == 1) {
507 locale = parts[0];
508 } else {
509 boost::split(parts, parts[0], boost::is_any_of("_"));
510 if (language_entries_.count(parts[0]) == 1) {
511 locale = parts[0];
512 }
513 }
514 }
515 }
516
517 // If we have the locale, grab the stats and set the message
518 if (language_entries_.count(locale) == 1) {
519 try {
520 const LanguageEntry& entry = language_entries_[locale];
521 Profile prof("i18n/translation_stats.conf");
522 Section& s = prof.get_safe_section("global");
523 const int total = s.get_int("total");
524 s = prof.get_safe_section(locale);
525 percent = static_cast<int>(floor(100 * s.get_int("translated") / total));
526 if (percent == 100) {
527 message =
528 /** TRANSLATORS: %s = language name */
529 (boost::format(_("The translation into %s is complete.")) % entry.descname).str();
530 } else {
531 /** TRANSLATORS: %1% = language name, %2% = percentage */
532 message = (boost::format(_("The translation into %1% is %2%%% complete.")) %
533 entry.descname % percent)
534 .str();
535 }
536 } catch (...) {
537 }
538 }
539 }
540
541 // We will want some help with incomplete translations. We set this lower than 100%,
542 // because some translators let things drop a bit sometimes because they're busy and
543 // will catch up with the work later.
544 if (percent <= 90) {
545 message = message + " " +
546 (boost::format(_("If you wish to help us translate, please visit %s")) %
547 "<font underline=1>widelands.org/wiki/TranslatingWidelands</font>")
548 .str();
549 }
550 // Make font a bit smaller so the link will fit at 800x600 resolution.
551 translation_info_.set_text(
552 as_richtext_paragraph(message, UI::FontStyle::kFsMenuTranslationInfo));
553 }
554
clicked_apply()555 void FullscreenMenuOptions::clicked_apply() {
556 end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kApplyOptions);
557 }
558
clicked_cancel()559 void FullscreenMenuOptions::clicked_cancel() {
560 g_sh->load_config();
561 clicked_back();
562 }
563
get_values()564 OptionsCtrl::OptionsStruct FullscreenMenuOptions::get_values() {
565 // Write all data from UI elements
566 // Interface options
567 if (language_dropdown_.has_selection()) {
568 os_.language = language_dropdown_.get_selected();
569 }
570 if (resolution_dropdown_.has_selection()) {
571 const uint32_t res_index = resolution_dropdown_.get_selected();
572 os_.xres = resolutions_[res_index].xres;
573 os_.yres = resolutions_[res_index].yres;
574 }
575 os_.fullscreen = fullscreen_.get_state();
576 os_.inputgrab = inputgrab_.get_state();
577 os_.sdl_cursor = sdl_cursor_.get_state();
578 os_.maxfps = sb_maxfps_.get_value();
579
580 // Windows options
581 os_.snap_win_overlap_only = snap_win_overlap_only_.get_state();
582 os_.dock_windows_to_edges = dock_windows_to_edges_.get_state();
583 os_.animate_map_panning = animate_map_panning_.get_state();
584 os_.panel_snap_distance = sb_dis_panel_.get_value();
585 os_.border_snap_distance = sb_dis_border_.get_value();
586
587 // Saving options
588 os_.autosave = sb_autosave_.get_value();
589 os_.rolling_autosave = sb_rolling_autosave_.get_value();
590 os_.zip = zip_.get_state();
591 os_.write_syncstreams = write_syncstreams_.get_state();
592
593 // Game options
594 os_.auto_roadbuild_mode = auto_roadbuild_mode_.get_state();
595 os_.transparent_chat = transparent_chat_.get_state();
596 os_.single_watchwin = single_watchwin_.get_state();
597 os_.ctrl_zoom = ctrl_zoom_.get_state();
598 os_.game_clock = game_clock_.get_state();
599
600 // Last tab for reloading the options menu
601 os_.active_tab = tabs_.active();
602 return os_;
603 }
604
605 /**
606 * Handles communication between window class and options
607 */
OptionsCtrl(Section & s)608 OptionsCtrl::OptionsCtrl(Section& s)
609 : opt_section_(s),
610 opt_dialog_(
611 std::unique_ptr<FullscreenMenuOptions>(new FullscreenMenuOptions(options_struct(0)))) {
612 handle_menu();
613 }
614
handle_menu()615 void OptionsCtrl::handle_menu() {
616 FullscreenMenuBase::MenuTarget i = opt_dialog_->run<FullscreenMenuBase::MenuTarget>();
617 if (i != FullscreenMenuBase::MenuTarget::kBack) {
618 save_options();
619 }
620 if (i == FullscreenMenuBase::MenuTarget::kApplyOptions) {
621 uint32_t active_tab = opt_dialog_->get_values().active_tab;
622 g_gr->change_resolution(opt_dialog_->get_values().xres, opt_dialog_->get_values().yres);
623 g_gr->set_fullscreen(opt_dialog_->get_values().fullscreen);
624 opt_dialog_.reset(new FullscreenMenuOptions(options_struct(active_tab)));
625 handle_menu(); // Restart general options menu
626 }
627 }
628
options_struct(uint32_t active_tab)629 OptionsCtrl::OptionsStruct OptionsCtrl::options_struct(uint32_t active_tab) {
630 OptionsStruct opt;
631 // Interface options
632 opt.xres = opt_section_.get_int("xres", DEFAULT_RESOLUTION_W);
633 opt.yres = opt_section_.get_int("yres", DEFAULT_RESOLUTION_H);
634 opt.fullscreen = opt_section_.get_bool("fullscreen", false);
635 opt.inputgrab = opt_section_.get_bool("inputgrab", false);
636 opt.maxfps = opt_section_.get_int("maxfps", 25);
637 opt.sdl_cursor = opt_section_.get_bool("sdl_cursor", true);
638
639 // Windows options
640 opt.snap_win_overlap_only = opt_section_.get_bool("snap_windows_only_when_overlapping", false);
641 opt.dock_windows_to_edges = opt_section_.get_bool("dock_windows_to_edges", false);
642 opt.animate_map_panning = opt_section_.get_bool("animate_map_panning", true);
643 opt.panel_snap_distance = opt_section_.get_int("panel_snap_distance", 0);
644 opt.border_snap_distance = opt_section_.get_int("border_snap_distance", 0);
645
646 // Saving options
647 opt.autosave = opt_section_.get_int("autosave", kDefaultAutosaveInterval * 60);
648 opt.rolling_autosave = opt_section_.get_int("rolling_autosave", 5);
649 opt.zip = !opt_section_.get_bool("nozip", false);
650 opt.write_syncstreams = opt_section_.get_bool("write_syncstreams", true);
651
652 // Game options
653 opt.auto_roadbuild_mode = opt_section_.get_bool("auto_roadbuild_mode", true);
654 opt.transparent_chat = opt_section_.get_bool("transparent_chat", true);
655 opt.single_watchwin = opt_section_.get_bool("single_watchwin", false);
656 opt.ctrl_zoom = opt_section_.get_bool("ctrl_zoom", false);
657 opt.game_clock = opt_section_.get_bool("game_clock", true);
658
659 // Language options
660 opt.language = opt_section_.get_string("language", "");
661
662 // Last tab for reloading the options menu
663 opt.active_tab = active_tab;
664 return opt;
665 }
666
save_options()667 void OptionsCtrl::save_options() {
668 OptionsCtrl::OptionsStruct opt = opt_dialog_->get_values();
669
670 // Interface options
671 opt_section_.set_int("xres", opt.xres);
672 opt_section_.set_int("yres", opt.yres);
673 opt_section_.set_bool("fullscreen", opt.fullscreen);
674 opt_section_.set_bool("inputgrab", opt.inputgrab);
675 opt_section_.set_int("maxfps", opt.maxfps);
676 opt_section_.set_bool("sdl_cursor", opt.sdl_cursor);
677
678 // Windows options
679 opt_section_.set_bool("snap_windows_only_when_overlapping", opt.snap_win_overlap_only);
680 opt_section_.set_bool("dock_windows_to_edges", opt.dock_windows_to_edges);
681 opt_section_.set_bool("animate_map_panning", opt.animate_map_panning);
682 opt_section_.set_int("panel_snap_distance", opt.panel_snap_distance);
683 opt_section_.set_int("border_snap_distance", opt.border_snap_distance);
684
685 // Saving options
686 opt_section_.set_int("autosave", opt.autosave * 60);
687 opt_section_.set_int("rolling_autosave", opt.rolling_autosave);
688 opt_section_.set_bool("nozip", !opt.zip);
689 opt_section_.set_bool("write_syncstreams", opt.write_syncstreams);
690
691 // Game options
692 opt_section_.set_bool("auto_roadbuild_mode", opt.auto_roadbuild_mode);
693 opt_section_.set_bool("transparent_chat", opt.transparent_chat);
694 opt_section_.set_bool("single_watchwin", opt.single_watchwin);
695 opt_section_.set_bool("ctrl_zoom", opt.ctrl_zoom);
696 opt_section_.set_bool("game_clock", opt.game_clock);
697
698 // Language options
699 opt_section_.set_string("language", opt.language);
700
701 WLApplication::get()->set_input_grab(opt.inputgrab);
702 g_mouse_cursor->set_use_sdl(opt_dialog_->get_values().sdl_cursor);
703 i18n::set_locale(opt.language);
704 UI::g_fh->reinitialize_fontset(i18n::get_locale());
705
706 // Sound options
707 g_sh->save_config();
708
709 // Now write to file
710 write_config();
711 }
712