tab_settings.lua 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  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" 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 = "path",
  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() .. DIR_DELIM .. 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 settings = parse_config_file(false, true)
  299. local selected_setting = 1
  300. local function get_current_value(setting)
  301. local value = core.setting_get(setting.name)
  302. if value == nil then
  303. value = setting.default
  304. end
  305. return value
  306. end
  307. local function create_change_setting_formspec(dialogdata)
  308. local setting = settings[selected_setting]
  309. local formspec = "size[10,5.2,true]" ..
  310. "button[5,4.5;2,1;btn_done;" .. fgettext("Save") .. "]" ..
  311. "button[3,4.5;2,1;btn_cancel;" .. fgettext("Cancel") .. "]" ..
  312. "tablecolumns[color;text]" ..
  313. "tableoptions[background=#00000000;highlight=#00000000;border=false]" ..
  314. "table[0,0;10,3;info;"
  315. if setting.readable_name then
  316. formspec = formspec .. "#FFFF00," .. fgettext(setting.readable_name)
  317. .. " (" .. core.formspec_escape(setting.name) .. "),"
  318. else
  319. formspec = formspec .. "#FFFF00," .. core.formspec_escape(setting.name) .. ","
  320. end
  321. formspec = formspec .. ",,"
  322. local comment_text = ""
  323. if setting.comment == "" then
  324. comment_text = fgettext_ne("(No description of setting given)")
  325. else
  326. comment_text = fgettext_ne(setting.comment)
  327. end
  328. for _, comment_line in ipairs(comment_text:split("\n", true)) do
  329. formspec = formspec .. "," .. core.formspec_escape(comment_line) .. ","
  330. end
  331. if setting.type == "flags" then
  332. formspec = formspec .. ",,"
  333. .. "," .. fgettext("Please enter a comma seperated list of flags.") .. ","
  334. .. "," .. fgettext("Possible values are: ")
  335. .. core.formspec_escape(setting.possible:gsub(",", ", ")) .. ","
  336. elseif setting.type == "noise_params" then
  337. formspec = formspec .. ",,"
  338. .. "," .. fgettext("Format: <offset>, <scale>, (<spreadX>, <spreadY>, <spreadZ>), <seed>, <octaves>, <persistence>") .. ","
  339. .. "," .. fgettext("Optionally the lacunarity can be appended with a leading comma.") .. ","
  340. elseif setting.type == "v3f" then
  341. formspec = formspec .. ",,"
  342. .. "," .. fgettext_ne("Format is 3 numbers separated by commas and inside brackets.") .. ","
  343. end
  344. formspec = formspec:sub(1, -2) -- remove trailing comma
  345. formspec = formspec .. ";1]"
  346. if setting.type == "bool" then
  347. local selected_index
  348. if core.is_yes(get_current_value(setting)) then
  349. selected_index = 2
  350. else
  351. selected_index = 1
  352. end
  353. formspec = formspec .. "dropdown[0.5,3.5;3,1;dd_setting_value;"
  354. .. fgettext("Disabled") .. "," .. fgettext("Enabled") .. ";"
  355. .. selected_index .. "]"
  356. elseif setting.type == "enum" then
  357. local selected_index = 0
  358. formspec = formspec .. "dropdown[0.5,3.5;3,1;dd_setting_value;"
  359. for index, value in ipairs(setting.values) do
  360. -- translating value is not possible, since it's the value
  361. -- that we set the setting to
  362. formspec = formspec .. core.formspec_escape(value) .. ","
  363. if get_current_value(setting) == value then
  364. selected_index = index
  365. end
  366. end
  367. if #setting.values > 0 then
  368. formspec = formspec:sub(1, -2) -- remove trailing comma
  369. end
  370. formspec = formspec .. ";" .. selected_index .. "]"
  371. elseif setting.type == "path" then
  372. local current_value = dialogdata.selected_path
  373. if not current_value then
  374. current_value = get_current_value(setting)
  375. end
  376. formspec = formspec .. "field[0.5,4;7.5,1;te_setting_value;;"
  377. .. core.formspec_escape(current_value) .. "]"
  378. .. "button[8,3.75;2,1;btn_browser_path;" .. fgettext("Browse") .. "]"
  379. else
  380. -- TODO: fancy input for float, int, flags, noise_params, v3f
  381. local width = 10
  382. local text = get_current_value(setting)
  383. if dialogdata.error_message then
  384. formspec = formspec .. "tablecolumns[color;text]" ..
  385. "tableoptions[background=#00000000;highlight=#00000000;border=false]" ..
  386. "table[5,3.9;5,0.6;error_message;#FF0000,"
  387. .. core.formspec_escape(dialogdata.error_message) .. ";0]"
  388. width = 5
  389. if dialogdata.entered_text then
  390. text = dialogdata.entered_text
  391. end
  392. end
  393. formspec = formspec .. "field[0.5,4;" .. width .. ",1;te_setting_value;;"
  394. .. core.formspec_escape(text) .. "]"
  395. end
  396. return formspec
  397. end
  398. local function handle_change_setting_buttons(this, fields)
  399. if fields["btn_done"] or fields["key_enter"] then
  400. local setting = settings[selected_setting]
  401. if setting.type == "bool" then
  402. local new_value = fields["dd_setting_value"]
  403. -- Note: new_value is the actual (translated) value shown in the dropdown
  404. core.setting_setbool(setting.name, new_value == fgettext("Enabled"))
  405. elseif setting.type == "enum" then
  406. local new_value = fields["dd_setting_value"]
  407. core.setting_set(setting.name, new_value)
  408. elseif setting.type == "int" then
  409. local new_value = tonumber(fields["te_setting_value"])
  410. if not new_value or math.floor(new_value) ~= new_value then
  411. this.data.error_message = fgettext_ne("Please enter a valid integer.")
  412. this.data.entered_text = fields["te_setting_value"]
  413. core.update_formspec(this:get_formspec())
  414. return true
  415. end
  416. if setting.min and new_value < setting.min then
  417. this.data.error_message = fgettext_ne("The value must be greater than $1.", setting.min)
  418. this.data.entered_text = fields["te_setting_value"]
  419. core.update_formspec(this:get_formspec())
  420. return true
  421. end
  422. if setting.max and new_value > setting.max then
  423. this.data.error_message = fgettext_ne("The value must be lower than $1.", setting.max)
  424. this.data.entered_text = fields["te_setting_value"]
  425. core.update_formspec(this:get_formspec())
  426. return true
  427. end
  428. core.setting_set(setting.name, new_value)
  429. elseif setting.type == "float" then
  430. local new_value = tonumber(fields["te_setting_value"])
  431. if not new_value then
  432. this.data.error_message = fgettext_ne("Please enter a valid number.")
  433. this.data.entered_text = fields["te_setting_value"]
  434. core.update_formspec(this:get_formspec())
  435. return true
  436. end
  437. core.setting_set(setting.name, new_value)
  438. elseif setting.type == "flags" then
  439. local new_value = fields["te_setting_value"]
  440. for _,value in ipairs(new_value:split(",", true)) do
  441. value = value:trim()
  442. local possible = "," .. setting.possible .. ","
  443. if not possible:find("," .. value .. ",", 0, true) then
  444. this.data.error_message = fgettext_ne("\"$1\" is not a valid flag.", value)
  445. this.data.entered_text = fields["te_setting_value"]
  446. core.update_formspec(this:get_formspec())
  447. return true
  448. end
  449. end
  450. core.setting_set(setting.name, new_value)
  451. else
  452. local new_value = fields["te_setting_value"]
  453. core.setting_set(setting.name, new_value)
  454. end
  455. core.setting_save()
  456. this:delete()
  457. return true
  458. end
  459. if fields["btn_cancel"] then
  460. this:delete()
  461. return true
  462. end
  463. if fields["btn_browser_path"] then
  464. core.show_file_open_dialog("dlg_browse_path", fgettext_ne("Select path"))
  465. end
  466. if fields["dlg_browse_path_accepted"] then
  467. this.data.selected_path = fields["dlg_browse_path_accepted"]
  468. core.update_formspec(this:get_formspec())
  469. end
  470. return false
  471. end
  472. local function create_settings_formspec(tabview, name, tabdata)
  473. local formspec = "tablecolumns[color;tree;text;text]" ..
  474. "tableoptions[background=#00000000;border=false]" ..
  475. "table[0,0;12,4.5;list_settings;"
  476. local current_level = 0
  477. for _, entry in ipairs(settings) do
  478. local name
  479. if not core.setting_getbool("main_menu_technical_settings") and entry.readable_name then
  480. name = fgettext_ne(entry.readable_name)
  481. else
  482. name = entry.name
  483. end
  484. if entry.type == "category" then
  485. current_level = entry.level
  486. formspec = formspec .. "#FFFF00," .. current_level .. "," .. fgettext(name) .. ",,"
  487. elseif entry.type == "bool" then
  488. local value = get_current_value(entry)
  489. if core.is_yes(value) then
  490. value = fgettext("Enabled")
  491. else
  492. value = fgettext("Disabled")
  493. end
  494. formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
  495. .. value .. ","
  496. elseif entry.type == "key" then
  497. -- ignore key settings, since we have a special dialog for them
  498. else
  499. formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
  500. .. core.formspec_escape(get_current_value(entry)) .. ","
  501. end
  502. end
  503. if #settings > 0 then
  504. formspec = formspec:sub(1, -2) -- remove trailing comma
  505. end
  506. formspec = formspec .. ";" .. selected_setting .. "]" ..
  507. "button[4,4.5;3,1;btn_change_keys;".. fgettext("Change keys") .. "]" ..
  508. "button[10,4.5;2,1;btn_edit;" .. fgettext("Edit") .. "]" ..
  509. "button[7,4.5;3,1;btn_restore;" .. fgettext("Restore Default") .. "]" ..
  510. "checkbox[0,4.5;cb_tech_settings;" .. fgettext("Show technical names") .. ";"
  511. .. dump(core.setting_getbool("main_menu_technical_settings")) .. "]"
  512. return formspec
  513. end
  514. local function handle_settings_buttons(this, fields, tabname, tabdata)
  515. local list_enter = false
  516. if fields["list_settings"] then
  517. selected_setting = core.get_table_index("list_settings")
  518. if core.explode_table_event(fields["list_settings"]).type == "DCL" then
  519. -- Directly toggle booleans
  520. local setting = settings[selected_setting]
  521. if setting.type == "bool" then
  522. local current_value = get_current_value(setting)
  523. core.setting_setbool(setting.name, not core.is_yes(current_value))
  524. core.setting_save()
  525. return true
  526. else
  527. list_enter = true
  528. end
  529. else
  530. return true
  531. end
  532. end
  533. if fields["btn_edit"] or list_enter then
  534. local setting = settings[selected_setting]
  535. if setting.type ~= "category" then
  536. local edit_dialog = dialog_create("change_setting", create_change_setting_formspec,
  537. handle_change_setting_buttons)
  538. edit_dialog:set_parent(this)
  539. this:hide()
  540. edit_dialog:show()
  541. end
  542. return true
  543. end
  544. if fields["btn_restore"] then
  545. local setting = settings[selected_setting]
  546. if setting.type ~= "category" then
  547. core.setting_set(setting.name, setting.default)
  548. core.setting_save()
  549. core.update_formspec(this:get_formspec())
  550. end
  551. return true
  552. end
  553. if fields["btn_change_keys"] then
  554. core.show_keys_menu()
  555. return true
  556. end
  557. if fields["cb_tech_settings"] then
  558. core.setting_set("main_menu_technical_settings", fields["cb_tech_settings"])
  559. core.setting_save()
  560. core.update_formspec(this:get_formspec())
  561. return true
  562. end
  563. return false
  564. end
  565. tab_settings = {
  566. name = "settings",
  567. caption = fgettext("Settings"),
  568. cbf_formspec = create_settings_formspec,
  569. cbf_button_handler = handle_settings_buttons,
  570. }
  571. local function create_minetest_conf_example()
  572. local result = "# This file contains a list of all available settings and their default value for minetest.conf\n" ..
  573. "\n" ..
  574. "# By default, all the settings are commented and not functional.\n" ..
  575. "# Uncomment settings by removing the preceding #.\n" ..
  576. "\n" ..
  577. "# minetest.conf is read by default from:\n" ..
  578. "# ../minetest.conf\n" ..
  579. "# ../../minetest.conf\n" ..
  580. "# Any other path can be chosen by passing the path as a parameter\n" ..
  581. "# to the program, eg. \"minetest.exe --config ../minetest.conf.example\".\n" ..
  582. "\n" ..
  583. "# Further documentation:\n" ..
  584. "# http://wiki.minetest.net/\n" ..
  585. "\n"
  586. local settings = parse_config_file(true, false)
  587. for _, entry in ipairs(settings) do
  588. if entry.type == "category" then
  589. if entry.level == 0 then
  590. result = result .. "#\n# " .. entry.name .. "\n#\n\n"
  591. else
  592. for i = 1, entry.level do
  593. result = result .. "#"
  594. end
  595. result = result .. "# " .. entry.name .. "\n\n"
  596. end
  597. else
  598. if entry.comment ~= "" then
  599. for _, comment_line in ipairs(entry.comment:split("\n", true)) do
  600. result = result .."# " .. comment_line .. "\n"
  601. end
  602. end
  603. result = result .. "# type: " .. entry.type
  604. if entry.min then
  605. result = result .. " min: " .. entry.min
  606. end
  607. if entry.max then
  608. result = result .. " max: " .. entry.max
  609. end
  610. if entry.values then
  611. result = result .. " values: " .. table.concat(entry.values, ", ")
  612. end
  613. if entry.possible then
  614. result = result .. " possible values: " .. entry.possible:gsub(",", ", ")
  615. end
  616. result = result .. "\n"
  617. result = result .. "# " .. entry.name .. " = ".. entry.default .. "\n\n"
  618. end
  619. end
  620. return result
  621. end
  622. local function create_translation_file()
  623. local result = "// This file is automatically generated\n" ..
  624. "// It conatins a bunch of fake gettext calls, to tell xgettext about the strings in config files\n" ..
  625. "// To update it, refer to the bottom of builtin/mainmenu/tab_settings.lua\n\n" ..
  626. "fake_function() {\n"
  627. local settings = parse_config_file(true, false)
  628. for _, entry in ipairs(settings) do
  629. if entry.type == "category" then
  630. local name_escaped = entry.name:gsub("\"", "\\\"")
  631. result = result .. "\tgettext(\"" .. name_escaped .. "\");\n"
  632. else
  633. if entry.readable_name then
  634. local readable_name_escaped = entry.readable_name:gsub("\"", "\\\"")
  635. result = result .. "\tgettext(\"" .. readable_name_escaped .. "\");\n"
  636. end
  637. if entry.comment ~= "" then
  638. local comment_escaped = entry.comment:gsub("\n", "\\n")
  639. comment_escaped = comment_escaped:gsub("\"", "\\\"")
  640. result = result .. "\tgettext(\"" .. comment_escaped .. "\");\n"
  641. end
  642. end
  643. end
  644. result = result .. "}\n"
  645. return result
  646. end
  647. if false then
  648. local file = io.open("minetest.conf.example", "w")
  649. if file then
  650. file:write(create_minetest_conf_example())
  651. file:close()
  652. end
  653. end
  654. if false then
  655. local file = io.open("src/settings_translation_file.cpp", "w")
  656. if file then
  657. file:write(create_translation_file())
  658. file:close()
  659. end
  660. end