2 --Copyright (C) 2015 PilzAdam
4 --This program is free software; you can redistribute it and/or modify
5 --it under the terms of the GNU Lesser General Public License as published by
6 --the Free Software Foundation; either version 2.1 of the License, or
7 --(at your option) any later version.
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 Lesser General Public License for more details.
14 --You should have received a copy of the GNU Lesser General Public License along
15 --with this program; if not, write to the Free Software Foundation, Inc.,
16 --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 local FILENAME = "settingtypes.txt"
20 local CHAR_CLASSES = {
22 VARIABLE = "[%w_%-%.]",
23 INTEGER = "[+-]?[%d]",
24 FLOAT = "[+-]?[%d%.]",
28 local function flags_to_table(flags)
29 return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
32 -- returns error message, or nil
33 local function parse_setting_line(settings, line, read_all, base_level, allow_secure)
35 -- strip carriage returns (CR, /r)
36 line = line:gsub("\r", "")
39 local comment = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$")
41 if settings.current_comment == "" then
42 settings.current_comment = comment
44 settings.current_comment = settings.current_comment .. "\n" .. comment
49 -- clear current_comment so only comments directly above a setting are bound to it
50 -- but keep a local reference to it for variables in the current line
51 local current_comment = settings.current_comment
52 settings.current_comment = ""
55 if line:match("^" .. CHAR_CLASSES.SPACE .. "*$") then
60 local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
62 table.insert(settings, {
64 level = stars:len() + base_level,
71 local first_part, name, readable_name, setting_type = line:match("^"
72 -- this first capture group matches the whole first part,
73 -- so we can later strip it from the rest of the line
75 .. "([" .. CHAR_CLASSES.VARIABLE .. "+)" -- variable name
76 .. CHAR_CLASSES.SPACE .. "*"
77 .. "%(([^%)]*)%)" -- readable name
78 .. CHAR_CLASSES.SPACE .. "*"
79 .. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
80 .. CHAR_CLASSES.SPACE .. "*"
83 if not first_part then
87 if name:match("secure%.[.]*") and not allow_secure then
88 return "Tried to add \"secure.\" setting"
91 if readable_name == "" then
94 local remaining_line = line:sub(first_part:len() + 1)
96 if setting_type == "int" then
97 local default, min, max = remaining_line:match("^"
98 -- first int is required, the last 2 are optional
99 .. "(" .. CHAR_CLASSES.INTEGER .. "+)" .. CHAR_CLASSES.SPACE .. "*"
100 .. "(" .. CHAR_CLASSES.INTEGER .. "*)" .. CHAR_CLASSES.SPACE .. "*"
101 .. "(" .. CHAR_CLASSES.INTEGER .. "*)"
104 if not default or not tonumber(default) then
105 return "Invalid integer setting"
110 table.insert(settings, {
112 readable_name = readable_name,
117 comment = current_comment,
122 if setting_type == "string"
123 or setting_type == "key" or setting_type == "v3f" then
124 local default = remaining_line:match("^(.*)$")
127 return "Invalid string setting"
129 if setting_type == "key" and not read_all then
130 -- ignore key type if read_all is false
134 table.insert(settings, {
136 readable_name = readable_name,
139 comment = current_comment,
144 if setting_type == "noise_params_2d"
145 or setting_type == "noise_params_3d" then
146 local default = remaining_line:match("^(.*)$")
149 return "Invalid string setting"
155 for match in default:gmatch("[+-]?[%d.-e]+") do -- All numeric characters
156 index = default:find("[+-]?[%d.-e]+", index) + match:len()
157 table.insert(values, match)
163 index = default:find("[^, ]", index)
166 flags = default:sub(index)
167 default = default:sub(1, index - 3) -- Make sure no flags in single-line format
169 table.insert(values, flags)
171 table.insert(settings, {
173 readable_name = readable_name,
186 persistence = values[8],
187 lacunarity = values[9],
191 comment = current_comment,
193 flags = flags_to_table("defaults,eased,absvalue")
198 if setting_type == "bool" then
199 if remaining_line ~= "false" and remaining_line ~= "true" then
200 return "Invalid boolean setting"
203 table.insert(settings, {
205 readable_name = readable_name,
207 default = remaining_line,
208 comment = current_comment,
213 if setting_type == "float" then
214 local default, min, max = remaining_line:match("^"
215 -- first float is required, the last 2 are optional
216 .. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "*"
217 .. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "*"
218 .. "(" .. CHAR_CLASSES.FLOAT .. "*)"
221 if not default or not tonumber(default) then
222 return "Invalid float setting"
227 table.insert(settings, {
229 readable_name = readable_name,
234 comment = current_comment,
239 if setting_type == "enum" then
240 local default, values = remaining_line:match("^"
241 -- first value (default) may be empty (i.e. is optional)
242 .. "(" .. CHAR_CLASSES.VARIABLE .. "*)" .. CHAR_CLASSES.SPACE .. "*"
243 .. "(" .. CHAR_CLASSES.FLAGS .. "+)"
246 if not default or values == "" then
247 return "Invalid enum setting"
250 table.insert(settings, {
252 readable_name = readable_name,
255 values = values:split(",", true),
256 comment = current_comment,
261 if setting_type == "path" or setting_type == "filepath" then
262 local default = remaining_line:match("^(.*)$")
265 return "Invalid path setting"
268 table.insert(settings, {
270 readable_name = readable_name,
273 comment = current_comment,
278 if setting_type == "flags" then
279 local default, possible = remaining_line:match("^"
280 -- first value (default) may be empty (i.e. is optional)
281 -- this is implemented by making the last value optional, and
282 -- swapping them around if it turns out empty.
283 .. "(" .. CHAR_CLASSES.FLAGS .. "+)" .. CHAR_CLASSES.SPACE .. "*"
284 .. "(" .. CHAR_CLASSES.FLAGS .. "*)"
287 if not default or not possible then
288 return "Invalid flags setting"
291 if possible == "" then
296 table.insert(settings, {
298 readable_name = readable_name,
301 possible = flags_to_table(possible),
302 comment = current_comment,
307 return "Invalid setting type \"" .. setting_type .. "\""
310 local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
311 -- store this helper variable in the table so it's easier to pass to parse_setting_line()
312 result.current_comment = ""
314 local line = file:read("*line")
316 local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure)
318 core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
320 line = file:read("*line")
323 result.current_comment = nil
326 -- read_all: whether to ignore certain setting types for GUI or not
327 -- parse_mods: whether to parse settingtypes.txt in mods and games
328 local function parse_config_file(read_all, parse_mods)
332 local builtin_path = core.get_builtin_path() .. FILENAME
333 local file = io.open(builtin_path, "r")
335 core.log("error", "Can't load " .. FILENAME)
339 parse_single_file(file, builtin_path, read_all, settings, 0, true)
346 local games_category_initialized = false
348 local game = pkgmgr.get_game(index)
350 local path = game.path .. DIR_DELIM .. FILENAME
351 local file = io.open(path, "r")
353 if not games_category_initialized then
354 fgettext_ne("Games") -- not used, but needed for xgettext
355 table.insert(settings, {
360 games_category_initialized = true
363 table.insert(settings, {
369 parse_single_file(file, path, read_all, settings, 2, false)
375 game = pkgmgr.get_game(index)
379 local mods_category_initialized = false
381 get_mods(core.get_modpath(), "mods", mods)
382 for _, mod in ipairs(mods) do
383 local path = mod.path .. DIR_DELIM .. FILENAME
384 local file = io.open(path, "r")
386 if not mods_category_initialized then
387 fgettext_ne("Mods") -- not used, but needed for xgettext
388 table.insert(settings, {
393 mods_category_initialized = true
396 table.insert(settings, {
402 parse_single_file(file, path, read_all, settings, 2, false)
412 local function filter_settings(settings, searchstring)
413 if not searchstring or searchstring == "" then
417 -- Setup the keyword list
419 for word in searchstring:lower():gmatch("%S+") do
420 table.insert(keywords, word)
424 local category_stack = {}
425 local current_level = 0
426 local best_setting = nil
427 for _, entry in pairs(settings) do
428 if entry.type == "category" then
429 -- Remove all settingless categories
430 while #category_stack > 0 and entry.level <= current_level do
431 table.remove(category_stack, #category_stack)
432 if #category_stack > 0 then
433 current_level = category_stack[#category_stack].level
439 -- Push category onto stack
440 category_stack[#category_stack + 1] = entry
441 current_level = entry.level
443 -- See if setting matches keywords
444 local setting_score = 0
445 for k = 1, #keywords do
446 local keyword = keywords[k]
448 if string.find(entry.name:lower(), keyword, 1, true) then
449 setting_score = setting_score + 1
452 if entry.readable_name and
453 string.find(fgettext(entry.readable_name):lower(), keyword, 1, true) then
454 setting_score = setting_score + 1
458 string.find(fgettext_ne(entry.comment):lower(), keyword, 1, true) then
459 setting_score = setting_score + 1
463 -- Add setting to results if match
464 if setting_score > 0 then
465 -- Add parent categories
466 for _, category in pairs(category_stack) do
467 result[#result + 1] = category
472 result[#result + 1] = entry
473 entry.score = setting_score
475 if not best_setting or
476 setting_score > result[best_setting].score then
477 best_setting = #result
482 return result, best_setting or -1
485 local full_settings = parse_config_file(false, true)
486 local search_string = ""
487 local settings = full_settings
488 local selected_setting = 1
490 local function get_current_value(setting)
491 local value = core.settings:get(setting.name)
493 value = setting.default
498 local function get_current_np_group(setting)
499 local value = core.settings:get_np_group(setting.name)
504 table.insert(t, value.offset)
505 table.insert(t, value.scale)
506 table.insert(t, value.spread.x)
507 table.insert(t, value.spread.y)
508 table.insert(t, value.spread.z)
509 table.insert(t, value.seed)
510 table.insert(t, value.octaves)
511 table.insert(t, value.persistence)
512 table.insert(t, value.lacunarity)
513 table.insert(t, value.flags)
518 local function get_current_np_group_as_string(setting)
519 local value = core.settings:get_np_group(setting.name)
521 return setting.default
523 return ("%g, %g, (%g, %g, %g), %g, %g, %g, %g"):format(
533 ) .. (value.flags ~= "" and (", " .. value.flags) or "")
536 local checkboxes = {} -- handle checkboxes events
538 local function create_change_setting_formspec(dialogdata)
539 local setting = settings[selected_setting]
540 -- Final formspec will be created at the end of this function
541 -- Default values below, may be changed depending on setting type
544 local description_height = 3
547 -- Setting-specific formspec elements
548 if setting.type == "bool" then
549 local selected_index = 1
550 if core.is_yes(get_current_value(setting)) then
553 formspec = "dropdown[3," .. height .. ";4,1;dd_setting_value;"
554 .. fgettext("Disabled") .. "," .. fgettext("Enabled") .. ";"
555 .. selected_index .. "]"
556 height = height + 1.25
558 elseif setting.type == "enum" then
559 local selected_index = 0
560 formspec = "dropdown[3," .. height .. ";4,1;dd_setting_value;"
561 for index, value in ipairs(setting.values) do
562 -- translating value is not possible, since it's the value
563 -- that we set the setting to
564 formspec = formspec .. core.formspec_escape(value) .. ","
565 if get_current_value(setting) == value then
566 selected_index = index
569 if #setting.values > 0 then
570 formspec = formspec:sub(1, -2) -- remove trailing comma
572 formspec = formspec .. ";" .. selected_index .. "]"
573 height = height + 1.25
575 elseif setting.type == "path" or setting.type == "filepath" then
576 local current_value = dialogdata.selected_path
577 if not current_value then
578 current_value = get_current_value(setting)
580 formspec = "field[0.28," .. height + 0.15 .. ";8,1;te_setting_value;;"
581 .. core.formspec_escape(current_value) .. "]"
582 .. "button[8," .. height - 0.15 .. ";2,1;btn_browser_"
583 .. setting.type .. ";" .. fgettext("Browse") .. "]"
584 height = height + 1.15
586 elseif setting.type == "noise_params_2d" or setting.type == "noise_params_3d" then
587 local t = get_current_np_group(setting)
589 if setting.type == "noise_params_2d" then
593 -- More space for 3x3 fields
594 description_height = description_height - 1.5
595 height = height - 1.5
598 local function add_field(x, name, label, value)
599 fields[#fields + 1] = ("field[%f,%f;3.3,1;%s;%s;%s]"):format(
600 x, height, name, label, core.formspec_escape(value or "")
604 height = height + 0.3
605 add_field(0.3, "te_offset", fgettext("Offset"), t[1])
606 add_field(3.6, "te_scale", fgettext("Scale"), t[2])
607 add_field(6.9, "te_seed", fgettext("Seed"), t[6])
608 height = height + 1.1
611 add_field(0.3, "te_spreadx", fgettext("X spread"), t[3])
612 if dimension == 3 then
613 add_field(3.6, "te_spready", fgettext("Y spread"), t[4])
615 fields[#fields + 1] = "label[4," .. height - 0.2 .. ";" ..
616 fgettext("2D Noise") .. "]"
618 add_field(6.9, "te_spreadz", fgettext("Z spread"), t[5])
619 height = height + 1.1
622 add_field(0.3, "te_octaves", fgettext("Octaves"), t[7])
623 add_field(3.6, "te_persist", fgettext("Persistence"), t[8])
624 add_field(6.9, "te_lacun", fgettext("Lacunarity"), t[9])
625 height = height + 1.1
628 local enabled_flags = flags_to_table(t[10])
630 for _, name in ipairs(enabled_flags) do
631 -- Index by name, to avoid iterating over all enabled_flags for every possible flag.
634 for _, name in ipairs(setting.flags) do
635 local checkbox_name = "cb_" .. name
636 local is_enabled = flags[name] == true -- to get false if nil
637 checkboxes[checkbox_name] = is_enabled
640 formspec = table.concat(fields)
641 .. "checkbox[0.5," .. height - 0.6 .. ";cb_defaults;"
642 --[[~ "defaults" is a noise parameter flag.
643 It describes the default processing options
644 for noise settings in main menu -> "All Settings". ]]
645 .. fgettext("defaults") .. ";" -- defaults
646 .. tostring(flags["defaults"] == true) .. "]" -- to get false if nil
647 .. "checkbox[5," .. height - 0.6 .. ";cb_eased;"
648 --[[~ "eased" is a noise parameter flag.
649 It is used to make the map smoother and
650 can be enabled in noise settings in
651 main menu -> "All Settings". ]]
652 .. fgettext("eased") .. ";" -- eased
653 .. tostring(flags["eased"] == true) .. "]"
654 .. "checkbox[5," .. height - 0.15 .. ";cb_absvalue;"
655 --[[~ "absvalue" is a noise parameter flag.
656 It is short for "absolute value".
657 It can be enabled in noise settings in
658 main menu -> "All Settings". ]]
659 .. fgettext("absvalue") .. ";" -- absvalue
660 .. tostring(flags["absvalue"] == true) .. "]"
663 elseif setting.type == "v3f" then
664 local val = get_current_value(setting)
666 for line in val:gmatch("[+-]?[%d.+-eE]+") do -- All numeric characters
667 table.insert(v3f, line)
670 height = height + 0.3
672 .. "field[0.3," .. height .. ";3.3,1;te_x;"
673 .. fgettext("X") .. ";" -- X
674 .. core.formspec_escape(v3f[1] or "") .. "]"
675 .. "field[3.6," .. height .. ";3.3,1;te_y;"
676 .. fgettext("Y") .. ";" -- Y
677 .. core.formspec_escape(v3f[2] or "") .. "]"
678 .. "field[6.9," .. height .. ";3.3,1;te_z;"
679 .. fgettext("Z") .. ";" -- Z
680 .. core.formspec_escape(v3f[3] or "") .. "]"
681 height = height + 1.1
683 elseif setting.type == "flags" then
684 local current_flags = flags_to_table(get_current_value(setting))
686 for _, name in ipairs(current_flags) do
687 -- Index by name, to avoid iterating over all enabled_flags for every possible flag.
688 if name:sub(1, 2) == "no" then
689 flags[name:sub(3)] = false
694 local flags_count = #setting.possible / 2
695 local max_height = math.ceil(flags_count / 2) / 2
697 -- More space for flags
698 description_height = description_height - 1
701 local fields = {} -- To build formspec
703 for _, name in ipairs(setting.possible) do
704 if name:sub(1, 2) ~= "no" then
706 local y = height + j / 2 - 0.75
707 if j - 1 >= flags_count / 2 then -- 2nd column
712 local checkbox_name = "cb_" .. name
713 local is_enabled = flags[name] == true -- to get false if nil
714 checkboxes[checkbox_name] = is_enabled
716 fields[#fields + 1] = ("checkbox[%f,%f;%s;%s;%s]"):format(
717 x, y, checkbox_name, name, tostring(is_enabled)
721 formspec = table.concat(fields)
722 height = height + max_height + 0.25
725 -- TODO: fancy input for float, int
726 local text = get_current_value(setting)
727 if dialogdata.error_message and dialogdata.entered_text then
728 text = dialogdata.entered_text
730 formspec = "field[0.28," .. height + 0.15 .. ";" .. width .. ",1;te_setting_value;;"
731 .. core.formspec_escape(text) .. "]"
732 height = height + 1.15
735 -- Box good, textarea bad. Calculate textarea size from box.
736 local function create_textfield(size, label, text, bg_color)
741 h = size.h * 1.16 + 0.12
743 return ("box[%f,%f;%f,%f;%s]textarea[%f,%f;%f,%f;;%s;%s]"):format(
744 size.x, size.y, size.w, size.h, bg_color or "#000",
745 textarea.x, textarea.y, textarea.w, textarea.h,
746 core.formspec_escape(label), core.formspec_escape(text)
751 -- When there's an error: Shrink description textarea and add error below
752 if dialogdata.error_message then
755 y = description_height - 0.4,
759 formspec = formspec ..
760 create_textfield(error_box, "", dialogdata.error_message, "#600")
761 description_height = description_height - 0.75
764 -- Get description field
765 local description_box = {
769 h = description_height
772 local setting_name = setting.name
773 if setting.readable_name then
774 setting_name = fgettext_ne(setting.readable_name) ..
775 " (" .. setting.name .. ")"
779 if setting.comment == "" then
780 comment_text = fgettext_ne("(No description of setting given)")
782 comment_text = fgettext_ne(setting.comment)
786 "size[" .. width .. "," .. height + 0.25 .. ",true]" ..
787 create_textfield(description_box, setting_name, comment_text) ..
789 "button[" .. width / 2 - 2.5 .. "," .. height - 0.4 .. ";2.5,1;btn_done;" ..
790 fgettext("Save") .. "]" ..
791 "button[" .. width / 2 .. "," .. height - 0.4 .. ";2.5,1;btn_cancel;" ..
792 fgettext("Cancel") .. "]"
796 local function handle_change_setting_buttons(this, fields)
797 local setting = settings[selected_setting]
798 if fields["btn_done"] or fields["key_enter"] then
799 if setting.type == "bool" then
800 local new_value = fields["dd_setting_value"]
801 -- Note: new_value is the actual (translated) value shown in the dropdown
802 core.settings:set_bool(setting.name, new_value == fgettext("Enabled"))
804 elseif setting.type == "enum" then
805 local new_value = fields["dd_setting_value"]
806 core.settings:set(setting.name, new_value)
808 elseif setting.type == "int" then
809 local new_value = tonumber(fields["te_setting_value"])
810 if not new_value or math.floor(new_value) ~= new_value then
811 this.data.error_message = fgettext_ne("Please enter a valid integer.")
812 this.data.entered_text = fields["te_setting_value"]
813 core.update_formspec(this:get_formspec())
816 if setting.min and new_value < setting.min then
817 this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min)
818 this.data.entered_text = fields["te_setting_value"]
819 core.update_formspec(this:get_formspec())
822 if setting.max and new_value > setting.max then
823 this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max)
824 this.data.entered_text = fields["te_setting_value"]
825 core.update_formspec(this:get_formspec())
828 core.settings:set(setting.name, new_value)
830 elseif setting.type == "float" then
831 local new_value = tonumber(fields["te_setting_value"])
832 if not new_value then
833 this.data.error_message = fgettext_ne("Please enter a valid number.")
834 this.data.entered_text = fields["te_setting_value"]
835 core.update_formspec(this:get_formspec())
838 if setting.min and new_value < setting.min then
839 this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min)
840 this.data.entered_text = fields["te_setting_value"]
841 core.update_formspec(this:get_formspec())
844 if setting.max and new_value > setting.max then
845 this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max)
846 this.data.entered_text = fields["te_setting_value"]
847 core.update_formspec(this:get_formspec())
850 core.settings:set(setting.name, new_value)
852 elseif setting.type == "flags" then
854 for _, name in ipairs(setting.possible) do
855 if name:sub(1, 2) ~= "no" then
856 if checkboxes["cb_" .. name] then
857 table.insert(values, name)
859 table.insert(values, "no" .. name)
866 local new_value = table.concat(values, ", ")
867 core.settings:set(setting.name, new_value)
869 elseif setting.type == "noise_params_2d" or setting.type == "noise_params_3d" then
871 for _, name in ipairs(setting.flags) do
872 if checkboxes["cb_" .. name] then
873 table.insert(np_flags, name)
879 if setting.type == "noise_params_2d" then
880 fields["te_spready"] = fields["te_spreadz"]
883 offset = fields["te_offset"],
884 scale = fields["te_scale"],
886 x = fields["te_spreadx"],
887 y = fields["te_spready"],
888 z = fields["te_spreadz"]
890 seed = fields["te_seed"],
891 octaves = fields["te_octaves"],
892 persistence = fields["te_persist"],
893 lacunarity = fields["te_lacun"],
894 flags = table.concat(np_flags, ", ")
896 core.settings:set_np_group(setting.name, new_value)
898 elseif setting.type == "v3f" then
899 local new_value = "("
900 .. fields["te_x"] .. ", "
901 .. fields["te_y"] .. ", "
902 .. fields["te_z"] .. ")"
903 core.settings:set(setting.name, new_value)
906 local new_value = fields["te_setting_value"]
907 core.settings:set(setting.name, new_value)
909 core.settings:write()
914 if fields["btn_cancel"] then
919 if fields["btn_browser_path"] then
920 core.show_path_select_dialog("dlg_browse_path",
921 fgettext_ne("Select directory"), false)
924 if fields["btn_browser_filepath"] then
925 core.show_path_select_dialog("dlg_browse_path",
926 fgettext_ne("Select file"), true)
929 if fields["dlg_browse_path_accepted"] then
930 this.data.selected_path = fields["dlg_browse_path_accepted"]
931 core.update_formspec(this:get_formspec())
934 if setting.type == "flags"
935 or setting.type == "noise_params_2d"
936 or setting.type == "noise_params_3d" then
937 for name, value in pairs(fields) do
938 if name:sub(1, 3) == "cb_" then
939 checkboxes[name] = value == "true"
947 local function create_settings_formspec(tabview, _, tabdata)
948 local formspec = "size[12,5.4;true]" ..
949 "tablecolumns[color;tree;text,width=28;text]" ..
950 "tableoptions[background=#00000000;border=false]" ..
951 "field[0.3,0.1;10.2,1;search_string;;" .. core.formspec_escape(search_string) .. "]" ..
952 "field_close_on_enter[search_string;false]" ..
953 "button[10.2,-0.2;2,1;search;" .. fgettext("Search") .. "]" ..
954 "table[0,0.8;12,3.5;list_settings;"
956 local current_level = 0
957 for _, entry in ipairs(settings) do
959 if not core.settings:get_bool("main_menu_technical_settings") and entry.readable_name then
960 name = fgettext_ne(entry.readable_name)
965 if entry.type == "category" then
966 current_level = entry.level
967 formspec = formspec .. "#FFFF00," .. current_level .. "," .. fgettext(name) .. ",,"
969 elseif entry.type == "bool" then
970 local value = get_current_value(entry)
971 if core.is_yes(value) then
972 value = fgettext("Enabled")
974 value = fgettext("Disabled")
976 formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
979 elseif entry.type == "key" then --luacheck: ignore
980 -- ignore key settings, since we have a special dialog for them
982 elseif entry.type == "noise_params_2d" or entry.type == "noise_params_3d" then
983 formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
984 .. core.formspec_escape(get_current_np_group_as_string(entry)) .. ","
987 formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
988 .. core.formspec_escape(get_current_value(entry)) .. ","
992 if #settings > 0 then
993 formspec = formspec:sub(1, -2) -- remove trailing comma
995 formspec = formspec .. ";" .. selected_setting .. "]" ..
996 "button[0,4.9;4,1;btn_back;".. fgettext("< Back to Settings page") .. "]" ..
997 "button[10,4.9;2,1;btn_edit;" .. fgettext("Edit") .. "]" ..
998 "button[7,4.9;3,1;btn_restore;" .. fgettext("Restore Default") .. "]" ..
999 "checkbox[0,4.3;cb_tech_settings;" .. fgettext("Show technical names") .. ";"
1000 .. dump(core.settings:get_bool("main_menu_technical_settings")) .. "]"
1005 local function handle_settings_buttons(this, fields, tabname, tabdata)
1006 local list_enter = false
1007 if fields["list_settings"] then
1008 selected_setting = core.get_table_index("list_settings")
1009 if core.explode_table_event(fields["list_settings"]).type == "DCL" then
1010 -- Directly toggle booleans
1011 local setting = settings[selected_setting]
1012 if setting and setting.type == "bool" then
1013 local current_value = get_current_value(setting)
1014 core.settings:set_bool(setting.name, not core.is_yes(current_value))
1015 core.settings:write()
1025 if fields.search or fields.key_enter_field == "search_string" then
1026 if search_string == fields.search_string then
1027 if selected_setting > 0 then
1028 -- Go to next result on enter press
1029 local i = selected_setting + 1
1030 local looped = false
1031 while i > #settings or settings[i].type == "category" do
1033 if i > #settings then
1034 -- Stop infinte looping
1042 selected_setting = i
1043 core.update_formspec(this:get_formspec())
1047 -- Search for setting
1048 search_string = fields.search_string
1049 settings, selected_setting = filter_settings(full_settings, search_string)
1050 core.update_formspec(this:get_formspec())
1055 if fields["btn_edit"] or list_enter then
1056 local setting = settings[selected_setting]
1057 if setting and setting.type ~= "category" then
1058 local edit_dialog = dialog_create("change_setting",
1059 create_change_setting_formspec, handle_change_setting_buttons)
1060 edit_dialog:set_parent(this)
1067 if fields["btn_restore"] then
1068 local setting = settings[selected_setting]
1069 if setting and setting.type ~= "category" then
1070 core.settings:remove(setting.name)
1071 core.settings:write()
1072 core.update_formspec(this:get_formspec())
1077 if fields["btn_back"] then
1082 if fields["cb_tech_settings"] then
1083 core.settings:set("main_menu_technical_settings", fields["cb_tech_settings"])
1084 core.settings:write()
1085 core.update_formspec(this:get_formspec())
1092 function create_adv_settings_dlg()
1093 local dlg = dialog_create("settings_advanced",
1094 create_settings_formspec,
1095 handle_settings_buttons,
1101 -- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'.
1102 -- For RUN_IN_PLACE the generated files may appear in the 'bin' folder.
1103 -- See comment and alternative line at the end of 'generate_from_settingtypes.lua'.
1105 --assert(loadfile(core.get_builtin_path().."mainmenu"..DIR_DELIM..
1106 -- "generate_from_settingtypes.lua"))(parse_config_file(true, false))