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 local comment = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$")
37 if settings.current_comment == "" then
38 settings.current_comment = comment
40 settings.current_comment = settings.current_comment .. "\n" .. comment
45 -- clear current_comment so only comments directly above a setting are bound to it
46 -- but keep a local reference to it for variables in the current line
47 local current_comment = settings.current_comment
48 settings.current_comment = ""
51 if line:match("^" .. CHAR_CLASSES.SPACE .. "*$") then
56 local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
58 table.insert(settings, {
60 level = stars:len() + base_level,
67 local first_part, name, readable_name, setting_type = line:match("^"
68 -- this first capture group matches the whole first part,
69 -- so we can later strip it from the rest of the line
71 .. "([" .. CHAR_CLASSES.VARIABLE .. "+)" -- variable name
72 .. CHAR_CLASSES.SPACE .. "*"
73 .. "%(([^%)]*)%)" -- readable name
74 .. CHAR_CLASSES.SPACE .. "*"
75 .. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
76 .. CHAR_CLASSES.SPACE .. "*"
79 if not first_part then
83 if name:match("secure%.[.]*") and not allow_secure then
84 return "Tried to add \"secure.\" setting"
87 if readable_name == "" then
90 local remaining_line = line:sub(first_part:len() + 1)
92 if setting_type == "int" then
93 local default, min, max = remaining_line:match("^"
94 -- first int is required, the last 2 are optional
95 .. "(" .. CHAR_CLASSES.INTEGER .. "+)" .. CHAR_CLASSES.SPACE .. "*"
96 .. "(" .. CHAR_CLASSES.INTEGER .. "*)" .. CHAR_CLASSES.SPACE .. "*"
97 .. "(" .. CHAR_CLASSES.INTEGER .. "*)"
100 if not default or not tonumber(default) then
101 return "Invalid integer setting"
106 table.insert(settings, {
108 readable_name = readable_name,
113 comment = current_comment,
118 if setting_type == "string"
119 or setting_type == "key" or setting_type == "v3f" then
120 local default = remaining_line:match("^(.*)$")
123 return "Invalid string setting"
125 if setting_type == "key" and not read_all then
126 -- ignore key type if read_all is false
130 table.insert(settings, {
132 readable_name = readable_name,
135 comment = current_comment,
140 if setting_type == "noise_params_2d"
141 or setting_type == "noise_params_3d" then
142 local default = remaining_line:match("^(.*)$")
145 return "Invalid string setting"
151 for line in default:gmatch("[+-]?[%d.-e]+") do -- All numeric characters
152 index = default:find("[+-]?[%d.-e]+", index) + line:len()
153 table.insert(values, line)
159 index = default:find("[^, ]", index)
162 flags = default:sub(index)
163 default = default:sub(1, index - 3) -- Make sure no flags in single-line format
165 table.insert(values, flags)
167 table.insert(settings, {
169 readable_name = readable_name,
182 persistence = values[8],
183 lacunarity = values[9],
187 comment = current_comment,
189 flags = flags_to_table("defaults,eased,absvalue")
194 if setting_type == "bool" then
195 if remaining_line ~= "false" and remaining_line ~= "true" then
196 return "Invalid boolean setting"
199 table.insert(settings, {
201 readable_name = readable_name,
203 default = remaining_line,
204 comment = current_comment,
209 if setting_type == "float" then
210 local default, min, max = remaining_line:match("^"
211 -- first float is required, the last 2 are optional
212 .. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "*"
213 .. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "*"
214 .. "(" .. CHAR_CLASSES.FLOAT .. "*)"
217 if not default or not tonumber(default) then
218 return "Invalid float setting"
223 table.insert(settings, {
225 readable_name = readable_name,
230 comment = current_comment,
235 if setting_type == "enum" then
236 local default, values = remaining_line:match("^"
237 -- first value (default) may be empty (i.e. is optional)
238 .. "(" .. CHAR_CLASSES.VARIABLE .. "*)" .. CHAR_CLASSES.SPACE .. "*"
239 .. "(" .. CHAR_CLASSES.FLAGS .. "+)"
242 if not default or values == "" then
243 return "Invalid enum setting"
246 table.insert(settings, {
248 readable_name = readable_name,
251 values = values:split(",", true),
252 comment = current_comment,
257 if setting_type == "path" or setting_type == "filepath" then
258 local default = remaining_line:match("^(.*)$")
261 return "Invalid path setting"
264 table.insert(settings, {
266 readable_name = readable_name,
269 comment = current_comment,
274 if setting_type == "flags" then
275 local default, possible = remaining_line:match("^"
276 -- first value (default) may be empty (i.e. is optional)
277 -- this is implemented by making the last value optional, and
278 -- swapping them around if it turns out empty.
279 .. "(" .. CHAR_CLASSES.FLAGS .. "+)" .. CHAR_CLASSES.SPACE .. "*"
280 .. "(" .. CHAR_CLASSES.FLAGS .. "*)"
283 if not default or not possible then
284 return "Invalid flags setting"
287 if possible == "" then
292 table.insert(settings, {
294 readable_name = readable_name,
297 possible = flags_to_table(possible),
298 comment = current_comment,
303 return "Invalid setting type \"" .. setting_type .. "\""
306 local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
307 -- store this helper variable in the table so it's easier to pass to parse_setting_line()
308 result.current_comment = ""
310 local line = file:read("*line")
312 local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure)
314 core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
316 line = file:read("*line")
319 result.current_comment = nil
322 -- read_all: whether to ignore certain setting types for GUI or not
323 -- parse_mods: whether to parse settingtypes.txt in mods and games
324 local function parse_config_file(read_all, parse_mods)
325 local builtin_path = core.get_builtin_path() .. FILENAME
326 local file = io.open(builtin_path, "r")
329 core.log("error", "Can't load " .. FILENAME)
333 parse_single_file(file, builtin_path, read_all, settings, 0, true)
339 local games_category_initialized = false
341 local game = pkgmgr.get_game(index)
343 local path = game.path .. DIR_DELIM .. FILENAME
344 local file = io.open(path, "r")
346 if not games_category_initialized then
347 local translation = fgettext_ne("Games"), -- not used, but needed for xgettext
348 table.insert(settings, {
353 games_category_initialized = true
356 table.insert(settings, {
362 parse_single_file(file, path, read_all, settings, 2, false)
368 game = pkgmgr.get_game(index)
372 local mods_category_initialized = false
374 get_mods(core.get_modpath(), mods)
375 for _, mod in ipairs(mods) do
376 local path = mod.path .. DIR_DELIM .. FILENAME
377 local file = io.open(path, "r")
379 if not mods_category_initialized then
380 local translation = fgettext_ne("Mods"), -- not used, but needed for xgettext
381 table.insert(settings, {
386 mods_category_initialized = true
389 table.insert(settings, {
395 parse_single_file(file, path, read_all, settings, 2, false)
405 local function filter_settings(settings, searchstring)
406 if not searchstring or searchstring == "" then
410 -- Setup the keyword list
412 for word in searchstring:lower():gmatch("%S+") do
413 table.insert(keywords, word)
417 local category_stack = {}
418 local current_level = 0
419 local best_setting = nil
420 for _, entry in pairs(settings) do
421 if entry.type == "category" then
422 -- Remove all settingless categories
423 while #category_stack > 0 and entry.level <= current_level do
424 table.remove(category_stack, #category_stack)
425 if #category_stack > 0 then
426 current_level = category_stack[#category_stack].level
432 -- Push category onto stack
433 category_stack[#category_stack + 1] = entry
434 current_level = entry.level
436 -- See if setting matches keywords
437 local setting_score = 0
438 for k = 1, #keywords do
439 local keyword = keywords[k]
441 if string.find(entry.name:lower(), keyword, 1, true) then
442 setting_score = setting_score + 1
445 if entry.readable_name and
446 string.find(fgettext(entry.readable_name):lower(), keyword, 1, true) then
447 setting_score = setting_score + 1
451 string.find(fgettext_ne(entry.comment):lower(), keyword, 1, true) then
452 setting_score = setting_score + 1
456 -- Add setting to results if match
457 if setting_score > 0 then
458 -- Add parent categories
459 for _, category in pairs(category_stack) do
460 result[#result + 1] = category
465 result[#result + 1] = entry
466 entry.score = setting_score
468 if not best_setting or
469 setting_score > result[best_setting].score then
470 best_setting = #result
475 return result, best_setting or -1
478 local full_settings = parse_config_file(false, true)
479 local search_string = ""
480 local settings = full_settings
481 local selected_setting = 1
483 local function get_current_value(setting)
484 local value = core.settings:get(setting.name)
486 value = setting.default
491 local function get_current_np_group(setting)
492 local value = core.settings:get_np_group(setting.name)
497 table.insert(t, value.offset)
498 table.insert(t, value.scale)
499 table.insert(t, value.spread.x)
500 table.insert(t, value.spread.y)
501 table.insert(t, value.spread.z)
502 table.insert(t, value.seed)
503 table.insert(t, value.octaves)
504 table.insert(t, value.persistence)
505 table.insert(t, value.lacunarity)
506 table.insert(t, value.flags)
511 local function get_current_np_group_as_string(setting)
512 local value = core.settings:get_np_group(setting.name)
517 t = value.offset .. ", " ..
518 value.scale .. ", (" ..
519 value.spread.x .. ", " ..
520 value.spread.y .. ", " ..
521 value.spread.z .. "), " ..
522 value.seed .. ", " ..
523 value.octaves .. ", " ..
524 value.persistence .. ", " ..
525 value.lacunarity .. ", " ..
531 local checkboxes = {} -- handle checkboxes events
533 local function create_change_setting_formspec(dialogdata)
534 local setting = settings[selected_setting]
535 -- Final formspec will be created at the end of this function
536 -- Default values below, may be changed depending on setting type
539 local description_height = 3
542 -- Setting-specific formspec elements
543 if setting.type == "bool" then
544 local selected_index = 1
545 if core.is_yes(get_current_value(setting)) then
548 formspec = "dropdown[3," .. height .. ";4,1;dd_setting_value;"
549 .. fgettext("Disabled") .. "," .. fgettext("Enabled") .. ";"
550 .. selected_index .. "]"
551 height = height + 1.25
553 elseif setting.type == "enum" then
554 local selected_index = 0
555 formspec = "dropdown[3," .. height .. ";4,1;dd_setting_value;"
556 for index, value in ipairs(setting.values) do
557 -- translating value is not possible, since it's the value
558 -- that we set the setting to
559 formspec = formspec .. core.formspec_escape(value) .. ","
560 if get_current_value(setting) == value then
561 selected_index = index
564 if #setting.values > 0 then
565 formspec = formspec:sub(1, -2) -- remove trailing comma
567 formspec = formspec .. ";" .. selected_index .. "]"
568 height = height + 1.25
570 elseif setting.type == "path" or setting.type == "filepath" then
571 local current_value = dialogdata.selected_path
572 if not current_value then
573 current_value = get_current_value(setting)
575 formspec = "field[0.28," .. height + 0.15 .. ";8,1;te_setting_value;;"
576 .. core.formspec_escape(current_value) .. "]"
577 .. "button[8," .. height - 0.15 .. ";2,1;btn_browser_"
578 .. setting.type .. ";" .. fgettext("Browse") .. "]"
579 height = height + 1.15
581 elseif setting.type == "noise_params_2d" or setting.type == "noise_params_3d" then
582 local t = get_current_np_group(setting)
584 if setting.type == "noise_params_2d" then
588 -- More space for 3x3 fields
589 description_height = description_height - 1.5
590 height = height - 1.5
593 local function add_field(x, name, label, value)
594 fields[#fields + 1] = ("field[%f,%f;3.3,1;%s;%s;%s]"):format(
595 x, height, name, label, core.formspec_escape(value or "")
599 height = height + 0.3
600 add_field(0.3, "te_offset", "Offset", t[1])
601 add_field(3.6, "te_scale", "Scale", t[2])
602 add_field(6.9, "te_seed", "Seed", t[6])
603 height = height + 1.1
606 add_field(0.3, "te_spreadx", "X spread", t[3])
607 if dimension == 3 then
608 add_field(3.6, "te_spready", "Y spread", t[4])
610 fields[#fields + 1] = "label[4," .. height - 0.2 .. ";2D Noise]"
612 add_field(6.9, "te_spreadz", "Z spread", t[5])
613 height = height + 1.1
616 add_field(0.3, "te_octaves", "Octaves", t[7])
617 add_field(3.6, "te_persist", "Persistance", t[8])
618 add_field(6.9, "te_lacun", "Lacunarity", t[9])
619 height = height + 1.1
622 local enabled_flags = flags_to_table(t[10])
624 for _, name in ipairs(enabled_flags) do
625 -- Index by name, to avoid iterating over all enabled_flags for every possible flag.
628 for _, name in ipairs(setting.flags) do
629 local checkbox_name = "cb_" .. name
630 local is_enabled = flags[name] == true -- to get false if nil
631 checkboxes[checkbox_name] = is_enabled
634 formspec = table.concat(fields)
635 .. "checkbox[0.5," .. height - 0.6 .. ";cb_defaults;defaults;" -- defaults
636 .. tostring(flags["defaults"] == true) .. "]" -- to get false if nil
637 .. "checkbox[5," .. height - 0.6 .. ";cb_eased;eased;" -- eased
638 .. tostring(flags["eased"] == true) .. "]"
639 .. "checkbox[5," .. height - 0.15 .. ";cb_absvalue;absvalue;" -- absvalue
640 .. tostring(flags["absvalue"] == true) .. "]"
643 elseif setting.type == "v3f" then
644 local val = get_current_value(setting)
646 for line in val:gmatch("[+-]?[%d.-e]+") do -- All numeric characters
647 table.insert(v3f, line)
650 height = height + 0.3
652 .. "field[0.3," .. height .. ";3.3,1;te_x;X;" -- X
653 .. core.formspec_escape(v3f[1] or "") .. "]"
654 .. "field[3.6," .. height .. ";3.3,1;te_y;Y;" -- Y
655 .. core.formspec_escape(v3f[2] or "") .. "]"
656 .. "field[6.9," .. height .. ";3.3,1;te_z;Z;" -- Z
657 .. core.formspec_escape(v3f[3] or "") .. "]"
658 height = height + 1.1
660 elseif setting.type == "flags" then
661 local enabled_flags = flags_to_table(get_current_value(setting))
663 for _, name in ipairs(enabled_flags) do
664 -- Index by name, to avoid iterating over all enabled_flags for every possible flag.
667 local flags_count = #setting.possible
668 local max_height = flags_count / 4
670 -- More space for flags
671 description_height = description_height - 1
674 local fields = {} -- To build formspec
675 for i, name in ipairs(setting.possible) do
677 local y = height + i / 2 - 0.75
678 if i - 1 >= flags_count / 2 then -- 2nd column
682 local checkbox_name = "cb_" .. name
683 local is_enabled = flags[name] == true -- to get false if nil
684 checkboxes[checkbox_name] = is_enabled
686 fields[#fields + 1] = ("checkbox[%f,%f;%s;%s;%s]"):format(
687 x, y, checkbox_name, name, tostring(is_enabled)
690 formspec = table.concat(fields)
691 height = height + max_height + 0.25
694 -- TODO: fancy input for float, int
695 local text = get_current_value(setting)
696 if dialogdata.error_message and dialogdata.entered_text then
697 text = dialogdata.entered_text
699 formspec = "field[0.28," .. height + 0.15 .. ";" .. width .. ",1;te_setting_value;;"
700 .. core.formspec_escape(text) .. "]"
701 height = height + 1.15
704 -- Box good, textarea bad. Calculate textarea size from box.
705 local function create_textfield(size, label, text, bg_color)
710 h = size.h * 1.16 + 0.12
712 return ("box[%f,%f;%f,%f;%s]textarea[%f,%f;%f,%f;;%s;%s]"):format(
713 size.x, size.y, size.w, size.h, bg_color or "#000",
714 textarea.x, textarea.y, textarea.w, textarea.h,
715 core.formspec_escape(label), core.formspec_escape(text)
720 -- When there's an error: Shrink description textarea and add error below
721 if dialogdata.error_message then
724 y = description_height - 0.4,
728 formspec = formspec ..
729 create_textfield(error_box, "", dialogdata.error_message, "#600")
730 description_height = description_height - 0.75
733 -- Get description field
734 local description_box = {
738 h = description_height
741 local setting_name = setting.name
742 if setting.readable_name then
743 setting_name = fgettext_ne(setting.readable_name) ..
744 " (" .. setting.name .. ")"
747 local comment_text = ""
748 if setting.comment == "" then
749 comment_text = fgettext_ne("(No description of setting given)")
751 comment_text = fgettext_ne(setting.comment)
755 "size[" .. width .. "," .. height + 0.25 .. ",true]" ..
756 create_textfield(description_box, setting_name, comment_text) ..
758 "button[" .. width / 2 - 2.5 .. "," .. height - 0.4 .. ";2.5,1;btn_done;" ..
759 fgettext("Save") .. "]" ..
760 "button[" .. width / 2 .. "," .. height - 0.4 .. ";2.5,1;btn_cancel;" ..
761 fgettext("Cancel") .. "]"
765 local function handle_change_setting_buttons(this, fields)
766 local setting = settings[selected_setting]
767 if fields["btn_done"] or fields["key_enter"] then
768 if setting.type == "bool" then
769 local new_value = fields["dd_setting_value"]
770 -- Note: new_value is the actual (translated) value shown in the dropdown
771 core.settings:set_bool(setting.name, new_value == fgettext("Enabled"))
773 elseif setting.type == "enum" then
774 local new_value = fields["dd_setting_value"]
775 core.settings:set(setting.name, new_value)
777 elseif setting.type == "int" then
778 local new_value = tonumber(fields["te_setting_value"])
779 if not new_value or math.floor(new_value) ~= new_value then
780 this.data.error_message = fgettext_ne("Please enter a valid integer.")
781 this.data.entered_text = fields["te_setting_value"]
782 core.update_formspec(this:get_formspec())
785 if setting.min and new_value < setting.min then
786 this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min)
787 this.data.entered_text = fields["te_setting_value"]
788 core.update_formspec(this:get_formspec())
791 if setting.max and new_value > setting.max then
792 this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max)
793 this.data.entered_text = fields["te_setting_value"]
794 core.update_formspec(this:get_formspec())
797 core.settings:set(setting.name, new_value)
799 elseif setting.type == "float" then
800 local new_value = tonumber(fields["te_setting_value"])
801 if not new_value then
802 this.data.error_message = fgettext_ne("Please enter a valid number.")
803 this.data.entered_text = fields["te_setting_value"]
804 core.update_formspec(this:get_formspec())
807 if setting.min and new_value < setting.min then
808 this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min)
809 this.data.entered_text = fields["te_setting_value"]
810 core.update_formspec(this:get_formspec())
813 if setting.max and new_value > setting.max then
814 this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max)
815 this.data.entered_text = fields["te_setting_value"]
816 core.update_formspec(this:get_formspec())
819 core.settings:set(setting.name, new_value)
821 elseif setting.type == "flags" then
823 for _, name in ipairs(setting.possible) do
824 if checkboxes["cb_" .. name] then
825 table.insert(values, name)
831 local new_value = table.concat(values, ", ")
832 core.settings:set(setting.name, new_value)
834 elseif setting.type == "noise_params_2d" or setting.type == "noise_params_3d" then
836 for _, name in ipairs(setting.flags) do
837 if checkboxes["cb_" .. name] then
838 table.insert(np_flags, name)
844 if setting.type == "noise_params_2d" then
845 fields["te_spready"] = fields["te_spreadz"]
848 offset = fields["te_offset"],
849 scale = fields["te_scale"],
851 x = fields["te_spreadx"],
852 y = fields["te_spready"],
853 z = fields["te_spreadz"]
855 seed = fields["te_seed"],
856 octaves = fields["te_octaves"],
857 persistence = fields["te_persist"],
858 lacunarity = fields["te_lacun"],
859 flags = table.concat(np_flags, ", ")
861 core.settings:set_np_group(setting.name, new_value)
863 elseif setting.type == "v3f" then
864 local new_value = "("
865 .. fields["te_x"] .. ", "
866 .. fields["te_y"] .. ", "
867 .. fields["te_z"] .. ")"
868 core.settings:set(setting.name, new_value)
871 local new_value = fields["te_setting_value"]
872 core.settings:set(setting.name, new_value)
874 core.settings:write()
879 if fields["btn_cancel"] then
884 if fields["btn_browser_path"] then
885 core.show_path_select_dialog("dlg_browse_path",
886 fgettext_ne("Select directory"), false)
889 if fields["btn_browser_filepath"] then
890 core.show_path_select_dialog("dlg_browse_path",
891 fgettext_ne("Select file"), true)
894 if fields["dlg_browse_path_accepted"] then
895 this.data.selected_path = fields["dlg_browse_path_accepted"]
896 core.update_formspec(this:get_formspec())
899 if setting.type == "flags"
900 or setting.type == "noise_params_2d"
901 or setting.type == "noise_params_3d" then
902 for name, value in pairs(fields) do
903 if name:sub(1, 3) == "cb_" then
904 checkboxes[name] = value == "true"
912 local function create_settings_formspec(tabview, name, tabdata)
913 local formspec = "size[12,5.4;true]" ..
914 "tablecolumns[color;tree;text,width=28;text]" ..
915 "tableoptions[background=#00000000;border=false]" ..
916 "field[0.3,0.1;10.2,1;search_string;;" .. core.formspec_escape(search_string) .. "]" ..
917 "field_close_on_enter[search_string;false]" ..
918 "button[10.2,-0.2;2,1;search;" .. fgettext("Search") .. "]" ..
919 "table[0,0.8;12,3.5;list_settings;"
921 local current_level = 0
922 for _, entry in ipairs(settings) do
924 if not core.settings:get_bool("main_menu_technical_settings") and entry.readable_name then
925 name = fgettext_ne(entry.readable_name)
930 if entry.type == "category" then
931 current_level = entry.level
932 formspec = formspec .. "#FFFF00," .. current_level .. "," .. fgettext(name) .. ",,"
934 elseif entry.type == "bool" then
935 local value = get_current_value(entry)
936 if core.is_yes(value) then
937 value = fgettext("Enabled")
939 value = fgettext("Disabled")
941 formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
944 elseif entry.type == "key" then
945 -- ignore key settings, since we have a special dialog for them
947 elseif entry.type == "noise_params_2d" or entry.type == "noise_params_3d" then
948 formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
949 .. core.formspec_escape(get_current_np_group_as_string(entry)) .. ","
952 formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
953 .. core.formspec_escape(get_current_value(entry)) .. ","
957 if #settings > 0 then
958 formspec = formspec:sub(1, -2) -- remove trailing comma
960 formspec = formspec .. ";" .. selected_setting .. "]" ..
961 "button[0,4.9;4,1;btn_back;".. fgettext("< Back to Settings page") .. "]" ..
962 "button[10,4.9;2,1;btn_edit;" .. fgettext("Edit") .. "]" ..
963 "button[7,4.9;3,1;btn_restore;" .. fgettext("Restore Default") .. "]" ..
964 "checkbox[0,4.3;cb_tech_settings;" .. fgettext("Show technical names") .. ";"
965 .. dump(core.settings:get_bool("main_menu_technical_settings")) .. "]"
970 local function handle_settings_buttons(this, fields, tabname, tabdata)
971 local list_enter = false
972 if fields["list_settings"] then
973 selected_setting = core.get_table_index("list_settings")
974 if core.explode_table_event(fields["list_settings"]).type == "DCL" then
975 -- Directly toggle booleans
976 local setting = settings[selected_setting]
977 if setting and setting.type == "bool" then
978 local current_value = get_current_value(setting)
979 core.settings:set_bool(setting.name, not core.is_yes(current_value))
980 core.settings:write()
990 if fields.search or fields.key_enter_field == "search_string" then
991 if search_string == fields.search_string then
992 if selected_setting > 0 then
993 -- Go to next result on enter press
994 local i = selected_setting + 1
996 while i > #settings or settings[i].type == "category" do
998 if i > #settings then
999 -- Stop infinte looping
1007 selected_setting = i
1008 core.update_formspec(this:get_formspec())
1012 -- Search for setting
1013 search_string = fields.search_string
1014 settings, selected_setting = filter_settings(full_settings, search_string)
1015 core.update_formspec(this:get_formspec())
1020 if fields["btn_edit"] or list_enter then
1021 local setting = settings[selected_setting]
1022 if setting and setting.type ~= "category" then
1023 local edit_dialog = dialog_create("change_setting", create_change_setting_formspec,
1024 handle_change_setting_buttons)
1025 edit_dialog:set_parent(this)
1032 if fields["btn_restore"] then
1033 local setting = settings[selected_setting]
1034 if setting and setting.type ~= "category" then
1035 core.settings:remove(setting.name)
1036 core.settings:write()
1037 core.update_formspec(this:get_formspec())
1042 if fields["btn_back"] then
1047 if fields["cb_tech_settings"] then
1048 core.settings:set("main_menu_technical_settings", fields["cb_tech_settings"])
1049 core.settings:write()
1050 core.update_formspec(this:get_formspec())
1057 function create_adv_settings_dlg()
1058 local dlg = dialog_create("settings_advanced",
1059 create_settings_formspec,
1060 handle_settings_buttons,
1066 -- Uncomment to generate minetest.conf.example and settings_translation_file.cpp
1067 -- For RUN_IN_PLACE the generated files may appear in the bin folder
1069 --assert(loadfile(core.get_builtin_path().."mainmenu"..DIR_DELIM.."generate_from_settingtypes.lua"))(parse_config_file(true, false))