dlg_settings_advanced.lua 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780
  1. --Minetest
  2. --Copyright (C) 2015 PilzAdam
  3. --
  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.
  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 Lesser General Public License for more details.
  13. --
  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.
  17. local FILENAME = "settingtypes.txt"
  18. local CHAR_CLASSES = {
  19. SPACE = "[%s]",
  20. VARIABLE = "[%w_%-%.]",
  21. INTEGER = "[+-]?[%d]",
  22. FLOAT = "[+-]?[%d%.]",
  23. FLAGS = "[%w_%-%.,]",
  24. }
  25. -- returns error message, or nil
  26. local function parse_setting_line(settings, line, read_all, base_level, allow_secure)
  27. -- comment
  28. local comment = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$")
  29. if comment then
  30. if settings.current_comment == "" then
  31. settings.current_comment = comment
  32. else
  33. settings.current_comment = settings.current_comment .. "\n" .. comment
  34. end
  35. return
  36. end
  37. -- clear current_comment so only comments directly above a setting are bound to it
  38. -- but keep a local reference to it for variables in the current line
  39. local current_comment = settings.current_comment
  40. settings.current_comment = ""
  41. -- empty lines
  42. if line:match("^" .. CHAR_CLASSES.SPACE .. "*$") then
  43. return
  44. end
  45. -- category
  46. local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
  47. if category then
  48. table.insert(settings, {
  49. name = category,
  50. level = stars:len() + base_level,
  51. type = "category",
  52. })
  53. return
  54. end
  55. -- settings
  56. local first_part, name, readable_name, setting_type = line:match("^"
  57. -- this first capture group matches the whole first part,
  58. -- so we can later strip it from the rest of the line
  59. .. "("
  60. .. "([" .. CHAR_CLASSES.VARIABLE .. "+)" -- variable name
  61. .. CHAR_CLASSES.SPACE .. "*"
  62. .. "%(([^%)]*)%)" -- readable name
  63. .. CHAR_CLASSES.SPACE .. "*"
  64. .. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
  65. .. CHAR_CLASSES.SPACE .. "*"
  66. .. ")")
  67. if not first_part then
  68. return "Invalid line"
  69. end
  70. if name:match("secure%.[.]*") and not allow_secure then
  71. return "Tried to add \"secure.\" setting"
  72. end
  73. if readable_name == "" then
  74. readable_name = nil
  75. end
  76. local remaining_line = line:sub(first_part:len() + 1)
  77. if setting_type == "int" then
  78. local default, min, max = remaining_line:match("^"
  79. -- first int is required, the last 2 are optional
  80. .. "(" .. CHAR_CLASSES.INTEGER .. "+)" .. CHAR_CLASSES.SPACE .. "*"
  81. .. "(" .. CHAR_CLASSES.INTEGER .. "*)" .. CHAR_CLASSES.SPACE .. "*"
  82. .. "(" .. CHAR_CLASSES.INTEGER .. "*)"
  83. .. "$")
  84. if not default or not tonumber(default) then
  85. return "Invalid integer setting"
  86. end
  87. min = tonumber(min)
  88. max = tonumber(max)
  89. table.insert(settings, {
  90. name = name,
  91. readable_name = readable_name,
  92. type = "int",
  93. default = default,
  94. min = min,
  95. max = max,
  96. comment = current_comment,
  97. })
  98. return
  99. end
  100. if setting_type == "string" or setting_type == "noise_params"
  101. or setting_type == "key" or setting_type == "v3f" then
  102. local default = remaining_line:match("^(.*)$")
  103. if not default then
  104. return "Invalid string setting"
  105. end
  106. if setting_type == "key" and not read_all then
  107. -- ignore key type if read_all is false
  108. return
  109. end
  110. table.insert(settings, {
  111. name = name,
  112. readable_name = readable_name,
  113. type = setting_type,
  114. default = default,
  115. comment = current_comment,
  116. })
  117. return
  118. end
  119. if setting_type == "bool" then
  120. if remaining_line ~= "false" and remaining_line ~= "true" then
  121. return "Invalid boolean setting"
  122. end
  123. table.insert(settings, {
  124. name = name,
  125. readable_name = readable_name,
  126. type = "bool",
  127. default = remaining_line,
  128. comment = current_comment,
  129. })
  130. return
  131. end
  132. if setting_type == "float" then
  133. local default, min, max = remaining_line:match("^"
  134. -- first float is required, the last 2 are optional
  135. .. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "*"
  136. .. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "*"
  137. .. "(" .. CHAR_CLASSES.FLOAT .. "*)"
  138. .."$")
  139. if not default or not tonumber(default) then
  140. return "Invalid float setting"
  141. end
  142. min = tonumber(min)
  143. max = tonumber(max)
  144. table.insert(settings, {
  145. name = name,
  146. readable_name = readable_name,
  147. type = "float",
  148. default = default,
  149. min = min,
  150. max = max,
  151. comment = current_comment,
  152. })
  153. return
  154. end
  155. if setting_type == "enum" then
  156. local default, values = remaining_line:match("^"
  157. -- first value (default) may be empty (i.e. is optional)
  158. .. "(" .. CHAR_CLASSES.VARIABLE .. "*)" .. CHAR_CLASSES.SPACE .. "*"
  159. .. "(" .. CHAR_CLASSES.FLAGS .. "+)"
  160. .. "$")
  161. if not default or values == "" then
  162. return "Invalid enum setting"
  163. end
  164. table.insert(settings, {
  165. name = name,
  166. readable_name = readable_name,
  167. type = "enum",
  168. default = default,
  169. values = values:split(",", true),
  170. comment = current_comment,
  171. })
  172. return
  173. end
  174. if setting_type == "path" or setting_type == "filepath" then
  175. local default = remaining_line:match("^(.*)$")
  176. if not default then
  177. return "Invalid path setting"
  178. end
  179. table.insert(settings, {
  180. name = name,
  181. readable_name = readable_name,
  182. type = setting_type,
  183. default = default,
  184. comment = current_comment,
  185. })
  186. return
  187. end
  188. if setting_type == "flags" then
  189. local default, possible = remaining_line:match("^"
  190. -- first value (default) may be empty (i.e. is optional)
  191. -- this is implemented by making the last value optional, and
  192. -- swapping them around if it turns out empty.
  193. .. "(" .. CHAR_CLASSES.FLAGS .. "+)" .. CHAR_CLASSES.SPACE .. "*"
  194. .. "(" .. CHAR_CLASSES.FLAGS .. "*)"
  195. .. "$")
  196. if not default or not possible then
  197. return "Invalid flags setting"
  198. end
  199. if possible == "" then
  200. possible = default
  201. default = ""
  202. end
  203. table.insert(settings, {
  204. name = name,
  205. readable_name = readable_name,
  206. type = "flags",
  207. default = default,
  208. possible = possible,
  209. comment = current_comment,
  210. })
  211. return
  212. end
  213. return "Invalid setting type \"" .. setting_type .. "\""
  214. end
  215. local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
  216. -- store this helper variable in the table so it's easier to pass to parse_setting_line()
  217. result.current_comment = ""
  218. local line = file:read("*line")
  219. while line do
  220. local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure)
  221. if error_msg then
  222. core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
  223. end
  224. line = file:read("*line")
  225. end
  226. result.current_comment = nil
  227. end
  228. -- read_all: whether to ignore certain setting types for GUI or not
  229. -- parse_mods: whether to parse settingtypes.txt in mods and games
  230. local function parse_config_file(read_all, parse_mods)
  231. local builtin_path = core.get_builtin_path() .. FILENAME
  232. local file = io.open(builtin_path, "r")
  233. local settings = {}
  234. if not file then
  235. core.log("error", "Can't load " .. FILENAME)
  236. return settings
  237. end
  238. parse_single_file(file, builtin_path, read_all, settings, 0, true)
  239. file:close()
  240. if parse_mods then
  241. -- Parse games
  242. local games_category_initialized = false
  243. local index = 1
  244. local game = gamemgr.get_game(index)
  245. while game do
  246. local path = game.path .. DIR_DELIM .. FILENAME
  247. local file = io.open(path, "r")
  248. if file then
  249. if not games_category_initialized then
  250. local translation = fgettext_ne("Games"), -- not used, but needed for xgettext
  251. table.insert(settings, {
  252. name = "Games",
  253. level = 0,
  254. type = "category",
  255. })
  256. games_category_initialized = true
  257. end
  258. table.insert(settings, {
  259. name = game.name,
  260. level = 1,
  261. type = "category",
  262. })
  263. parse_single_file(file, path, read_all, settings, 2, false)
  264. file:close()
  265. end
  266. index = index + 1
  267. game = gamemgr.get_game(index)
  268. end
  269. -- Parse mods
  270. local mods_category_initialized = false
  271. local mods = {}
  272. get_mods(core.get_modpath(), mods)
  273. for _, mod in ipairs(mods) do
  274. local path = mod.path .. DIR_DELIM .. FILENAME
  275. local file = io.open(path, "r")
  276. if file then
  277. if not mods_category_initialized then
  278. local translation = fgettext_ne("Mods"), -- not used, but needed for xgettext
  279. table.insert(settings, {
  280. name = "Mods",
  281. level = 0,
  282. type = "category",
  283. })
  284. mods_category_initialized = true
  285. end
  286. table.insert(settings, {
  287. name = mod.name,
  288. level = 1,
  289. type = "category",
  290. })
  291. parse_single_file(file, path, read_all, settings, 2, false)
  292. file:close()
  293. end
  294. end
  295. end
  296. return settings
  297. end
  298. local function filter_settings(settings, searchstring)
  299. if not searchstring or searchstring == "" then
  300. return settings, -1
  301. end
  302. -- Setup the keyword list
  303. local keywords = {}
  304. for word in searchstring:lower():gmatch("%S+") do
  305. table.insert(keywords, word)
  306. end
  307. local result = {}
  308. local category_stack = {}
  309. local current_level = 0
  310. local best_setting = nil
  311. for _, entry in pairs(settings) do
  312. if entry.type == "category" then
  313. -- Remove all settingless categories
  314. while #category_stack > 0 and entry.level <= current_level do
  315. table.remove(category_stack, #category_stack)
  316. if #category_stack > 0 then
  317. current_level = category_stack[#category_stack].level
  318. else
  319. current_level = 0
  320. end
  321. end
  322. -- Push category onto stack
  323. category_stack[#category_stack + 1] = entry
  324. current_level = entry.level
  325. else
  326. -- See if setting matches keywords
  327. local setting_score = 0
  328. for k = 1, #keywords do
  329. local keyword = keywords[k]
  330. if string.find(entry.name:lower(), keyword, 1, true) then
  331. setting_score = setting_score + 1
  332. end
  333. if entry.readable_name and
  334. string.find(fgettext(entry.readable_name):lower(), keyword, 1, true) then
  335. setting_score = setting_score + 1
  336. end
  337. if entry.comment and
  338. string.find(fgettext_ne(entry.comment):lower(), keyword, 1, true) then
  339. setting_score = setting_score + 1
  340. end
  341. end
  342. -- Add setting to results if match
  343. if setting_score > 0 then
  344. -- Add parent categories
  345. for _, category in pairs(category_stack) do
  346. result[#result + 1] = category
  347. end
  348. category_stack = {}
  349. -- Add setting
  350. result[#result + 1] = entry
  351. entry.score = setting_score
  352. if not best_setting or
  353. setting_score > result[best_setting].score then
  354. best_setting = #result
  355. end
  356. end
  357. end
  358. end
  359. return result, best_setting or -1
  360. end
  361. local full_settings = parse_config_file(false, true)
  362. local search_string = ""
  363. local settings = full_settings
  364. local selected_setting = 1
  365. local function get_current_value(setting)
  366. local value = core.settings:get(setting.name)
  367. if value == nil then
  368. value = setting.default
  369. end
  370. return value
  371. end
  372. local function create_change_setting_formspec(dialogdata)
  373. local setting = settings[selected_setting]
  374. local formspec = "size[10,5.2,true]" ..
  375. "button[5,4.5;2,1;btn_done;" .. fgettext("Save") .. "]" ..
  376. "button[3,4.5;2,1;btn_cancel;" .. fgettext("Cancel") .. "]" ..
  377. "tablecolumns[color;text]" ..
  378. "tableoptions[background=#00000000;highlight=#00000000;border=false]" ..
  379. "table[0,0;10,3;info;"
  380. if setting.readable_name then
  381. formspec = formspec .. "#FFFF00," .. fgettext(setting.readable_name)
  382. .. " (" .. core.formspec_escape(setting.name) .. "),"
  383. else
  384. formspec = formspec .. "#FFFF00," .. core.formspec_escape(setting.name) .. ","
  385. end
  386. formspec = formspec .. ",,"
  387. local comment_text = ""
  388. if setting.comment == "" then
  389. comment_text = fgettext_ne("(No description of setting given)")
  390. else
  391. comment_text = fgettext_ne(setting.comment)
  392. end
  393. for _, comment_line in ipairs(comment_text:split("\n", true)) do
  394. formspec = formspec .. "," .. core.formspec_escape(comment_line) .. ","
  395. end
  396. if setting.type == "flags" then
  397. formspec = formspec .. ",,"
  398. .. "," .. fgettext("Please enter a comma seperated list of flags.") .. ","
  399. .. "," .. fgettext("Possible values are: ")
  400. .. core.formspec_escape(setting.possible:gsub(",", ", ")) .. ","
  401. elseif setting.type == "noise_params" then
  402. formspec = formspec .. ",,"
  403. .. "," .. fgettext("Format:") .. ","
  404. .. "," .. fgettext("<offset>, <scale>, (<spreadX>, <spreadY>, <spreadZ>),") .. ","
  405. .. "," .. fgettext("<seed>, <octaves>, <persistence>, <lacunarity>") .. ","
  406. elseif setting.type == "v3f" then
  407. formspec = formspec .. ",,"
  408. .. "," .. fgettext_ne("Format is 3 numbers separated by commas and inside brackets.") .. ","
  409. end
  410. formspec = formspec:sub(1, -2) -- remove trailing comma
  411. formspec = formspec .. ";1]"
  412. if setting.type == "bool" then
  413. local selected_index
  414. if core.is_yes(get_current_value(setting)) then
  415. selected_index = 2
  416. else
  417. selected_index = 1
  418. end
  419. formspec = formspec .. "dropdown[0.5,3.5;3,1;dd_setting_value;"
  420. .. fgettext("Disabled") .. "," .. fgettext("Enabled") .. ";"
  421. .. selected_index .. "]"
  422. elseif setting.type == "enum" then
  423. local selected_index = 0
  424. formspec = formspec .. "dropdown[0.5,3.5;3,1;dd_setting_value;"
  425. for index, value in ipairs(setting.values) do
  426. -- translating value is not possible, since it's the value
  427. -- that we set the setting to
  428. formspec = formspec .. core.formspec_escape(value) .. ","
  429. if get_current_value(setting) == value then
  430. selected_index = index
  431. end
  432. end
  433. if #setting.values > 0 then
  434. formspec = formspec:sub(1, -2) -- remove trailing comma
  435. end
  436. formspec = formspec .. ";" .. selected_index .. "]"
  437. elseif setting.type == "path" or setting.type == "filepath" then
  438. local current_value = dialogdata.selected_path
  439. if not current_value then
  440. current_value = get_current_value(setting)
  441. end
  442. formspec = formspec .. "field[0.5,4;7.5,1;te_setting_value;;"
  443. .. core.formspec_escape(current_value) .. "]"
  444. .. "button[8,3.75;2,1;btn_browser_" .. setting.type .. ";" .. fgettext("Browse") .. "]"
  445. else
  446. -- TODO: fancy input for float, int, flags, noise_params, v3f
  447. local width = 10
  448. local text = get_current_value(setting)
  449. if dialogdata.error_message then
  450. formspec = formspec .. "tablecolumns[color;text]" ..
  451. "tableoptions[background=#00000000;highlight=#00000000;border=false]" ..
  452. "table[5,3.9;5,0.6;error_message;#FF0000,"
  453. .. core.formspec_escape(dialogdata.error_message) .. ";0]"
  454. width = 5
  455. if dialogdata.entered_text then
  456. text = dialogdata.entered_text
  457. end
  458. end
  459. formspec = formspec .. "field[0.5,4;" .. width .. ",1;te_setting_value;;"
  460. .. core.formspec_escape(text) .. "]"
  461. end
  462. return formspec
  463. end
  464. local function handle_change_setting_buttons(this, fields)
  465. if fields["btn_done"] or fields["key_enter"] then
  466. local setting = settings[selected_setting]
  467. if setting.type == "bool" then
  468. local new_value = fields["dd_setting_value"]
  469. -- Note: new_value is the actual (translated) value shown in the dropdown
  470. core.settings:set_bool(setting.name, new_value == fgettext("Enabled"))
  471. elseif setting.type == "enum" then
  472. local new_value = fields["dd_setting_value"]
  473. core.settings:set(setting.name, new_value)
  474. elseif setting.type == "int" then
  475. local new_value = tonumber(fields["te_setting_value"])
  476. if not new_value or math.floor(new_value) ~= new_value then
  477. this.data.error_message = fgettext_ne("Please enter a valid integer.")
  478. this.data.entered_text = fields["te_setting_value"]
  479. core.update_formspec(this:get_formspec())
  480. return true
  481. end
  482. if setting.min and new_value < setting.min then
  483. this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min)
  484. this.data.entered_text = fields["te_setting_value"]
  485. core.update_formspec(this:get_formspec())
  486. return true
  487. end
  488. if setting.max and new_value > setting.max then
  489. this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max)
  490. this.data.entered_text = fields["te_setting_value"]
  491. core.update_formspec(this:get_formspec())
  492. return true
  493. end
  494. core.settings:set(setting.name, new_value)
  495. elseif setting.type == "float" then
  496. local new_value = tonumber(fields["te_setting_value"])
  497. if not new_value then
  498. this.data.error_message = fgettext_ne("Please enter a valid number.")
  499. this.data.entered_text = fields["te_setting_value"]
  500. core.update_formspec(this:get_formspec())
  501. return true
  502. end
  503. core.settings:set(setting.name, new_value)
  504. elseif setting.type == "flags" then
  505. local new_value = fields["te_setting_value"]
  506. for _,value in ipairs(new_value:split(",", true)) do
  507. value = value:trim()
  508. local possible = "," .. setting.possible .. ","
  509. if not possible:find("," .. value .. ",", 0, true) then
  510. this.data.error_message = fgettext_ne("\"$1\" is not a valid flag.", value)
  511. this.data.entered_text = fields["te_setting_value"]
  512. core.update_formspec(this:get_formspec())
  513. return true
  514. end
  515. end
  516. core.settings:set(setting.name, new_value)
  517. else
  518. local new_value = fields["te_setting_value"]
  519. core.settings:set(setting.name, new_value)
  520. end
  521. core.settings:write()
  522. this:delete()
  523. return true
  524. end
  525. if fields["btn_cancel"] then
  526. this:delete()
  527. return true
  528. end
  529. if fields["btn_browser_path"] then
  530. core.show_path_select_dialog("dlg_browse_path",
  531. fgettext_ne("Select directory"), false)
  532. end
  533. if fields["btn_browser_filepath"] then
  534. core.show_path_select_dialog("dlg_browse_path",
  535. fgettext_ne("Select file"), true)
  536. end
  537. if fields["dlg_browse_path_accepted"] then
  538. this.data.selected_path = fields["dlg_browse_path_accepted"]
  539. core.update_formspec(this:get_formspec())
  540. end
  541. return false
  542. end
  543. local function create_settings_formspec(tabview, name, tabdata)
  544. local formspec = "size[12,6.5;true]" ..
  545. "tablecolumns[color;tree;text,width=32;text]" ..
  546. "tableoptions[background=#00000000;border=false]" ..
  547. "field[0.3,0.1;10.2,1;search_string;;" .. core.formspec_escape(search_string) .. "]" ..
  548. "field_close_on_enter[search_string;false]" ..
  549. "button[10.2,-0.2;2,1;search;" .. fgettext("Search") .. "]" ..
  550. "table[0,0.8;12,4.5;list_settings;"
  551. local current_level = 0
  552. for _, entry in ipairs(settings) do
  553. local name
  554. if not core.settings:get_bool("main_menu_technical_settings") and entry.readable_name then
  555. name = fgettext_ne(entry.readable_name)
  556. else
  557. name = entry.name
  558. end
  559. if entry.type == "category" then
  560. current_level = entry.level
  561. formspec = formspec .. "#FFFF00," .. current_level .. "," .. fgettext(name) .. ",,"
  562. elseif entry.type == "bool" then
  563. local value = get_current_value(entry)
  564. if core.is_yes(value) then
  565. value = fgettext("Enabled")
  566. else
  567. value = fgettext("Disabled")
  568. end
  569. formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
  570. .. value .. ","
  571. elseif entry.type == "key" then
  572. -- ignore key settings, since we have a special dialog for them
  573. else
  574. formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
  575. .. core.formspec_escape(get_current_value(entry)) .. ","
  576. end
  577. end
  578. if #settings > 0 then
  579. formspec = formspec:sub(1, -2) -- remove trailing comma
  580. end
  581. formspec = formspec .. ";" .. selected_setting .. "]" ..
  582. "button[0,6;4,1;btn_back;".. fgettext("< Back to Settings page") .. "]" ..
  583. "button[10,6;2,1;btn_edit;" .. fgettext("Edit") .. "]" ..
  584. "button[7,6;3,1;btn_restore;" .. fgettext("Restore Default") .. "]" ..
  585. "checkbox[0,5.3;cb_tech_settings;" .. fgettext("Show technical names") .. ";"
  586. .. dump(core.settings:get_bool("main_menu_technical_settings")) .. "]"
  587. return formspec
  588. end
  589. local function handle_settings_buttons(this, fields, tabname, tabdata)
  590. local list_enter = false
  591. if fields["list_settings"] then
  592. selected_setting = core.get_table_index("list_settings")
  593. if core.explode_table_event(fields["list_settings"]).type == "DCL" then
  594. -- Directly toggle booleans
  595. local setting = settings[selected_setting]
  596. if setting and setting.type == "bool" then
  597. local current_value = get_current_value(setting)
  598. core.settings:set_bool(setting.name, not core.is_yes(current_value))
  599. core.settings:write()
  600. return true
  601. else
  602. list_enter = true
  603. end
  604. else
  605. return true
  606. end
  607. end
  608. if fields.search or fields.key_enter_field == "search_string" then
  609. if search_string == fields.search_string then
  610. if selected_setting > 0 then
  611. -- Go to next result on enter press
  612. local i = selected_setting + 1
  613. local looped = false
  614. while i > #settings or settings[i].type == "category" do
  615. i = i + 1
  616. if i > #settings then
  617. -- Stop infinte looping
  618. if looped then
  619. return false
  620. end
  621. i = 1
  622. looped = true
  623. end
  624. end
  625. selected_setting = i
  626. core.update_formspec(this:get_formspec())
  627. return true
  628. end
  629. else
  630. -- Search for setting
  631. search_string = fields.search_string
  632. settings, selected_setting = filter_settings(full_settings, search_string)
  633. core.update_formspec(this:get_formspec())
  634. end
  635. return true
  636. end
  637. if fields["btn_edit"] or list_enter then
  638. local setting = settings[selected_setting]
  639. if setting and setting.type ~= "category" then
  640. local edit_dialog = dialog_create("change_setting", create_change_setting_formspec,
  641. handle_change_setting_buttons)
  642. edit_dialog:set_parent(this)
  643. this:hide()
  644. edit_dialog:show()
  645. end
  646. return true
  647. end
  648. if fields["btn_restore"] then
  649. local setting = settings[selected_setting]
  650. if setting and setting.type ~= "category" then
  651. core.settings:set(setting.name, setting.default)
  652. core.settings:write()
  653. core.update_formspec(this:get_formspec())
  654. end
  655. return true
  656. end
  657. if fields["btn_back"] then
  658. this:delete()
  659. return true
  660. end
  661. if fields["cb_tech_settings"] then
  662. core.settings:set("main_menu_technical_settings", fields["cb_tech_settings"])
  663. core.settings:write()
  664. core.update_formspec(this:get_formspec())
  665. return true
  666. end
  667. return false
  668. end
  669. function create_adv_settings_dlg()
  670. local dlg = dialog_create("settings_advanced",
  671. create_settings_formspec,
  672. handle_settings_buttons,
  673. nil)
  674. return dlg
  675. end
  676. -- Uncomment to generate minetest.conf.example and settings_translation_file.cpp
  677. -- For RUN_IN_PLACE the generated files may appear in the bin folder
  678. --assert(loadfile(core.get_builtin_path().."mainmenu"..DIR_DELIM.."generate_from_settingtypes.lua"))(parse_config_file(true, false))